1pub const DynamicSection = struct {
2 soname: ?u32 = null,
3 needed: std.ArrayList(u32) = .empty,
4 rpath: u32 = 0,
5
6 pub fn deinit(dt: *DynamicSection, allocator: Allocator) void {
7 dt.needed.deinit(allocator);
8 }
9
10 pub fn addNeeded(dt: *DynamicSection, shared: *SharedObject, elf_file: *Elf) !void {
11 const comp = elf_file.base.comp;
12 const gpa = comp.gpa;
13 const off = try elf_file.insertDynString(shared.soname());
14 try dt.needed.append(gpa, off);
15 }
16
17 pub fn setRpath(dt: *DynamicSection, rpath_list: []const []const u8, elf_file: *Elf) !void {
18 if (rpath_list.len == 0) return;
19 const comp = elf_file.base.comp;
20 const gpa = comp.gpa;
21 var rpath = std.array_list.Managed(u8).init(gpa);
22 defer rpath.deinit();
23 for (rpath_list, 0..) |path, i| {
24 if (i > 0) try rpath.append(':');
25 try rpath.appendSlice(path);
26 }
27 dt.rpath = try elf_file.insertDynString(rpath.items);
28 }
29
30 pub fn setSoname(dt: *DynamicSection, soname: []const u8, elf_file: *Elf) !void {
31 dt.soname = try elf_file.insertDynString(soname);
32 }
33
34 fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 {
35 _ = dt;
36 var flags: u64 = 0;
37 if (elf_file.z_now) {
38 flags |= elf.DF_BIND_NOW;
39 }
40 for (elf_file.got.entries.items) |entry| switch (entry.tag) {
41 .gottp => {
42 flags |= elf.DF_STATIC_TLS;
43 break;
44 },
45 else => {},
46 };
47 if (elf_file.has_text_reloc) {
48 flags |= elf.DF_TEXTREL;
49 }
50 return if (flags > 0) flags else null;
51 }
52
53 fn getFlags1(dt: DynamicSection, elf_file: *Elf) ?u64 {
54 const comp = elf_file.base.comp;
55 _ = dt;
56 var flags_1: u64 = 0;
57 if (elf_file.z_now) {
58 flags_1 |= elf.DF_1_NOW;
59 }
60 if (elf_file.base.isExe() and comp.config.pie) {
61 flags_1 |= elf.DF_1_PIE;
62 }
63 // if (elf_file.z_nodlopen) {
64 // flags_1 |= elf.DF_1_NOOPEN;
65 // }
66 return if (flags_1 > 0) flags_1 else null;
67 }
68
69 pub fn size(dt: DynamicSection, elf_file: *Elf) usize {
70 var nentries: usize = 0;
71 nentries += dt.needed.items.len; // NEEDED
72 if (dt.soname != null) nentries += 1; // SONAME
73 if (dt.rpath > 0) nentries += 1; // RUNPATH
74 if (elf_file.sectionByName(".init") != null) nentries += 1; // INIT
75 if (elf_file.sectionByName(".fini") != null) nentries += 1; // FINI
76 if (elf_file.sectionByName(".preinit_array") != null) nentries += 2; // PREINIT_ARRAY
77 if (elf_file.sectionByName(".init_array") != null) nentries += 2; // INIT_ARRAY
78 if (elf_file.sectionByName(".fini_array") != null) nentries += 2; // FINI_ARRAY
79 if (elf_file.section_indexes.rela_dyn != null) nentries += 3; // RELA
80 if (elf_file.section_indexes.rela_plt != null) nentries += 3; // JMPREL
81 if (elf_file.section_indexes.got_plt != null) nentries += 1; // PLTGOT
82 nentries += 1; // HASH
83 if (elf_file.section_indexes.gnu_hash != null) nentries += 1; // GNU_HASH
84 if (elf_file.has_text_reloc) nentries += 1; // TEXTREL
85 nentries += 1; // SYMTAB
86 nentries += 1; // SYMENT
87 nentries += 1; // STRTAB
88 nentries += 1; // STRSZ
89 if (elf_file.section_indexes.versym != null) nentries += 1; // VERSYM
90 if (elf_file.section_indexes.verneed != null) nentries += 2; // VERNEED
91 if (dt.getFlags(elf_file) != null) nentries += 1; // FLAGS
92 if (dt.getFlags1(elf_file) != null) nentries += 1; // FLAGS_1
93 if (!elf_file.isEffectivelyDynLib()) nentries += 1; // DEBUG
94 nentries += 1; // NULL
95 return nentries * @sizeOf(elf.Elf64_Dyn);
96 }
97
98 pub fn write(dt: DynamicSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
99 const shdrs = elf_file.sections.items(.shdr);
100
101 // NEEDED
102 for (dt.needed.items) |off| {
103 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_NEEDED, .d_val = off }), .little);
104 }
105
106 if (dt.soname) |off| {
107 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_SONAME, .d_val = off }), .little);
108 }
109
110 // RUNPATH
111 // TODO add option in Options to revert to old RPATH tag
112 if (dt.rpath > 0) {
113 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_RUNPATH, .d_val = dt.rpath }), .little);
114 }
115
116 // INIT
117 if (elf_file.sectionByName(".init")) |shndx| {
118 const addr = shdrs[shndx].sh_addr;
119 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_INIT, .d_val = addr }), .little);
120 }
121
122 // FINI
123 if (elf_file.sectionByName(".fini")) |shndx| {
124 const addr = shdrs[shndx].sh_addr;
125 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_FINI, .d_val = addr }), .little);
126 }
127
128 // PREINIT_ARRAY
129 if (elf_file.sectionByName(".preinit_array")) |shndx| {
130 const shdr = shdrs[shndx];
131 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_PREINIT_ARRAY, .d_val = shdr.sh_addr }), .little);
132 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_PREINIT_ARRAYSZ, .d_val = shdr.sh_size }), .little);
133 }
134
135 // INIT_ARRAY
136 if (elf_file.sectionByName(".init_array")) |shndx| {
137 const shdr = shdrs[shndx];
138 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_INIT_ARRAY, .d_val = shdr.sh_addr }), .little);
139 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_INIT_ARRAYSZ, .d_val = shdr.sh_size }), .little);
140 }
141
142 // FINI_ARRAY
143 if (elf_file.sectionByName(".fini_array")) |shndx| {
144 const shdr = shdrs[shndx];
145 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_FINI_ARRAY, .d_val = shdr.sh_addr }), .little);
146 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_FINI_ARRAYSZ, .d_val = shdr.sh_size }), .little);
147 }
148
149 // RELA
150 if (elf_file.section_indexes.rela_dyn) |shndx| {
151 const shdr = shdrs[shndx];
152 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_RELA, .d_val = shdr.sh_addr }), .little);
153 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_RELASZ, .d_val = shdr.sh_size }), .little);
154 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_RELAENT, .d_val = shdr.sh_entsize }), .little);
155 }
156
157 // JMPREL
158 if (elf_file.section_indexes.rela_plt) |shndx| {
159 const shdr = shdrs[shndx];
160 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_JMPREL, .d_val = shdr.sh_addr }), .little);
161 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_PLTRELSZ, .d_val = shdr.sh_size }), .little);
162 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_PLTREL, .d_val = elf.DT_RELA }), .little);
163 }
164
165 // PLTGOT
166 if (elf_file.section_indexes.got_plt) |shndx| {
167 const addr = shdrs[shndx].sh_addr;
168 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_PLTGOT, .d_val = addr }), .little);
169 }
170
171 {
172 assert(elf_file.section_indexes.hash != null);
173 const addr = shdrs[elf_file.section_indexes.hash.?].sh_addr;
174 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_HASH, .d_val = addr }), .little);
175 }
176
177 if (elf_file.section_indexes.gnu_hash) |shndx| {
178 const addr = shdrs[shndx].sh_addr;
179 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_GNU_HASH, .d_val = addr }), .little);
180 }
181
182 // TEXTREL
183 if (elf_file.has_text_reloc) {
184 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_TEXTREL, .d_val = 0 }), .little);
185 }
186
187 // SYMTAB + SYMENT
188 {
189 assert(elf_file.section_indexes.dynsymtab != null);
190 const shdr = shdrs[elf_file.section_indexes.dynsymtab.?];
191 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_SYMTAB, .d_val = shdr.sh_addr }), .little);
192 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_SYMENT, .d_val = shdr.sh_entsize }), .little);
193 }
194
195 // STRTAB + STRSZ
196 {
197 assert(elf_file.section_indexes.dynstrtab != null);
198 const shdr = shdrs[elf_file.section_indexes.dynstrtab.?];
199 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_STRTAB, .d_val = shdr.sh_addr }), .little);
200 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_STRSZ, .d_val = shdr.sh_size }), .little);
201 }
202
203 // VERSYM
204 if (elf_file.section_indexes.versym) |shndx| {
205 const addr = shdrs[shndx].sh_addr;
206 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_VERSYM, .d_val = addr }), .little);
207 }
208
209 // VERNEED + VERNEEDNUM
210 if (elf_file.section_indexes.verneed) |shndx| {
211 const addr = shdrs[shndx].sh_addr;
212 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_VERNEED, .d_val = addr }), .little);
213 try writer.writeStruct(@as(elf.Elf64_Dyn, .{
214 .d_tag = elf.DT_VERNEEDNUM,
215 .d_val = elf_file.verneed.verneed.items.len,
216 }), .little);
217 }
218
219 // FLAGS
220 if (dt.getFlags(elf_file)) |flags| {
221 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_FLAGS, .d_val = flags }), .little);
222 }
223 // FLAGS_1
224 if (dt.getFlags1(elf_file)) |flags_1| {
225 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_FLAGS_1, .d_val = flags_1 }), .little);
226 }
227
228 // DEBUG
229 if (!elf_file.isEffectivelyDynLib()) try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_DEBUG, .d_val = 0 }), .little);
230
231 // NULL
232 try writer.writeStruct(@as(elf.Elf64_Dyn, .{ .d_tag = elf.DT_NULL, .d_val = 0 }), .little);
233 }
234};
235
236pub const GotSection = struct {
237 entries: std.ArrayList(Entry) = .empty,
238 output_symtab_ctx: Elf.SymtabCtx = .{},
239 tlsld_index: ?u32 = null,
240 flags: Flags = .{},
241
242 pub const Index = u32;
243
244 const Flags = packed struct {
245 needs_rela: bool = false,
246 needs_tlsld: bool = false,
247 };
248
249 const Tag = enum {
250 got,
251 tlsld,
252 tlsgd,
253 gottp,
254 tlsdesc,
255 };
256
257 const Entry = struct {
258 tag: Tag,
259 ref: Elf.Ref,
260 cell_index: Index,
261
262 /// Returns how many indexes in the GOT this entry uses.
263 pub inline fn len(entry: Entry) usize {
264 return switch (entry.tag) {
265 .got, .gottp => 1,
266 .tlsld, .tlsgd, .tlsdesc => 2,
267 };
268 }
269
270 pub fn address(entry: Entry, elf_file: *Elf) i64 {
271 const ptr_bytes = elf_file.archPtrWidthBytes();
272 const shdr = &elf_file.sections.items(.shdr)[elf_file.section_indexes.got.?];
273 return @as(i64, @intCast(shdr.sh_addr)) + entry.cell_index * ptr_bytes;
274 }
275 };
276
277 pub fn deinit(got: *GotSection, allocator: Allocator) void {
278 got.entries.deinit(allocator);
279 }
280
281 fn allocateEntry(got: *GotSection, allocator: Allocator) !Index {
282 try got.entries.ensureUnusedCapacity(allocator, 1);
283 // TODO add free list
284 const index = @as(Index, @intCast(got.entries.items.len));
285 const entry = got.entries.addOneAssumeCapacity();
286 const cell_index: Index = if (index > 0) blk: {
287 const last = got.entries.items[index - 1];
288 break :blk last.cell_index + @as(Index, @intCast(last.len()));
289 } else 0;
290 entry.* = .{ .tag = undefined, .ref = undefined, .cell_index = cell_index };
291 return index;
292 }
293
294 pub fn addGotSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !Index {
295 const comp = elf_file.base.comp;
296 const gpa = comp.gpa;
297 const index = try got.allocateEntry(gpa);
298 const entry = &got.entries.items[index];
299 entry.tag = .got;
300 entry.ref = ref;
301 const symbol = elf_file.symbol(ref).?;
302 symbol.flags.has_got = true;
303 if (symbol.flags.import or symbol.isIFunc(elf_file) or
304 ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.isAbs(elf_file)))
305 {
306 got.flags.needs_rela = true;
307 }
308 symbol.addExtra(.{ .got = index }, elf_file);
309 return index;
310 }
311
312 pub fn addTlsLdSymbol(got: *GotSection, elf_file: *Elf) !void {
313 const comp = elf_file.base.comp;
314 const gpa = comp.gpa;
315 assert(got.flags.needs_tlsld);
316 const index = try got.allocateEntry(gpa);
317 const entry = &got.entries.items[index];
318 entry.tag = .tlsld;
319 entry.ref = .{ .index = 0, .file = 0 }; // unused
320 got.flags.needs_rela = true;
321 got.tlsld_index = index;
322 }
323
324 pub fn addTlsGdSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void {
325 const comp = elf_file.base.comp;
326 const gpa = comp.gpa;
327 const index = try got.allocateEntry(gpa);
328 const entry = &got.entries.items[index];
329 entry.tag = .tlsgd;
330 entry.ref = ref;
331 const symbol = elf_file.symbol(ref).?;
332 symbol.flags.has_tlsgd = true;
333 if (symbol.flags.import or elf_file.isEffectivelyDynLib()) got.flags.needs_rela = true;
334 symbol.addExtra(.{ .tlsgd = index }, elf_file);
335 }
336
337 pub fn addGotTpSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void {
338 const comp = elf_file.base.comp;
339 const gpa = comp.gpa;
340 const index = try got.allocateEntry(gpa);
341 const entry = &got.entries.items[index];
342 entry.tag = .gottp;
343 entry.ref = ref;
344 const symbol = elf_file.symbol(ref).?;
345 symbol.flags.has_gottp = true;
346 if (symbol.flags.import or elf_file.isEffectivelyDynLib()) got.flags.needs_rela = true;
347 symbol.addExtra(.{ .gottp = index }, elf_file);
348 }
349
350 pub fn addTlsDescSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void {
351 const comp = elf_file.base.comp;
352 const gpa = comp.gpa;
353 const index = try got.allocateEntry(gpa);
354 const entry = &got.entries.items[index];
355 entry.tag = .tlsdesc;
356 entry.ref = ref;
357 const symbol = elf_file.symbol(ref).?;
358 symbol.flags.has_tlsdesc = true;
359 got.flags.needs_rela = true;
360 symbol.addExtra(.{ .tlsdesc = index }, elf_file);
361 }
362
363 pub fn size(got: GotSection, elf_file: *Elf) usize {
364 var s: usize = 0;
365 for (got.entries.items) |entry| {
366 s += elf_file.archPtrWidthBytes() * entry.len();
367 }
368 return s;
369 }
370
371 pub fn write(got: GotSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
372 const comp = elf_file.base.comp;
373 const is_dyn_lib = elf_file.isEffectivelyDynLib();
374 const apply_relocs = true; // TODO add user option for this
375
376 for (got.entries.items) |entry| {
377 const symbol = elf_file.symbol(entry.ref);
378 switch (entry.tag) {
379 .got => {
380 const value = blk: {
381 const value = symbol.?.address(.{ .plt = false }, elf_file);
382 if (symbol.?.flags.import) break :blk 0;
383 if (symbol.?.isIFunc(elf_file))
384 break :blk if (apply_relocs) value else 0;
385 if ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and
386 !symbol.?.isAbs(elf_file))
387 {
388 break :blk if (apply_relocs) value else 0;
389 }
390 break :blk value;
391 };
392 try writeInt(value, elf_file, writer);
393 },
394 .tlsld => {
395 try writeInt(if (is_dyn_lib) @as(i64, 0) else 1, elf_file, writer);
396 try writeInt(0, elf_file, writer);
397 },
398 .tlsgd => {
399 if (symbol.?.flags.import) {
400 try writeInt(0, elf_file, writer);
401 try writeInt(0, elf_file, writer);
402 } else {
403 try writeInt(if (is_dyn_lib) @as(i64, 0) else 1, elf_file, writer);
404 const offset = symbol.?.address(.{}, elf_file) - elf_file.dtpAddress();
405 try writeInt(offset, elf_file, writer);
406 }
407 },
408 .gottp => {
409 if (symbol.?.flags.import) {
410 try writeInt(0, elf_file, writer);
411 } else if (is_dyn_lib) {
412 const offset = if (apply_relocs)
413 symbol.?.address(.{}, elf_file) - elf_file.tlsAddress()
414 else
415 0;
416 try writeInt(offset, elf_file, writer);
417 } else {
418 const offset = symbol.?.address(.{}, elf_file) - elf_file.tpAddress();
419 try writeInt(offset, elf_file, writer);
420 }
421 },
422 .tlsdesc => {
423 try writeInt(0, elf_file, writer);
424 const offset: i64 = if (apply_relocs and !symbol.?.flags.import)
425 symbol.?.address(.{}, elf_file) - elf_file.tlsAddress()
426 else
427 0;
428 try writeInt(offset, elf_file, writer);
429 },
430 }
431 }
432 }
433
434 pub fn addRela(got: GotSection, elf_file: *Elf) !void {
435 const comp = elf_file.base.comp;
436 const gpa = comp.gpa;
437 const is_dyn_lib = elf_file.isEffectivelyDynLib();
438 const cpu_arch = elf_file.getTarget().cpu.arch;
439 try elf_file.rela_dyn.ensureUnusedCapacity(gpa, got.numRela(elf_file));
440
441 relocs_log.debug(".got", .{});
442
443 for (got.entries.items) |entry| {
444 const symbol = elf_file.symbol(entry.ref);
445 const extra = if (symbol) |s| s.extra(elf_file) else null;
446
447 switch (entry.tag) {
448 .got => {
449 const offset: u64 = @intCast(symbol.?.gotAddress(elf_file));
450 if (symbol.?.flags.import) {
451 elf_file.addRelaDynAssumeCapacity(.{
452 .offset = offset,
453 .sym = extra.?.dynamic,
454 .type = relocation.encode(.glob_dat, cpu_arch),
455 .target = symbol,
456 });
457 continue;
458 }
459 if (symbol.?.isIFunc(elf_file)) {
460 elf_file.addRelaDynAssumeCapacity(.{
461 .offset = offset,
462 .type = relocation.encode(.irel, cpu_arch),
463 .addend = symbol.?.address(.{ .plt = false }, elf_file),
464 .target = symbol,
465 });
466 continue;
467 }
468 if ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and
469 !symbol.?.isAbs(elf_file))
470 {
471 elf_file.addRelaDynAssumeCapacity(.{
472 .offset = offset,
473 .type = relocation.encode(.rel, cpu_arch),
474 .addend = symbol.?.address(.{ .plt = false }, elf_file),
475 .target = symbol,
476 });
477 }
478 },
479
480 .tlsld => {
481 if (is_dyn_lib) {
482 const offset: u64 = @intCast(entry.address(elf_file));
483 elf_file.addRelaDynAssumeCapacity(.{
484 .offset = offset,
485 .type = relocation.encode(.dtpmod, cpu_arch),
486 });
487 }
488 },
489
490 .tlsgd => {
491 const offset: u64 = @intCast(symbol.?.tlsGdAddress(elf_file));
492 if (symbol.?.flags.import) {
493 elf_file.addRelaDynAssumeCapacity(.{
494 .offset = offset,
495 .sym = extra.?.dynamic,
496 .type = relocation.encode(.dtpmod, cpu_arch),
497 .target = symbol,
498 });
499 elf_file.addRelaDynAssumeCapacity(.{
500 .offset = offset + 8,
501 .sym = extra.?.dynamic,
502 .type = relocation.encode(.dtpoff, cpu_arch),
503 .target = symbol,
504 });
505 } else if (is_dyn_lib) {
506 elf_file.addRelaDynAssumeCapacity(.{
507 .offset = offset,
508 .sym = extra.?.dynamic,
509 .type = relocation.encode(.dtpmod, cpu_arch),
510 .target = symbol,
511 });
512 }
513 },
514
515 .gottp => {
516 const offset: u64 = @intCast(symbol.?.gotTpAddress(elf_file));
517 if (symbol.?.flags.import) {
518 elf_file.addRelaDynAssumeCapacity(.{
519 .offset = offset,
520 .sym = extra.?.dynamic,
521 .type = relocation.encode(.tpoff, cpu_arch),
522 .target = symbol,
523 });
524 } else if (is_dyn_lib) {
525 elf_file.addRelaDynAssumeCapacity(.{
526 .offset = offset,
527 .type = relocation.encode(.tpoff, cpu_arch),
528 .addend = symbol.?.address(.{}, elf_file) - elf_file.tlsAddress(),
529 .target = symbol,
530 });
531 }
532 },
533
534 .tlsdesc => {
535 const offset: u64 = @intCast(symbol.?.tlsDescAddress(elf_file));
536 elf_file.addRelaDynAssumeCapacity(.{
537 .offset = offset,
538 .sym = if (symbol.?.flags.import) extra.?.dynamic else 0,
539 .type = relocation.encode(.tlsdesc, cpu_arch),
540 .addend = if (symbol.?.flags.import) 0 else symbol.?.address(.{}, elf_file) - elf_file.tlsAddress(),
541 .target = symbol,
542 });
543 },
544 }
545 }
546 }
547
548 pub fn numRela(got: GotSection, elf_file: *Elf) usize {
549 const comp = elf_file.base.comp;
550 const is_dyn_lib = elf_file.isEffectivelyDynLib();
551 var num: usize = 0;
552 for (got.entries.items) |entry| {
553 const symbol = elf_file.symbol(entry.ref);
554 switch (entry.tag) {
555 .got => if (symbol.?.flags.import or symbol.?.isIFunc(elf_file) or
556 ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and
557 !symbol.?.isAbs(elf_file)))
558 {
559 num += 1;
560 },
561
562 .tlsld => if (is_dyn_lib) {
563 num += 1;
564 },
565
566 .tlsgd => if (symbol.?.flags.import) {
567 num += 2;
568 } else if (is_dyn_lib) {
569 num += 1;
570 },
571
572 .gottp => if (symbol.?.flags.import or is_dyn_lib) {
573 num += 1;
574 },
575
576 .tlsdesc => num += 1,
577 }
578 }
579 return num;
580 }
581
582 pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void {
583 got.output_symtab_ctx.nlocals = @as(u32, @intCast(got.entries.items.len));
584 for (got.entries.items) |entry| {
585 const symbol_name = if (elf_file.symbol(entry.ref)) |sym| sym.name(elf_file) else "";
586 got.output_symtab_ctx.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1;
587 }
588 }
589
590 pub fn writeSymtab(got: GotSection, elf_file: *Elf) void {
591 for (got.entries.items, got.output_symtab_ctx.ilocal..) |entry, ilocal| {
592 const symbol = elf_file.symbol(entry.ref);
593 const symbol_name = if (symbol) |s| s.name(elf_file) else "";
594 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
595 elf_file.strtab.appendSliceAssumeCapacity(symbol_name);
596 elf_file.strtab.appendAssumeCapacity('$');
597 elf_file.strtab.appendSliceAssumeCapacity(@tagName(entry.tag));
598 elf_file.strtab.appendAssumeCapacity(0);
599 const st_value = entry.address(elf_file);
600 const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes();
601 elf_file.symtab.items[ilocal] = .{
602 .st_name = st_name,
603 .st_info = elf.STT_OBJECT,
604 .st_other = 0,
605 .st_shndx = @intCast(elf_file.section_indexes.got.?),
606 .st_value = @intCast(st_value),
607 .st_size = st_size,
608 };
609 }
610 }
611
612 const Format = struct {
613 got: GotSection,
614 elf_file: *Elf,
615
616 pub fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
617 const got = f.got;
618 const elf_file = f.elf_file;
619 try writer.writeAll("GOT\n");
620 for (got.entries.items) |entry| {
621 const symbol = elf_file.symbol(entry.ref).?;
622 try writer.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
623 entry.cell_index,
624 entry.address(elf_file),
625 entry.ref,
626 symbol.address(.{}, elf_file),
627 symbol.name(elf_file),
628 });
629 }
630 }
631 };
632
633 pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Alt(Format, Format.default) {
634 return .{ .data = .{ .got = got, .elf_file = elf_file } };
635 }
636};
637
638pub const PltSection = struct {
639 symbols: std.ArrayList(Elf.Ref) = .empty,
640 output_symtab_ctx: Elf.SymtabCtx = .{},
641
642 pub fn deinit(plt: *PltSection, allocator: Allocator) void {
643 plt.symbols.deinit(allocator);
644 }
645
646 pub fn addSymbol(plt: *PltSection, ref: Elf.Ref, elf_file: *Elf) !void {
647 const comp = elf_file.base.comp;
648 const gpa = comp.gpa;
649 const index = @as(u32, @intCast(plt.symbols.items.len));
650 const symbol = elf_file.symbol(ref).?;
651 symbol.flags.has_plt = true;
652 symbol.addExtra(.{ .plt = index }, elf_file);
653 try plt.symbols.append(gpa, ref);
654 }
655
656 pub fn size(plt: PltSection, elf_file: *Elf) usize {
657 const cpu_arch = elf_file.getTarget().cpu.arch;
658 return preambleSize(cpu_arch) + plt.symbols.items.len * entrySize(cpu_arch);
659 }
660
661 pub fn preambleSize(cpu_arch: std.Target.Cpu.Arch) usize {
662 return switch (cpu_arch) {
663 .x86_64 => 32,
664 .aarch64 => 8 * @sizeOf(u32),
665 else => @panic("TODO implement preambleSize for this cpu arch"),
666 };
667 }
668
669 pub fn entrySize(cpu_arch: std.Target.Cpu.Arch) usize {
670 return switch (cpu_arch) {
671 .x86_64 => 16,
672 .aarch64 => 4 * @sizeOf(u32),
673 else => @panic("TODO implement entrySize for this cpu arch"),
674 };
675 }
676
677 pub fn write(plt: PltSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
678 const cpu_arch = elf_file.getTarget().cpu.arch;
679 switch (cpu_arch) {
680 .x86_64 => try x86_64.write(plt, elf_file, writer),
681 .aarch64 => try aarch64.write(plt, elf_file, writer),
682 else => return error.UnsupportedCpuArch,
683 }
684 }
685
686 pub fn addRela(plt: PltSection, elf_file: *Elf) !void {
687 const comp = elf_file.base.comp;
688 const gpa = comp.gpa;
689 const cpu_arch = elf_file.getTarget().cpu.arch;
690 try elf_file.rela_plt.ensureUnusedCapacity(gpa, plt.numRela());
691
692 relocs_log.debug(".plt", .{});
693
694 for (plt.symbols.items) |ref| {
695 const sym = elf_file.symbol(ref).?;
696 assert(sym.flags.import);
697 const extra = sym.extra(elf_file);
698 const r_offset: u64 = @intCast(sym.gotPltAddress(elf_file));
699 const r_sym: u64 = extra.dynamic;
700 const r_type = relocation.encode(.jump_slot, cpu_arch);
701
702 relocs_log.debug(" {f}: [{x} => {d}({s})] + 0", .{
703 relocation.fmtRelocType(r_type, cpu_arch),
704 r_offset,
705 r_sym,
706 sym.name(elf_file),
707 });
708
709 elf_file.rela_plt.appendAssumeCapacity(.{
710 .r_offset = r_offset,
711 .r_info = (r_sym << 32) | r_type,
712 .r_addend = 0,
713 });
714 }
715 }
716
717 pub fn numRela(plt: PltSection) usize {
718 return plt.symbols.items.len;
719 }
720
721 pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void {
722 plt.output_symtab_ctx.nlocals = @as(u32, @intCast(plt.symbols.items.len));
723 for (plt.symbols.items) |ref| {
724 const name = elf_file.symbol(ref).?.name(elf_file);
725 plt.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1;
726 }
727 }
728
729 pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void {
730 const cpu_arch = elf_file.getTarget().cpu.arch;
731 for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |ref, ilocal| {
732 const sym = elf_file.symbol(ref).?;
733 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
734 elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
735 elf_file.strtab.appendSliceAssumeCapacity("$plt");
736 elf_file.strtab.appendAssumeCapacity(0);
737 elf_file.symtab.items[ilocal] = .{
738 .st_name = st_name,
739 .st_info = elf.STT_FUNC,
740 .st_other = 0,
741 .st_shndx = @intCast(elf_file.section_indexes.plt.?),
742 .st_value = @intCast(sym.pltAddress(elf_file)),
743 .st_size = entrySize(cpu_arch),
744 };
745 }
746 }
747
748 const Format = struct {
749 plt: PltSection,
750 elf_file: *Elf,
751
752 pub fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
753 const plt = f.plt;
754 const elf_file = f.elf_file;
755 try writer.writeAll("PLT\n");
756 for (plt.symbols.items, 0..) |ref, i| {
757 const symbol = elf_file.symbol(ref).?;
758 try writer.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
759 i,
760 symbol.pltAddress(elf_file),
761 ref,
762 symbol.address(.{}, elf_file),
763 symbol.name(elf_file),
764 });
765 }
766 }
767 };
768
769 pub fn fmt(plt: PltSection, elf_file: *Elf) std.fmt.Alt(Format, Format.default) {
770 return .{ .data = .{ .plt = plt, .elf_file = elf_file } };
771 }
772
773 const x86_64 = struct {
774 fn write(plt: PltSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
775 dev.checkAny(&.{ .llvm_backend, .x86_64_backend });
776 const shdrs = elf_file.sections.items(.shdr);
777 const plt_addr = shdrs[elf_file.section_indexes.plt.?].sh_addr;
778 const got_plt_addr = shdrs[elf_file.section_indexes.got_plt.?].sh_addr;
779 var preamble = [_]u8{
780 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
781 0x41, 0x53, // push r11
782 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push qword ptr [rip] -> .got.plt[1]
783 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[2]
784 };
785 var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4;
786 mem.writeInt(i32, preamble[8..][0..4], @as(i32, @intCast(disp)), .little);
787 disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4;
788 mem.writeInt(i32, preamble[14..][0..4], @as(i32, @intCast(disp)), .little);
789 try writer.writeAll(&preamble);
790 try writer.splatByteAll(0xcc, preambleSize(.x86_64) - preamble.len);
791
792 for (plt.symbols.items, 0..) |ref, i| {
793 const sym = elf_file.symbol(ref).?;
794 const target_addr = sym.gotPltAddress(elf_file);
795 const source_addr = sym.pltAddress(elf_file);
796 disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4;
797 var entry = [_]u8{
798 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
799 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N
800 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N]
801 };
802 mem.writeInt(i32, entry[6..][0..4], @as(i32, @intCast(i)), .little);
803 mem.writeInt(i32, entry[12..][0..4], @as(i32, @intCast(disp)), .little);
804 try writer.writeAll(&entry);
805 }
806 }
807 };
808
809 const aarch64 = struct {
810 fn write(plt: PltSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
811 dev.checkAny(&.{ .llvm_backend, .aarch64_backend });
812 {
813 const shdrs = elf_file.sections.items(.shdr);
814 const plt_addr: i64 = @intCast(shdrs[elf_file.section_indexes.plt.?].sh_addr);
815 const got_plt_addr: i64 = @intCast(shdrs[elf_file.section_indexes.got_plt.?].sh_addr);
816 // TODO: relax if possible
817 // .got.plt[2]
818 const pages = try util.calcNumberOfPages(plt_addr + 4, got_plt_addr + 16);
819 const ldr_off: u12 = @truncate(@as(u64, @bitCast(got_plt_addr + 16)));
820 const add_off: u12 = @truncate(@as(u64, @bitCast(got_plt_addr + 16)));
821
822 const preamble = [_]util.encoding.Instruction{
823 .stp(.x16, .x30, .{ .pre_index = .{ .base = .sp, .index = -16 } }),
824 .adrp(.x16, pages << 12),
825 .ldr(.x17, .{ .unsigned_offset = .{ .base = .x16, .offset = ldr_off } }),
826 .add(.x16, .x16, .{ .immediate = add_off }),
827 .br(.x17),
828 .nop(),
829 .nop(),
830 .nop(),
831 };
832 comptime assert(preamble.len == 8);
833 for (preamble) |inst| try writer.writeInt(util.encoding.Instruction.Backing, @bitCast(inst), .little);
834 }
835
836 for (plt.symbols.items) |ref| {
837 const sym = elf_file.symbol(ref).?;
838 const target_addr = sym.gotPltAddress(elf_file);
839 const source_addr = sym.pltAddress(elf_file);
840 const pages = try util.calcNumberOfPages(source_addr, target_addr);
841 const ldr_off: u12 = @truncate(@as(u64, @bitCast(target_addr)));
842 const add_off: u12 = @truncate(@as(u64, @bitCast(target_addr)));
843 const insts = [_]util.encoding.Instruction{
844 .adrp(.x16, pages << 12),
845 .ldr(.x17, .{ .unsigned_offset = .{ .base = .x16, .offset = ldr_off } }),
846 .add(.x16, .x16, .{ .immediate = add_off }),
847 .br(.x17),
848 };
849 comptime assert(insts.len == 4);
850 for (insts) |inst| try writer.writeInt(util.encoding.Instruction.Backing, @bitCast(inst), .little);
851 }
852 }
853
854 const util = @import("../aarch64.zig");
855 };
856};
857
858pub const GotPltSection = struct {
859 pub const preamble_size = 24;
860
861 pub fn size(got_plt: GotPltSection, elf_file: *Elf) usize {
862 _ = got_plt;
863 return preamble_size + elf_file.plt.symbols.items.len * 8;
864 }
865
866 pub fn write(got_plt: GotPltSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
867 _ = got_plt;
868 {
869 // [0]: _DYNAMIC
870 const symbol = elf_file.linkerDefinedPtr().?.dynamicSymbol(elf_file).?;
871 try writer.writeInt(u64, @intCast(symbol.address(.{}, elf_file)), .little);
872 }
873 // [1]: 0x0
874 // [2]: 0x0
875 try writer.writeInt(u64, 0x0, .little);
876 try writer.writeInt(u64, 0x0, .little);
877 if (elf_file.section_indexes.plt) |shndx| {
878 const plt_addr = elf_file.sections.items(.shdr)[shndx].sh_addr;
879 for (0..elf_file.plt.symbols.items.len) |_| {
880 // [N]: .plt
881 try writer.writeInt(u64, plt_addr, .little);
882 }
883 }
884 }
885};
886
887pub const PltGotSection = struct {
888 symbols: std.ArrayList(Elf.Ref) = .empty,
889 output_symtab_ctx: Elf.SymtabCtx = .{},
890
891 pub fn deinit(plt_got: *PltGotSection, allocator: Allocator) void {
892 plt_got.symbols.deinit(allocator);
893 }
894
895 pub fn addSymbol(plt_got: *PltGotSection, ref: Elf.Ref, elf_file: *Elf) !void {
896 const comp = elf_file.base.comp;
897 const gpa = comp.gpa;
898 const index = @as(u32, @intCast(plt_got.symbols.items.len));
899 const symbol = elf_file.symbol(ref).?;
900 symbol.flags.has_pltgot = true;
901 symbol.addExtra(.{ .plt_got = index }, elf_file);
902 try plt_got.symbols.append(gpa, ref);
903 }
904
905 pub fn size(plt_got: PltGotSection, elf_file: *Elf) usize {
906 return plt_got.symbols.items.len * entrySize(elf_file.getTarget().cpu.arch);
907 }
908
909 pub fn entrySize(cpu_arch: std.Target.Cpu.Arch) usize {
910 return switch (cpu_arch) {
911 .x86_64 => 16,
912 .aarch64 => 4 * @sizeOf(u32),
913 else => @panic("TODO implement PltGotSection.entrySize for this arch"),
914 };
915 }
916
917 pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
918 const cpu_arch = elf_file.getTarget().cpu.arch;
919 switch (cpu_arch) {
920 .x86_64 => try x86_64.write(plt_got, elf_file, writer),
921 .aarch64 => try aarch64.write(plt_got, elf_file, writer),
922 else => return error.UnsupportedCpuArch,
923 }
924 }
925
926 pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void {
927 plt_got.output_symtab_ctx.nlocals = @as(u32, @intCast(plt_got.symbols.items.len));
928 for (plt_got.symbols.items) |ref| {
929 const name = elf_file.symbol(ref).?.name(elf_file);
930 plt_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1;
931 }
932 }
933
934 pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf) void {
935 for (plt_got.symbols.items, plt_got.output_symtab_ctx.ilocal..) |ref, ilocal| {
936 const sym = elf_file.symbol(ref).?;
937 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
938 elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
939 elf_file.strtab.appendSliceAssumeCapacity("$pltgot");
940 elf_file.strtab.appendAssumeCapacity(0);
941 elf_file.symtab.items[ilocal] = .{
942 .st_name = st_name,
943 .st_info = elf.STT_FUNC,
944 .st_other = 0,
945 .st_shndx = @intCast(elf_file.section_indexes.plt_got.?),
946 .st_value = @intCast(sym.pltGotAddress(elf_file)),
947 .st_size = 16,
948 };
949 }
950 }
951
952 const x86_64 = struct {
953 pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
954 dev.checkAny(&.{ .llvm_backend, .x86_64_backend });
955 for (plt_got.symbols.items) |ref| {
956 const sym = elf_file.symbol(ref).?;
957 const target_addr = sym.gotAddress(elf_file);
958 const source_addr = sym.pltGotAddress(elf_file);
959 const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 6)) - 4;
960 var entry = [_]u8{
961 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
962 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got[N]
963 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
964 };
965 mem.writeInt(i32, entry[6..][0..4], @as(i32, @intCast(disp)), .little);
966 try writer.writeAll(&entry);
967 }
968 }
969 };
970
971 const aarch64 = struct {
972 fn write(plt_got: PltGotSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
973 dev.checkAny(&.{ .llvm_backend, .aarch64_backend });
974 for (plt_got.symbols.items) |ref| {
975 const sym = elf_file.symbol(ref).?;
976 const target_addr = sym.gotAddress(elf_file);
977 const source_addr = sym.pltGotAddress(elf_file);
978 const pages = try util.calcNumberOfPages(source_addr, target_addr);
979 const off: u12 = @truncate(@as(u64, @bitCast(target_addr)));
980 const insts = [_]util.encoding.Instruction{
981 .adrp(.x16, pages << 12),
982 .ldr(.x17, .{ .unsigned_offset = .{ .base = .x16, .offset = off } }),
983 .br(.x17),
984 .nop(),
985 };
986 comptime assert(insts.len == 4);
987 for (insts) |inst| try writer.writeInt(util.encoding.Instruction.Backing, @bitCast(inst), .little);
988 }
989 }
990
991 const util = @import("../aarch64.zig");
992 };
993};
994
995pub const CopyRelSection = struct {
996 symbols: std.ArrayList(Elf.Ref) = .empty,
997
998 pub fn deinit(copy_rel: *CopyRelSection, allocator: Allocator) void {
999 copy_rel.symbols.deinit(allocator);
1000 }
1001
1002 pub fn addSymbol(copy_rel: *CopyRelSection, ref: Elf.Ref, elf_file: *Elf) !void {
1003 const comp = elf_file.base.comp;
1004 const gpa = comp.gpa;
1005 const index = @as(u32, @intCast(copy_rel.symbols.items.len));
1006 const symbol = elf_file.symbol(ref).?;
1007 symbol.flags.import = true;
1008 symbol.flags.@"export" = true;
1009 symbol.flags.has_copy_rel = true;
1010 symbol.flags.weak = false;
1011 symbol.addExtra(.{ .copy_rel = index }, elf_file);
1012 try copy_rel.symbols.append(gpa, ref);
1013
1014 const shared_object = symbol.file(elf_file).?.shared_object;
1015 if (shared_object.aliases == null) {
1016 try shared_object.initSymbolAliases(elf_file);
1017 }
1018
1019 const aliases = shared_object.symbolAliases(ref.index, elf_file);
1020 for (aliases) |alias| {
1021 if (alias == ref.index) continue;
1022 const alias_sym = &shared_object.symbols.items[alias];
1023 alias_sym.flags.import = true;
1024 alias_sym.flags.@"export" = true;
1025 alias_sym.flags.has_copy_rel = true;
1026 alias_sym.flags.needs_copy_rel = true;
1027 alias_sym.flags.weak = false;
1028 try elf_file.dynsym.addSymbol(.{ .index = alias, .file = shared_object.index }, elf_file);
1029 }
1030 }
1031
1032 pub fn updateSectionSize(copy_rel: CopyRelSection, shndx: u32, elf_file: *Elf) !void {
1033 const shdr = &elf_file.sections.items(.shdr)[shndx];
1034 for (copy_rel.symbols.items) |ref| {
1035 const symbol = elf_file.symbol(ref).?;
1036 const shared_object = symbol.file(elf_file).?.shared_object;
1037 const alignment = try symbol.dsoAlignment(elf_file);
1038 symbol.value = @intCast(mem.alignForward(u64, shdr.sh_size, alignment));
1039 shdr.sh_addralign = @max(shdr.sh_addralign, alignment);
1040 shdr.sh_size = @as(u64, @intCast(symbol.value)) + symbol.elfSym(elf_file).st_size;
1041
1042 const aliases = shared_object.symbolAliases(ref.index, elf_file);
1043 for (aliases) |alias| {
1044 if (alias == ref.index) continue;
1045 const alias_sym = &shared_object.symbols.items[alias];
1046 alias_sym.value = symbol.value;
1047 }
1048 }
1049 }
1050
1051 pub fn addRela(copy_rel: CopyRelSection, elf_file: *Elf) !void {
1052 const comp = elf_file.base.comp;
1053 const gpa = comp.gpa;
1054 const cpu_arch = elf_file.getTarget().cpu.arch;
1055 try elf_file.rela_dyn.ensureUnusedCapacity(gpa, copy_rel.numRela());
1056
1057 relocs_log.debug(".copy.rel", .{});
1058
1059 for (copy_rel.symbols.items) |ref| {
1060 const sym = elf_file.symbol(ref).?;
1061 assert(sym.flags.import and sym.flags.has_copy_rel);
1062 const extra = sym.extra(elf_file);
1063 elf_file.addRelaDynAssumeCapacity(.{
1064 .offset = @intCast(sym.address(.{}, elf_file)),
1065 .sym = extra.dynamic,
1066 .type = relocation.encode(.copy, cpu_arch),
1067 });
1068 }
1069 }
1070
1071 pub fn numRela(copy_rel: CopyRelSection) usize {
1072 return copy_rel.symbols.items.len;
1073 }
1074};
1075
1076pub const DynsymSection = struct {
1077 entries: std.ArrayList(Entry) = .empty,
1078
1079 pub const Entry = struct {
1080 /// Ref of the symbol which gets privilege of getting a dynamic treatment
1081 ref: Elf.Ref,
1082 /// Offset into .dynstrtab
1083 off: u32,
1084 };
1085
1086 pub fn deinit(dynsym: *DynsymSection, allocator: Allocator) void {
1087 dynsym.entries.deinit(allocator);
1088 }
1089
1090 pub fn addSymbol(dynsym: *DynsymSection, ref: Elf.Ref, elf_file: *Elf) !void {
1091 const comp = elf_file.base.comp;
1092 const gpa = comp.gpa;
1093 const index = @as(u32, @intCast(dynsym.entries.items.len + 1));
1094 const sym = elf_file.symbol(ref).?;
1095 sym.flags.has_dynamic = true;
1096 sym.addExtra(.{ .dynamic = index }, elf_file);
1097 const off = try elf_file.insertDynString(sym.name(elf_file));
1098 try dynsym.entries.append(gpa, .{ .ref = ref, .off = off });
1099 }
1100
1101 pub fn sort(dynsym: *DynsymSection, elf_file: *Elf) void {
1102 const Sort = struct {
1103 pub fn lessThan(ctx: *Elf, lhs: Entry, rhs: Entry) bool {
1104 const lhs_sym = ctx.symbol(lhs.ref).?;
1105 const rhs_sym = ctx.symbol(rhs.ref).?;
1106
1107 if (lhs_sym.flags.@"export" != rhs_sym.flags.@"export") {
1108 return rhs_sym.flags.@"export";
1109 }
1110
1111 // TODO cache hash values
1112 const nbuckets = ctx.gnu_hash.num_buckets;
1113 const lhs_hash = GnuHashSection.hasher(lhs_sym.name(ctx)) % nbuckets;
1114 const rhs_hash = GnuHashSection.hasher(rhs_sym.name(ctx)) % nbuckets;
1115
1116 if (lhs_hash == rhs_hash)
1117 return lhs_sym.extra(ctx).dynamic < rhs_sym.extra(ctx).dynamic;
1118 return lhs_hash < rhs_hash;
1119 }
1120 };
1121
1122 var num_exports: u32 = 0;
1123 for (dynsym.entries.items) |entry| {
1124 const sym = elf_file.symbol(entry.ref).?;
1125 if (sym.flags.@"export") num_exports += 1;
1126 }
1127
1128 elf_file.gnu_hash.num_buckets = @divTrunc(num_exports, GnuHashSection.load_factor) + 1;
1129
1130 std.mem.sort(Entry, dynsym.entries.items, elf_file, Sort.lessThan);
1131
1132 for (dynsym.entries.items, 1..) |entry, index| {
1133 const sym = elf_file.symbol(entry.ref).?;
1134 var extra = sym.extra(elf_file);
1135 extra.dynamic = @as(u32, @intCast(index));
1136 sym.setExtra(extra, elf_file);
1137 }
1138 }
1139
1140 pub fn size(dynsym: DynsymSection) usize {
1141 return dynsym.count() * @sizeOf(elf.Elf64_Sym);
1142 }
1143
1144 pub fn count(dynsym: DynsymSection) u32 {
1145 return @as(u32, @intCast(dynsym.entries.items.len + 1));
1146 }
1147
1148 pub fn write(dynsym: DynsymSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
1149 try writer.writeStruct(Elf.null_sym, .little);
1150 for (dynsym.entries.items) |entry| {
1151 const sym = elf_file.symbol(entry.ref).?;
1152 var out_sym: elf.Elf64_Sym = Elf.null_sym;
1153 sym.setOutputSym(elf_file, &out_sym);
1154 out_sym.st_name = entry.off;
1155 try writer.writeStruct(out_sym, .little);
1156 }
1157 }
1158};
1159
1160pub const HashSection = struct {
1161 buffer: std.ArrayList(u8) = .empty,
1162
1163 pub fn deinit(hs: *HashSection, allocator: Allocator) void {
1164 hs.buffer.deinit(allocator);
1165 }
1166
1167 pub fn generate(hs: *HashSection, elf_file: *Elf) !void {
1168 if (elf_file.dynsym.count() == 1) return;
1169
1170 const comp = elf_file.base.comp;
1171 const gpa = comp.gpa;
1172 const nsyms = elf_file.dynsym.count();
1173
1174 var buckets = try gpa.alloc(u32, nsyms);
1175 defer gpa.free(buckets);
1176 @memset(buckets, 0);
1177
1178 var chains = try gpa.alloc(u32, nsyms);
1179 defer gpa.free(chains);
1180 @memset(chains, 0);
1181
1182 for (elf_file.dynsym.entries.items, 1..) |entry, i| {
1183 const name = elf_file.getDynString(entry.off);
1184 const hash = hasher(name) % buckets.len;
1185 chains[@as(u32, @intCast(i))] = buckets[hash];
1186 buckets[hash] = @as(u32, @intCast(i));
1187 }
1188
1189 try hs.buffer.ensureTotalCapacityPrecise(gpa, (2 + nsyms * 2) * 4);
1190 var w: std.Io.Writer = .fixed(hs.buffer.unusedCapacitySlice());
1191 w.writeInt(u32, @as(u32, @intCast(nsyms)), .little) catch unreachable;
1192 w.writeInt(u32, @as(u32, @intCast(nsyms)), .little) catch unreachable;
1193 w.writeAll(@ptrCast(buckets)) catch unreachable;
1194 w.writeAll(@ptrCast(chains)) catch unreachable;
1195 hs.buffer.items.len += w.end;
1196 }
1197
1198 pub inline fn size(hs: HashSection) usize {
1199 return hs.buffer.items.len;
1200 }
1201
1202 pub fn hasher(name: [:0]const u8) u32 {
1203 var h: u32 = 0;
1204 var g: u32 = 0;
1205 for (name) |c| {
1206 h = (h << 4) + c;
1207 g = h & 0xf0000000;
1208 if (g > 0) h ^= g >> 24;
1209 h &= ~g;
1210 }
1211 return h;
1212 }
1213};
1214
1215pub const GnuHashSection = struct {
1216 num_buckets: u32 = 0,
1217 num_bloom: u32 = 1,
1218 num_exports: u32 = 0,
1219
1220 pub const load_factor = 8;
1221 pub const header_size = 16;
1222 pub const bloom_shift = 26;
1223
1224 fn getExports(elf_file: *Elf) []const DynsymSection.Entry {
1225 const start = for (elf_file.dynsym.entries.items, 0..) |entry, i| {
1226 const sym = elf_file.symbol(entry.ref).?;
1227 if (sym.flags.@"export") break i;
1228 } else elf_file.dynsym.entries.items.len;
1229 return elf_file.dynsym.entries.items[start..];
1230 }
1231
1232 inline fn bitCeil(x: u64) u64 {
1233 if (@popCount(x) == 1) return x;
1234 return @as(u64, @intCast(@as(u128, 1) << (64 - @clz(x))));
1235 }
1236
1237 pub fn calcSize(hash: *GnuHashSection, elf_file: *Elf) !void {
1238 hash.num_exports = @as(u32, @intCast(getExports(elf_file).len));
1239 if (hash.num_exports > 0) {
1240 const num_bits = hash.num_exports * 12;
1241 hash.num_bloom = @as(u32, @intCast(bitCeil(@divTrunc(num_bits, 64))));
1242 }
1243 }
1244
1245 pub fn size(hash: GnuHashSection) usize {
1246 return header_size + hash.num_bloom * 8 + hash.num_buckets * 4 + hash.num_exports * 4;
1247 }
1248
1249 pub fn write(hash: GnuHashSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
1250 const exports = getExports(elf_file);
1251 const export_off = elf_file.dynsym.count() - hash.num_exports;
1252
1253 try writer.writeInt(u32, hash.num_buckets, .little);
1254 try writer.writeInt(u32, export_off, .little);
1255 try writer.writeInt(u32, hash.num_bloom, .little);
1256 try writer.writeInt(u32, bloom_shift, .little);
1257
1258 const comp = elf_file.base.comp;
1259 const gpa = comp.gpa;
1260 const hashes = try gpa.alloc(u32, exports.len);
1261 defer gpa.free(hashes);
1262 const indices = try gpa.alloc(u32, exports.len);
1263 defer gpa.free(indices);
1264
1265 // Compose and write the bloom filter
1266 const bloom = try gpa.alloc(u64, hash.num_bloom);
1267 defer gpa.free(bloom);
1268 @memset(bloom, 0);
1269
1270 for (exports, 0..) |entry, i| {
1271 const sym = elf_file.symbol(entry.ref).?;
1272 const h = hasher(sym.name(elf_file));
1273 hashes[i] = h;
1274 indices[i] = h % hash.num_buckets;
1275 const idx = @divTrunc(h, 64) % hash.num_bloom;
1276 bloom[idx] |= @as(u64, 1) << @as(u6, @intCast(h % 64));
1277 bloom[idx] |= @as(u64, 1) << @as(u6, @intCast((h >> bloom_shift) % 64));
1278 }
1279
1280 try writer.writeSliceEndian(u64, bloom, .little);
1281
1282 // Fill in the hash bucket indices
1283 const buckets = try gpa.alloc(u32, hash.num_buckets);
1284 defer gpa.free(buckets);
1285 @memset(buckets, 0);
1286
1287 for (0..hash.num_exports) |i| {
1288 if (buckets[indices[i]] == 0) {
1289 buckets[indices[i]] = @as(u32, @intCast(i + export_off));
1290 }
1291 }
1292
1293 try writer.writeSliceEndian(u32, buckets, .little);
1294
1295 // Finally, write the hash table
1296 const table = try gpa.alloc(u32, hash.num_exports);
1297 defer gpa.free(table);
1298 @memset(table, 0);
1299
1300 for (0..hash.num_exports) |i| {
1301 const h = hashes[i];
1302 if (i == exports.len - 1 or indices[i] != indices[i + 1]) {
1303 table[i] = h | 1;
1304 } else {
1305 table[i] = h & ~@as(u32, 1);
1306 }
1307 }
1308
1309 try writer.writeSliceEndian(u32, table, .little);
1310 }
1311
1312 pub fn hasher(name: [:0]const u8) u32 {
1313 var h: u32 = 5381;
1314 for (name) |c| {
1315 h = (h << 5) +% h +% c;
1316 }
1317 return h;
1318 }
1319};
1320
1321pub const VerneedSection = struct {
1322 verneed: std.ArrayList(elf.Elf64_Verneed) = .empty,
1323 vernaux: std.ArrayList(elf.Vernaux) = .empty,
1324 index: elf.Versym = .{ .VERSION = elf.Versym.GLOBAL.VERSION + 1, .HIDDEN = false },
1325
1326 pub fn deinit(vern: *VerneedSection, allocator: Allocator) void {
1327 vern.verneed.deinit(allocator);
1328 vern.vernaux.deinit(allocator);
1329 }
1330
1331 pub fn generate(vern: *VerneedSection, elf_file: *Elf) !void {
1332 const dynsyms = elf_file.dynsym.entries.items;
1333 var versyms = elf_file.versym.items;
1334
1335 const VersionedSymbol = struct {
1336 /// Index in the output version table
1337 index: usize,
1338 /// Index of the defining this symbol version shared object file
1339 shared_object: File.Index,
1340 /// Version index
1341 version_index: elf.Versym,
1342
1343 fn soname(this: @This(), ctx: *Elf) []const u8 {
1344 const shared_object = ctx.file(this.shared_object).?.shared_object;
1345 return shared_object.soname();
1346 }
1347
1348 fn versionString(this: @This(), ctx: *Elf) [:0]const u8 {
1349 const shared_object = ctx.file(this.shared_object).?.shared_object;
1350 return shared_object.versionString(this.version_index);
1351 }
1352
1353 pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool {
1354 if (lhs.shared_object == rhs.shared_object)
1355 return @as(u16, @bitCast(lhs.version_index)) < @as(u16, @bitCast(rhs.version_index));
1356 return mem.lessThan(u8, lhs.soname(ctx), rhs.soname(ctx));
1357 }
1358 };
1359
1360 const comp = elf_file.base.comp;
1361 const gpa = comp.gpa;
1362 var verneed = std.array_list.Managed(VersionedSymbol).init(gpa);
1363 defer verneed.deinit();
1364 try verneed.ensureTotalCapacity(dynsyms.len);
1365
1366 for (dynsyms, 1..) |entry, i| {
1367 const symbol = elf_file.symbol(entry.ref).?;
1368 if (symbol.flags.import and symbol.version_index.VERSION > elf.Versym.GLOBAL.VERSION) {
1369 const shared_object = symbol.file(elf_file).?.shared_object;
1370 verneed.appendAssumeCapacity(.{
1371 .index = i,
1372 .shared_object = shared_object.index,
1373 .version_index = symbol.version_index,
1374 });
1375 }
1376 }
1377
1378 mem.sort(VersionedSymbol, verneed.items, elf_file, VersionedSymbol.lessThan);
1379
1380 var last = verneed.items[0];
1381 var last_verneed = try vern.addVerneed(last.soname(elf_file), elf_file);
1382 var last_vernaux = try vern.addVernaux(last_verneed, last.versionString(elf_file), elf_file);
1383 versyms[last.index] = @bitCast(last_vernaux.other);
1384
1385 for (verneed.items[1..]) |ver| {
1386 if (ver.shared_object == last.shared_object) {
1387 if (ver.version_index != last.version_index) {
1388 last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file);
1389 }
1390 } else {
1391 last_verneed = try vern.addVerneed(ver.soname(elf_file), elf_file);
1392 last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file);
1393 }
1394 last = ver;
1395 versyms[ver.index] = @bitCast(last_vernaux.other);
1396 }
1397
1398 // Fixup offsets
1399 var count: usize = 0;
1400 var verneed_off: u32 = 0;
1401 var vernaux_off: u32 = @as(u32, @intCast(vern.verneed.items.len)) * @sizeOf(elf.Elf64_Verneed);
1402 for (vern.verneed.items, 0..) |*vsym, vsym_i| {
1403 if (vsym_i < vern.verneed.items.len - 1) vsym.vn_next = @sizeOf(elf.Elf64_Verneed);
1404 vsym.vn_aux = vernaux_off - verneed_off;
1405 var inner_off: u32 = 0;
1406 for (vern.vernaux.items[count..][0..vsym.vn_cnt], 0..) |*vaux, vaux_i| {
1407 if (vaux_i < vsym.vn_cnt - 1) vaux.next = @sizeOf(elf.Vernaux);
1408 inner_off += @sizeOf(elf.Vernaux);
1409 }
1410 vernaux_off += inner_off;
1411 verneed_off += @sizeOf(elf.Elf64_Verneed);
1412 count += vsym.vn_cnt;
1413 }
1414 }
1415
1416 fn addVerneed(vern: *VerneedSection, soname: []const u8, elf_file: *Elf) !*elf.Elf64_Verneed {
1417 const comp = elf_file.base.comp;
1418 const gpa = comp.gpa;
1419 const sym = try vern.verneed.addOne(gpa);
1420 sym.* = .{
1421 .vn_version = 1,
1422 .vn_cnt = 0,
1423 .vn_file = try elf_file.insertDynString(soname),
1424 .vn_aux = 0,
1425 .vn_next = 0,
1426 };
1427 return sym;
1428 }
1429
1430 fn addVernaux(
1431 vern: *VerneedSection,
1432 verneed_sym: *elf.Elf64_Verneed,
1433 version: [:0]const u8,
1434 elf_file: *Elf,
1435 ) !elf.Vernaux {
1436 const comp = elf_file.base.comp;
1437 const gpa = comp.gpa;
1438 const sym = try vern.vernaux.addOne(gpa);
1439 sym.* = .{
1440 .hash = HashSection.hasher(version),
1441 .flags = 0,
1442 .other = @bitCast(vern.index),
1443 .name = try elf_file.insertDynString(version),
1444 .next = 0,
1445 };
1446 verneed_sym.vn_cnt += 1;
1447 vern.index.VERSION += 1;
1448 return sym.*;
1449 }
1450
1451 pub fn size(vern: VerneedSection) usize {
1452 return vern.verneed.items.len * @sizeOf(elf.Elf64_Verneed) + vern.vernaux.items.len * @sizeOf(elf.Vernaux);
1453 }
1454
1455 pub fn write(vern: VerneedSection, writer: *std.Io.Writer) !void {
1456 try writer.writeSliceEndian(elf.Elf64_Verneed, vern.verneed.items, .little);
1457 try writer.writeSliceEndian(elf.Vernaux, vern.vernaux.items, .little);
1458 }
1459};
1460
1461pub const GroupSection = struct {
1462 shndx: u32,
1463 cg_ref: Elf.Ref,
1464
1465 fn group(cgs: GroupSection, elf_file: *Elf) *Elf.Group {
1466 const cg_file = elf_file.file(cgs.cg_ref.file).?;
1467 return cg_file.object.group(cgs.cg_ref.index);
1468 }
1469
1470 pub fn symbol(cgs: GroupSection, elf_file: *Elf) *Symbol {
1471 const cg = cgs.group(elf_file);
1472 const object = cg.file(elf_file).object;
1473 const shdr = object.shdrs.items[cg.shndx];
1474 return &object.symbols.items[shdr.sh_info];
1475 }
1476
1477 pub fn size(cgs: GroupSection, elf_file: *Elf) usize {
1478 const cg = cgs.group(elf_file);
1479 const members = cg.members(elf_file);
1480 return (members.len + 1) * @sizeOf(u32);
1481 }
1482
1483 pub fn write(cgs: GroupSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
1484 const cg = cgs.group(elf_file);
1485 const object = cg.file(elf_file).object;
1486 const members = cg.members(elf_file);
1487 try writer.writeInt(u32, if (cg.is_comdat) elf.GRP_COMDAT else 0, .little);
1488 for (members) |shndx| {
1489 const shdr = object.shdrs.items[shndx];
1490 switch (shdr.sh_type) {
1491 elf.SHT_RELA => {
1492 const atom_index = object.atoms_indexes.items[shdr.sh_info];
1493 const atom = object.atom(atom_index).?;
1494 const rela_shndx = for (elf_file.sections.items(.shdr), 0..) |rela_shdr, rela_shndx| {
1495 if (rela_shdr.sh_type == elf.SHT_RELA and
1496 atom.output_section_index == rela_shdr.sh_info)
1497 break rela_shndx;
1498 } else unreachable;
1499 try writer.writeInt(u32, @intCast(rela_shndx), .little);
1500 },
1501 else => {
1502 const atom_index = object.atoms_indexes.items[shndx];
1503 const atom = object.atom(atom_index).?;
1504 try writer.writeInt(u32, atom.output_section_index, .little);
1505 },
1506 }
1507 }
1508 }
1509};
1510
1511fn writeInt(value: anytype, elf_file: *Elf, writer: *std.Io.Writer) !void {
1512 const entry_size = elf_file.archPtrWidthBytes();
1513 const target = elf_file.getTarget();
1514 const endian = target.cpu.arch.endian();
1515 switch (entry_size) {
1516 2 => try writer.writeInt(i16, @intCast(value), endian),
1517 4 => try writer.writeInt(i32, @intCast(value), endian),
1518 8 => try writer.writeInt(i64, value, endian),
1519 else => unreachable,
1520 }
1521}
1522
1523const assert = std.debug.assert;
1524const builtin = @import("builtin");
1525const dev = @import("../../dev.zig");
1526const elf = std.elf;
1527const math = std.math;
1528const mem = std.mem;
1529const log = std.log.scoped(.link);
1530const relocs_log = std.log.scoped(.link_relocs);
1531const relocation = @import("relocation.zig");
1532const std = @import("std");
1533
1534const Allocator = std.mem.Allocator;
1535const Elf = @import("../Elf.zig");
1536const File = @import("file.zig").File;
1537const SharedObject = @import("SharedObject.zig");
1538const Symbol = @import("Symbol.zig");