authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-21 11:01:50+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-26 06:48:53+01:00
log15314965c2fc45bf52f703046bff138e414e789c
treec338c87939a72a1e1ae83035d552765cfedfc6dd
parent3c6bcad995f26e33d11e7f5c4b28d66ff872b0d1
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: scan input DSO symbol tables


1 files changed, 209 insertions(+), 102 deletions(-)

src/link/Elf2.zig+209-102
...@@ -54,6 +54,14 @@ globals: struct {...@@ -54,6 +54,14 @@ globals: struct {
54/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,54/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
55/// because the vast majority of nodes which can export global symbols actually will not.55/// because the vast majority of nodes which can export global symbols actually will not.
56node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),56node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),
57/// Contains all globals symbols defined in any needed DSO. This map serves two purposes:
58///
59/// * If we discover an undefined reference to one of these symbols, we will know the associated
60/// symbol type, which is important because it may cause us to create a PLT entry.
61///
62/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a
63/// linked DSO, so can emit "undefined global symbol" errors for any other undefined references.
64dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT),
57shstrtab: StringTable,65shstrtab: StringTable,
58strtab: StringTable,66strtab: StringTable,
59dynstr: StringTable,67dynstr: StringTable,
...@@ -431,55 +439,60 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe...@@ -431,55 +439,60 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
431 try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size);439 try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size);
432 }440 }
433441
434 try elf.got.plt.ensureUnusedCapacity(gpa, len);442 try elf.ensureUnusedPltCapacity(len);
435 const need_plt_capacity = elf.got.plt.count() + len;443 }
444 },
445 }
446}
447fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void {
448 const gpa = elf.base.comp.gpa;
436449
437 switch (elf.ehdrField(.machine)) {450 try elf.got.plt.ensureUnusedCapacity(gpa, len);
438 else => |machine| @panic(@tagName(machine)),451 const need_plt_capacity = elf.got.plt.count() + len;
439 .X86_64 => {
440 // Ensure the `.plt` section's node is big enough
441 const plt_need_size: usize = 16 * (1 + need_plt_capacity);
442 _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
443 if (plt_cur_size < plt_need_size) {
444 const new_size = plt_need_size +| plt_need_size / MappedFile.growth_factor;
445 try elf.shndx.plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
446 }
447452
448 // Ensure the `.got.plt` section's node is big enough453 switch (elf.ehdrField(.machine)) {
449 const got_plt_need_size: usize = switch (elf.identClass()) {454 else => |machine| @panic(@tagName(machine)),
450 .NONE, _ => unreachable,455 .X86_64 => {
451 inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity),456 // Ensure the `.plt` section's node is big enough
452 };457 const plt_need_size: usize = 16 * (1 + need_plt_capacity);
453 _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);458 _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
454 if (got_plt_cur_size < got_plt_need_size) {459 if (plt_cur_size < plt_need_size) {
455 const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor;460 const new_size = plt_need_size +| plt_need_size / MappedFile.growth_factor;
456 try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size);461 try elf.shndx.plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
457 }462 }
458463
459 // Ensure the `.plt.sec` section's node is big enough464 // Ensure the `.got.plt` section's node is big enough
460 const plt_sec_need_size: usize = 16 * need_plt_capacity;465 const got_plt_need_size: usize = switch (elf.identClass()) {
461 _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf);466 .NONE, _ => unreachable,
462 if (plt_sec_cur_size < plt_sec_need_size) {467 inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity),
463 const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;468 };
464 try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);469 _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
465 }470 if (got_plt_cur_size < got_plt_need_size) {
471 const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor;
472 try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
473 }
466474
467 // Ensure the `.rela.plt` section's node is big enough475 // Ensure the `.plt.sec` section's node is big enough
468 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;476 const plt_sec_need_size: usize = 16 * need_plt_capacity;
469 const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) {477 _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
470 inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity),478 if (plt_sec_cur_size < plt_sec_need_size) {
471 };479 const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;
472 _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf);480 try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);
473 if (rela_plt_cur_size < rela_plt_need_size) {481 }
474 const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor;482
475 try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size);483 // Ensure the `.rela.plt` section's node is big enough
476 } else {484 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;
477 // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can485 const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) {
478 // be updated if we do indeed add a PLT entry.486 inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity),
479 try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf);487 };
480 }488 _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
481 },489 if (rela_plt_cur_size < rela_plt_need_size) {
482 }490 const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor;
491 try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size);
492 } else {
493 // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can
494 // be updated if we do indeed add a PLT entry.
495 try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf);
483 }496 }
484 },497 },
485 }498 }
...@@ -700,6 +713,11 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -700,6 +713,11 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
700 .weak => .WEAK,713 .weak => .WEAK,
701 };714 };
702715
716 const @"type": std.elf.STT = switch (opts.type) {
717 .NOTYPE => elf.dso_globals.get(opts.name.strtab) orelse .NOTYPE,
718 else => |t| t,
719 };
720
703 const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len);721 const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len);
704 elf.symtab.appendAssumeCapacity(.{722 elf.symtab.appendAssumeCapacity(.{
705 .node = opts.node,723 .node = opts.node,
...@@ -718,7 +736,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -718,7 +736,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
718 .name = @intFromEnum(opts.name.strtab),736 .name = @intFromEnum(opts.name.strtab),
719 .value = @intCast(opts.value),737 .value = @intCast(opts.value),
720 .size = @intCast(opts.size),738 .size = @intCast(opts.size),
721 .info = .{ .type = opts.type, .bind = bind },739 .info = .{ .type = @"type", .bind = bind },
722 .other = .{ .visibility = opts.visibility },740 .other = .{ .visibility = opts.visibility },
723 .shndx = opts.shndx.toSection().?,741 .shndx = opts.shndx.toSection().?,
724 };742 };
...@@ -754,7 +772,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -754,7 +772,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
754 .name = @intFromEnum(opts.name.dynstr),772 .name = @intFromEnum(opts.name.dynstr),
755 .value = @intCast(opts.value),773 .value = @intCast(opts.value),
756 .size = @intCast(opts.size),774 .size = @intCast(opts.size),
757 .info = .{ .type = opts.type, .bind = bind },775 .info = .{ .type = @"type", .bind = bind },
758 .other = .{ .visibility = opts.visibility },776 .other = .{ .visibility = opts.visibility },
759 .shndx = opts.shndx.toSection().?,777 .shndx = opts.shndx.toSection().?,
760 };778 };
...@@ -783,7 +801,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -783,7 +801,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
783 if (new_global_ptr.dynsym_index != 0 and801 if (new_global_ptr.dynsym_index != 0 and
784 opts.visibility == .DEFAULT and802 opts.visibility == .DEFAULT and
785 opts.shndx == .UNDEF and803 opts.shndx == .UNDEF and
786 opts.type == .FUNC)804 @"type" == .FUNC)
787 {805 {
788 // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.806 // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.
789 // We therefore might need a PLT entry, so let's add one now. TODO: it'd be good to remove807 // We therefore might need a PLT entry, so let's add one now. TODO: it'd be good to remove
...@@ -1935,6 +1953,7 @@ fn create(...@@ -1935,6 +1953,7 @@ fn create(
1935 .weak_undef = .empty,1953 .weak_undef = .empty,
1936 },1954 },
1937 .node_global_symbols = .empty,1955 .node_global_symbols = .empty,
1956 .dso_globals = .empty,
1938 .shstrtab = .{ .map = .empty },1957 .shstrtab = .{ .map = .empty },
1939 .strtab = .{ .map = .empty },1958 .strtab = .{ .map = .empty },
1940 .dynstr = .{ .map = .empty },1959 .dynstr = .{ .map = .empty },
...@@ -1981,6 +2000,7 @@ pub fn deinit(elf: *Elf) void {...@@ -1981,6 +2000,7 @@ pub fn deinit(elf: *Elf) void {
1981 elf.globals.strong_undef.deinit(gpa);2000 elf.globals.strong_undef.deinit(gpa);
1982 elf.globals.weak_undef.deinit(gpa);2001 elf.globals.weak_undef.deinit(gpa);
1983 elf.node_global_symbols.deinit(gpa);2002 elf.node_global_symbols.deinit(gpa);
2003 elf.dso_globals.deinit(gpa);
1984 elf.shstrtab.map.deinit(gpa);2004 elf.shstrtab.map.deinit(gpa);
1985 elf.strtab.map.deinit(gpa);2005 elf.strtab.map.deinit(gpa);
1986 elf.dynstr.map.deinit(gpa);2006 elf.dynstr.map.deinit(gpa);
...@@ -3183,7 +3203,14 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError ||...@@ -3183,7 +3203,14 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError ||
3183 else => |e| return e,3203 else => |e| return e,
3184 };3204 };
3185 },3205 },
3186 .dso_exact => |dso_exact| try elf.loadDsoExact(dso_exact.name),3206 .dso_exact => |dso_exact| {
3207 log.debug("load dso_exact '{f}'", .{std.zig.fmtString(dso_exact.name)});
3208 if (elf.shndx.dynamic != .UNDEF) {
3209 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, dso_exact.name), {});
3210 }
3211 // TODO: we need to get a resolved file path from the frontend, because we need to read
3212 // the shared object to discover symbol types.
3213 },
3187 }3214 }
3188}3215}
3189fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {3216fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
...@@ -3643,6 +3670,7 @@ fn loadObject(...@@ -3643,6 +3670,7 @@ fn loadObject(
3643}3670}
3644fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {3671fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
3645 const comp = elf.base.comp;3672 const comp = elf.base.comp;
3673 const gpa = comp.gpa;
3646 const diags = &comp.link_diags;3674 const diags = &comp.link_diags;
3647 const r = &fr.interface;3675 const r = &fr.interface;
36483676
...@@ -3657,68 +3685,147 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -3657,68 +3685,147 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
3657 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});3685 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
3658 if (ehdr.machine != elf.ehdrField(.machine))3686 if (ehdr.machine != elf.ehdrField(.machine))
3659 return diags.failParse(path, "bad machine", .{});3687 return diags.failParse(path, "bad machine", .{});
3660 if (ehdr.phoff == 0 or ehdr.phnum <= 1)3688 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
3661 return diags.failParse(path, "no program headers", .{});3689 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
3662 try fr.seekTo(ehdr.phoff);3690 var dynamic_sh: ?ElfN.Shdr = null;
3663 const dynamic_ph = for (0..ehdr.phnum) |_| {3691 var dynsym_sh: ?ElfN.Shdr = null;
3664 const ph = try r.peekStruct(ElfN.Phdr, target_endian);3692 for (0..ehdr.shnum) |_| {
3665 try r.discardAll(ehdr.phentsize);3693 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
3666 switch (ph.type) {3694 try r.discardAll(ehdr.shentsize);
3667 else => {},3695 switch (sh.type) {
3668 .DYNAMIC => break ph,3696 else => {},
3697 .DYNAMIC => dynamic_sh = sh,
3698 .DYNSYM => dynsym_sh = sh,
3699 }
3669 }3700 }
3670 } else return diags.failParse(path, "no dynamic segment", .{});3701 break :sh .{
3702 dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}),
3703 dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}),
3704 };
3705 };
3706 const dynstr_sh: ElfN.Shdr = sh: {
3707 if (dynsym_sh.link >= ehdr.shnum) {
3708 return diags.failParse(path, "bad dynamic string table section index", .{});
3709 }
3710 try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize);
3711 break :sh try r.peekStruct(ElfN.Shdr, target_endian);
3712 };
3713
3714 if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) {
3715 return diags.failParse(path, "bad dynamic section entsize", .{});
3716 }
3671 const dynnum = std.math.divExact(3717 const dynnum = std.math.divExact(
3672 u32,3718 u32,
3673 @intCast(dynamic_ph.filesz),3719 @intCast(dynamic_sh.size),
3674 @sizeOf(ElfN.Addr) * 2,3720 @sizeOf(ElfN.Addr) * 2,
3675 ) catch return diags.failParse(3721 ) catch return diags.failParse(
3676 path,3722 path,
3677 "dynamic segment filesz (0x{x}) is not a multiple of entsize (0x{x})",3723 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",
3678 .{ dynamic_ph.filesz, @sizeOf(ElfN.Addr) * 2 },3724 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },
3725 );
3726
3727 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {
3728 return diags.failParse(path, "bad dynsym entsize", .{});
3729 }
3730 const symnum = std.math.divExact(
3731 u32,
3732 @intCast(dynsym_sh.size),
3733 @intCast(dynsym_sh.entsize),
3734 ) catch return diags.failParse(
3735 path,
3736 "dynsym size (0x{x}) is not a multiple of entsize (0x{x})",
3737 .{ dynsym_sh.size, dynsym_sh.entsize },
3679 );3738 );
3680 var strtab: ?ElfN.Addr = null;3739
3681 var strsz: ?ElfN.Addr = null;3740 const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size));
3682 var soname: ?ElfN.Addr = null;3741 defer gpa.free(dynstr);
3683 try fr.seekTo(dynamic_ph.offset);3742 try fr.seekTo(dynstr_sh.offset);
3684 for (0..dynnum) |_| {3743 try r.readSliceAll(dynstr);
3744
3745 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.
3746 try fr.seekTo(dynamic_sh.offset);
3747 const soname: []const u8 = for (0..dynnum) |_| {
3685 const tag = try r.takeInt(ElfN.Addr, target_endian);3748 const tag = try r.takeInt(ElfN.Addr, target_endian);
3686 const val = try r.takeInt(ElfN.Addr, target_endian);3749 const val = try r.takeInt(ElfN.Addr, target_endian);
3687 switch (tag) {3750 if (tag == std.elf.DT_SONAME) {
3688 else => {},3751 // val is a dynstr index
3689 std.elf.DT_STRTAB => strtab = val,3752 if (val >= dynstr.len) {
3690 std.elf.DT_STRSZ => strsz = val,3753 return diags.failParse(path, "bad soname string", .{});
3691 std.elf.DT_SONAME => soname = val,3754 }
3755 break std.mem.sliceTo(dynstr[@intCast(val)..], 0);
3692 }3756 }
3693 }3757 } else std.fs.path.basename(path.sub_path);
3694 if (strtab == null or soname == null)3758 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});
3695 return elf.loadDsoExact(std.fs.path.basename(path.sub_path));3759
3696 if (strsz) |size| if (soname.? >= size)3760 // Scan the symbol table and populate `elf.dso_globals`.
3697 return diags.failParse(path, "bad soname string", .{});3761 const first_global = @min(dynsym_sh.info, symnum);
3698 try fr.seekTo(ehdr.phoff);3762 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);
3699 const ph = for (0..ehdr.phnum) |_| {3763 try elf.ensureUnusedPltCapacity(symnum - first_global);
3700 const ph = try r.peekStruct(ElfN.Phdr, target_endian);3764 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);
3701 try r.discardAll(ehdr.phentsize);3765 for (first_global..symnum) |_| {
3702 switch (ph.type) {3766 const sym = try r.peekStruct(ElfN.Sym, target_endian);
3703 else => {},3767 try r.discardAll(@intCast(dynsym_sh.entsize));
3704 .LOAD => if (strtab.? >= ph.vaddr and3768
3705 strtab.? + (strsz orelse 0) <= ph.vaddr + ph.filesz) break ph,3769 switch (sym.info.bind) {
3770 else => continue,
3771 .GLOBAL, .WEAK, .GNU_UNIQUE => {},
3706 }3772 }
3707 } else return diags.failParse(path, "strtab not part of a loaded segment", .{});3773 // STV_HIDDEN/STV_INTERNAL symbols should be marked as STB_LOCAL and hence skipped
3708 try fr.seekTo(strtab.? + soname.? - ph.vaddr + ph.offset);3774 // above, but we might as well double-check.
3709 return elf.loadDsoExact(r.peekSentinel(0) catch |err| switch (err) {3775 switch (sym.other.visibility) {
3710 error.StreamTooLong => return diags.failParse(path, "soname too lang", .{}),3776 .HIDDEN, .INTERNAL => continue,
3711 else => |e| return e,3777 .DEFAULT, .PROTECTED => {},
3712 });3778 }
3779
3780 if (sym.name >= dynstr.len) {
3781 return diags.failParse(path, "bad symbol name string", .{});
3782 }
3783
3784 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));
3785 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
3786 if (!gop.found_existing or gop.value_ptr.* == .NOTYPE) {
3787 gop.value_ptr.* = sym.info.type;
3788 }
3789
3790 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
3791 // its type now.
3792 update_sym_type: {
3793 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
3794 elf.globals.weak_undef.getPtr(name) orelse
3795 break :update_sym_type;
3796
3797 if (global_ptr.dynsym_index == 0) break :update_sym_type;
3798
3799 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));
3800 switch (elf.targetLoad(&sym_ptr.other).visibility) {
3801 .HIDDEN, .INTERNAL, .PROTECTED => break :update_sym_type,
3802 .DEFAULT => {},
3803 }
3804
3805 const cur_info = elf.targetLoad(&sym_ptr.info);
3806 if (cur_info.type == .NOTYPE) {
3807 elf.targetStore(&sym_ptr.info, .{
3808 .bind = cur_info.bind,
3809 .type = sym.info.type,
3810 });
3811
3812 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
3813 elf.targetStore(&dynsym_ptr.info, .{
3814 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
3815 .type = sym.info.type,
3816 });
3817
3818 if (sym.info.type == .FUNC) {
3819 // We've just determined that this symbol actually needs a PLT entry.
3820 elf.addPltEntry(name, global_ptr.dynsym_index);
3821 // TODO: we therefore need to re-apply PLT32 relocs for that symbol!
3822 }
3823 }
3824 }
3825 }
3713 },3826 },
3714 }3827 }
3715}3828}
3716fn loadDsoExact(elf: *Elf, name: []const u8) !void {
3717 log.debug("loadDsoExact({f})", .{std.zig.fmtString(name)});
3718 if (elf.shndx.dynamic != .UNDEF) {
3719 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, name), {});
3720 }
3721}
37223829
3723/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.3830/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.
3724///3831///
...@@ -4434,14 +4541,14 @@ pub fn flush(...@@ -4434,14 +4541,14 @@ pub fn flush(
4434 _ = arena;4541 _ = arena;
4435 _ = prog_node;4542 _ = prog_node;
44364543
4437 if (elf.ehdrField(.type) != .REL and4544 if (comp.config.output_mode == .Exe) {
4438 elf.shndx.dynamic == .UNDEF and4545 var any_undef = false;
4439 elf.globals.strong_undef.count() > 0)
4440 {
4441 for (elf.globals.strong_undef.keys()) |name| {4546 for (elf.globals.strong_undef.keys()) |name| {
4547 if (elf.dso_globals.contains(name)) continue;
4548 any_undef = true;
4442 comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});4549 comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
4443 }4550 }
4444 return error.LinkFailure;4551 if (any_undef) return error.LinkFailure;
4445 }4552 }
44464553
4447 while (try elf.idle(tid)) {}4554 while (try elf.idle(tid)) {}