| ... | ... | @@ -479,9 +479,8 @@ const Section = struct { |
| 479 | 479 | } |
| 480 | 480 | |
| 481 | 481 | fn vaddr(s: Index, elf: *Elf) u64 { |
| 482 | | return switch (s.get(elf).lsi) { |
| 483 | | .null => 0, |
| 484 | | else => |lsi| Symbol.Id.local(lsi).value(elf), |
| 482 | return switch (elf.shdrPtr(s)) { |
| 483 | inline else => |shdr| elf.targetLoad(&shdr.addr), |
| 485 | 484 | }; |
| 486 | 485 | } |
| 487 | 486 | |
| ... | ... | @@ -1730,16 +1729,12 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 1730 | 1729 | elf.moveDemotedGlobal(new_global_ptr); |
| 1731 | 1730 | } |
| 1732 | 1731 | |
| 1733 | | if (new_global_ptr.dynsym_index != 0 and |
| 1734 | | opts.visibility == .DEFAULT and |
| 1735 | | opts.shndx == .UNDEF and |
| 1736 | | (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC)) |
| 1737 | | { |
| 1738 | | // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO. |
| 1739 | | // We therefore might need a PLT entry, so let's add one now. |
| 1740 | | elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index); |
| 1741 | | // TODO: we also need to emit a PLT entry if the symbol could be preempted/interposed! By |
| 1742 | | // not doing that we're basically implementing the behavior of `-Bsymbolic-functions`. |
| 1732 | switch (@"type") { |
| 1733 | .FUNC, .GNU_IFUNC => if (!elf.haveCanonicalSymbolDefinition(.global(opts.name.strtab))) { |
| 1734 | // This STT_FUNC symbol might be defined externally, so it needs a PLT entry. |
| 1735 | elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index); |
| 1736 | }, |
| 1737 | else => {}, |
| 1743 | 1738 | } |
| 1744 | 1739 | |
| 1745 | 1740 | return .global(opts.name.strtab); |
| ... | ... | @@ -1852,9 +1847,7 @@ fn setGlobalSymbolValue( |
| 1852 | 1847 | // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error. |
| 1853 | 1848 | // This also allows the PLT entry to be reused---see `pltEntryIsDead`. |
| 1854 | 1849 | if (elf.plt.getIndex(global_name)) |plt_index| { |
| 1855 | | // TODO: we might still need the PLT entry if the symbol could be preempted/interposed! See |
| 1856 | | // matching comment at the end of `addGlobalSymbolAssumeCapacity`. |
| 1857 | | if (!elf.pltEntryIsDead(plt_index)) { |
| 1850 | if (elf.haveCanonicalSymbolDefinition(.global(global_name)) and !elf.pltEntryIsDead(plt_index)) { |
| 1858 | 1851 | elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index)); |
| 1859 | 1852 | assert(elf.pltEntryIsDead(plt_index)); |
| 1860 | 1853 | } |
| ... | ... | @@ -2372,6 +2365,32 @@ fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global { |
| 2372 | 2365 | return null; |
| 2373 | 2366 | } |
| 2374 | 2367 | |
| 2368 | fn haveCanonicalSymbolDefinition(elf: *Elf, sym: Symbol.Id) bool { |
| 2369 | const global_name = switch (sym.unwrap()) { |
| 2370 | .local => return true, |
| 2371 | .global => |name| name, |
| 2372 | }; |
| 2373 | |
| 2374 | if (elf.shndx.dynamic == .UNDEF) return true; |
| 2375 | |
| 2376 | const global_ptr = elf.globals.strong_def.getPtr(global_name) orelse |
| 2377 | elf.globals.weak_def.getPtr(global_name) orelse |
| 2378 | return false; // no definition at all |
| 2379 | |
| 2380 | if (elf.base.comp.config.output_mode == .Exe) { |
| 2381 | // Symbols defined in executables cannot be preempted |
| 2382 | return true; |
| 2383 | } |
| 2384 | |
| 2385 | const visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) { |
| 2386 | inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility, |
| 2387 | }; |
| 2388 | return switch (visibility) { |
| 2389 | .INTERNAL, .HIDDEN, .PROTECTED => true, // protection prevents preemption |
| 2390 | .DEFAULT => false, |
| 2391 | }; |
| 2392 | } |
| 2393 | |
| 2375 | 2394 | pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { |
| 2376 | 2395 | const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) { |
| 2377 | 2396 | .file, |
| ... | ... | @@ -5785,11 +5804,9 @@ fn addSymbolRelocAssumeCapacity( |
| 5785 | 5804 | assert(elf.ehdrField(.type) != .REL); |
| 5786 | 5805 | |
| 5787 | 5806 | const rela_index: Section.RelaIndex.Optional = r: { |
| 5788 | | if (elf.shndx.dynamic == .UNDEF) break :r .none; |
| 5789 | | const global_name = switch (target.unwrap()) { |
| 5790 | | .local => break :r .none, |
| 5791 | | .global => |name| name, |
| 5792 | | }; |
| 5807 | if (elf.haveCanonicalSymbolDefinition(target)) break :r .none; |
| 5808 | // If the definition is (potentially) external, `target` must be global. |
| 5809 | const global_name = target.unwrap().global; |
| 5793 | 5810 | |
| 5794 | 5811 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 5795 | 5812 | else => |machine| @panic(@tagName(machine)), |
| ... | ... | @@ -5850,16 +5867,9 @@ fn addSymbolRelocAssumeCapacity( |
| 5850 | 5867 | }, |
| 5851 | 5868 | }, |
| 5852 | 5869 | }; |
| 5853 | | // TODO: even if the symbol is locally defined, preemption/interposition is a |
| 5854 | | // possibility, which this condition does not currently consider! |
| 5855 | | if (elf.globals.strong_def.contains(global_name) or |
| 5856 | | elf.globals.weak_def.contains(global_name)) |
| 5857 | | { |
| 5858 | | break :r .none; |
| 5859 | | } |
| 5860 | 5870 | |
| 5861 | 5871 | const dynsym_index = elf.globalByName(global_name).?.dynsym_index; |
| 5862 | | if (dynsym_index == 0) break :r .none; |
| 5872 | assert(dynsym_index != 0); |
| 5863 | 5873 | |
| 5864 | 5874 | switch (elf.nodeWantsDsoRelocation(node)) { |
| 5865 | 5875 | .no => break :r .none, |
| ... | ... | @@ -5986,25 +5996,8 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 5986 | 5996 | } = switch (elf.got.keys()[got_index]) { |
| 5987 | 5997 | .reserved => .{ .unsigned = 0 }, |
| 5988 | 5998 | .tpoff => |sym_id| val: { |
| 5989 | | // We will break from this block if we require a relocation. |
| 5990 | | known: { |
| 5991 | | if (elf.base.comp.config.output_mode != .Exe) { |
| 5992 | | // Only the executable's per-module TLS block is at a known offset from the |
| 5993 | | // general TLS pointer. |
| 5994 | | break :known; |
| 5995 | | } |
| 5996 | | switch (sym_id.unwrap()) { |
| 5997 | | .local => {}, |
| 5998 | | .global => |name| if (elf.globals.strong_undef.contains(name) or |
| 5999 | | elf.globals.weak_undef.contains(name)) |
| 6000 | | { |
| 6001 | | // This is an external TLS symbol, so we don't know its offset. |
| 6002 | | break :known; |
| 6003 | | }, |
| 6004 | | } |
| 6005 | | // It's a symbol which we define, the symbol is not interposable because we're the |
| 6006 | | // executable, and we know our per-module TLS block's offset because we're the |
| 6007 | | // executable. We therefore know this value! |
| 5999 | // Only the executable's per-module TLS block is at a known offset from the TLS pointer. |
| 6000 | if (elf.base.comp.config.output_mode == .Exe and elf.haveCanonicalSymbolDefinition(sym_id)) { |
| 6008 | 6001 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 6009 | 6002 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 6010 | 6003 | inline else => |phdr| tls_size: { |
| ... | ... | @@ -6036,46 +6029,13 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6036 | 6029 | } }, |
| 6037 | 6030 | }; |
| 6038 | 6031 | }, |
| 6039 | | .symbol, .tlsgd1 => |sym_id, tag| val: { |
| 6040 | | const name = switch (sym_id.unwrap()) { |
| 6041 | | .local => break :val .{ .unsigned = sym_id.value(elf) }, |
| 6042 | | .global => |name| name, |
| 6043 | | }; |
| 6044 | | // If the symbol is *defined* in this module, we might be able to avoid the relocation. |
| 6045 | | const need_reloc: bool = need_reloc: { |
| 6046 | | const global = g: { |
| 6047 | | if (elf.globals.strong_def.getPtr(name)) |g| break :g g; |
| 6048 | | if (elf.globals.weak_def.getPtr(name)) |g| break :g g; |
| 6049 | | // The global is undefined, which probably means we need a relocation---unless |
| 6050 | | // we have created a copy relocation for it, in which case we own the canonical |
| 6051 | | // address of this symbol in this DSO! |
| 6052 | | break :need_reloc !elf.copied_globals.contains(name); |
| 6053 | | }; |
| 6054 | | |
| 6055 | | // We have a definition, but it might be interposable (aka preemptible). There |
| 6056 | | // are two cases where it is not and so we can (and, in fact, must) elide the |
| 6057 | | // runtime relocation: |
| 6058 | | // * We are the executable. Symbols from executables cannot be interposed. |
| 6059 | | // * The symbol's visibility disallows interposition. |
| 6060 | | if (elf.base.comp.config.output_mode == .Exe) { |
| 6061 | | break :need_reloc false; |
| 6062 | | } |
| 6063 | | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { |
| 6064 | | inline else => |sym| elf.targetLoad(&sym.other).visibility, |
| 6065 | | }; |
| 6066 | | break :need_reloc switch (visibility) { |
| 6067 | | .DEFAULT => true, |
| 6068 | | .INTERNAL, .HIDDEN, .PROTECTED => false, |
| 6069 | | }; |
| 6070 | | }; |
| 6071 | | |
| 6072 | | if (!need_reloc) { |
| 6073 | | break :val .{ .unsigned = sym_id.value(elf) }; |
| 6032 | .symbol, .tlsgd1 => |sym, tag| val: { |
| 6033 | if (elf.haveCanonicalSymbolDefinition(sym)) { |
| 6034 | break :val .{ .unsigned = sym.value(elf) }; |
| 6074 | 6035 | } |
| 6075 | | |
| 6076 | 6036 | break :val .{ .reloc = .{ |
| 6077 | 6037 | .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf), |
| 6078 | | .dynsym_index = elf.globalByName(name).?.dynsym_index, |
| 6038 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| 6079 | 6039 | .addend = 0, |
| 6080 | 6040 | } }; |
| 6081 | 6041 | }, |
| ... | ... | @@ -6088,31 +6048,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6088 | 6048 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, |
| 6089 | 6049 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 6090 | 6050 | }, |
| 6091 | | .dynsym_index = switch (sym.unwrap()) { |
| 6092 | | .local => 0, |
| 6093 | | .global => |name| dsi: { |
| 6094 | | // Like in the `.tlsgd1` case, we need to check for a non-interposable definition. |
| 6095 | | if (elf.globals.strong_def.getPtr(name) orelse |
| 6096 | | elf.globals.weak_def.getPtr(name)) |global| |
| 6097 | | { |
| 6098 | | if (elf.base.comp.config.output_mode == .Exe) { |
| 6099 | | break :dsi 0; // non-interposable definition |
| 6100 | | } |
| 6101 | | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { |
| 6102 | | inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility, |
| 6103 | | }; |
| 6104 | | switch (visibility) { |
| 6105 | | .DEFAULT => {}, |
| 6106 | | .INTERNAL, .HIDDEN, .PROTECTED => { |
| 6107 | | break :dsi 0; // non-interposable definition |
| 6108 | | }, |
| 6109 | | } |
| 6110 | | } |
| 6111 | | // `sym` is either undefined or an interposable definition, so use its |
| 6112 | | // actual dynsym index. |
| 6113 | | break :dsi elf.globalByName(name).?.dynsym_index; |
| 6114 | | }, |
| 6115 | | }, |
| 6051 | .dynsym_index = if (elf.haveCanonicalSymbolDefinition(sym)) 0 else elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| 6116 | 6052 | .addend = 0, |
| 6117 | 6053 | }, |
| 6118 | 6054 | }, |