lib/tldr/src/formats/elf/got.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const root = @import("../../root.zig");
  3 const addressing = @import("address/root.zig");
  4 const format = @import("format.zig");
  5 const layout = @import("layout/root.zig");
  6 const output_section = @import("section.zig");
  7 const parser = @import("parser.zig");
  8 const relocation = @import("relocation/root.zig");
  9 const sections = @import("sections.zig");
 10 
 11 const Allocator = std.mem.Allocator;
 12 const model = root.model;
 13 const ObjectFile = parser.ObjectFile;
 14 const GlobalSymbol = layout.GlobalSymbol;
 15 const GotEntry = layout.GotEntry;
 16 const GotLayout = layout.GotLayout;
 17 const ObjectLayout = layout.ObjectLayout;
 18 const OutputSection = layout.OutputSection;
 19 const OutputSectionKind = output_section.OutputSectionKind;
 20 const SymbolRef = layout.SymbolRef;
 21 const output_section_count = output_section.output_section_count;
 22 const foldedSection = sections.foldedSection;
 23 const sectionDiscarded = sections.sectionDiscarded;
 24 const sectionRelocationsAffectOutput = relocation.sectionRelocationsAffectOutput;
 25 const symbolAddress = addressing.symbolAddress;
 26 const symbolSectionDiscarded = sections.symbolSectionDiscarded;
 27 const writeU64 = format.writeU64;
 28 
 29 pub fn collectEntries(
 30     allocator: Allocator,
 31     objects: []const ObjectFile,
 32     output_sections: *[output_section_count]OutputSection,
 33     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
 34 ) model.Error!GotLayout {
 35     var entries: std.ArrayListUnmanaged(GotEntry) = .empty;
 36     errdefer entries.deinit(allocator);
 37 
 38     for (objects, 0..) |object, object_index| {
 39         if (object.relocations.len == 0) continue;
 40         for (object.sections, 0..) |_, section_index| {
 41             if (!sectionRelocationsAffectOutput(object, section_index)) continue;
 42             if (sectionDiscarded(object, section_index)) continue;
 43             if (foldedSection(object, section_index) != null) continue;
 44             for (object.relocationsForSection(section_index)) |relocation_record| {
 45                 if (relocation_record.relocationType() != @backingInt(std.elf.R_X86_64.GOTPCREL)) continue;
 46                 const ref = try canonicalSymbolRef(objects, globals, object_index, @intCast(relocation_record.symbolIndex()));
 47                 if ((GotLayout{ .entries = entries.items }).entryOffset(ref) != null) continue;
 48                 const offset = try checkedOffset(entries.items.len);
 49                 try entries.append(allocator, .{ .ref = ref, .offset = offset });
 50             }
 51         }
 52     }
 53 
 54     if (entries.items.len != 0) {
 55         const size = try checkedSize(entries.items.len);
 56         var output = &output_sections[@backingInt(OutputSectionKind.got)];
 57         output.file_size = size;
 58         output.memory_size = size;
 59         output.alignment = @max(output.alignment, 8);
 60     }
 61 
 62     return .{ .entries = try entries.toOwnedSlice(allocator) };
 63 }
 64 
 65 pub fn collectEntriesWithIfunc(
 66     allocator: Allocator,
 67     objects: []const ObjectFile,
 68     output_sections: *[output_section_count]OutputSection,
 69     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
 70     ifunc_collector: anytype,
 71 ) model.Error!GotLayout {
 72     var entries: std.ArrayListUnmanaged(GotEntry) = .empty;
 73     errdefer entries.deinit(allocator);
 74 
 75     for (objects, 0..) |object, object_index| {
 76         if (object.relocations.len == 0) continue;
 77         for (object.sections, 0..) |_, section_index| {
 78             if (sectionDiscarded(object, section_index)) continue;
 79             if (foldedSection(object, section_index) != null) continue;
 80             const affects_output = sectionRelocationsAffectOutput(object, section_index);
 81             if (!affects_output) continue;
 82             for (object.relocationsForSection(section_index)) |relocation_record| {
 83                 try ifunc_collector.observe(allocator, object_index, relocation_record);
 84                 if (relocation_record.relocationType() != @backingInt(std.elf.R_X86_64.GOTPCREL)) continue;
 85                 const ref = try canonicalSymbolRef(objects, globals, object_index, @intCast(relocation_record.symbolIndex()));
 86                 if ((GotLayout{ .entries = entries.items }).entryOffset(ref) != null) continue;
 87                 const offset = try checkedOffset(entries.items.len);
 88                 try entries.append(allocator, .{ .ref = ref, .offset = offset });
 89             }
 90         }
 91     }
 92 
 93     if (entries.items.len != 0) {
 94         const size = try checkedSize(entries.items.len);
 95         var output = &output_sections[@backingInt(OutputSectionKind.got)];
 96         output.file_size = size;
 97         output.memory_size = size;
 98         output.alignment = @max(output.alignment, 8);
 99     }
100 
101     return .{ .entries = try entries.toOwnedSlice(allocator) };
102 }
103 
104 pub fn canonicalSymbolRef(
105     objects: []const ObjectFile,
106     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
107     object_index: usize,
108     symbol_index: usize,
109 ) model.Error!SymbolRef {
110     if (object_index >= objects.len) return error.UndefinedSymbol;
111     const object = objects[object_index];
112     if (symbol_index >= object.symbols.len) return error.UndefinedSymbol;
113     const symbol = object.symbols[symbol_index];
114     const current_ref = SymbolRef{ .object_index = object_index, .symbol_index = symbol_index };
115     if (symbol.isUndefined()) {
116         const global = globals.get(symbol.name) orelse return current_ref;
117         return global.ref;
118     }
119     if ((symbolSectionDiscarded(object, symbol) or symbol.isCommon()) and symbol.name.len != 0) {
120         const global = globals.get(symbol.name) orelse return current_ref;
121         return global.ref;
122     }
123     return current_ref;
124 }
125 
126 pub fn writePayload(
127     image: []u8,
128     objects: []const ObjectFile,
129     layouts: []const ObjectLayout,
130     output_sections: []const OutputSection,
131     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
132     symbol_addresses: *addressing.Cache,
133     got_layout: GotLayout,
134 ) model.Error!void {
135     if (got_layout.entries.len == 0) return;
136     const got = output_sections[@backingInt(OutputSectionKind.got)];
137     for (got_layout.entries) |entry| {
138         const address = try symbolAddress(.serial, objects, layouts, output_sections, globals, symbol_addresses, entry.ref.object_index, entry.ref.symbol_index);
139         writeU64(image, @intCast(got.file_offset + entry.offset), address);
140     }
141 }
142 
143 fn checkedOffset(entry_count: usize) model.Error!u64 {
144     if (entry_count > std.math.maxInt(u64) / 8) return error.InvalidRange;
145     return @as(u64, @intCast(entry_count)) * 8;
146 }
147 
148 fn checkedSize(entry_count: usize) model.Error!u64 {
149     return checkedOffset(entry_count);
150 }