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 const shdrs = elf_file.sections.items(.shdr);
776 const plt_addr = shdrs[elf_file.section_indexes.plt.?].sh_addr;
777 const got_plt_addr = shdrs[elf_file.section_indexes.got_plt.?].sh_addr;
778 var preamble = [_]u8{
779 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
780 0x41, 0x53, // push r11
781 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push qword ptr [rip] -> .got.plt[1]
782 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[2]
783 };
784 var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4;
785 mem.writeInt(i32, preamble[8..][0..4], @as(i32, @intCast(disp)), .little);
786 disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4;
787 mem.writeInt(i32, preamble[14..][0..4], @as(i32, @intCast(disp)), .little);
788 try writer.writeAll(&preamble);
789 try writer.splatByteAll(0xcc, preambleSize(.x86_64) - preamble.len);
790
791 for (plt.symbols.items, 0..) |ref, i| {
792 const sym = elf_file.symbol(ref).?;
793 const target_addr = sym.gotPltAddress(elf_file);
794 const source_addr = sym.pltAddress(elf_file);
795 disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4;
796 var entry = [_]u8{
797 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
798 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N
799 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N]
800 };
801 mem.writeInt(i32, entry[6..][0..4], @as(i32, @intCast(i)), .little);
802 mem.writeInt(i32, entry[12..][0..4], @as(i32, @intCast(disp)), .little);
803 try writer.writeAll(&entry);
804 }
805 }
806 };
807
808 const aarch64 = struct {
809 fn write(plt: PltSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
810 {
811 const shdrs = elf_file.sections.items(.shdr);
812 const plt_addr: i64 = @intCast(shdrs[elf_file.section_indexes.plt.?].sh_addr);
813 const got_plt_addr: i64 = @intCast(shdrs[elf_file.section_indexes.got_plt.?].sh_addr);
814 // TODO: relax if possible
815 // .got.plt[2]
816 const pages = try util.calcNumberOfPages(plt_addr + 4, got_plt_addr + 16);
817 const ldr_off: u12 = @truncate(@as(u64, @bitCast(got_plt_addr + 16)));
818 const add_off: u12 = @truncate(@as(u64, @bitCast(got_plt_addr + 16)));
819
820 const preamble = [_]util.encoding.Instruction{
821 .stp(.x16, .x30, .{ .pre_index = .{ .base = .sp, .index = -16 } }),
822 .adrp(.x16, pages << 12),
823 .ldr(.x17, .{ .unsigned_offset = .{ .base = .x16, .offset = ldr_off } }),
824 .add(.x16, .x16, .{ .immediate = add_off }),
825 .br(.x17),
826 .nop(),
827 .nop(),
828 .nop(),
829 };
830 comptime assert(preamble.len == 8);
831 for (preamble) |inst| try writer.writeInt(util.encoding.Instruction.Backing, @bitCast(inst), .little);
832 }
833
834 for (plt.symbols.items) |ref| {
835 const sym = elf_file.symbol(ref).?;
836 const target_addr = sym.gotPltAddress(elf_file);
837 const source_addr = sym.pltAddress(elf_file);
838 const pages = try util.calcNumberOfPages(source_addr, target_addr);
839 const ldr_off: u12 = @truncate(@as(u64, @bitCast(target_addr)));
840 const add_off: u12 = @truncate(@as(u64, @bitCast(target_addr)));
841 const insts = [_]util.encoding.Instruction{
842 .adrp(.x16, pages << 12),
843 .ldr(.x17, .{ .unsigned_offset = .{ .base = .x16, .offset = ldr_off } }),
844 .add(.x16, .x16, .{ .immediate = add_off }),
845 .br(.x17),
846 };
847 comptime assert(insts.len == 4);
848 for (insts) |inst| try writer.writeInt(util.encoding.Instruction.Backing, @bitCast(inst), .little);
849 }
850 }
851
852 const util = @import("../aarch64.zig");
853 };
854};
855
856pub const GotPltSection = struct {
857 pub const preamble_size = 24;
858
859 pub fn size(got_plt: GotPltSection, elf_file: *Elf) usize {
860 _ = got_plt;
861 return preamble_size + elf_file.plt.symbols.items.len * 8;
862 }
863
864 pub fn write(got_plt: GotPltSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
865 _ = got_plt;
866 {
867 // [0]: _DYNAMIC
868 const symbol = elf_file.linkerDefinedPtr().?.dynamicSymbol(elf_file).?;
869 try writer.writeInt(u64, @intCast(symbol.address(.{}, elf_file)), .little);
870 }
871 // [1]: 0x0
872 // [2]: 0x0
873 try writer.writeInt(u64, 0x0, .little);
874 try writer.writeInt(u64, 0x0, .little);
875 if (elf_file.section_indexes.plt) |shndx| {
876 const plt_addr = elf_file.sections.items(.shdr)[shndx].sh_addr;
877 for (0..elf_file.plt.symbols.items.len) |_| {
878 // [N]: .plt
879 try writer.writeInt(u64, plt_addr, .little);
880 }
881 }
882 }
883};
884
885pub const PltGotSection = struct {
886 symbols: std.ArrayList(Elf.Ref) = .empty,
887 output_symtab_ctx: Elf.SymtabCtx = .{},
888
889 pub fn deinit(plt_got: *PltGotSection, allocator: Allocator) void {
890 plt_got.symbols.deinit(allocator);
891 }
892
893 pub fn addSymbol(plt_got: *PltGotSection, ref: Elf.Ref, elf_file: *Elf) !void {
894 const comp = elf_file.base.comp;
895 const gpa = comp.gpa;
896 const index = @as(u32, @intCast(plt_got.symbols.items.len));
897 const symbol = elf_file.symbol(ref).?;
898 symbol.flags.has_pltgot = true;
899 symbol.addExtra(.{ .plt_got = index }, elf_file);
900 try plt_got.symbols.append(gpa, ref);
901 }
902
903 pub fn size(plt_got: PltGotSection, elf_file: *Elf) usize {
904 return plt_got.symbols.items.len * entrySize(elf_file.getTarget().cpu.arch);
905 }
906
907 pub fn entrySize(cpu_arch: std.Target.Cpu.Arch) usize {
908 return switch (cpu_arch) {
909 .x86_64 => 16,
910 .aarch64 => 4 * @sizeOf(u32),
911 else => @panic("TODO implement PltGotSection.entrySize for this arch"),
912 };
913 }
914
915 pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
916 const cpu_arch = elf_file.getTarget().cpu.arch;
917 switch (cpu_arch) {
918 .x86_64 => try x86_64.write(plt_got, elf_file, writer),
919 .aarch64 => try aarch64.write(plt_got, elf_file, writer),
920 else => return error.UnsupportedCpuArch,
921 }
922 }
923
924 pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void {
925 plt_got.output_symtab_ctx.nlocals = @as(u32, @intCast(plt_got.symbols.items.len));
926 for (plt_got.symbols.items) |ref| {
927 const name = elf_file.symbol(ref).?.name(elf_file);
928 plt_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1;
929 }
930 }
931
932 pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf) void {
933 for (plt_got.symbols.items, plt_got.output_symtab_ctx.ilocal..) |ref, ilocal| {
934 const sym = elf_file.symbol(ref).?;
935 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
936 elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
937 elf_file.strtab.appendSliceAssumeCapacity("$pltgot");
938 elf_file.strtab.appendAssumeCapacity(0);
939 elf_file.symtab.items[ilocal] = .{
940 .st_name = st_name,
941 .st_info = elf.STT_FUNC,
942 .st_other = 0,
943 .st_shndx = @intCast(elf_file.section_indexes.plt_got.?),
944 .st_value = @intCast(sym.pltGotAddress(elf_file)),
945 .st_size = 16,
946 };
947 }
948 }
949
950 const x86_64 = struct {
951 pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
952 for (plt_got.symbols.items) |ref| {
953 const sym = elf_file.symbol(ref).?;
954 const target_addr = sym.gotAddress(elf_file);
955 const source_addr = sym.pltGotAddress(elf_file);
956 const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 6)) - 4;
957 var entry = [_]u8{
958 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
959 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got[N]
960 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
961 };
962 mem.writeInt(i32, entry[6..][0..4], @as(i32, @intCast(disp)), .little);
963 try writer.writeAll(&entry);
964 }
965 }
966 };
967
968 const aarch64 = struct {
969 fn write(plt_got: PltGotSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
970 for (plt_got.symbols.items) |ref| {
971 const sym = elf_file.symbol(ref).?;
972 const target_addr = sym.gotAddress(elf_file);
973 const source_addr = sym.pltGotAddress(elf_file);
974 const pages = try util.calcNumberOfPages(source_addr, target_addr);
975 const off: u12 = @truncate(@as(u64, @bitCast(target_addr)));
976 const insts = [_]util.encoding.Instruction{
977 .adrp(.x16, pages << 12),
978 .ldr(.x17, .{ .unsigned_offset = .{ .base = .x16, .offset = off } }),
979 .br(.x17),
980 .nop(),
981 };
982 comptime assert(insts.len == 4);
983 for (insts) |inst| try writer.writeInt(util.encoding.Instruction.Backing, @bitCast(inst), .little);
984 }
985 }
986
987 const util = @import("../aarch64.zig");
988 };
989};
990
991pub const CopyRelSection = struct {
992 symbols: std.ArrayList(Elf.Ref) = .empty,
993
994 pub fn deinit(copy_rel: *CopyRelSection, allocator: Allocator) void {
995 copy_rel.symbols.deinit(allocator);
996 }
997
998 pub fn addSymbol(copy_rel: *CopyRelSection, ref: Elf.Ref, elf_file: *Elf) !void {
999 const comp = elf_file.base.comp;
1000 const gpa = comp.gpa;
1001 const index = @as(u32, @intCast(copy_rel.symbols.items.len));
1002 const symbol = elf_file.symbol(ref).?;
1003 symbol.flags.import = true;
1004 symbol.flags.@"export" = true;
1005 symbol.flags.has_copy_rel = true;
1006 symbol.flags.weak = false;
1007 symbol.addExtra(.{ .copy_rel = index }, elf_file);
1008 try copy_rel.symbols.append(gpa, ref);
1009
1010 const shared_object = symbol.file(elf_file).?.shared_object;
1011 if (shared_object.aliases == null) {
1012 try shared_object.initSymbolAliases(elf_file);
1013 }
1014
1015 const aliases = shared_object.symbolAliases(ref.index, elf_file);
1016 for (aliases) |alias| {
1017 if (alias == ref.index) continue;
1018 const alias_sym = &shared_object.symbols.items[alias];
1019 alias_sym.flags.import = true;
1020 alias_sym.flags.@"export" = true;
1021 alias_sym.flags.has_copy_rel = true;
1022 alias_sym.flags.needs_copy_rel = true;
1023 alias_sym.flags.weak = false;
1024 try elf_file.dynsym.addSymbol(.{ .index = alias, .file = shared_object.index }, elf_file);
1025 }
1026 }
1027
1028 pub fn updateSectionSize(copy_rel: CopyRelSection, shndx: u32, elf_file: *Elf) !void {
1029 const shdr = &elf_file.sections.items(.shdr)[shndx];
1030 for (copy_rel.symbols.items) |ref| {
1031 const symbol = elf_file.symbol(ref).?;
1032 const shared_object = symbol.file(elf_file).?.shared_object;
1033 const alignment = try symbol.dsoAlignment(elf_file);
1034 symbol.value = @intCast(mem.alignForward(u64, shdr.sh_size, alignment));
1035 shdr.sh_addralign = @max(shdr.sh_addralign, alignment);
1036 shdr.sh_size = @as(u64, @intCast(symbol.value)) + symbol.elfSym(elf_file).st_size;
1037
1038 const aliases = shared_object.symbolAliases(ref.index, elf_file);
1039 for (aliases) |alias| {
1040 if (alias == ref.index) continue;
1041 const alias_sym = &shared_object.symbols.items[alias];
1042 alias_sym.value = symbol.value;
1043 }
1044 }
1045 }
1046
1047 pub fn addRela(copy_rel: CopyRelSection, elf_file: *Elf) !void {
1048 const comp = elf_file.base.comp;
1049 const gpa = comp.gpa;
1050 const cpu_arch = elf_file.getTarget().cpu.arch;
1051 try elf_file.rela_dyn.ensureUnusedCapacity(gpa, copy_rel.numRela());
1052
1053 relocs_log.debug(".copy.rel", .{});
1054
1055 for (copy_rel.symbols.items) |ref| {
1056 const sym = elf_file.symbol(ref).?;
1057 assert(sym.flags.import and sym.flags.has_copy_rel);
1058 const extra = sym.extra(elf_file);
1059 elf_file.addRelaDynAssumeCapacity(.{
1060 .offset = @intCast(sym.address(.{}, elf_file)),
1061 .sym = extra.dynamic,
1062 .type = relocation.encode(.copy, cpu_arch),
1063 });
1064 }
1065 }
1066
1067 pub fn numRela(copy_rel: CopyRelSection) usize {
1068 return copy_rel.symbols.items.len;
1069 }
1070};
1071
1072pub const DynsymSection = struct {
1073 entries: std.ArrayList(Entry) = .empty,
1074
1075 pub const Entry = struct {
1076 /// Ref of the symbol which gets privilege of getting a dynamic treatment
1077 ref: Elf.Ref,
1078 /// Offset into .dynstrtab
1079 off: u32,
1080 };
1081
1082 pub fn deinit(dynsym: *DynsymSection, allocator: Allocator) void {
1083 dynsym.entries.deinit(allocator);
1084 }
1085
1086 pub fn addSymbol(dynsym: *DynsymSection, ref: Elf.Ref, elf_file: *Elf) !void {
1087 const comp = elf_file.base.comp;
1088 const gpa = comp.gpa;
1089 const index = @as(u32, @intCast(dynsym.entries.items.len + 1));
1090 const sym = elf_file.symbol(ref).?;
1091 sym.flags.has_dynamic = true;
1092 sym.addExtra(.{ .dynamic = index }, elf_file);
1093 const off = try elf_file.insertDynString(sym.name(elf_file));
1094 try dynsym.entries.append(gpa, .{ .ref = ref, .off = off });
1095 }
1096
1097 pub fn sort(dynsym: *DynsymSection, elf_file: *Elf) void {
1098 const Sort = struct {
1099 pub fn lessThan(ctx: *Elf, lhs: Entry, rhs: Entry) bool {
1100 const lhs_sym = ctx.symbol(lhs.ref).?;
1101 const rhs_sym = ctx.symbol(rhs.ref).?;
1102
1103 if (lhs_sym.flags.@"export" != rhs_sym.flags.@"export") {
1104 return rhs_sym.flags.@"export";
1105 }
1106
1107 // TODO cache hash values
1108 const nbuckets = ctx.gnu_hash.num_buckets;
1109 const lhs_hash = GnuHashSection.hasher(lhs_sym.name(ctx)) % nbuckets;
1110 const rhs_hash = GnuHashSection.hasher(rhs_sym.name(ctx)) % nbuckets;
1111
1112 if (lhs_hash == rhs_hash)
1113 return lhs_sym.extra(ctx).dynamic < rhs_sym.extra(ctx).dynamic;
1114 return lhs_hash < rhs_hash;
1115 }
1116 };
1117
1118 var num_exports: u32 = 0;
1119 for (dynsym.entries.items) |entry| {
1120 const sym = elf_file.symbol(entry.ref).?;
1121 if (sym.flags.@"export") num_exports += 1;
1122 }
1123
1124 elf_file.gnu_hash.num_buckets = @divTrunc(num_exports, GnuHashSection.load_factor) + 1;
1125
1126 std.mem.sort(Entry, dynsym.entries.items, elf_file, Sort.lessThan);
1127
1128 for (dynsym.entries.items, 1..) |entry, index| {
1129 const sym = elf_file.symbol(entry.ref).?;
1130 var extra = sym.extra(elf_file);
1131 extra.dynamic = @as(u32, @intCast(index));
1132 sym.setExtra(extra, elf_file);
1133 }
1134 }
1135
1136 pub fn size(dynsym: DynsymSection) usize {
1137 return dynsym.count() * @sizeOf(elf.Elf64_Sym);
1138 }
1139
1140 pub fn count(dynsym: DynsymSection) u32 {
1141 return @as(u32, @intCast(dynsym.entries.items.len + 1));
1142 }
1143
1144 pub fn write(dynsym: DynsymSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
1145 try writer.writeStruct(Elf.null_sym, .little);
1146 for (dynsym.entries.items) |entry| {
1147 const sym = elf_file.symbol(entry.ref).?;
1148 var out_sym: elf.Elf64_Sym = Elf.null_sym;
1149 sym.setOutputSym(elf_file, &out_sym);
1150 out_sym.st_name = entry.off;
1151 try writer.writeStruct(out_sym, .little);
1152 }
1153 }
1154};
1155
1156pub const HashSection = struct {
1157 buffer: std.ArrayList(u8) = .empty,
1158
1159 pub fn deinit(hs: *HashSection, allocator: Allocator) void {
1160 hs.buffer.deinit(allocator);
1161 }
1162
1163 pub fn generate(hs: *HashSection, elf_file: *Elf) !void {
1164 if (elf_file.dynsym.count() == 1) return;
1165
1166 const comp = elf_file.base.comp;
1167 const gpa = comp.gpa;
1168 const nsyms = elf_file.dynsym.count();
1169
1170 var buckets = try gpa.alloc(u32, nsyms);
1171 defer gpa.free(buckets);
1172 @memset(buckets, 0);
1173
1174 var chains = try gpa.alloc(u32, nsyms);
1175 defer gpa.free(chains);
1176 @memset(chains, 0);
1177
1178 for (elf_file.dynsym.entries.items, 1..) |entry, i| {
1179 const name = elf_file.getDynString(entry.off);
1180 const hash = hasher(name) % buckets.len;
1181 chains[@as(u32, @intCast(i))] = buckets[hash];
1182 buckets[hash] = @as(u32, @intCast(i));
1183 }
1184
1185 try hs.buffer.ensureTotalCapacityPrecise(gpa, (2 + nsyms * 2) * 4);
1186 var w: std.Io.Writer = .fixed(hs.buffer.unusedCapacitySlice());
1187 w.writeInt(u32, @as(u32, @intCast(nsyms)), .little) catch unreachable;
1188 w.writeInt(u32, @as(u32, @intCast(nsyms)), .little) catch unreachable;
1189 w.writeAll(@ptrCast(buckets)) catch unreachable;
1190 w.writeAll(@ptrCast(chains)) catch unreachable;
1191 hs.buffer.items.len += w.end;
1192 }
1193
1194 pub inline fn size(hs: HashSection) usize {
1195 return hs.buffer.items.len;
1196 }
1197
1198 pub fn hasher(name: [:0]const u8) u32 {
1199 var h: u32 = 0;
1200 var g: u32 = 0;
1201 for (name) |c| {
1202 h = (h << 4) + c;
1203 g = h & 0xf0000000;
1204 if (g > 0) h ^= g >> 24;
1205 h &= ~g;
1206 }
1207 return h;
1208 }
1209};
1210
1211pub const GnuHashSection = struct {
1212 num_buckets: u32 = 0,
1213 num_bloom: u32 = 1,
1214 num_exports: u32 = 0,
1215
1216 pub const load_factor = 8;
1217 pub const header_size = 16;
1218 pub const bloom_shift = 26;
1219
1220 fn getExports(elf_file: *Elf) []const DynsymSection.Entry {
1221 const start = for (elf_file.dynsym.entries.items, 0..) |entry, i| {
1222 const sym = elf_file.symbol(entry.ref).?;
1223 if (sym.flags.@"export") break i;
1224 } else elf_file.dynsym.entries.items.len;
1225 return elf_file.dynsym.entries.items[start..];
1226 }
1227
1228 inline fn bitCeil(x: u64) u64 {
1229 if (@popCount(x) == 1) return x;
1230 return @as(u64, @intCast(@as(u128, 1) << (64 - @clz(x))));
1231 }
1232
1233 pub fn calcSize(hash: *GnuHashSection, elf_file: *Elf) !void {
1234 hash.num_exports = @as(u32, @intCast(getExports(elf_file).len));
1235 if (hash.num_exports > 0) {
1236 const num_bits = hash.num_exports * 12;
1237 hash.num_bloom = @as(u32, @intCast(bitCeil(@divTrunc(num_bits, 64))));
1238 }
1239 }
1240
1241 pub fn size(hash: GnuHashSection) usize {
1242 return header_size + hash.num_bloom * 8 + hash.num_buckets * 4 + hash.num_exports * 4;
1243 }
1244
1245 pub fn write(hash: GnuHashSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
1246 const exports = getExports(elf_file);
1247 const export_off = elf_file.dynsym.count() - hash.num_exports;
1248
1249 try writer.writeInt(u32, hash.num_buckets, .little);
1250 try writer.writeInt(u32, export_off, .little);
1251 try writer.writeInt(u32, hash.num_bloom, .little);
1252 try writer.writeInt(u32, bloom_shift, .little);
1253
1254 const comp = elf_file.base.comp;
1255 const gpa = comp.gpa;
1256 const hashes = try gpa.alloc(u32, exports.len);
1257 defer gpa.free(hashes);
1258 const indices = try gpa.alloc(u32, exports.len);
1259 defer gpa.free(indices);
1260
1261 // Compose and write the bloom filter
1262 const bloom = try gpa.alloc(u64, hash.num_bloom);
1263 defer gpa.free(bloom);
1264 @memset(bloom, 0);
1265
1266 for (exports, 0..) |entry, i| {
1267 const sym = elf_file.symbol(entry.ref).?;
1268 const h = hasher(sym.name(elf_file));
1269 hashes[i] = h;
1270 indices[i] = h % hash.num_buckets;
1271 const idx = @divTrunc(h, 64) % hash.num_bloom;
1272 bloom[idx] |= @as(u64, 1) << @as(u6, @intCast(h % 64));
1273 bloom[idx] |= @as(u64, 1) << @as(u6, @intCast((h >> bloom_shift) % 64));
1274 }
1275
1276 try writer.writeSliceEndian(u64, bloom, .little);
1277
1278 // Fill in the hash bucket indices
1279 const buckets = try gpa.alloc(u32, hash.num_buckets);
1280 defer gpa.free(buckets);
1281 @memset(buckets, 0);
1282
1283 for (0..hash.num_exports) |i| {
1284 if (buckets[indices[i]] == 0) {
1285 buckets[indices[i]] = @as(u32, @intCast(i + export_off));
1286 }
1287 }
1288
1289 try writer.writeSliceEndian(u32, buckets, .little);
1290
1291 // Finally, write the hash table
1292 const table = try gpa.alloc(u32, hash.num_exports);
1293 defer gpa.free(table);
1294 @memset(table, 0);
1295
1296 for (0..hash.num_exports) |i| {
1297 const h = hashes[i];
1298 if (i == exports.len - 1 or indices[i] != indices[i + 1]) {
1299 table[i] = h | 1;
1300 } else {
1301 table[i] = h & ~@as(u32, 1);
1302 }
1303 }
1304
1305 try writer.writeSliceEndian(u32, table, .little);
1306 }
1307
1308 pub fn hasher(name: [:0]const u8) u32 {
1309 var h: u32 = 5381;
1310 for (name) |c| {
1311 h = (h << 5) +% h +% c;
1312 }
1313 return h;
1314 }
1315};
1316
1317pub const VerneedSection = struct {
1318 verneed: std.ArrayList(elf.Elf64_Verneed) = .empty,
1319 vernaux: std.ArrayList(elf.Vernaux) = .empty,
1320 index: elf.Versym = .{ .VERSION = elf.Versym.GLOBAL.VERSION + 1, .HIDDEN = false },
1321
1322 pub fn deinit(vern: *VerneedSection, allocator: Allocator) void {
1323 vern.verneed.deinit(allocator);
1324 vern.vernaux.deinit(allocator);
1325 }
1326
1327 pub fn generate(vern: *VerneedSection, elf_file: *Elf) !void {
1328 const dynsyms = elf_file.dynsym.entries.items;
1329 var versyms = elf_file.versym.items;
1330
1331 const VersionedSymbol = struct {
1332 /// Index in the output version table
1333 index: usize,
1334 /// Index of the defining this symbol version shared object file
1335 shared_object: File.Index,
1336 /// Version index
1337 version_index: elf.Versym,
1338
1339 fn soname(this: @This(), ctx: *Elf) []const u8 {
1340 const shared_object = ctx.file(this.shared_object).?.shared_object;
1341 return shared_object.soname();
1342 }
1343
1344 fn versionString(this: @This(), ctx: *Elf) [:0]const u8 {
1345 const shared_object = ctx.file(this.shared_object).?.shared_object;
1346 return shared_object.versionString(this.version_index);
1347 }
1348
1349 pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool {
1350 if (lhs.shared_object == rhs.shared_object)
1351 return @as(u16, @bitCast(lhs.version_index)) < @as(u16, @bitCast(rhs.version_index));
1352 return mem.lessThan(u8, lhs.soname(ctx), rhs.soname(ctx));
1353 }
1354 };
1355
1356 const comp = elf_file.base.comp;
1357 const gpa = comp.gpa;
1358 var verneed = std.array_list.Managed(VersionedSymbol).init(gpa);
1359 defer verneed.deinit();
1360 try verneed.ensureTotalCapacity(dynsyms.len);
1361
1362 for (dynsyms, 1..) |entry, i| {
1363 const symbol = elf_file.symbol(entry.ref).?;
1364 if (symbol.flags.import and symbol.version_index.VERSION > elf.Versym.GLOBAL.VERSION) {
1365 const shared_object = symbol.file(elf_file).?.shared_object;
1366 verneed.appendAssumeCapacity(.{
1367 .index = i,
1368 .shared_object = shared_object.index,
1369 .version_index = symbol.version_index,
1370 });
1371 }
1372 }
1373
1374 mem.sort(VersionedSymbol, verneed.items, elf_file, VersionedSymbol.lessThan);
1375
1376 var last = verneed.items[0];
1377 var last_verneed = try vern.addVerneed(last.soname(elf_file), elf_file);
1378 var last_vernaux = try vern.addVernaux(last_verneed, last.versionString(elf_file), elf_file);
1379 versyms[last.index] = @bitCast(last_vernaux.other);
1380
1381 for (verneed.items[1..]) |ver| {
1382 if (ver.shared_object == last.shared_object) {
1383 if (ver.version_index != last.version_index) {
1384 last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file);
1385 }
1386 } else {
1387 last_verneed = try vern.addVerneed(ver.soname(elf_file), elf_file);
1388 last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file);
1389 }
1390 last = ver;
1391 versyms[ver.index] = @bitCast(last_vernaux.other);
1392 }
1393
1394 // Fixup offsets
1395 var count: usize = 0;
1396 var verneed_off: u32 = 0;
1397 var vernaux_off: u32 = @as(u32, @intCast(vern.verneed.items.len)) * @sizeOf(elf.Elf64_Verneed);
1398 for (vern.verneed.items, 0..) |*vsym, vsym_i| {
1399 if (vsym_i < vern.verneed.items.len - 1) vsym.vn_next = @sizeOf(elf.Elf64_Verneed);
1400 vsym.vn_aux = vernaux_off - verneed_off;
1401 var inner_off: u32 = 0;
1402 for (vern.vernaux.items[count..][0..vsym.vn_cnt], 0..) |*vaux, vaux_i| {
1403 if (vaux_i < vsym.vn_cnt - 1) vaux.next = @sizeOf(elf.Vernaux);
1404 inner_off += @sizeOf(elf.Vernaux);
1405 }
1406 vernaux_off += inner_off;
1407 verneed_off += @sizeOf(elf.Elf64_Verneed);
1408 count += vsym.vn_cnt;
1409 }
1410 }
1411
1412 fn addVerneed(vern: *VerneedSection, soname: []const u8, elf_file: *Elf) !*elf.Elf64_Verneed {
1413 const comp = elf_file.base.comp;
1414 const gpa = comp.gpa;
1415 const sym = try vern.verneed.addOne(gpa);
1416 sym.* = .{
1417 .vn_version = 1,
1418 .vn_cnt = 0,
1419 .vn_file = try elf_file.insertDynString(soname),
1420 .vn_aux = 0,
1421 .vn_next = 0,
1422 };
1423 return sym;
1424 }
1425
1426 fn addVernaux(
1427 vern: *VerneedSection,
1428 verneed_sym: *elf.Elf64_Verneed,
1429 version: [:0]const u8,
1430 elf_file: *Elf,
1431 ) !elf.Vernaux {
1432 const comp = elf_file.base.comp;
1433 const gpa = comp.gpa;
1434 const sym = try vern.vernaux.addOne(gpa);
1435 sym.* = .{
1436 .hash = HashSection.hasher(version),
1437 .flags = 0,
1438 .other = @bitCast(vern.index),
1439 .name = try elf_file.insertDynString(version),
1440 .next = 0,
1441 };
1442 verneed_sym.vn_cnt += 1;
1443 vern.index.VERSION += 1;
1444 return sym.*;
1445 }
1446
1447 pub fn size(vern: VerneedSection) usize {
1448 return vern.verneed.items.len * @sizeOf(elf.Elf64_Verneed) + vern.vernaux.items.len * @sizeOf(elf.Vernaux);
1449 }
1450
1451 pub fn write(vern: VerneedSection, writer: *std.Io.Writer) !void {
1452 try writer.writeSliceEndian(elf.Elf64_Verneed, vern.verneed.items, .little);
1453 try writer.writeSliceEndian(elf.Vernaux, vern.vernaux.items, .little);
1454 }
1455};
1456
1457pub const GroupSection = struct {
1458 shndx: u32,
1459 cg_ref: Elf.Ref,
1460
1461 fn group(cgs: GroupSection, elf_file: *Elf) *Elf.Group {
1462 const cg_file = elf_file.file(cgs.cg_ref.file).?;
1463 return cg_file.object.group(cgs.cg_ref.index);
1464 }
1465
1466 pub fn symbol(cgs: GroupSection, elf_file: *Elf) *Symbol {
1467 const cg = cgs.group(elf_file);
1468 const object = cg.file(elf_file).object;
1469 const shdr = object.shdrs.items[cg.shndx];
1470 return &object.symbols.items[shdr.sh_info];
1471 }
1472
1473 pub fn size(cgs: GroupSection, elf_file: *Elf) usize {
1474 const cg = cgs.group(elf_file);
1475 const members = cg.members(elf_file);
1476 return (members.len + 1) * @sizeOf(u32);
1477 }
1478
1479 pub fn write(cgs: GroupSection, elf_file: *Elf, writer: *std.Io.Writer) !void {
1480 const cg = cgs.group(elf_file);
1481 const object = cg.file(elf_file).object;
1482 const members = cg.members(elf_file);
1483 try writer.writeInt(u32, if (cg.is_comdat) elf.GRP_COMDAT else 0, .little);
1484 for (members) |shndx| {
1485 const shdr = object.shdrs.items[shndx];
1486 switch (shdr.sh_type) {
1487 elf.SHT_RELA => {
1488 const atom_index = object.atoms_indexes.items[shdr.sh_info];
1489 const atom = object.atom(atom_index).?;
1490 const rela_shndx = for (elf_file.sections.items(.shdr), 0..) |rela_shdr, rela_shndx| {
1491 if (rela_shdr.sh_type == elf.SHT_RELA and
1492 atom.output_section_index == rela_shdr.sh_info)
1493 break rela_shndx;
1494 } else unreachable;
1495 try writer.writeInt(u32, @intCast(rela_shndx), .little);
1496 },
1497 else => {
1498 const atom_index = object.atoms_indexes.items[shndx];
1499 const atom = object.atom(atom_index).?;
1500 try writer.writeInt(u32, atom.output_section_index, .little);
1501 },
1502 }
1503 }
1504 }
1505};
1506
1507fn writeInt(value: anytype, elf_file: *Elf, writer: *std.Io.Writer) !void {
1508 const entry_size = elf_file.archPtrWidthBytes();
1509 const target = elf_file.getTarget();
1510 const endian = target.cpu.arch.endian();
1511 switch (entry_size) {
1512 2 => try writer.writeInt(i16, @intCast(value), endian),
1513 4 => try writer.writeInt(i32, @intCast(value), endian),
1514 8 => try writer.writeInt(i64, value, endian),
1515 else => unreachable,
1516 }
1517}
1518
1519const assert = std.debug.assert;
1520const builtin = @import("builtin");
1521const elf = std.elf;
1522const math = std.math;
1523const mem = std.mem;
1524const log = std.log.scoped(.link);
1525const relocs_log = std.log.scoped(.link_relocs);
1526const relocation = @import("relocation.zig");
1527const std = @import("std");
1528
1529const Allocator = std.mem.Allocator;
1530const Elf = @import("../Elf.zig");
1531const File = @import("file.zig").File;
1532const SharedObject = @import("SharedObject.zig");
1533const Symbol = @import("Symbol.zig");