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) = .{},
2222phdr_to_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
2323/// File offset into the shdr table.
2424shdr_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
2629/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
2730/// Same order as in the file.
......@@ -61,6 +64,7 @@ strtab: StringTable(.strtab) = .{},
6164
6265/// Representation of the GOT table as committed to the file.
6366got: GotSection = .{},
67rela_dyn: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
6468
6569/// Tracked section headers
6670text_section_index: ?u16 = null,
......@@ -109,6 +113,9 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},
109113resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
110114symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
111115
116has_text_reloc: bool = false,
117num_ifunc_dynrelocs: usize = 0,
118
112119phdr_table_dirty: bool = false,
113120shdr_table_dirty: bool = false,
114121
......@@ -317,6 +324,10 @@ pub fn deinit(self: *Elf) void {
317324 self.shdrs.deinit(gpa);
318325 self.phdr_to_shdr_table.deinit(gpa);
319326 self.phdrs.deinit(gpa);
327 for (self.output_sections.values()) |*list| {
328 list.deinit(gpa);
329 }
330 self.output_sections.deinit(gpa);
320331 self.shstrtab.deinit(gpa);
321332 self.strtab.deinit(gpa);
322333 self.symbols.deinit(gpa);
......@@ -358,6 +369,7 @@ pub fn deinit(self: *Elf) void {
358369 self.comdat_groups.deinit(gpa);
359370 self.comdat_groups_owners.deinit(gpa);
360371 self.comdat_groups_table.deinit(gpa);
372 self.rela_dyn.deinit(gpa);
361373}
362374
363375pub 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
12491261 for (self.objects.items) |index| {
12501262 try self.file(index).?.object.addAtomsToOutputSections(self);
12511263 }
1264 try self.sortInitFini();
12521265 try self.updateSectionSizes();
12531266
12541267 try self.allocateSections();
......@@ -1316,7 +1329,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
13161329 const code = try zig_module.codeAlloc(self, atom_index);
13171330 defer gpa.free(code);
13181331 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 };
13201339 try self.base.file.?.pwriteAll(code, file_offset);
13211340 }
13221341
......@@ -3460,6 +3479,69 @@ fn initSections(self: *Elf) !void {
34603479 }
34613480}
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
34633545fn sectionRank(self: *Elf, shdr: elf.Elf64_Shdr) u8 {
34643546 const name = self.shstrtab.getAssumeExists(shdr.sh_name);
34653547 const flags = shdr.sh_flags;
......@@ -3926,6 +4008,8 @@ fn writeAtoms(self: *Elf) !void {
39264008 if (shdr.sh_type == elf.SHT_NULL) continue;
39274009 if (shdr.sh_type == elf.SHT_NOBITS) continue;
39284010
4011 const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
4012
39294013 log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)});
39304014
39314015 const buffer = try gpa.alloc(u8, shdr.sh_size);
......@@ -3937,8 +4021,32 @@ fn writeAtoms(self: *Elf) !void {
39374021 0;
39384022 @memset(buffer, padding_byte);
39394023
3940 for (self.objects.items) |index| {
3941 try self.file(index).?.object.writeAtoms(self, @intCast(shndx), buffer, &undefs);
4024 for (atom_list.items) |atom_index| {
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 }
39424050 }
39434051
39444052 try self.base.file.?.pwriteAll(buffer, shdr.sh_offset);
......@@ -4495,9 +4603,58 @@ pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
44954603 } else return null;
44964604}
44974605
4498pub fn calcNumIRelativeRelocs(self: *Elf) u64 {
4499 _ = self;
4500 unreachable; // TODO
4606const RelaDyn = struct {
4607 offset: u64,
4608 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;
45014658}
45024659
45034660pub 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 {
316316}
317317
318318pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {
319 const is_static = elf_file.isStatic();
319320 const is_dyn_lib = elf_file.isDynLib();
320321 const file_ptr = self.file(elf_file).?;
321322 const rels = self.relocs(elf_file);
......@@ -348,14 +349,23 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
348349 // Report an undefined symbol.
349350 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
351357 // While traversing relocations, mark symbols that require special handling such as
352358 // pointer indirection via GOT, or a stub trampoline via PLT.
353359 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
356364 elf.R_X86_64_32,
357365 elf.R_X86_64_32S,
358 => {},
366 => {
367 try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
368 },
359369
360370 elf.R_X86_64_GOT32,
361371 elf.R_X86_64_GOT64,
......@@ -377,23 +387,14 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
377387 }
378388 },
379389
380 elf.R_X86_64_PC32 => {},
381
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 }
390 elf.R_X86_64_PC32 => {
391 try self.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
389392 },
390393
391394 elf.R_X86_64_TLSGD => {
392395 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
393396
394 if (elf_file.isStatic() or
395 (!symbol.flags.import and !is_dyn_lib))
396 {
397 if (is_static or (!symbol.flags.import and !is_dyn_lib)) {
397398 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
398399 // We skip the next relocation.
399400 i += 1;
......@@ -405,9 +406,21 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
405406 }
406407 },
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
408421 elf.R_X86_64_GOTTPOFF => {
409422 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;
411424 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
412425 break :blk true;
413426 };
......@@ -416,21 +429,245 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
416429 }
417430 },
418431
419 else => {
420 var err = try elf_file.addErrorWithNotes(1);
421 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{
422 fmtRelocType(rel.r_type()),
423 });
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 });
432 elf.R_X86_64_GOTPC32_TLSDESC => {
433 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);
434 if (!should_relax) {
435 symbol.flags.needs_tlsdesc = true;
436 }
429437 },
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),
430454 }
431455 }
432456}
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
434671// This function will report any undefined non-weak symbols that are not imports.
435672fn reportUndefined(
436673 self: Atom,
......@@ -504,7 +741,6 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
504741 const TP = @as(i64, @intCast(elf_file.tpAddress()));
505742 // Address of the dynamic thread pointer.
506743 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
507 _ = DTP;
508744
509745 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{
510746 fmtRelocType(r_type),
......@@ -520,18 +756,20 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
520756 switch (rel.r_type()) {
521757 elf.R_X86_64_NONE => unreachable,
522758
523 elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A),
524
525 elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))),
526 elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))),
759 elf.R_X86_64_64 => {
760 try self.resolveDynAbsReloc(
761 target,
762 rel,
763 dynAbsRelocAction(target, elf_file),
764 elf_file,
765 cwriter,
766 );
767 },
527768
528769 elf.R_X86_64_PLT32,
529770 elf.R_X86_64_PC32,
530771 => 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
535773 elf.R_X86_64_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))),
536774 elf.R_X86_64_GOTPC32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(GOT + A - P))),
537775 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 {
554792 try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P)));
555793 },
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
557801 elf.R_X86_64_TPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - TP))),
558802 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
560807 elf.R_X86_64_TLSGD => {
561808 if (target.flags.has_tlsgd) {
562 // TODO
563 // const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));
564 // try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
809 const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));
810 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
565811 } else if (target.flags.has_gottp) {
566 // TODO
567 // const S_ = @as(i64, @intCast(target.getGotTpAddress(elf_file)));
568 // try relaxTlsGdToIe(relocs[i .. i + 2], @intCast(S_ - P), elf_file, &stream);
812 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
813 try x86_64.relaxTlsGdToIe(self, rels[i .. i + 2], @intCast(S_ - P), elf_file, &stream);
569814 i += 1;
570815 } else {
571816 try x86_64.relaxTlsGdToLe(
......@@ -579,11 +824,42 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
579824 }
580825 },
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
582859 elf.R_X86_64_GOTTPOFF => {
583860 if (target.flags.has_gottp) {
584 // TODO
585 // const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
586 // try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
861 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
862 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
587863 } else {
588864 x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable;
589865 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP)));
......@@ -595,6 +871,98 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
595871 }
596872}
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
598966pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {
599967 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
6821050 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
6831051 try cwriter.writeIntLittle(i64, @as(i64, @intCast(size + A)));
6841052 },
685 else => {
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 },
1053 else => try self.reportUnhandledRelocError(rel, elf_file),
6961054 }
6971055 }
6981056}
......@@ -854,6 +1212,95 @@ const x86_64 = struct {
8541212 }
8551213 }
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
8571304 pub fn canRelaxGotTpOff(code: []const u8) bool {
8581305 const old_inst = disassemble(code) orelse return false;
8591306 switch (old_inst.encoding.mnemonic) {
......@@ -885,6 +1332,22 @@ const x86_64 = struct {
8851332 }
8861333 }
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
8881351 pub fn relaxTlsGdToLe(
8891352 self: Atom,
8901353 rels: []align(1) const elf.Elf64_Rela,
src/link/Elf/Object.zig+1-28
......@@ -20,7 +20,6 @@ cies: std.ArrayListUnmanaged(Cie) = .{},
2020alive: bool = true,
2121num_dynrelocs: u32 = 0,
2222
23output_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{},
2423output_symtab_size: Elf.SymtabSize = .{},
2524
2625pub fn isObject(file: std.fs.File) bool {
......@@ -43,10 +42,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
4342 self.comdat_groups.deinit(allocator);
4443 self.fdes.deinit(allocator);
4544 self.cies.deinit(allocator);
46 for (self.output_sections.values()) |*list| {
47 list.deinit(allocator);
48 }
49 self.output_sections.deinit(allocator);
5045}
5146
5247pub fn parse(self: *Object, elf_file: *Elf) !void {
......@@ -635,7 +630,7 @@ pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void {
635630
636631 if (shdr.sh_type == elf.SHT_NOBITS) continue;
637632 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);
639634 if (!gop.found_existing) gop.value_ptr.* = .{};
640635 try gop.value_ptr.append(gpa, atom_index);
641636 }
......@@ -680,28 +675,6 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
680675 }
681676}
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
705678pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
706679 for (self.locals()) |local_index| {
707680 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)
128128 return .{ .found_existing = false, .index = index };
129129}
130130
131// pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
132// if (!symbol.flags.tlsgd) return 0;
133// const extra = symbol.getExtra(elf_file).?;
134// return elf_file.getGotEntryAddress(extra.tlsgd);
135// }
131pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
132 if (!symbol.flags.has_tlsgd) return 0;
133 const extras = symbol.extra(elf_file).?;
134 const entry = elf_file.got.entries.items[extras.tlsgd];
135 return entry.address(elf_file);
136}
136137
137// pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {
138// if (!symbol.flags.gottp) return 0;
139// const extra = symbol.getExtra(elf_file).?;
140// return elf_file.getGotEntryAddress(extra.gottp);
141// }
138pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {
139 if (!symbol.flags.has_gottp) return 0;
140 const extras = symbol.extra(elf_file).?;
141 const entry = elf_file.got.entries.items[extras.gottp];
142 return entry.address(elf_file);
143}
142144
143// pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {
144// if (!symbol.flags.tlsdesc) return 0;
145// const extra = symbol.getExtra(elf_file).?;
146// return elf_file.getGotEntryAddress(extra.tlsdesc);
147// }
145pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {
146 if (!symbol.flags.has_tlsdesc) return 0;
147 const extras = symbol.extra(elf_file).?;
148 const entry = elf_file.got.entries.items[extras.tlsdesc];
149 return entry.address(elf_file);
150}
148151
149152// pub fn alignment(symbol: Symbol, elf_file: *Elf) !u64 {
150153// const file = symbol.getFile(elf_file) orelse return 0;
......@@ -318,12 +321,12 @@ pub const Flags = packed struct {
318321
319322 /// Whether the symbol contains PLT indirection.
320323 needs_plt: bool = false,
321 plt: bool = false,
324 has_plt: bool = false,
322325 /// Whether the PLT entry is canonical.
323326 is_canonical: bool = false,
324327
325328 /// Whether the symbol contains COPYREL directive.
326 copy_rel: bool = false,
329 needs_copy_rel: bool = false,
327330 has_copy_rel: bool = false,
328331 has_dynamic: bool = false,
329332
......@@ -336,7 +339,8 @@ pub const Flags = packed struct {
336339 has_gottp: bool = false,
337340
338341 /// Whether the symbol contains TLSDESC indirection.
339 tlsdesc: bool = false,
342 needs_tlsdesc: bool = false,
343 has_tlsdesc: bool = false,
340344};
341345
342346pub const Extra = struct {
src/link/Elf/synthetic_sections.zig+8-2
......@@ -1,10 +1,16 @@
11pub const GotSection = struct {
22 entries: std.ArrayListUnmanaged(Entry) = .{},
3 needs_rela: bool = false,
43 output_symtab_size: Elf.SymtabSize = .{},
4 tlsld_index: ?u32 = null,
5 flags: Flags = .{},
56
67 pub const Index = u32;
78
9 const Flags = packed struct {
10 needs_rela: bool = false,
11 needs_tlsld: bool = false,
12 };
13
814 const Tag = enum {
915 got,
1016 tlsld,
......@@ -57,7 +63,7 @@ pub const GotSection = struct {
5763 entry.symbol_index = sym_index;
5864 const symbol = elf_file.symbol(sym_index);
5965 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;
6167 if (symbol.extra(elf_file)) |extra| {
6268 var new_extra = extra;
6369 new_extra.got = index;