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 }