authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-04 12:06:52+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-16 19:33:04+02:00
logd6cec5a586c55f471d3f830f34d7a5f668337fb9
tree65080226790b92497314415963eb2f132c2c3f13
parent66f34b15e8573d669ef99d47337162f2aa91d602

elf: add more prepwork for linking c++ objects


5 files changed, 710 insertions(+), 107 deletions(-)

src/link/Elf.zig+163-6
...@@ -22,6 +22,9 @@ shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},...@@ -22,6 +22,9 @@ shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
22phdr_to_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},22phdr_to_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
23/// File offset into the shdr table.23/// File offset into the shdr table.
24shdr_table_offset: ?u64 = null,24shdr_table_offset: ?u64 = null,
25/// Table of lists of atoms per output section.
26/// This table is not used to track incrementally generated atoms.
27output_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{},
2528
26/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.29/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
27/// Same order as in the file.30/// Same order as in the file.
...@@ -61,6 +64,7 @@ strtab: StringTable(.strtab) = .{},...@@ -61,6 +64,7 @@ strtab: StringTable(.strtab) = .{},
6164
62/// Representation of the GOT table as committed to the file.65/// Representation of the GOT table as committed to the file.
63got: GotSection = .{},66got: GotSection = .{},
67rela_dyn: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
6468
65/// Tracked section headers69/// Tracked section headers
66text_section_index: ?u16 = null,70text_section_index: ?u16 = null,
...@@ -109,6 +113,9 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},...@@ -109,6 +113,9 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},
109resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},113resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
110symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},114symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
111115
116has_text_reloc: bool = false,
117num_ifunc_dynrelocs: usize = 0,
118
112phdr_table_dirty: bool = false,119phdr_table_dirty: bool = false,
113shdr_table_dirty: bool = false,120shdr_table_dirty: bool = false,
114121
...@@ -317,6 +324,10 @@ pub fn deinit(self: *Elf) void {...@@ -317,6 +324,10 @@ pub fn deinit(self: *Elf) void {
317 self.shdrs.deinit(gpa);324 self.shdrs.deinit(gpa);
318 self.phdr_to_shdr_table.deinit(gpa);325 self.phdr_to_shdr_table.deinit(gpa);
319 self.phdrs.deinit(gpa);326 self.phdrs.deinit(gpa);
327 for (self.output_sections.values()) |*list| {
328 list.deinit(gpa);
329 }
330 self.output_sections.deinit(gpa);
320 self.shstrtab.deinit(gpa);331 self.shstrtab.deinit(gpa);
321 self.strtab.deinit(gpa);332 self.strtab.deinit(gpa);
322 self.symbols.deinit(gpa);333 self.symbols.deinit(gpa);
...@@ -358,6 +369,7 @@ pub fn deinit(self: *Elf) void {...@@ -358,6 +369,7 @@ pub fn deinit(self: *Elf) void {
358 self.comdat_groups.deinit(gpa);369 self.comdat_groups.deinit(gpa);
359 self.comdat_groups_owners.deinit(gpa);370 self.comdat_groups_owners.deinit(gpa);
360 self.comdat_groups_table.deinit(gpa);371 self.comdat_groups_table.deinit(gpa);
372 self.rela_dyn.deinit(gpa);
361}373}
362374
363pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {375pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
...@@ -1249,6 +1261,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1249,6 +1261,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1249 for (self.objects.items) |index| {1261 for (self.objects.items) |index| {
1250 try self.file(index).?.object.addAtomsToOutputSections(self);1262 try self.file(index).?.object.addAtomsToOutputSections(self);
1251 }1263 }
1264 try self.sortInitFini();
1252 try self.updateSectionSizes();1265 try self.updateSectionSizes();
12531266
1254 try self.allocateSections();1267 try self.allocateSections();
...@@ -1316,7 +1329,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1316,7 +1329,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1316 const code = try zig_module.codeAlloc(self, atom_index);1329 const code = try zig_module.codeAlloc(self, atom_index);
1317 defer gpa.free(code);1330 defer gpa.free(code);
1318 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;1331 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
1319 try atom_ptr.resolveRelocsAlloc(self, code);1332 atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) {
1333 // TODO
1334 error.RelaxFail, error.InvalidInstruction, error.CannotEncode => {
1335 log.err("relaxing intructions failed; TODO this should be a fatal linker error", .{});
1336 },
1337 else => |e| return e,
1338 };
1320 try self.base.file.?.pwriteAll(code, file_offset);1339 try self.base.file.?.pwriteAll(code, file_offset);
1321 }1340 }
13221341
...@@ -3460,6 +3479,69 @@ fn initSections(self: *Elf) !void {...@@ -3460,6 +3479,69 @@ fn initSections(self: *Elf) !void {
3460 }3479 }
3461}3480}
34623481
3482fn sortInitFini(self: *Elf) !void {
3483 const gpa = self.base.allocator;
3484
3485 const Entry = struct {
3486 priority: i32,
3487 atom_index: Atom.Index,
3488
3489 pub fn lessThan(ctx: void, lhs: @This(), rhs: @This()) bool {
3490 _ = ctx;
3491 return lhs.priority < rhs.priority;
3492 }
3493 };
3494
3495 for (self.shdrs.items, 0..) |*shdr, shndx| {
3496 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
3497
3498 var is_init_fini = false;
3499 var is_ctor_dtor = false;
3500 switch (shdr.sh_type) {
3501 elf.SHT_PREINIT_ARRAY,
3502 elf.SHT_INIT_ARRAY,
3503 elf.SHT_FINI_ARRAY,
3504 => is_init_fini = true,
3505 else => {
3506 const name = self.shstrtab.getAssumeExists(shdr.sh_name);
3507 is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null;
3508 },
3509 }
3510
3511 if (!is_init_fini and !is_ctor_dtor) continue;
3512
3513 const atom_list = self.output_sections.getPtr(@intCast(shndx)) orelse continue;
3514
3515 var entries = std.ArrayList(Entry).init(gpa);
3516 try entries.ensureTotalCapacityPrecise(atom_list.items.len);
3517 defer entries.deinit();
3518
3519 for (atom_list.items) |atom_index| {
3520 const atom_ptr = self.atom(atom_index).?;
3521 const object = atom_ptr.file(self).?.object;
3522 const priority = blk: {
3523 if (is_ctor_dtor) {
3524 if (mem.indexOf(u8, object.path, "crtbegin") != null) break :blk std.math.minInt(i32);
3525 if (mem.indexOf(u8, object.path, "crtend") != null) break :blk std.math.maxInt(i32);
3526 }
3527 const default: i32 = if (is_ctor_dtor) -1 else std.math.maxInt(i32);
3528 const name = atom_ptr.name(self);
3529 var it = mem.splitBackwards(u8, name, ".");
3530 const priority = std.fmt.parseUnsigned(u16, it.first(), 10) catch default;
3531 break :blk priority;
3532 };
3533 entries.appendAssumeCapacity(.{ .priority = priority, .atom_index = atom_index });
3534 }
3535
3536 mem.sort(Entry, entries.items, {}, Entry.lessThan);
3537
3538 atom_list.clearRetainingCapacity();
3539 for (entries.items) |entry| {
3540 atom_list.appendAssumeCapacity(entry.atom_index);
3541 }
3542 }
3543}
3544
3463fn sectionRank(self: *Elf, shdr: elf.Elf64_Shdr) u8 {3545fn sectionRank(self: *Elf, shdr: elf.Elf64_Shdr) u8 {
3464 const name = self.shstrtab.getAssumeExists(shdr.sh_name);3546 const name = self.shstrtab.getAssumeExists(shdr.sh_name);
3465 const flags = shdr.sh_flags;3547 const flags = shdr.sh_flags;
...@@ -3926,6 +4008,8 @@ fn writeAtoms(self: *Elf) !void {...@@ -3926,6 +4008,8 @@ fn writeAtoms(self: *Elf) !void {
3926 if (shdr.sh_type == elf.SHT_NULL) continue;4008 if (shdr.sh_type == elf.SHT_NULL) continue;
3927 if (shdr.sh_type == elf.SHT_NOBITS) continue;4009 if (shdr.sh_type == elf.SHT_NOBITS) continue;
39284010
4011 const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
4012
3929 log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)});4013 log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)});
39304014
3931 const buffer = try gpa.alloc(u8, shdr.sh_size);4015 const buffer = try gpa.alloc(u8, shdr.sh_size);
...@@ -3937,8 +4021,32 @@ fn writeAtoms(self: *Elf) !void {...@@ -3937,8 +4021,32 @@ fn writeAtoms(self: *Elf) !void {
3937 0;4021 0;
3938 @memset(buffer, padding_byte);4022 @memset(buffer, padding_byte);
39394023
3940 for (self.objects.items) |index| {4024 for (atom_list.items) |atom_index| {
3941 try self.file(index).?.object.writeAtoms(self, @intCast(shndx), buffer, &undefs);4025 const atom_ptr = self.atom(atom_index).?;
4026 assert(atom_ptr.flags.alive);
4027
4028 const object = atom_ptr.file(self).?.object;
4029 const offset = atom_ptr.value - shdr.sh_addr;
4030
4031 log.debug("writing atom({d}) at 0x{x}", .{ atom_index, shdr.sh_offset + offset });
4032
4033 // TODO decompress directly into provided buffer
4034 const out_code = buffer[offset..][0..atom_ptr.size];
4035 const in_code = try object.codeDecompressAlloc(self, atom_index);
4036 defer gpa.free(in_code);
4037 @memcpy(out_code, in_code);
4038
4039 if (shdr.sh_flags & elf.SHF_ALLOC == 0) {
4040 try atom_ptr.resolveRelocsNonAlloc(self, out_code, &undefs);
4041 } else {
4042 atom_ptr.resolveRelocsAlloc(self, out_code) catch |err| switch (err) {
4043 // TODO
4044 error.RelaxFail, error.InvalidInstruction, error.CannotEncode => {
4045 log.err("relaxing intructions failed; TODO this should be a fatal linker error", .{});
4046 },
4047 else => |e| return e,
4048 };
4049 }
3942 }4050 }
39434051
3944 try self.base.file.?.pwriteAll(buffer, shdr.sh_offset);4052 try self.base.file.?.pwriteAll(buffer, shdr.sh_offset);
...@@ -4495,9 +4603,58 @@ pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {...@@ -4495,9 +4603,58 @@ pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
4495 } else return null;4603 } else return null;
4496}4604}
44974605
4498pub fn calcNumIRelativeRelocs(self: *Elf) u64 {4606const RelaDyn = struct {
4499 _ = self;4607 offset: u64,
4500 unreachable; // TODO4608 sym: u64 = 0,
4609 type: u32,
4610 addend: i64 = 0,
4611};
4612
4613pub fn addRelaDyn(self: *Elf, opts: RelaDyn) !void {
4614 try self.rela_dyn.ensureUnusedCapacity(self.base.alloctor, 1);
4615 self.addRelaDynAssumeCapacity(opts);
4616}
4617
4618pub fn addRelaDynAssumeCapacity(self: *Elf, opts: RelaDyn) void {
4619 self.rela_dyn.appendAssumeCapacity(.{
4620 .r_offset = opts.offset,
4621 .r_info = (opts.sym << 32) | opts.type,
4622 .r_addend = opts.addend,
4623 });
4624}
4625
4626fn sortRelaDyn(self: *Elf) void {
4627 const Sort = struct {
4628 fn rank(rel: elf.Elf64_Rela) u2 {
4629 return switch (rel.r_type()) {
4630 elf.R_X86_64_RELATIVE => 0,
4631 elf.R_X86_64_IRELATIVE => 2,
4632 else => 1,
4633 };
4634 }
4635
4636 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
4637 _ = ctx;
4638 if (rank(lhs) == rank(rhs)) {
4639 if (lhs.r_sym() == rhs.r_sym()) return lhs.r_offset < rhs.r_offset;
4640 return lhs.r_sym() < rhs.r_sym();
4641 }
4642 return rank(lhs) < rank(rhs);
4643 }
4644 };
4645 mem.sort(elf.Elf64_Rela, self.rela_dyn.items, {}, Sort.lessThan);
4646}
4647
4648fn calcNumIRelativeRelocs(self: *Elf) usize {
4649 var count: usize = self.num_ifunc_dynrelocs;
4650
4651 for (self.got.entries.items) |entry| {
4652 if (entry.tag != .got) continue;
4653 const sym = self.symbol(entry.symbol_index);
4654 if (sym.isIFunc(self)) count += 1;
4655 }
4656
4657 return count;
4501}4658}
45024659
4503pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom {4660pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom {
src/link/Elf/Atom.zig+516-53
...@@ -316,6 +316,7 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool {...@@ -316,6 +316,7 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool {
316}316}
317317
318pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {318pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {
319 const is_static = elf_file.isStatic();
319 const is_dyn_lib = elf_file.isDynLib();320 const is_dyn_lib = elf_file.isDynLib();
320 const file_ptr = self.file(elf_file).?;321 const file_ptr = self.file(elf_file).?;
321 const rels = self.relocs(elf_file);322 const rels = self.relocs(elf_file);
...@@ -348,14 +349,23 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype...@@ -348,14 +349,23 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
348 // Report an undefined symbol.349 // Report an undefined symbol.
349 try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs);350 try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs);
350351
352 if (symbol.isIFunc(elf_file)) {
353 symbol.flags.needs_got = true;
354 symbol.flags.needs_plt = true;
355 }
356
351 // While traversing relocations, mark symbols that require special handling such as357 // While traversing relocations, mark symbols that require special handling such as
352 // pointer indirection via GOT, or a stub trampoline via PLT.358 // pointer indirection via GOT, or a stub trampoline via PLT.
353 switch (rel.r_type()) {359 switch (rel.r_type()) {
354 elf.R_X86_64_64 => {},360 elf.R_X86_64_64 => {
361 try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
362 },
355363
356 elf.R_X86_64_32,364 elf.R_X86_64_32,
357 elf.R_X86_64_32S,365 elf.R_X86_64_32S,
358 => {},366 => {
367 try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
368 },
359369
360 elf.R_X86_64_GOT32,370 elf.R_X86_64_GOT32,
361 elf.R_X86_64_GOT64,371 elf.R_X86_64_GOT64,
...@@ -377,23 +387,14 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype...@@ -377,23 +387,14 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
377 }387 }
378 },388 },
379389
380 elf.R_X86_64_PC32 => {},390 elf.R_X86_64_PC32 => {
381391 try self.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
382 elf.R_X86_64_TPOFF32,
383 elf.R_X86_64_TPOFF64,
384 => {
385 if (is_dyn_lib) {
386 // TODO
387 // self.picError(symbol, rel, elf_file);
388 }
389 },392 },
390393
391 elf.R_X86_64_TLSGD => {394 elf.R_X86_64_TLSGD => {
392 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr395 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
393396
394 if (elf_file.isStatic() or397 if (is_static or (!symbol.flags.import and !is_dyn_lib)) {
395 (!symbol.flags.import and !is_dyn_lib))
396 {
397 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a398 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
398 // We skip the next relocation.399 // We skip the next relocation.
399 i += 1;400 i += 1;
...@@ -405,9 +406,21 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype...@@ -405,9 +406,21 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
405 }406 }
406 },407 },
407408
409 elf.R_X86_64_TLSLD => {
410 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
411
412 if (is_static or !is_dyn_lib) {
413 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
414 // We skip the next relocation.
415 i += 1;
416 } else {
417 elf_file.got.flags.needs_tlsld = true;
418 }
419 },
420
408 elf.R_X86_64_GOTTPOFF => {421 elf.R_X86_64_GOTTPOFF => {
409 const should_relax = blk: {422 const should_relax = blk: {
410 // if (!elf_file.options.relax or is_shared or symbol.flags.import) break :blk false;423 if (is_dyn_lib or symbol.flags.import) break :blk false;
411 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;424 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
412 break :blk true;425 break :blk true;
413 };426 };
...@@ -416,21 +429,245 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype...@@ -416,21 +429,245 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
416 }429 }
417 },430 },
418431
419 else => {432 elf.R_X86_64_GOTPC32_TLSDESC => {
420 var err = try elf_file.addErrorWithNotes(1);433 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);
421 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{434 if (!should_relax) {
422 fmtRelocType(rel.r_type()),435 symbol.flags.needs_tlsdesc = true;
423 });436 }
424 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
425 self.file(elf_file).?.fmtPath(),
426 self.name(elf_file),
427 r_offset,
428 });
429 },437 },
438
439 elf.R_X86_64_TPOFF32,
440 elf.R_X86_64_TPOFF64,
441 => {
442 if (is_dyn_lib) try self.reportPicError(symbol, rel, elf_file);
443 },
444
445 elf.R_X86_64_GOTOFF64,
446 elf.R_X86_64_DTPOFF32,
447 elf.R_X86_64_DTPOFF64,
448 elf.R_X86_64_SIZE32,
449 elf.R_X86_64_SIZE64,
450 elf.R_X86_64_TLSDESC_CALL,
451 => {},
452
453 else => try self.reportUnhandledRelocError(rel, elf_file),
430 }454 }
431 }455 }
432}456}
433457
458fn scanReloc(
459 self: Atom,
460 symbol: *Symbol,
461 rel: elf.Elf64_Rela,
462 action: RelocAction,
463 elf_file: *Elf,
464) error{OutOfMemory}!void {
465 const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;
466 const object = self.file(elf_file).?.object;
467
468 switch (action) {
469 .none => {},
470
471 .@"error" => if (symbol.isAbs(elf_file))
472 try self.reportNoPicError(symbol, rel, elf_file)
473 else
474 try self.reportPicError(symbol, rel, elf_file),
475
476 .copyrel => {
477 if (elf_file.base.options.z_nocopyreloc) {
478 if (symbol.isAbs(elf_file))
479 try self.reportNoPicError(symbol, rel, elf_file)
480 else
481 try self.reportPicError(symbol, rel, elf_file);
482 } else {
483 symbol.flags.needs_copy_rel = true;
484 }
485 },
486
487 .dyn_copyrel => {
488 if (is_writeable or elf_file.base.options.z_nocopyreloc) {
489 if (!is_writeable) {
490 if (elf_file.base.options.z_notext) {
491 elf_file.has_text_reloc = true;
492 } else {
493 try self.reportTextRelocError(symbol, rel, elf_file);
494 }
495 }
496 object.num_dynrelocs += 1;
497 } else {
498 symbol.flags.needs_copy_rel = true;
499 }
500 },
501
502 .plt => {
503 symbol.flags.needs_plt = true;
504 },
505
506 .cplt => {
507 symbol.flags.needs_plt = true;
508 symbol.flags.is_canonical = true;
509 },
510
511 .dyn_cplt => {
512 if (is_writeable) {
513 object.num_dynrelocs += 1;
514 } else {
515 symbol.flags.needs_plt = true;
516 symbol.flags.is_canonical = true;
517 }
518 },
519
520 .dynrel, .baserel, .ifunc => {
521 if (!is_writeable) {
522 if (elf_file.base.options.z_notext) {
523 elf_file.has_text_reloc = true;
524 } else {
525 try self.reportTextRelocError(symbol, rel, elf_file);
526 }
527 }
528 object.num_dynrelocs += 1;
529
530 if (action == .ifunc) elf_file.num_ifunc_dynrelocs += 1;
531 },
532 }
533}
534
535const RelocAction = enum {
536 none,
537 @"error",
538 copyrel,
539 dyn_copyrel,
540 plt,
541 dyn_cplt,
542 cplt,
543 dynrel,
544 baserel,
545 ifunc,
546};
547
548fn pcRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
549 // zig fmt: off
550 const table: [3][4]RelocAction = .{
551 // Abs Local Import data Import func
552 .{ .@"error", .none, .@"error", .plt }, // Shared object
553 .{ .@"error", .none, .copyrel, .plt }, // PIE
554 .{ .none, .none, .copyrel, .cplt }, // Non-PIE
555 };
556 // zig fmt: on
557 const output = outputType(elf_file);
558 const data = dataType(symbol, elf_file);
559 return table[output][data];
560}
561
562fn absRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
563 // zig fmt: off
564 const table: [3][4]RelocAction = .{
565 // Abs Local Import data Import func
566 .{ .none, .@"error", .@"error", .@"error" }, // Shared object
567 .{ .none, .@"error", .@"error", .@"error" }, // PIE
568 .{ .none, .none, .copyrel, .cplt }, // Non-PIE
569 };
570 // zig fmt: on
571 const output = outputType(elf_file);
572 const data = dataType(symbol, elf_file);
573 return table[output][data];
574}
575
576fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
577 if (symbol.isIFunc(elf_file)) return .ifunc;
578 // zig fmt: off
579 const table: [3][4]RelocAction = .{
580 // Abs Local Import data Import func
581 .{ .none, .baserel, .dynrel, .dynrel }, // Shared object
582 .{ .none, .baserel, .dynrel, .dynrel }, // PIE
583 .{ .none, .none, .dyn_copyrel, .dyn_cplt }, // Non-PIE
584 };
585 // zig fmt: on
586 const output = outputType(elf_file);
587 const data = dataType(symbol, elf_file);
588 return table[output][data];
589}
590
591fn outputType(elf_file: *Elf) u2 {
592 return switch (elf_file.base.options.output_mode) {
593 .Obj => unreachable,
594 .Lib => 0,
595 .Exe => if (elf_file.base.options.pie) 1 else 2,
596 };
597}
598
599fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
600 if (symbol.isAbs(elf_file)) return 0;
601 if (!symbol.flags.import) return 1;
602 if (symbol.type(elf_file) != elf.STT_FUNC) return 2;
603 return 3;
604}
605
606fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void {
607 var err = try elf_file.addErrorWithNotes(1);
608 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
609 fmtRelocType(rel.r_type()),
610 rel.r_offset,
611 });
612 try err.addNote(elf_file, "in {}:{s}", .{
613 self.file(elf_file).?.fmtPath(),
614 self.name(elf_file),
615 });
616}
617
618fn reportTextRelocError(
619 self: Atom,
620 symbol: *const Symbol,
621 rel: elf.Elf64_Rela,
622 elf_file: *Elf,
623) error{OutOfMemory}!void {
624 var err = try elf_file.addErrorWithNotes(1);
625 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
626 rel.r_offset,
627 symbol.name(elf_file),
628 });
629 try err.addNote(elf_file, "in {}:{s}", .{
630 self.file(elf_file).?.fmtPath(),
631 self.name(elf_file),
632 });
633}
634
635fn reportPicError(
636 self: Atom,
637 symbol: *const Symbol,
638 rel: elf.Elf64_Rela,
639 elf_file: *Elf,
640) error{OutOfMemory}!void {
641 var err = try elf_file.addErrorWithNotes(2);
642 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
643 rel.r_offset,
644 symbol.name(elf_file),
645 });
646 try err.addNote(elf_file, "in {}:{s}", .{
647 self.file(elf_file).?.fmtPath(),
648 self.name(elf_file),
649 });
650 try err.addNote(elf_file, "recompile with -fPIC", .{});
651}
652
653fn reportNoPicError(
654 self: Atom,
655 symbol: *const Symbol,
656 rel: elf.Elf64_Rela,
657 elf_file: *Elf,
658) error{OutOfMemory}!void {
659 var err = try elf_file.addErrorWithNotes(2);
660 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
661 rel.r_offset,
662 symbol.name(elf_file),
663 });
664 try err.addNote(elf_file, "in {}:{s}", .{
665 self.file(elf_file).?.fmtPath(),
666 self.name(elf_file),
667 });
668 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
669}
670
434// This function will report any undefined non-weak symbols that are not imports.671// This function will report any undefined non-weak symbols that are not imports.
435fn reportUndefined(672fn reportUndefined(
436 self: Atom,673 self: Atom,
...@@ -504,7 +741,6 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -504,7 +741,6 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
504 const TP = @as(i64, @intCast(elf_file.tpAddress()));741 const TP = @as(i64, @intCast(elf_file.tpAddress()));
505 // Address of the dynamic thread pointer.742 // Address of the dynamic thread pointer.
506 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));743 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
507 _ = DTP;
508744
509 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{745 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{
510 fmtRelocType(r_type),746 fmtRelocType(r_type),
...@@ -520,18 +756,20 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -520,18 +756,20 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
520 switch (rel.r_type()) {756 switch (rel.r_type()) {
521 elf.R_X86_64_NONE => unreachable,757 elf.R_X86_64_NONE => unreachable,
522758
523 elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A),759 elf.R_X86_64_64 => {
524760 try self.resolveDynAbsReloc(
525 elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))),761 target,
526 elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))),762 rel,
763 dynAbsRelocAction(target, elf_file),
764 elf_file,
765 cwriter,
766 );
767 },
527768
528 elf.R_X86_64_PLT32,769 elf.R_X86_64_PLT32,
529 elf.R_X86_64_PC32,770 elf.R_X86_64_PC32,
530 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))),771 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))),
531772
532 elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(G + GOT + A))),
533 elf.R_X86_64_GOT64 => try cwriter.writeIntLittle(u64, @as(u64, @intCast(G + GOT + A))),
534
535 elf.R_X86_64_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))),773 elf.R_X86_64_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))),
536 elf.R_X86_64_GOTPC32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(GOT + A - P))),774 elf.R_X86_64_GOTPC32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(GOT + A - P))),
537 elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A - P),775 elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A - P),
...@@ -554,18 +792,25 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -554,18 +792,25 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
554 try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P)));792 try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P)));
555 },793 },
556794
795 elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))),
796 elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))),
797
798 elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(G + GOT + A))),
799 elf.R_X86_64_GOT64 => try cwriter.writeIntLittle(u64, @as(u64, @intCast(G + GOT + A))),
800
557 elf.R_X86_64_TPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - TP))),801 elf.R_X86_64_TPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - TP))),
558 elf.R_X86_64_TPOFF64 => try cwriter.writeIntLittle(i64, S + A - TP),802 elf.R_X86_64_TPOFF64 => try cwriter.writeIntLittle(i64, S + A - TP),
559803
804 elf.R_X86_64_DTPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - DTP))),
805 elf.R_X86_64_DTPOFF64 => try cwriter.writeIntLittle(i64, S + A - DTP),
806
560 elf.R_X86_64_TLSGD => {807 elf.R_X86_64_TLSGD => {
561 if (target.flags.has_tlsgd) {808 if (target.flags.has_tlsgd) {
562 // TODO809 const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));
563 // const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));810 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
564 // try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
565 } else if (target.flags.has_gottp) {811 } else if (target.flags.has_gottp) {
566 // TODO812 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
567 // const S_ = @as(i64, @intCast(target.getGotTpAddress(elf_file)));813 try x86_64.relaxTlsGdToIe(self, rels[i .. i + 2], @intCast(S_ - P), elf_file, &stream);
568 // try relaxTlsGdToIe(relocs[i .. i + 2], @intCast(S_ - P), elf_file, &stream);
569 i += 1;814 i += 1;
570 } else {815 } else {
571 try x86_64.relaxTlsGdToLe(816 try x86_64.relaxTlsGdToLe(
...@@ -579,11 +824,42 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -579,11 +824,42 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
579 }824 }
580 },825 },
581826
827 elf.R_X86_64_TLSLD => {
828 if (elf_file.got.tlsld_index) |entry_index| {
829 const tlsld_entry = elf_file.got.entries.items[entry_index];
830 const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file)));
831 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
832 } else {
833 try x86_64.relaxTlsLdToLe(
834 self,
835 rels[i .. i + 2],
836 @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))),
837 elf_file,
838 &stream,
839 );
840 i += 1;
841 }
842 },
843
844 elf.R_X86_64_GOTPC32_TLSDESC => {
845 if (target.flags.has_tlsdesc) {
846 const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file)));
847 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
848 } else {
849 try x86_64.relaxGotPcTlsDesc(code[rel.r_offset - 3 ..]);
850 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP)));
851 }
852 },
853
854 elf.R_X86_64_TLSDESC_CALL => if (!target.flags.has_tlsdesc) {
855 // call -> nop
856 try cwriter.writeAll(&.{ 0x66, 0x90 });
857 },
858
582 elf.R_X86_64_GOTTPOFF => {859 elf.R_X86_64_GOTTPOFF => {
583 if (target.flags.has_gottp) {860 if (target.flags.has_gottp) {
584 // TODO861 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
585 // const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));862 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
586 // try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
587 } else {863 } else {
588 x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable;864 x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable;
589 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP)));865 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP)));
...@@ -595,6 +871,98 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -595,6 +871,98 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
595 }871 }
596}872}
597873
874fn resolveDynAbsReloc(
875 self: Atom,
876 target: *const Symbol,
877 rel: elf.Elf64_Rela,
878 action: RelocAction,
879 elf_file: *Elf,
880 writer: anytype,
881) !void {
882 const P = self.value + rel.r_offset;
883 const A = rel.r_addend;
884 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
885 const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;
886 const object = self.file(elf_file).?.object;
887
888 try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, object.num_dynrelocs);
889
890 switch (action) {
891 .@"error",
892 .plt,
893 => unreachable,
894
895 .copyrel,
896 .cplt,
897 .none,
898 => try writer.writeIntLittle(i32, @as(i32, @truncate(S + A))),
899
900 .dyn_copyrel => {
901 if (is_writeable or elf_file.base.options.z_nocopyreloc) {
902 elf_file.addRelaDynAssumeCapacity(.{
903 .offset = P,
904 .sym = target.extra(elf_file).?.dynamic,
905 .type = elf.R_X86_64_64,
906 .addend = A,
907 });
908 try applyDynamicReloc(A, elf_file, writer);
909 } else {
910 try writer.writeIntLittle(i32, @as(i32, @truncate(S + A)));
911 }
912 },
913
914 .dyn_cplt => {
915 if (is_writeable) {
916 elf_file.addRelaDynAssumeCapacity(.{
917 .offset = P,
918 .sym = target.extra(elf_file).?.dynamic,
919 .type = elf.R_X86_64_64,
920 .addend = A,
921 });
922 try applyDynamicReloc(A, elf_file, writer);
923 } else {
924 try writer.writeIntLittle(i32, @as(i32, @truncate(S + A)));
925 }
926 },
927
928 .dynrel => {
929 elf_file.addRelaDynAssumeCapacity(.{
930 .offset = P,
931 .sym = target.extra(elf_file).?.dynamic,
932 .type = elf.R_X86_64_64,
933 .addend = A,
934 });
935 try applyDynamicReloc(A, elf_file, writer);
936 },
937
938 .baserel => {
939 elf_file.addRelaDynAssumeCapacity(.{
940 .offset = P,
941 .type = elf.R_X86_64_RELATIVE,
942 .addend = S + A,
943 });
944 try applyDynamicReloc(S + A, elf_file, writer);
945 },
946
947 .ifunc => {
948 const S_ = @as(i64, @intCast(target.address(.{ .plt = false }, elf_file)));
949 elf_file.addRelaDynAssumeCapacity(.{
950 .offset = P,
951 .type = elf.R_X86_64_IRELATIVE,
952 .addend = S_ + A,
953 });
954 try applyDynamicReloc(S_ + A, elf_file, writer);
955 },
956 }
957}
958
959fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void {
960 _ = elf_file;
961 // if (elf_file.options.apply_dynamic_relocs) {
962 try writer.writeIntLittle(i64, value);
963 // }
964}
965
598pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {966pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {
599 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });967 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
600968
...@@ -682,17 +1050,7 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any...@@ -682,17 +1050,7 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any
682 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));1050 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
683 try cwriter.writeIntLittle(i64, @as(i64, @intCast(size + A)));1051 try cwriter.writeIntLittle(i64, @as(i64, @intCast(size + A)));
684 },1052 },
685 else => {1053 else => try self.reportUnhandledRelocError(rel, elf_file),
686 var err = try elf_file.addErrorWithNotes(1);
687 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{
688 fmtRelocType(r_type),
689 });
690 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
691 self.file(elf_file).?.fmtPath(),
692 self.name(elf_file),
693 r_offset,
694 });
695 },
696 }1054 }
697 }1055 }
698}1056}
...@@ -854,6 +1212,95 @@ const x86_64 = struct {...@@ -854,6 +1212,95 @@ const x86_64 = struct {
854 }1212 }
855 }1213 }
8561214
1215 pub fn relaxTlsGdToIe(
1216 self: Atom,
1217 rels: []align(1) const elf.Elf64_Rela,
1218 value: i32,
1219 elf_file: *Elf,
1220 stream: anytype,
1221 ) !void {
1222 assert(rels.len == 2);
1223 const writer = stream.writer();
1224 switch (rels[1].r_type()) {
1225 elf.R_X86_64_PC32,
1226 elf.R_X86_64_PLT32,
1227 => {
1228 var insts = [_]u8{
1229 0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // movq %fs:0,%rax
1230 0x48, 0x03, 0x05, 0, 0, 0, 0, // add foo@gottpoff(%rip), %rax
1231 };
1232 std.mem.writeIntLittle(i32, insts[12..][0..4], value - 12);
1233 try stream.seekBy(-4);
1234 try writer.writeAll(&insts);
1235 },
1236
1237 else => {
1238 var err = try elf_file.addErrorWithNotes(1);
1239 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
1240 fmtRelocType(rels[0].r_type()),
1241 fmtRelocType(rels[1].r_type()),
1242 });
1243 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1244 self.file(elf_file).?.fmtPath(),
1245 self.name(elf_file),
1246 rels[0].r_offset,
1247 });
1248 },
1249 }
1250 }
1251
1252 pub fn relaxTlsLdToLe(
1253 self: Atom,
1254 rels: []align(1) const elf.Elf64_Rela,
1255 value: i32,
1256 elf_file: *Elf,
1257 stream: anytype,
1258 ) !void {
1259 assert(rels.len == 2);
1260 const writer = stream.writer();
1261 switch (rels[1].r_type()) {
1262 elf.R_X86_64_PC32,
1263 elf.R_X86_64_PLT32,
1264 => {
1265 var insts = [_]u8{
1266 0x31, 0xc0, // xor %eax, %eax
1267 0x64, 0x48, 0x8b, 0, // mov %fs:(%rax), %rax
1268 0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax
1269 };
1270 std.mem.writeIntLittle(i32, insts[8..][0..4], value);
1271 try stream.seekBy(-3);
1272 try writer.writeAll(&insts);
1273 },
1274
1275 elf.R_X86_64_GOTPCREL,
1276 elf.R_X86_64_GOTPCRELX,
1277 => {
1278 var insts = [_]u8{
1279 0x31, 0xc0, // xor %eax, %eax
1280 0x64, 0x48, 0x8b, 0, // mov %fs:(%rax), %rax
1281 0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax
1282 0x90, // nop
1283 };
1284 std.mem.writeIntLittle(i32, insts[8..][0..4], value);
1285 try stream.seekBy(-3);
1286 try writer.writeAll(&insts);
1287 },
1288
1289 else => {
1290 var err = try elf_file.addErrorWithNotes(1);
1291 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
1292 fmtRelocType(rels[0].r_type()),
1293 fmtRelocType(rels[1].r_type()),
1294 });
1295 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1296 self.file(elf_file).?.fmtPath(),
1297 self.name(elf_file),
1298 rels[0].r_offset,
1299 });
1300 },
1301 }
1302 }
1303
857 pub fn canRelaxGotTpOff(code: []const u8) bool {1304 pub fn canRelaxGotTpOff(code: []const u8) bool {
858 const old_inst = disassemble(code) orelse return false;1305 const old_inst = disassemble(code) orelse return false;
859 switch (old_inst.encoding.mnemonic) {1306 switch (old_inst.encoding.mnemonic) {
...@@ -885,6 +1332,22 @@ const x86_64 = struct {...@@ -885,6 +1332,22 @@ const x86_64 = struct {
885 }1332 }
886 }1333 }
8871334
1335 pub fn relaxGotPcTlsDesc(code: []u8) !void {
1336 const old_inst = disassemble(code) orelse return error.RelaxFail;
1337 switch (old_inst.encoding.mnemonic) {
1338 .lea => {
1339 const inst = try Instruction.new(old_inst.prefix, .mov, &.{
1340 old_inst.ops[0],
1341 // TODO: hack to force imm32s in the assembler
1342 .{ .imm = Immediate.s(-129) },
1343 });
1344 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1345 encode(&.{inst}, code) catch return error.RelaxFail;
1346 },
1347 else => return error.RelaxFail,
1348 }
1349 }
1350
888 pub fn relaxTlsGdToLe(1351 pub fn relaxTlsGdToLe(
889 self: Atom,1352 self: Atom,
890 rels: []align(1) const elf.Elf64_Rela,1353 rels: []align(1) const elf.Elf64_Rela,
src/link/Elf/Object.zig+1-28
...@@ -20,7 +20,6 @@ cies: std.ArrayListUnmanaged(Cie) = .{},...@@ -20,7 +20,6 @@ cies: std.ArrayListUnmanaged(Cie) = .{},
20alive: bool = true,20alive: bool = true,
21num_dynrelocs: u32 = 0,21num_dynrelocs: u32 = 0,
2222
23output_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{},
24output_symtab_size: Elf.SymtabSize = .{},23output_symtab_size: Elf.SymtabSize = .{},
2524
26pub fn isObject(file: std.fs.File) bool {25pub fn isObject(file: std.fs.File) bool {
...@@ -43,10 +42,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void {...@@ -43,10 +42,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
43 self.comdat_groups.deinit(allocator);42 self.comdat_groups.deinit(allocator);
44 self.fdes.deinit(allocator);43 self.fdes.deinit(allocator);
45 self.cies.deinit(allocator);44 self.cies.deinit(allocator);
46 for (self.output_sections.values()) |*list| {
47 list.deinit(allocator);
48 }
49 self.output_sections.deinit(allocator);
50}45}
5146
52pub fn parse(self: *Object, elf_file: *Elf) !void {47pub fn parse(self: *Object, elf_file: *Elf) !void {
...@@ -635,7 +630,7 @@ pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void {...@@ -635,7 +630,7 @@ pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void {
635630
636 if (shdr.sh_type == elf.SHT_NOBITS) continue;631 if (shdr.sh_type == elf.SHT_NOBITS) continue;
637 const gpa = elf_file.base.allocator;632 const gpa = elf_file.base.allocator;
638 const gop = try self.output_sections.getOrPut(gpa, atom.output_section_index);633 const gop = try elf_file.output_sections.getOrPut(gpa, atom.output_section_index);
639 if (!gop.found_existing) gop.value_ptr.* = .{};634 if (!gop.found_existing) gop.value_ptr.* = .{};
640 try gop.value_ptr.append(gpa, atom_index);635 try gop.value_ptr.append(gpa, atom_index);
641 }636 }
...@@ -680,28 +675,6 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void {...@@ -680,28 +675,6 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
680 }675 }
681}676}
682677
683pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffer: []u8, undefs: anytype) !void {
684 const gpa = elf_file.base.allocator;
685 const atom_list = self.output_sections.get(output_section_index) orelse return;
686 const shdr = elf_file.shdrs.items[output_section_index];
687 for (atom_list.items) |atom_index| {
688 const atom = elf_file.atom(atom_index).?;
689 assert(atom.flags.alive);
690 const offset = atom.value - shdr.sh_addr;
691 log.debug("writing atom({d}) at 0x{x}", .{ atom_index, shdr.sh_offset + offset });
692 // TODO decompress directly into provided buffer
693 const out_code = buffer[offset..][0..atom.size];
694 const in_code = try self.codeDecompressAlloc(elf_file, atom_index);
695 defer gpa.free(in_code);
696 @memcpy(out_code, in_code);
697
698 if (shdr.sh_flags & elf.SHF_ALLOC == 0)
699 try atom.resolveRelocsNonAlloc(elf_file, out_code, undefs)
700 else
701 try atom.resolveRelocsAlloc(elf_file, out_code);
702 }
703}
704
705pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {678pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
706 for (self.locals()) |local_index| {679 for (self.locals()) |local_index| {
707 const local = elf_file.symbol(local_index);680 const local = elf_file.symbol(local_index);
src/link/Elf/Symbol.zig+22-18
...@@ -128,23 +128,26 @@ pub fn getOrCreateGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf)...@@ -128,23 +128,26 @@ pub fn getOrCreateGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf)
128 return .{ .found_existing = false, .index = index };128 return .{ .found_existing = false, .index = index };
129}129}
130130
131// pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {131pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
132// if (!symbol.flags.tlsgd) return 0;132 if (!symbol.flags.has_tlsgd) return 0;
133// const extra = symbol.getExtra(elf_file).?;133 const extras = symbol.extra(elf_file).?;
134// return elf_file.getGotEntryAddress(extra.tlsgd);134 const entry = elf_file.got.entries.items[extras.tlsgd];
135// }135 return entry.address(elf_file);
136}
136137
137// pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {138pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {
138// if (!symbol.flags.gottp) return 0;139 if (!symbol.flags.has_gottp) return 0;
139// const extra = symbol.getExtra(elf_file).?;140 const extras = symbol.extra(elf_file).?;
140// return elf_file.getGotEntryAddress(extra.gottp);141 const entry = elf_file.got.entries.items[extras.gottp];
141// }142 return entry.address(elf_file);
143}
142144
143// pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {145pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {
144// if (!symbol.flags.tlsdesc) return 0;146 if (!symbol.flags.has_tlsdesc) return 0;
145// const extra = symbol.getExtra(elf_file).?;147 const extras = symbol.extra(elf_file).?;
146// return elf_file.getGotEntryAddress(extra.tlsdesc);148 const entry = elf_file.got.entries.items[extras.tlsdesc];
147// }149 return entry.address(elf_file);
150}
148151
149// pub fn alignment(symbol: Symbol, elf_file: *Elf) !u64 {152// pub fn alignment(symbol: Symbol, elf_file: *Elf) !u64 {
150// const file = symbol.getFile(elf_file) orelse return 0;153// const file = symbol.getFile(elf_file) orelse return 0;
...@@ -318,12 +321,12 @@ pub const Flags = packed struct {...@@ -318,12 +321,12 @@ pub const Flags = packed struct {
318321
319 /// Whether the symbol contains PLT indirection.322 /// Whether the symbol contains PLT indirection.
320 needs_plt: bool = false,323 needs_plt: bool = false,
321 plt: bool = false,324 has_plt: bool = false,
322 /// Whether the PLT entry is canonical.325 /// Whether the PLT entry is canonical.
323 is_canonical: bool = false,326 is_canonical: bool = false,
324327
325 /// Whether the symbol contains COPYREL directive.328 /// Whether the symbol contains COPYREL directive.
326 copy_rel: bool = false,329 needs_copy_rel: bool = false,
327 has_copy_rel: bool = false,330 has_copy_rel: bool = false,
328 has_dynamic: bool = false,331 has_dynamic: bool = false,
329332
...@@ -336,7 +339,8 @@ pub const Flags = packed struct {...@@ -336,7 +339,8 @@ pub const Flags = packed struct {
336 has_gottp: bool = false,339 has_gottp: bool = false,
337340
338 /// Whether the symbol contains TLSDESC indirection.341 /// Whether the symbol contains TLSDESC indirection.
339 tlsdesc: bool = false,342 needs_tlsdesc: bool = false,
343 has_tlsdesc: bool = false,
340};344};
341345
342pub const Extra = struct {346pub const Extra = struct {
src/link/Elf/synthetic_sections.zig+8-2
...@@ -1,10 +1,16 @@...@@ -1,10 +1,16 @@
1pub const GotSection = struct {1pub const GotSection = struct {
2 entries: std.ArrayListUnmanaged(Entry) = .{},2 entries: std.ArrayListUnmanaged(Entry) = .{},
3 needs_rela: bool = false,
4 output_symtab_size: Elf.SymtabSize = .{},3 output_symtab_size: Elf.SymtabSize = .{},
4 tlsld_index: ?u32 = null,
5 flags: Flags = .{},
56
6 pub const Index = u32;7 pub const Index = u32;
78
9 const Flags = packed struct {
10 needs_rela: bool = false,
11 needs_tlsld: bool = false,
12 };
13
8 const Tag = enum {14 const Tag = enum {
9 got,15 got,
10 tlsld,16 tlsld,
...@@ -57,7 +63,7 @@ pub const GotSection = struct {...@@ -57,7 +63,7 @@ pub const GotSection = struct {
57 entry.symbol_index = sym_index;63 entry.symbol_index = sym_index;
58 const symbol = elf_file.symbol(sym_index);64 const symbol = elf_file.symbol(sym_index);
59 if (symbol.flags.import or symbol.isIFunc(elf_file) or (elf_file.base.options.pic and !symbol.isAbs(elf_file)))65 if (symbol.flags.import or symbol.isIFunc(elf_file) or (elf_file.base.options.pic and !symbol.isAbs(elf_file)))
60 got.needs_rela = true;66 got.flags.needs_rela = true;
61 if (symbol.extra(elf_file)) |extra| {67 if (symbol.extra(elf_file)) |extra| {
62 var new_extra = extra;68 var new_extra = extra;
63 new_extra.got = index;69 new_extra.got = index;