| ... | @@ -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. |
| 56 | node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)), | 56 | node_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. |
| | 64 | dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT), |
| 57 | shstrtab: StringTable, | 65 | shstrtab: StringTable, |
| 58 | strtab: StringTable, | 66 | strtab: StringTable, |
| 59 | dynstr: StringTable, | 67 | dynstr: 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 | } |
| 433 | | 441 | |
| 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 | } |
| | 447 | fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { |
| | 448 | const gpa = elf.base.comp.gpa; |
| 436 | | 449 | |
| 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 | } | | |
| 447 | | 452 | |
| 448 | // Ensure the `.got.plt` section's node is big enough | 453 | 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 | } |
| 458 | | 463 | |
| 459 | // Ensure the `.plt.sec` section's node is big enough | 464 | // 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 | } |
| 466 | | 474 | |
| 467 | // Ensure the `.rela.plt` section's node is big enough | 475 | // 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 can | 485 | 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 | }; |
| 702 | | 715 | |
| | 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 and | 801 | if (new_global_ptr.dynsym_index != 0 and |
| 784 | opts.visibility == .DEFAULT and | 802 | opts.visibility == .DEFAULT and |
| 785 | opts.shndx == .UNDEF and | 803 | 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 remove | 807 | // 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 | } |
| 3189 | fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | 3216 | fn 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 | } |
| 3644 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | 3671 | fn 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; |
| 3648 | | 3676 | |
| ... | @@ -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 and | 3768 | |
| 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 | } |
| 3716 | fn 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 | } | | |
| 3722 | | 3829 | |
| 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; |
| 4436 | | 4543 | |
| 4437 | if (elf.ehdrField(.type) != .REL and | 4544 | if (comp.config.output_mode == .Exe) { |
| 4438 | elf.shndx.dynamic == .UNDEF and | 4545 | 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 | } |
| 4446 | | 4553 | |
| 4447 | while (try elf.idle(tid)) {} | 4554 | while (try elf.idle(tid)) {} |