authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-22 08:16:20+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-22 12:31:01+01:00
log9101d0395e28053a8cbceac40821afd15db127d3
tree5f413a617b7a54d178a83e78bddde171d2daa027
parent93b5572d4e58b77d85bf059aca160ea09ce9e465
signature Commit is signed but in an unrecognized format.

Elf2: implement GNU symbol versioning

Resolves: https://codeberg.org/ziglang/zig/issues/36665

2 files changed, 2120 insertions(+), 901 deletions(-)

lib/std/elf.zig+9
...@@ -1565,6 +1565,7 @@ pub const Verdaux = extern struct {...@@ -1565,6 +1565,7 @@ pub const Verdaux = extern struct {
1565 name: Word,1565 name: Word,
1566 next: Word,1566 next: Word,
1567};1567};
1568/// Deprecated, use `std.elf.Verneed`
1568pub const Elf32_Verneed = extern struct {1569pub const Elf32_Verneed = extern struct {
1569 vn_version: Half,1570 vn_version: Half,
1570 vn_cnt: Half,1571 vn_cnt: Half,
...@@ -1572,6 +1573,7 @@ pub const Elf32_Verneed = extern struct {...@@ -1572,6 +1573,7 @@ pub const Elf32_Verneed = extern struct {
1572 vn_aux: Word,1573 vn_aux: Word,
1573 vn_next: Word,1574 vn_next: Word,
1574};1575};
1576/// Deprecated, use `std.elf.Verneed`
1575pub const Elf64_Verneed = extern struct {1577pub const Elf64_Verneed = extern struct {
1576 vn_version: Half,1578 vn_version: Half,
1577 vn_cnt: Half,1579 vn_cnt: Half,
...@@ -1579,6 +1581,13 @@ pub const Elf64_Verneed = extern struct {...@@ -1579,6 +1581,13 @@ pub const Elf64_Verneed = extern struct {
1579 vn_aux: Word,1581 vn_aux: Word,
1580 vn_next: Word,1582 vn_next: Word,
1581};1583};
1584pub const Verneed = extern struct {
1585 version: Half,
1586 cnt: Half,
1587 file: Word,
1588 aux: Word,
1589 next: Word,
1590};
1582pub const Vernaux = extern struct {1591pub const Vernaux = extern struct {
1583 hash: Word,1592 hash: Word,
1584 flags: Half,1593 flags: Half,
src/link/Elf2.zig+2111-901
...@@ -52,6 +52,9 @@ shndx: struct {...@@ -52,6 +52,9 @@ shndx: struct {
52 tdata: Section.Index,52 tdata: Section.Index,
53 rela_dyn: Section.Index,53 rela_dyn: Section.Index,
54 rela_plt: Section.Index,54 rela_plt: Section.Index,
55 gnu_version: Section.Index,
56 gnu_version_d: Section.Index,
57 gnu_version_r: Section.Index,
55 debug_abbrev: Section.Index,58 debug_abbrev: Section.Index,
56 debug_addr: Section.Index,59 debug_addr: Section.Index,
57 eh_frame_hdr: Section.Index,60 eh_frame_hdr: Section.Index,
...@@ -76,52 +79,62 @@ dynamic: struct {...@@ -76,52 +79,62 @@ dynamic: struct {
76},79},
77symtab: std.ArrayList(Symbol),80symtab: std.ArrayList(Symbol),
78globals: std.ArrayList(Symbol.Global),81globals: std.ArrayList(Symbol.Global),
79/// Accessed with `Symbol.Global.NameAdapter`. Items map 1--1 to `globals`.82/// Accessed with `Symbol.Global.VersionedName.Adapter`. Items map 1--1 to `globals`.
80globals_by_name: std.array_hash_map.Custom(void, void, void, true),83globals_by_name: std.array_hash_map.Custom(void, void, void, true),
84/// Accessed with `Symbol.Global.DefaultVersionAdapter`.
85default_version_globals: std.array_hash_map.Custom(Symbol.Global.Index, void, void, true),
81/// Set of all strong undef globals which we have also not yet seen defined in any input DSO. We86/// Set of all strong undef globals which we have also not yet seen defined in any input DSO. We
82/// maintain this set to allow efficient reporting of "undefined global symbol" errors.87/// maintain this set to allow efficient reporting of "undefined global symbol" errors.
83unknown_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),88unknown_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
89/// Key is a global which has multiple definitions, one an actual definition for that global, and
90/// the other because it is an alias for another global (see `Symbol.Global.Index.resolveAlias`).
91defined_alias_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
84/// Key is an undef global for which we have created a "copy relocation" (`R_*_COPY`).92/// Key is an undef global for which we have created a "copy relocation" (`R_*_COPY`).
85copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, struct {93copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, struct {
86 node: MappedFile.Node.Index,94 node: MappedFile.Node.Index,
87 /// The index of this global's runtime relocation in `.rela.dyn`.95 /// The index of this global's runtime relocation in `.rela.dyn`.
88 rela_index: Section.RelaIndex,96 rela_index: Section.RelaIndex,
89}),97}),
90/// Key is an undef global for which we would *like* to create a copy relocation (`R_*_COPY`), but98/// When rearranging entries in the dynamic symbol table, we need to map a dynamic symbol index to
91/// cannot because we have not seen an appropriate definition in a linked DSO yet.99/// the corresponding `Symbol.Global.Index` which "owns" that dynsym entry. Usually this is usually
100/// by simply looking up the dynamic symbol's name in `globals_by_name`, but versioned symbols may
101/// have different names in the dynamic symbol table than the normal symbol table. If they do, they
102/// are added to this map, which is checked in `Elf.globalByDynsym` before doing the aforementioned
103/// name lookup.
92///104///
93/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we105/// Key is index into `.dynsym`.
94/// will remove it from this map and call `maybeAddCopyRelocation`.106versioned_dynsym_owners: std.array_hash_map.Auto(u32, Symbol.Global.Index),
95want_copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),107/// Accessed with `VerdefAdapter`. Entries in `.gnu.version_d` are contiguous verdef/verdaux pairs
108/// (see `VerdefEntry`), and the "base" version is not in this map, so a given `index` in this map
109/// refers to the version defined at offset `(index + 1) * @sizeOf(VerdefEntry)` into the section.
110verdef: std.array_hash_map.Custom(void, void, void, true),
111/// Accessed with `VerneedAdapter`. Entries in `.gnu.version_r` are either `std.elf.Verneed` or
112/// `std.elf.Vernaux`---these types are the same size, and the entry at a given `index` in this map
113/// refers to the verneed/vernaux at offset `index * @sizeOf(VerneedEntry)`. The stored key
114/// indicates whether the entry is a verneed or a vernaux.
115verneed: std.array_hash_map.Custom(VerneedAdapter.StoredKey, void, void, true),
116/// Index into `Elf.verneed` of the last entry which represented a file. Tracked so that new files
117/// can be added to the linked list in the verneed section.
118last_verneed_file_index: usize,
119/// The next unused symbol version ID, referenced by entries in the `.gnu.version` section and
120/// defined by entries in the `.gnu.version_d` and `.gnu.version_r` sections. Initially 2, because
121/// 0 and 1 are reserved. When `verdefId` or `verneedId` adds a new version, they will assign it
122/// the ID stored here (and increment this field).
123///
124/// Note that documentation and ELF headers call these "version indices" rather than "version IDs",
125/// but that's a misnomer: they are opaque IDs with no requirement to be contiguous or to be defined
126/// in order. That said, dynamic linker implementations may internally create a version lookup table
127/// indexed by the ID, so for efficiency it's good to keep them dense.
128next_version_id: u16,
96/// Key is a node which is a valid `Symbol.node` value, value is the first global symbol in that129/// Key is a node which is a valid `Symbol.node` value, value is the first global symbol in that
97/// node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.130/// node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
98///131///
99/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,132/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
100/// because the vast majority of nodes which can export global symbols actually will not.133/// because the vast majority of nodes which can export global symbols actually will not.
101node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Global.Index),134node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Global.Index),
102/// Contains all globals symbols defined in any needed DSO. This map serves three purposes:135/// See the definition of `DsoGlobals`.
103///136dso_globals: DsoGlobals,
104/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has137
105/// type `STT_FUNC`, in which case we will create a PLT entry.
106///
107/// * If we discover a direct relocation (i.e. no GOT or PLT indirection) targeting one of these
108/// symbols, we know whether the symbol has type `STT_OBJECT` and we know its size and alignment,
109/// so we can emit a copy relocation for that symbol instead of using a text relocation.
110///
111/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a
112/// linked DSO, so can emit "undefined global symbol" errors for any other undefined references.
113dso_globals: std.array_hash_map.Auto(String(.strtab), struct {
114 type: std.elf.STT,
115 size: u64,
116 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and so
117 /// becomes a part of the executable's address space despite being defined by a different DSO,
118 /// we need to know its alignment requirement so that we don't break other code. This isn't
119 /// actually stored on the symbol---instead we compute a maximum alignment from the alignment of
120 /// the section containing the symbol, and the symbol's offset within the section. I know this
121 /// sounds like a terrible hack, but it is *genuinely* how you're supposed to do this. Copy
122 /// relocations suck.
123 alignment: Alignment,
124}),
125shstrtab: StringTable,138shstrtab: StringTable,
126strtab: StringTable,139strtab: StringTable,
127dynstr: StringTable,140dynstr: StringTable,
...@@ -276,7 +289,7 @@ const Node = union(enum) {...@@ -276,7 +289,7 @@ const Node = union(enum) {
276 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.289 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
277 ///290 ///
278 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because291 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because
279 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `setGlobalSymbolValue`.292 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `updateGlobalDynamic`.
280 copied_global: Symbol.Global.Index,293 copied_global: Symbol.Global.Index,
281 /// May contain relocations.294 /// May contain relocations.
282 nav: NavMapIndex,295 nav: NavMapIndex,
...@@ -1725,9 +1738,7 @@ const SymbolReloc = struct {...@@ -1725,9 +1738,7 @@ const SymbolReloc = struct {
1725 break :type reloc.target.value(elf) +% addend -% tls_size;1738 break :type reloc.target.value(elf) +% addend -% tls_size;
1726 },1739 },
1727 },1740 },
1728 .size => switch (elf.symPtr(reloc.target.index(elf))) {1741 .size => reloc.target.size(elf),
1729 inline else => |sym| elf.targetLoad(&sym.size),
1730 },
1731 .special => return reloc.type.action.special.applyInner(1742 .special => return reloc.type.action.special.applyInner(
1732 elf,1743 elf,
1733 reloc.target,1744 reloc.target,
...@@ -2178,52 +2189,152 @@ const GotReloc = struct {...@@ -2178,52 +2189,152 @@ const GotReloc = struct {
2178 }2189 }
2179};2190};
21802191
2181fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {2192/// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version,
2182 const gpa = elf.base.comp.gpa;2193/// type, size, and alignment are all stored, as well as the soname of the DSO defining it. If one
2194/// of these symbols ends up being referenced by the ELF module we are producing, we need this
2195/// information for a few reasons:
2196///
2197/// * If an undefined symbol has an external definition with type `STT_FUNC`, we know to create a
2198/// PLT entry for it.
2199///
2200/// * If an undefined symbol has an external definition with type `STT_OBJECT`, we can create a copy
2201/// relocation for it if necessary.
2202///
2203/// * If an undefined versioned symbol has an external definition, we can add the required entry to
2204/// `.gnu.version_r` because we know the soname of the DSO defining it.
2205///
2206/// * Similarly, if an undefined unversioned symbol matches an external definition of a default
2207/// symbol version, we know to emit the default version for the dynamic symbol table entry even
2208/// though that symbol version was not explicitly requested.
2209///
2210/// * If an undefined symbol does *not* have any external definition, when emitting a dynamic
2211/// executable, we can emit an "undefined global symbol" link error for it.
2212const DsoGlobals = struct {
2213 string_bytes: std.ArrayList(u8),
21832214
2184 try elf.symtab.ensureUnusedCapacity(gpa, len);2215 /// Contains all global symbols defined in any needed DSO.
2216 ///
2217 /// Accessed using `DsoGlobals.Symbol.Adapter`. Adapted key type is `DsoGlobals.Symbol.Key`.
2218 symbols: std.array_hash_map.Custom(DsoGlobals.Symbol, void, void, true),
21852219
2186 // If adding locals, we may need to move one global out of the way for each local. If adding2220 /// Contains all default-version global symbols defined in any needed DSO.
2187 // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals2221 ///
2188 // around to keep `.dynsym` compact. Either way, the maximum is N.2222 /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`.
2189 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len);2223 ///
2224 /// Stored key is an index into `DsoGlobals.symbols`.
2225 default_sym_vers: std.array_hash_map.Custom(u32, void, void, true),
21902226
2191 {2227 /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because
2192 // Ensure the symtab section's node is big enough2228 /// these string bytes are in a `std.ArrayList` rather than a section of the output binary.
2193 const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {2229 const String = enum(u32) {
2194 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),2230 _,
2231 fn slice(s: DsoGlobals.String, dso_globals: *const DsoGlobals) [:0]const u8 {
2232 const overlong = dso_globals.string_bytes.items[@backingInt(s)..];
2233 return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0];
2234 }
2235 const Optional = enum(u32) {
2236 none = std.math.maxInt(u32),
2237 _,
2238 fn unwrap(os: DsoGlobals.String.Optional) ?DsoGlobals.String {
2239 return switch (os) {
2240 .none => null,
2241 _ => @fromBackingInt(@backingInt(os)),
2242 };
2243 }
2244 fn wrap(s: DsoGlobals.String) DsoGlobals.String.Optional {
2245 return @bitCast(s);
2246 }
2195 };2247 };
2196 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_node_size);2248 };
2197 }
21982249
2199 switch (kind) {2250 const Symbol = struct {
2200 .all_local => {},2251 name: DsoGlobals.String,
2201 .maybe_global => {2252 /// `.none` if the symbol is unversioned.
2202 try elf.globals.ensureUnusedCapacity(gpa, len);2253 version: DsoGlobals.String.Optional,
2203 try elf.globals_by_name.ensureUnusedCapacity(gpa, len);2254 type: std.elf.STT,
2204 try elf.unknown_globals.ensureUnusedCapacity(gpa, len);2255 soname: Elf.String(.dynstr),
2256 size: u64,
2257 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and
2258 /// so becomes a part of the executable's address space despite being defined by a different
2259 /// DSO, we need to know its alignment requirement so that we don't break other code. This
2260 /// isn't actually stored on the symbol---instead we compute a maximum alignment from the
2261 /// alignment of the section containing the symbol, and the symbol's offset within the
2262 /// section. I know this sounds like a terrible hack, but it is *genuinely* how you're
2263 /// supposed to do this. Copy relocations suck.
2264 alignment: Alignment,
2265
2266 const Key = struct {
2267 name: []const u8,
2268 version: ?[]const u8,
2269 };
22052270
2206 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);2271 const Adapter = struct {
2272 dso_globals: *const DsoGlobals,
2273 pub fn eql(ctx: Adapter, lhs_key: Key, rhs_symbol: DsoGlobals.Symbol, _: usize) bool {
2274 const dso_globals = ctx.dso_globals;
2275 const rhs_name = rhs_symbol.name.slice(dso_globals);
2276 if (!std.mem.eql(u8, lhs_key.name, rhs_name)) return false;
2277 if (lhs_key.version) |lhs_version| {
2278 const rhs_version = rhs_symbol.version.unwrap() orelse return false;
2279 if (!std.mem.eql(u8, lhs_version, rhs_version.slice(dso_globals))) return false;
2280 } else {
2281 if (rhs_symbol.version != .none) return false;
2282 }
2283 return true;
2284 }
22072285
2208 if (elf.shndx.dynsym != .UNDEF) {2286 pub fn hash(ctx: Adapter, key: Key) u32 {
2209 const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {2287 _ = ctx;
2210 inline else => |shdr, class| .{2288 var h: std.hash.Wyhash = .init(key.name.len);
2211 elf.targetLoad(&shdr.size),2289 h.update(key.name);
2212 @sizeOf(class.ElfN().Sym),2290 if (key.version) |version| {
2213 },2291 h.update(&.{1});
2214 };2292 h.update(version);
2215 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));2293 } else {
2294 h.update(&.{0});
2295 }
2296 return @truncate(h.final());
2297 }
2298 };
22162299
2217 const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size;2300 const DefaultVersionAdapter = struct {
2218 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, dynsym_need_size);2301 dso_globals: *const DsoGlobals,
2302 pub fn eql(ctx: DefaultVersionAdapter, lhs_name: []const u8, rhs_symbol_index: u32, _: usize) bool {
2303 const rhs_symbol = &ctx.dso_globals.symbols.keys()[rhs_symbol_index];
2304 return std.mem.eql(u8, lhs_name, rhs_symbol.name.slice(ctx.dso_globals));
2305 }
2306 pub fn hash(ctx: DefaultVersionAdapter, name: []const u8) u32 {
2307 _ = ctx;
2308 return std.array_hash_map.hashString(name);
2309 }
2310 };
2311 };
22192312
2220 try elf.ensureDynsymHashCapacity(dynsym_cur_len + len);2313 fn find(dso_globals: *const DsoGlobals, name: []const u8, version: ?[]const u8) ?*const DsoGlobals.Symbol {
2314 if (dso_globals.symbols.getIndexAdapted(@as(DsoGlobals.Symbol.Key, .{
2315 .name = name,
2316 .version = version,
2317 }), @as(DsoGlobals.Symbol.Adapter, .{
2318 .dso_globals = dso_globals,
2319 }))) |index| {
2320 return &dso_globals.symbols.keys()[index];
2321 } else {
2322 return null;
2323 }
2324 }
22212325
2222 try elf.ensureUnusedPltCapacity(len);2326 fn findDefaultVersion(dso_globals: *const DsoGlobals, name: []const u8) ?*const DsoGlobals.Symbol {
2223 }2327 const adapter: DsoGlobals.Symbol.DefaultVersionAdapter = .{
2224 },2328 .dso_globals = dso_globals,
2329 };
2330 if (dso_globals.default_sym_vers.getIndexAdapted(name, adapter)) |default_index| {
2331 const symbol_index = dso_globals.default_sym_vers.keys()[default_index];
2332 return &dso_globals.symbols.keys()[symbol_index];
2333 } else {
2334 return null;
2335 }
2225 }2336 }
2226}2337};
22272338
2228fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {2339fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
2229 const gpa = elf.base.comp.gpa;2340 const gpa = elf.base.comp.gpa;
...@@ -2391,34 +2502,6 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {...@@ -2391,34 +2502,6 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2391 }2502 }
2392}2503}
23932504
2394fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
2395 const gpa = elf.base.comp.gpa;
2396
2397 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
2398
2399 try elf.plt.ensureUnusedCapacity(gpa, len);
2400 const need_plt_count = elf.plt.count() + len;
2401
2402 const plt = elf.targetPltInfo();
2403
2404 // Ensure the `.plt` section's node is big enough:
2405 {
2406 const need_size: usize = plt.entry_size * (1 + need_plt_count);
2407 try elf.shndx.plt.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2408 }
2409
2410 // If there is a `.got.plt` section, ensure its node is big enough
2411 if (plt.got_plt) |got_plt| {
2412 const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count);
2413 try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2414 }
2415
2416 // If there is a `.plt.sec` section, ensure its node is big enough
2417 if (plt.plt_sec) |plt_sec| {
2418 const need_size: usize = plt_sec.entry_size * need_plt_count;
2419 try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2420 }
2421}
2422/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at2505/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
2423/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.2506/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
2424fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {2507fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
...@@ -2441,13 +2524,23 @@ fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {...@@ -2441,13 +2524,23 @@ fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
24412524
2442const AddLocalSymbolOptions = struct {2525const AddLocalSymbolOptions = struct {
2443 node: MappedFile.Node.Index.Optional,2526 node: MappedFile.Node.Index.Optional,
2444 name: String(.strtab),2527 name: []const u8,
2445 value: u64,2528 value: u64,
2446 size: u64,2529 size: u64,
2447 type: std.elf.STT,2530 type: std.elf.STT,
2448 shndx: Section.Index,2531 shndx: Section.Index,
2449};2532};
2450fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.LocalIndex {2533fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex {
2534 const gpa = elf.base.comp.gpa;
2535
2536 try elf.symtab.ensureUnusedCapacity(gpa, 1);
2537 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, 1);
2538 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, switch (elf.shdrPtr(.symtab)) {
2539 inline else => |shdr, class| elf.targetLoad(&shdr.size) + @sizeOf(class.ElfN().Sym),
2540 });
2541
2542 const name = try elf.string(.strtab, opts.name);
2543
2451 switch (elf.shdrPtr(.symtab)) {2544 switch (elf.shdrPtr(.symtab)) {
2452 inline else => |shdr, class| {2545 inline else => |shdr, class| {
2453 const ent_size = @sizeOf(class.ElfN().Sym);2546 const ent_size = @sizeOf(class.ElfN().Sym);
...@@ -2476,8 +2569,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -2476,8 +2569,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
2476 // ...then the `elf.symtab` metadata...2569 // ...then the `elf.symtab` metadata...
2477 new_index.ptr(elf).* = target_index.ptr(elf).*;2570 new_index.ptr(elf).* = target_index.ptr(elf).*;
2478 // ...then update the `elf.globals` tracking.2571 // ...then update the `elf.globals` tracking.
2479 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));2572 const gsi = elf.globalBySym(new_index);
2480 const gsi = elf.globalByName(global_name.slice(elf)).?;
2481 gsi.ptr(elf).symtab_index = new_index;2573 gsi.ptr(elf).symtab_index = new_index;
24822574
2483 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {2575 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {
...@@ -2492,7 +2584,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -2492,7 +2584,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
2492 };2584 };
24932585
2494 target_sym.* = .{2586 target_sym.* = .{
2495 .name = @backingInt(opts.name),2587 .name = @backingInt(name),
2496 .value = @intCast(opts.value),2588 .value = @intCast(opts.value),
2497 .size = @intCast(opts.size),2589 .size = @intCast(opts.size),
2498 .info = .{ .type = opts.type, .bind = .LOCAL },2590 .info = .{ .type = opts.type, .bind = .LOCAL },
...@@ -2508,77 +2600,105 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -2508,77 +2600,105 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
2508 }2600 }
2509}2601}
25102602
2511fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {2603fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2512 node: MappedFile.Node.Index.Optional,2604 MultipleDefinitions,
2513 name: []const u8,2605 MultipleDefaultVersions,
2514 lib_name: ?[]const u8 = null,2606 UndefinedDefaultVersion,
2515 value: u64,2607} || Error)!Symbol.Global.Index {
2516 size: u64,
2517 type: std.elf.STT,
2518 bind: Symbol.Global.Bind,
2519 visibility: std.elf.STV,
2520 shndx: Section.Index,
2521}) (error{MultipleDefinitions} || Error)!Symbol.Global.Index {
2522 _ = opts.lib_name; // TODO2608 _ = opts.lib_name; // TODO
25232609
2524 const adapter: Symbol.Global.NameAdapter = .{ .elf = elf };2610 const gpa = elf.base.comp.gpa;
2525 const gop = elf.globals_by_name.getOrPutAssumeCapacityAdapted(opts.name, adapter);2611
2612 try elf.symtab.ensureUnusedCapacity(gpa, 1);
2613 try elf.globals.ensureUnusedCapacity(gpa, 1);
2614 try elf.globals_by_name.ensureUnusedCapacity(gpa, 1);
2615 try elf.node_global_symbols.ensureUnusedCapacity(gpa, 1);
2616
2617 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, switch (elf.shdrPtr(.symtab)) {
2618 inline else => |shdr, class| elf.targetLoad(&shdr.size) + @sizeOf(class.ElfN().Sym),
2619 });
2620
2621 const name = parseVersionedSymbolName(opts.name);
2622
2623 if (name.version != null and name.is_default_version) {
2624 if (opts.shndx == .UNDEF) {
2625 return error.UndefinedDefaultVersion;
2626 }
2627 }
2628
2629 const gop = elf.globals_by_name.getOrPutAssumeCapacityAdapted(@as(Symbol.Global.VersionedName, .{
2630 .name = name.name,
2631 .version = name.version,
2632 }), @as(Symbol.Global.VersionedName.Adapter, .{ .elf = elf }));
2526 const gsi: Symbol.Global.Index = @fromBackingInt(@intCast(gop.index));2633 const gsi: Symbol.Global.Index = @fromBackingInt(@intCast(gop.index));
25272634
2528 if (gop.found_existing) {2635 if (gop.found_existing) {
2636 var force_apply_relocs: bool = false;
2637
2638 if (name.version != null and name.is_default_version) {
2639 const default_gop = try elf.default_version_globals.getOrPutAdapted(
2640 elf.base.comp.gpa,
2641 name.name,
2642 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
2643 );
2644 if (!default_gop.found_existing) {
2645 // This symbol is becoming a default version.
2646 default_gop.key_ptr.* = gsi;
2647 const new_strtab_name = try elf.string(.strtab, opts.name);
2648 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2649 inline else => |sym| elf.targetStore(&sym.name, @backingInt(new_strtab_name)),
2650 }
2651 force_apply_relocs = true;
2652 } else if (default_gop.key_ptr.* != gsi) {
2653 return error.MultipleDefaultVersions;
2654 }
2655 }
2656
2657 if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) {
2658 force_apply_relocs = true;
2659 }
2660
2529 // There's already a symbol with this name, we just need to potentially set its value, and2661 // There's already a symbol with this name, we just need to potentially set its value, and
2530 // to update its bind and visibility.2662 // to update its bind and visibility.
2531 const new_bind: Symbol.Global.Bind = new_bind: {2663 if (opts.shndx == .UNDEF) {
2532 if (opts.shndx == .UNDEF) {2664 // We are not defining the symbol, so we won't set its value and we should only
2533 // We are not defining the symbol, so we won't set its value and we should only2665 // update its bind if it is undefined and we won't weaken it.
2534 // update its bind if it is undefined and we won't weaken it.2666 if (!gsi.defined(elf) and
2535 if (!Symbol.Id.global(gsi).defined(elf) and2667 gsi.ptr(elf).status.bind == .weak and
2536 gsi.bind(elf) == .weak and2668 opts.bind == .strong)
2537 opts.bind == .strong)2669 {
2538 {2670 elf.setGlobalSymbolBind(gsi, .strong);
2539 // Since the symbol is not only undefined but is now also strong, we now *need*
2540 // to see a definition for it at some point.
2541 if (!elf.dso_globals.contains(Symbol.Id.global(gsi).name(elf))) {
2542 elf.unknown_globals.putAssumeCapacityNoClobber(gsi, {});
2543 }
2544 break :new_bind .strong;
2545 } else {
2546 break :new_bind gsi.bind(elf);
2547 }
2548 }2671 }
2549 // We're providing a definition. If there is no other definition yet, that's easy, but2672 } else if (!gsi.defined(elf)) {
2550 // if there is, we need to figure out which to keep.2673 elf.setGlobalSymbolBind(gsi, opts.bind);
2551 if (!Symbol.Id.global(gsi).defined(elf)) {2674 try elf.setGlobalSymbolValue(gsi, .{
2552 _ = elf.unknown_globals.swapRemove(gsi); // previously undef so might be here2675 .node = opts.node,
2553 elf.setGlobalSymbolValue(gsi, .{2676 .value = opts.value,
2554 .node = opts.node,2677 .size = opts.size,
2555 .value = opts.value,2678 .type = opts.type,
2556 .size = opts.size,2679 .shndx = opts.shndx,
2557 .type = opts.type,2680 });
2558 .shndx = opts.shndx,2681 force_apply_relocs = true;
2559 });2682 } else switch (opts.bind) {
2560 break :new_bind opts.bind;2683 .strong => switch (gsi.ptr(elf).status.bind) {
2561 }2684 .strong => return error.MultipleDefinitions,
2562 switch (opts.bind) {
2563 .strong => switch (gsi.bind(elf)) {
2564 .strong => return error.MultipleDefinitions,
2565 .weak => {
2566 elf.setGlobalSymbolValue(gsi, .{
2567 .node = opts.node,
2568 .value = opts.value,
2569 .size = opts.size,
2570 .type = opts.type,
2571 .shndx = opts.shndx,
2572 });
2573 break :new_bind .strong;
2574 },
2575 },
2576 .weak => {2685 .weak => {
2577 break :new_bind gsi.bind(elf);2686 elf.setGlobalSymbolBind(gsi, .strong);
2687 try elf.setGlobalSymbolValue(gsi, .{
2688 .node = opts.node,
2689 .value = opts.value,
2690 .size = opts.size,
2691 .type = opts.type,
2692 .shndx = opts.shndx,
2693 });
2694 force_apply_relocs = true;
2578 },2695 },
2579 }2696 },
2580 };2697 .weak => {},
2581 elf.mergeGlobalSymbolVisibility(gsi, opts.visibility, new_bind);2698 }
2699
2700 try elf.updateGlobalDynamic(gsi, force_apply_relocs);
2701
2582 return gsi;2702 return gsi;
2583 }2703 }
25842704
...@@ -2599,13 +2719,6 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {...@@ -2599,13 +2719,6 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
2599 .weak => .WEAK,2719 .weak => .WEAK,
2600 };2720 };
26012721
2602 const @"type": std.elf.STT = switch (opts.type) {
2603 .NOTYPE => if (elf.dso_globals.get(strtab_name)) |dso_global| t: {
2604 break :t dso_global.type;
2605 } else .NOTYPE,
2606 else => |t| t,
2607 };
2608
2609 const sym_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));2722 const sym_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));
2610 elf.symtab.appendAssumeCapacity(.{2723 elf.symtab.appendAssumeCapacity(.{
2611 .node = opts.node,2724 .node = opts.node,
...@@ -2624,7 +2737,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {...@@ -2624,7 +2737,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
2624 .name = @backingInt(strtab_name),2737 .name = @backingInt(strtab_name),
2625 .value = @intCast(opts.value),2738 .value = @intCast(opts.value),
2626 .size = @intCast(opts.size),2739 .size = @intCast(opts.size),
2627 .info = .{ .type = @"type", .bind = bind },2740 .info = .{ .type = opts.type, .bind = bind },
2628 .other = .{ .visibility = opts.visibility },2741 .other = .{ .visibility = opts.visibility },
2629 .shndx = opts.shndx.toSection().?,2742 .shndx = opts.shndx.toSection().?,
2630 };2743 };
...@@ -2646,43 +2759,16 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {...@@ -2646,43 +2759,16 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
2646 };2759 };
26472760
2648 gsi.ptr(elf).* = .{2761 gsi.ptr(elf).* = .{
2762 .status = .{
2763 .bind = opts.bind,
2764 .want_static_value = false,
2765 .owns_dynsym = false,
2766 .any_static_target_relocs = false,
2767 .any_static_relative_target_relocs = false,
2768 .any_dynamic_target_relocs = false,
2769 },
2770 .dynsym_index = 0,
2649 .symtab_index = sym_index,2771 .symtab_index = sym_index,
2650 .dynsym_index = dynsym_index: {
2651 if (force_local_bind) {
2652 break :dynsym_index switch (opts.bind) {
2653 .strong => .demoted_strong,
2654 .weak => .demoted_weak,
2655 };
2656 }
2657 if (elf.shndx.dynsym == .UNDEF) {
2658 break :dynsym_index .no_section;
2659 }
2660 const dynstr_name = try elf.string(.dynstr, opts.name);
2661 switch (elf.shdrPtr(elf.shndx.dynsym)) {
2662 inline else => |shdr, class| {
2663 const Sym = class.ElfN().Sym;
2664 // Increase the dynamic symbol table size...
2665 const old_size = elf.targetLoad(&shdr.size);
2666 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2667 const dynsym_index: u32 = @intCast(@divExact(old_size, @sizeOf(Sym)));
2668 // ...then populate the newly-valid symbol pointer
2669 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2670 sym.* = .{
2671 .name = @backingInt(dynstr_name),
2672 .value = @intCast(opts.value),
2673 .size = @intCast(opts.size),
2674 .info = .{ .type = @"type", .bind = bind },
2675 .other = .{ .visibility = opts.visibility },
2676 .shndx = opts.shndx.toSection().?,
2677 };
2678 if (elf.targetEndian() != std.lang.Endian.native) {
2679 std.mem.byteSwapAllFields(Sym, sym);
2680 }
2681 elf.appendDynsymHashEntry(dynsym_index);
2682 break :dynsym_index .wrap(dynsym_index);
2683 },
2684 }
2685 },
2686 .prev_in_node = .none,2772 .prev_in_node = .none,
2687 .next_in_node = old_head,2773 .next_in_node = old_head,
2688 };2774 };
...@@ -2694,281 +2780,601 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {...@@ -2694,281 +2780,601 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
2694 old_head_ptr.prev_in_node = .wrap(gsi);2780 old_head_ptr.prev_in_node = .wrap(gsi);
2695 }2781 }
26962782
2697 if (opts.shndx == .UNDEF and
2698 opts.bind == .strong and
2699 !elf.dso_globals.contains(strtab_name))
2700 {
2701 elf.unknown_globals.putAssumeCapacityNoClobber(gsi, {});
2702 }
2703
2704 if (force_local_bind) {2783 if (force_local_bind) {
2705 elf.moveDemotedGlobal(gsi);2784 elf.moveDemotedGlobal(gsi);
2706 }2785 }
27072786
2708 switch (@"type") {2787 if (name.version != null and name.is_default_version) {
2709 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and2788 const default_gop = try elf.default_version_globals.getOrPutAdapted(
2710 elf.classifySymbolValue(.global(gsi)) == .dynamic)2789 elf.base.comp.gpa,
2711 {2790 name.name,
2712 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.2791 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
2713 elf.addPltEntry(gsi);2792 );
2714 },2793 if (!default_gop.found_existing) {
2715 else => {},2794 // This symbol is becoming a default version.
2795 default_gop.key_ptr.* = gsi;
2796 } else if (default_gop.key_ptr.* != gsi) {
2797 return error.MultipleDefaultVersions;
2798 }
2716 }2799 }
27172800
2801 // We must pass `force_apply_relocs` as `true` here, because even though `gsi` itself was only
2802 // just created (so there are no relocations targeting it yet), it is possible that another
2803 // symbol is now an alias of it, in which case that symbol *does* need target relocations to be
2804 // re-applied.
2805 try elf.updateGlobalDynamic(gsi, true);
2806
2718 return gsi;2807 return gsi;
2719}2808}
2720fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {
2721 node: MappedFile.Node.Index.Optional,
2722 value: u64,
2723 size: u64,
2724 type: std.elf.STT,
2725 shndx: Section.Index,
2726}) void {
2727 const global_ptr = gsi.ptr(elf);
27282809
2729 assert(new.shndx != .UNDEF);2810/// Updates the dynsym index associated with the given global symbol; populates that dynsym entry if
2730 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {2811/// necessary; removes unnecessary dynamic relocations; creates/removes PLT entries as needed; and
2731 if (global_ptr.next_in_node.unwrap()) |next_gsi| {2812/// creates/removes copy relocations as needed. Put simply, updates all state related to dynamic
2732 assert(next_gsi.ptr(elf).prev_in_node == Symbol.Global.Index.Optional.wrap(gsi));2813/// linking for the given symbol.
2733 next_gsi.ptr(elf).prev_in_node = global_ptr.prev_in_node;2814///
2734 }2815/// As well as on creation, this function should be called whenever any of the following properties
2735 if (global_ptr.prev_in_node.unwrap()) |prev_gsi| {2816/// of a global symbol changes:
2736 assert(prev_gsi.ptr(elf).next_in_node == Symbol.Global.Index.Optional.wrap(gsi));2817///
2737 prev_gsi.ptr(elf).next_in_node = global_ptr.next_in_node;2818/// * visibility
2819/// * type
2820/// * whether a copy relocation is required
2821/// * whether a PLT entry is required
2822/// * whether it is defined
2823/// * whether there is a matching symbol in `dso_globals`
2824/// * for versioned symbols, whether it is the default symbol version
2825/// * for unversioned symbols, this symbol's default version in `dso_globals`
2826///
2827/// This function may re-apply relocations targeting the global. If the caller has itself performed
2828/// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as
2829/// `true` to guarantee this function to do so.
2830fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_relocs: bool) Error!void {
2831 const gpa = elf.base.comp.gpa;
2832
2833 const gsi = orig_gsi.resolveAlias(elf);
2834
2835 const maybe_alias_gsi = gsi.findAliaser(elf);
2836
2837 var apply_relocs: bool = force_apply_relocs;
2838
2839 _ = elf.defined_alias_globals.swapRemove(gsi); // not an alias
2840
2841 if (maybe_alias_gsi) |alias_gsi| {
2842 if (gsi.defined(elf) and
2843 alias_gsi.defined(elf) and
2844 gsi.ptr(elf).status.bind == .strong and
2845 alias_gsi.ptr(elf).status.bind == .strong)
2846 {
2847 // `gsi` and `alias_gsi` both have strong definitions. For now we've arbitrarily decided
2848 // that `gsi`'s definition wins, but this needs to cause a "multiple definitions" error!
2849 try elf.defined_alias_globals.put(gpa, alias_gsi, {});
2738 } else {2850 } else {
2739 // We're the start of the linked list, so we need to change the head.2851 _ = elf.defined_alias_globals.swapRemove(alias_gsi);
2740 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
2741 elf.node_global_symbols.getPtr(old_node).?.* = next_gsi;
2742 } else {
2743 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == gsi);
2744 }
2745 }2852 }
2746 } else {
2747 assert(global_ptr.next_in_node == .none);
2748 assert(global_ptr.prev_in_node == .none);
2749 }
27502853
2751 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {2854 _ = elf.unknown_globals.swapRemove(alias_gsi);
2752 // This is a quite rare case: there was a definition for this symbol in a shared library2855 if (try elf.deleteOwnedDynsym(alias_gsi)) {
2753 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*2856 apply_relocs = true;
2754 // definition which replaces it. We know that our definition cannot be preempted because we2857 }
2755 // are the executable (only executables can have copy relocations!), so we definitely do not
2756 // need the copy relocation.
2757
2758 // All we actually need to do is remove the entry from `copied_globals` (already done), and
2759 // delete the actual `R_*_COPY` relocation. Of course, we also need to re-apply relocations
2760 // targeting this symbol, but we were going to do that at the end of this function anyway.
2761 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
2762 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we
2763 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.
2764 } else {
2765 _ = elf.want_copied_globals.swapRemove(gsi);
2766 }2858 }
27672859
2768 global_ptr.symtab_index.ptr(elf).node = new.node;2860 const maybe_dso_global: ?*const DsoGlobals.Symbol = dso_global: {
27692861 if (gsi.defined(elf)) {
2770 const old_head: Symbol.Global.Index.Optional = old_head: {2862 break :dso_global null;
2771 const new_node = new.node.unwrap() orelse break :old_head .none;2863 }
2772 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);2864 if (elf.dso_globals.find(gsi.name(elf), gsi.version(elf))) |dso_global| {
2773 const old_head: Symbol.Global.Index.Optional = if (gop.found_existing) .wrap(gop.value_ptr.*) else .none;2865 break :dso_global dso_global;
2774 gop.value_ptr.* = gsi;2866 }
2775 break :old_head old_head;2867 if (gsi.version(elf) == null) {
2868 if (elf.dso_globals.findDefaultVersion(gsi.name(elf))) |dso_global| {
2869 break :dso_global dso_global;
2870 }
2871 }
2872 break :dso_global null;
2776 };2873 };
27772874
2778 global_ptr.prev_in_node = .none;2875 const visibility: std.elf.STV, const sym_type: std.elf.STT = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2779 global_ptr.next_in_node = old_head;2876 inline else => |sym| info: {
27802877 // If the symbol is undefined with type `STT_NOTYPE`, but there is a type available in
2781 if (old_head.unwrap()) |old_head_gsi| {2878 // an input DSO, then use that symbol type!
2782 assert(old_head_gsi.ptr(elf).prev_in_node == .none);2879 if (maybe_dso_global) |dso_global| {
2783 old_head_gsi.ptr(elf).prev_in_node = .wrap(gsi);2880 if (elf.targetLoad(&sym.shndx) == std.elf.SHN_UNDEF and
2784 }2881 elf.targetLoad(&sym.info).type == .NOTYPE and
2882 dso_global.type != .NOTYPE)
2883 {
2884 elf.targetStore(&sym.info, .{
2885 .type = dso_global.type,
2886 .bind = elf.targetLoad(&sym.info).bind,
2887 });
2888 }
2889 }
27852890
2786 // Now for the easy bit where we actually update the symtab entry.2891 break :info .{
2787 switch (elf.symPtr(global_ptr.symtab_index)) {2892 elf.targetLoad(&sym.other).visibility,
2788 inline else => |sym| {2893 elf.targetLoad(&sym.info).type,
2789 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.2894 };
2790 elf.targetStore(&sym.size, @intCast(new.size));
2791 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
2792 const old_bind = elf.targetLoad(&sym.info).bind;
2793 elf.targetStore(&sym.info, .{
2794 .type = new.type,
2795 .bind = old_bind,
2796 });
2797 },2895 },
2798 }2896 };
27992897
2800 // ...and also the dynsym entry if there is one.2898 const omit_dynsym: bool = omit: {
2801 if (global_ptr.dynsym_index.unwrap()) |dynsym_index| switch (elf.dynsymPtr(dynsym_index)) {2899 if (elf.shndx.dynsym == .UNDEF) break :omit true;
2802 inline else => |sym| {2900 break :omit switch (visibility) {
2803 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.2901 .HIDDEN, .INTERNAL => true,
2804 elf.targetStore(&sym.size, @intCast(new.size));2902 .DEFAULT, .PROTECTED => false,
2805 elf.targetStore(&sym.shndx, new.shndx.toSection().?);2903 };
2806 const old_bind = elf.targetLoad(&sym.info).bind;
2807 elf.targetStore(&sym.info, .{
2808 .type = new.type,
2809 .bind = old_bind,
2810 });
2811 },
2812 };2904 };
2905 if (omit_dynsym) {
2906 if (try elf.deleteOwnedDynsym(gsi)) {
2907 apply_relocs = true;
2908 }
2909 if (gsi.ptr(elf).dynsym_index != 0) {
2910 gsi.ptr(elf).dynsym_index = 0;
2911 try elf.changed_symtab_index.put(gpa, gsi, {});
2912 }
2913 if (maybe_alias_gsi) |alias_gsi| {
2914 if (alias_gsi.ptr(elf).dynsym_index != 0) {
2915 alias_gsi.ptr(elf).dynsym_index = 0;
2916 try elf.changed_symtab_index.put(gpa, alias_gsi, {});
2917 }
2918 }
28132919
2814 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to2920 if (!gsi.defined(elf) and
2815 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.2921 gsi.ptr(elf).status.bind == .strong and
2816 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.2922 maybe_dso_global == null)
2817 if (elf.plt.getIndex(gsi)) |plt_index| {
2818 if (!elf.pltEntryIsDead(plt_index) and
2819 elf.classifySymbolValue(.global(gsi)) != .dynamic)
2820 {2923 {
2821 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));2924 try elf.unknown_globals.put(gpa, gsi, {});
2822 assert(elf.pltEntryIsDead(plt_index));2925 } else {
2926 _ = elf.unknown_globals.swapRemove(gsi);
2927 }
2928
2929 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);
2930 gsi.deleteDynamicTargetRelocs(elf);
2931 if (maybe_alias_gsi) |alias_gsi| {
2932 alias_gsi.deleteDynamicTargetRelocs(elf);
2823 }2933 }
2824 }
28252934
2826 // If this symbol was previously undefined, relocations targeting it may have been lowered to2935 if (apply_relocs) {
2827 // runtime relocations which we have now discovered we do not need, so delete those. This does2936 Symbol.Id.global(gsi).applyTargetRelocs(elf);
2828 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.2937 if (maybe_alias_gsi) |alias_gsi| {
2829 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(gsi)) != .dynamic) {2938 Symbol.Id.global(alias_gsi).applyTargetRelocs(elf);
2830 Symbol.Id.global(gsi).deleteDynamicTargetRelocs(elf);2939 }
2940 }
2941 return;
2831 }2942 }
28322943
2833 // Finally, update the symbol value, re-applying target relocations. Also note that because we2944 // We need an owned dynsym entry. We can either reuse an existing one or create a new one.
2834 // possibly removed the PLT entry above, some relocations which were previously targeting the2945 const new_dynsym_index: u32 = owned_dynsym_index: {
2835 // PLT will now instead target the symbol itself.2946 if (gsi.ptr(elf).status.owns_dynsym) {
2836 Symbol.Id.global(gsi).flushMoved(elf, new.value);2947 break :owned_dynsym_index gsi.ptr(elf).dynsym_index;
2837}2948 }
2838/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
2839/// undefined---their visibility values are combined to determine the resulting visibility, which
2840/// can also affect the bind of the symbol we output.
2841fn mergeGlobalSymbolVisibility(
2842 elf: *Elf,
2843 gsi: Symbol.Global.Index,
2844 other_visibility: std.elf.STV,
2845 bind: Symbol.Global.Bind,
2846) void {
2847 const global_ptr = gsi.ptr(elf);
28482949
2849 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {2950 // We're going to add a new entry to dynsym; ensure there's space in the relevant sections.
2850 inline else => |sym| elf.targetLoad(&sym.other).visibility,2951 const dynsym_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2851 };2952 inline else => |shdr, class| .{
2953 elf.targetLoad(&shdr.size),
2954 @sizeOf(class.ElfN().Sym),
2955 },
2956 };
2957 const need_dynsym_len: u32 = @intCast(@divExact(dynsym_size, dynsym_ent_size) + 1);
28522958
2853 // We will combine the old and new visibilities. The combined visibility is basically whichever2959 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_dynsym_len * dynsym_ent_size);
2854 // is "stricter", with most strict being INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.2960 try elf.shndx.gnu_version.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_dynsym_len * 2);
2961 try elf.ensureDynsymHashCapacity(need_dynsym_len);
28552962
2856 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or2963 const dynstr_name: String(.dynstr) = name: {
2857 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to2964 // Explicitly reserve space first because that reservation could invalidate the name slice.
2858 // a relocatable), then the symbol should have binding `STB_LOCAL` in the output. Therefore, if2965 try elf.ensureAdditionalStringCapacity(.dynstr, gsi.name(elf).len);
2859 // we are putting the global in this state for the first time---let's call it "demoting" the2966 break :name elf.stringAssumeCapacity(.dynstr, gsi.name(elf));
2860 // global to `STB_LOCAL`---we'll need to update its bind in the symtab.2967 };
2861 const Demote = enum { no, yes_already, yes_new };2968
2862 const new_visibility: std.elf.STV, const demote_if_module: Demote = switch (old_visibility) {2969 const dynsym_index: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2863 .INTERNAL => .{ .INTERNAL, .yes_already },2970 inline else => |shdr, class| new_dynsym_index: {
2864 .HIDDEN => switch (other_visibility) {2971 const Sym = class.ElfN().Sym;
2865 .INTERNAL => .{ .INTERNAL, .yes_already },2972
2866 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, .yes_already },2973 // Increase the dynamic symbol table size...
2867 },2974 const old_size = elf.targetLoad(&shdr.size);
2868 .PROTECTED => switch (other_visibility) {2975 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2869 .INTERNAL => .{ .INTERNAL, .yes_new },2976 const dynsym_index: u32 = @intCast(@divExact(old_size, @sizeOf(Sym)));
2870 .HIDDEN => .{ .HIDDEN, .yes_new },2977
2871 .PROTECTED, .DEFAULT => .{ .PROTECTED, .no },2978 // ...and the same for the symbol version table.
2872 },2979 const versym_shdr = @field(elf.shdrPtr(elf.shndx.gnu_version), @tagName(class));
2873 .DEFAULT => switch (other_visibility) {2980 assert(elf.targetLoad(&versym_shdr.size) == dynsym_index * 2);
2874 .INTERNAL => .{ .INTERNAL, .yes_new },2981 elf.targetStore(&versym_shdr.size, (dynsym_index + 1) * 2);
2875 .HIDDEN => .{ .HIDDEN, .yes_new },2982
2876 .PROTECTED => .{ .PROTECTED, .no },2983 // Populate the symbol name (the other fields are set later).
2877 .DEFAULT => .{ .DEFAULT, .no },2984 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2878 },2985 elf.targetStore(&sym.name, @backingInt(dynstr_name));
2879 };2986
2880 const demote: Demote = switch (elf.ehdrType()) {2987 break :new_dynsym_index dynsym_index;
2881 .REL => .no, // nothing is demoted in relocatables
2882 .EXEC, .DYN => demote_if_module,
2883 };
2884 switch (demote) {
2885 .no => switch (elf.symPtr(global_ptr.symtab_index)) {
2886 inline else => |sym, class| {
2887 // Just update the symtab entry with the new visibility and binding.
2888 const old_info = elf.targetLoad(&sym.info);
2889 assert(old_info.bind != .LOCAL); // symbol should not be demoted
2890 const new_info: class.ElfN().Sym.Info = .{
2891 .type = old_info.type,
2892 .bind = switch (bind) {
2893 .strong => .GLOBAL,
2894 .weak => .WEAK,
2895 },
2896 };
2897 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2898 elf.targetStore(&sym.info, new_info);
2899
2900 // Also update dynsym if necessary.
2901 if (global_ptr.dynsym_index.unwrap()) |dynsym_index| {
2902 const dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2903 assert(elf.targetLoad(&dynsym_ptr.info) == old_info);
2904 elf.targetStore(&dynsym_ptr.other, .{ .visibility = new_visibility });
2905 elf.targetStore(&dynsym_ptr.info, new_info);
2906 } else {
2907 assert(global_ptr.dynsym_index == .no_section); // symbol should not be demoted
2908 }
2909 },2988 },
2910 },2989 };
29112990
2912 .yes_already => {2991 elf.appendDynsymHashEntry(dynsym_index);
2913 // Update the visibility in the symtab entry...2992
2914 switch (elf.symPtr(global_ptr.symtab_index)) {2993 if (gsi.version(elf) != null) {
2915 inline else => |sym| {2994 try elf.versioned_dynsym_owners.putNoClobber(gpa, dynsym_index, gsi);
2916 assert(elf.targetLoad(&sym.info).bind == .LOCAL); // symbol should be demoted2995 }
2917 elf.targetStore(&sym.other, .{ .visibility = new_visibility });2996
2918 },2997 break :owned_dynsym_index dynsym_index;
2998 };
2999
3000 gsi.ptr(elf).status.owns_dynsym = true;
3001
3002 if (gsi.ptr(elf).dynsym_index != new_dynsym_index) {
3003 gsi.ptr(elf).dynsym_index = new_dynsym_index;
3004 try elf.changed_symtab_index.put(gpa, gsi, {});
3005 }
3006 if (maybe_alias_gsi) |alias_gsi| {
3007 if (alias_gsi.ptr(elf).dynsym_index != new_dynsym_index) {
3008 alias_gsi.ptr(elf).dynsym_index = new_dynsym_index;
3009 try elf.changed_symtab_index.put(gpa, alias_gsi, {});
3010 }
3011 }
3012
3013 {
3014 const dynsym_versym: std.elf.Versym = versym: {
3015 // If there's a definition, just use the version in the symbol name.
3016 if (gsi.defined(elf)) {
3017 if (gsi.version(elf)) |orig_version_slice| {
3018 // We want to add the version to dynstr, but we need to explicitly reserve space
3019 // first because that reservation could invalidate the version slice.
3020 try elf.ensureAdditionalStringCapacity(.dynstr, orig_version_slice.len);
3021 const version_dynstr = elf.stringAssumeCapacity(.dynstr, gsi.version(elf).?);
3022 break :versym .{
3023 .VERSION = try elf.verdefId(version_dynstr),
3024 .HIDDEN = !gsi.isDefaultDefinition(elf),
3025 };
3026 }
3027 break :versym .GLOBAL;
2919 }3028 }
2920 // ...and update the binding tracked in `global_ptr.dynsym_index`.3029
2921 switch (global_ptr.dynsym_index) {3030 // ...but if it's undefined, we need to consult the matching input DSO global.
2922 .demoted_strong, .demoted_weak => {},3031 if (maybe_dso_global) |dso_global| {
2923 .no_section => unreachable, // `.demoted_strong` or `.demoted_weak` should take priority3032 if (dso_global.version.unwrap()) |version| {
2924 _ => unreachable, // demoted symbol should not have a dynsym index3033 const version_dynstr = try elf.string(.dynstr, version.slice(&elf.dso_globals));
3034 break :versym .{
3035 .VERSION = try elf.verneedId(dso_global.soname, version_dynstr),
3036 .HIDDEN = false,
3037 };
3038 }
2925 }3039 }
2926 global_ptr.dynsym_index = switch (bind) {3040
2927 .strong => .demoted_strong,3041 break :versym .LOCAL;
2928 .weak => .demoted_weak,3042 };
3043
3044 elf.targetStore(&elf.versymSlice()[new_dynsym_index], dynsym_versym);
3045 }
3046
3047 // Populate the dynsym info from the symtab.
3048 switch (elf.dynsymPtr(new_dynsym_index)) {
3049 inline else => |dynsym, class| {
3050 const sym = @field(elf.symPtr(gsi.ptr(elf).symtab_index), @tagName(class));
3051 // No byte swaps needed here because src values are already in target endian.
3052 dynsym.* = .{
3053 .name = dynsym.name,
3054 .value = sym.value,
3055 .size = sym.size,
3056 .info = sym.info,
3057 .other = sym.other,
3058 .shndx = sym.shndx,
2929 };3059 };
2930 },3060 },
3061 }
29313062
2932 .yes_new => {3063 const need_plt_entry: bool = plt: {
2933 // Update the binding and visibility in the symtab, but don't bother with dynsym because3064 switch (sym_type) {
2934 // we're going to delete the dynsym entry in a moment.3065 .FUNC, .GNU_IFUNC => {},
2935 switch (elf.symPtr(global_ptr.symtab_index)) {3066 else => {
2936 inline else => |sym| {3067 // Only functions can have PLT entries.
2937 const old_info = elf.targetLoad(&sym.info);3068 break :plt false;
2938 assert(old_info.bind != .LOCAL); // symbol should not be already demoted3069 },
2939 elf.targetStore(&sym.info, .{ .type = old_info.type, .bind = .LOCAL });3070 }
2940 elf.targetStore(&sym.other, .{ .visibility = new_visibility });3071 if (visibility == .PROTECTED) {
2941 },3072 // No need for a PLT entry, since any definition is guaranteed to be defined locally.
3073 break :plt false;
3074 }
3075 if (gsi.defined(elf) and elf.base.comp.config.output_mode == .Exe) {
3076 // No need for a PLT entry, since there's already a non-preemptible local definition.
3077 break :plt false;
3078 }
3079 break :plt true;
3080 };
3081 if (need_plt_entry) {
3082 if (try elf.ensurePltEntry(gsi)) {
3083 apply_relocs = true;
3084 }
3085 } else {
3086 if (elf.plt.getIndex(gsi)) |plt_index| {
3087 if (!elf.pltEntryIsDead(plt_index)) {
3088 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
3089 assert(elf.pltEntryIsDead(plt_index));
3090 apply_relocs = true;
2942 }3091 }
3092 }
3093 }
29433094
2944 // If there was a dynsym entry, it is no longer necessary.3095 const want_copy_rel: bool = copy: {
2945 if (global_ptr.dynsym_index.unwrap()) |dynsym_index| {3096 if (elf.base.comp.config.output_mode != .Exe) {
2946 elf.deleteDynsym(dynsym_index);3097 // Only executables can contain copy relocations.
2947 } else {3098 break :copy false;
2948 assert(global_ptr.dynsym_index == .no_section); // symbol should not be already demoted3099 }
2949 }3100 if (gsi.defined(elf)) {
3101 // No need for a copy relocation when there's a local definition (which we know is not
3102 // preemptible because this is the executable).
3103 break :copy false;
3104 }
3105 if (!gsi.ptr(elf).status.want_static_value) {
3106 // Nobody's actually requesting a copy relocation, so no need to make one... unless the
3107 // alias is actually the one who needs it!
3108 const alias_gsi = maybe_alias_gsi orelse break :copy false;
3109 if (!alias_gsi.ptr(elf).status.want_static_value) break :copy false;
3110 }
3111 const dso_global = maybe_dso_global orelse {
3112 // We cannot create a copy relocation until we see the symbol in an input DSO.
3113 break :copy false;
3114 };
3115 if (dso_global.type != .OBJECT) {
3116 // We can only create copy relocations for `STT_OBJECT` symbols.
3117 break :copy false;
3118 }
3119 // A copy relocation was requested and is possible!
3120 break :copy true;
3121 };
3122 if (want_copy_rel) {
3123 const dso_global = maybe_dso_global.?;
3124
3125 const shndx: Section.Index = .data;
3126
3127 try shndx.ensureAligned(elf, dso_global.alignment);
29503128
2951 // Instead, that field is repurposed to track the symbol's binding.3129 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2952 global_ptr.dynsym_index = switch (bind) {3130 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
2953 .strong => .demoted_strong,3131 }
2954 .weak => .demoted_weak,3132 switch (elf.dynsymPtr(new_dynsym_index)) {
3133 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
3134 }
3135
3136 const gop = try elf.copied_globals.getOrPut(gpa, gsi);
3137 if (gop.found_existing) {
3138 if (dso_global.alignment.compare(.gt, gop.value_ptr.node.alignment(&elf.mf))) {
3139 try gop.value_ptr.node.realign(gpa, &elf.mf, dso_global.alignment);
3140 }
3141 try gop.value_ptr.node.ensureMinimumSize(gpa, &elf.mf, dso_global.size);
3142 } else {
3143 errdefer assert(elf.copied_globals.pop().?.key == gsi);
3144 try elf.nodes.ensureUnusedCapacity(gpa, 1);
3145 const node = elf.addNodeAssumeCapacity(
3146 try shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
3147 .size = dso_global.alignment.forward(dso_global.size),
3148 .alignment = dso_global.alignment,
3149 }),
3150 .{ .copied_global = gsi },
3151 );
3152 const vaddr = elf.computeNodeVAddr(node);
3153 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
3154 .type = .copy(elf),
3155 .offset = vaddr,
3156 .raw_sym_index = new_dynsym_index,
3157 .addend = 0,
3158 });
3159 gop.value_ptr.* = .{
3160 .node = node,
3161 .rela_index = rela_index,
2955 };3162 };
29563163
2957 // If there was a copy relocation for this symbol, it is now unnecessary. See equivalent3164 // We have added a copy relocation so we now own the canonical address of the symbol.
2958 // logic in `setGlobalSymbolValue` for an explanation of this case.3165 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2959 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {3166 inline else => |sym| elf.targetStore(&sym.value, @intCast(vaddr)),
2960 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);3167 }
2961 // TODO: once we can delete `MappedFile` nodes, delete `copied_global_kv.value.node`3168 switch (elf.dynsymPtr(new_dynsym_index)) {
3169 inline else => |sym| elf.targetStore(&sym.value, @intCast(vaddr)),
3170 }
3171 apply_relocs = true;
3172 }
3173 } else {
3174 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
3175 // We previously made a copy relocation, but now have deemed it unnecessary.
3176 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
3177 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the
3178 // space), we should delete `copied_global_kv.value.node`, which is an
3179 // "orphaned" `copied_global` node.
3180 apply_relocs = true;
3181 }
3182 }
3183
3184 if (!gsi.defined(elf) and
3185 gsi.ptr(elf).status.bind == .strong and
3186 maybe_dso_global == null)
3187 {
3188 try elf.unknown_globals.put(gpa, gsi, {});
3189 } else {
3190 _ = elf.unknown_globals.swapRemove(gsi);
3191 }
3192
3193 if (elf.classifySymbolValue(.global(gsi)) != .dynamic) {
3194 gsi.deleteDynamicTargetRelocs(elf);
3195 if (maybe_alias_gsi) |alias_gsi| {
3196 alias_gsi.deleteDynamicTargetRelocs(elf);
3197 }
3198 }
3199
3200 if (apply_relocs) {
3201 Symbol.Id.global(gsi).applyTargetRelocs(elf);
3202 if (maybe_alias_gsi) |alias_gsi| {
3203 Symbol.Id.global(alias_gsi).applyTargetRelocs(elf);
3204 }
3205 }
3206}
3207/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` with the
3208/// `force_apply_relocs` argument set to `true`. This ensures that the dynamic symbol table is kept
3209/// up-to-date, unnecessary PLT entries or copy relocations are removed, and that relocations are
3210/// applied when necessary.
3211fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {
3212 node: MappedFile.Node.Index.Optional,
3213 value: u64,
3214 size: u64,
3215 type: std.elf.STT,
3216 shndx: Section.Index,
3217}) Error!void {
3218 assert(new.shndx != .UNDEF);
3219
3220 const global_ptr = gsi.ptr(elf);
3221
3222 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
3223 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
3224 assert(next_gsi.ptr(elf).prev_in_node == Symbol.Global.Index.Optional.wrap(gsi));
3225 next_gsi.ptr(elf).prev_in_node = global_ptr.prev_in_node;
3226 }
3227 if (global_ptr.prev_in_node.unwrap()) |prev_gsi| {
3228 assert(prev_gsi.ptr(elf).next_in_node == Symbol.Global.Index.Optional.wrap(gsi));
3229 prev_gsi.ptr(elf).next_in_node = global_ptr.next_in_node;
3230 } else {
3231 // We're the start of the linked list, so we need to change the head.
3232 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
3233 elf.node_global_symbols.getPtr(old_node).?.* = next_gsi;
2962 } else {3234 } else {
2963 _ = elf.want_copied_globals.swapRemove(gsi);3235 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == gsi);
2964 }3236 }
3237 }
3238 } else {
3239 assert(global_ptr.next_in_node == .none);
3240 assert(global_ptr.prev_in_node == .none);
3241 }
3242
3243 global_ptr.symtab_index.ptr(elf).node = new.node;
3244
3245 const old_head: Symbol.Global.Index.Optional = old_head: {
3246 const new_node = new.node.unwrap() orelse break :old_head .none;
3247 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
3248 const old_head: Symbol.Global.Index.Optional = if (gop.found_existing) .wrap(gop.value_ptr.*) else .none;
3249 gop.value_ptr.* = gsi;
3250 break :old_head old_head;
3251 };
3252
3253 global_ptr.prev_in_node = .none;
3254 global_ptr.next_in_node = old_head;
3255
3256 if (old_head.unwrap()) |old_head_gsi| {
3257 assert(old_head_gsi.ptr(elf).prev_in_node == .none);
3258 old_head_gsi.ptr(elf).prev_in_node = .wrap(gsi);
3259 }
29653260
2966 // Finally, we need to update the global's symtab index so that it is adjacent to the3261 // Now for the easy bit where we actually update the symtab entry.
2967 // other `STB_LOCAL` symbols instead of the global symbols.3262 switch (elf.symPtr(global_ptr.symtab_index)) {
2968 elf.moveDemotedGlobal(gsi);3263 inline else => |sym| {
3264 elf.targetStore(&sym.value, @intCast(new.value));
3265 elf.targetStore(&sym.size, @intCast(new.size));
3266 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
3267 const old_bind = elf.targetLoad(&sym.info).bind;
3268 elf.targetStore(&sym.info, .{
3269 .type = new.type,
3270 .bind = old_bind,
3271 });
3272 },
3273 }
3274}
3275
3276fn setGlobalSymbolBind(
3277 elf: *Elf,
3278 gsi: Symbol.Global.Index,
3279 bind: Symbol.Global.Bind,
3280) void {
3281 gsi.ptr(elf).status.bind = bind;
3282 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3283 inline else => |sym| {
3284 const old_info = elf.targetLoad(&sym.info);
3285 elf.targetStore(&sym.info, .{
3286 .type = old_info.type,
3287 .bind = switch (old_info.bind) {
3288 .LOCAL => .LOCAL, // demoted global
3289 else => switch (bind) {
3290 .strong => .GLOBAL,
3291 .weak => .WEAK,
3292 },
3293 },
3294 });
2969 },3295 },
2970 }3296 }
2971}3297}
3298
3299/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
3300/// undefined---their visibility values are combined to determine the resulting visibility.
3301///
3302/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes
3303/// to symbol visibility can affect the dynamic symbol table entry. If this function returns `true`,
3304/// then that `updateGlobalDynamic` call must have the `force_apply_relocs` argument set to `true`,
3305/// because this symbol's classification has changed (it is no longer a dynamic symbol) so
3306/// relocations targeting the symbol must be re-applied.
3307fn mergeGlobalSymbolVisibility(
3308 elf: *Elf,
3309 gsi: Symbol.Global.Index,
3310 other_visibility: std.elf.STV,
3311) bool {
3312 const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3313 inline else => |sym| elf.targetLoad(&sym.other).visibility,
3314 };
3315
3316 // The new visibility is basically whichever of the old and new is "stricter", with the most
3317 // strict being INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
3318 const new_visibility: std.elf.STV = switch (old_visibility) {
3319 .INTERNAL => .INTERNAL,
3320 .HIDDEN => switch (other_visibility) {
3321 .INTERNAL => .INTERNAL,
3322 .HIDDEN, .PROTECTED, .DEFAULT => .HIDDEN,
3323 },
3324 .PROTECTED => switch (other_visibility) {
3325 .INTERNAL => .INTERNAL,
3326 .HIDDEN => .HIDDEN,
3327 .PROTECTED, .DEFAULT => .PROTECTED,
3328 },
3329 .DEFAULT => switch (other_visibility) {
3330 .INTERNAL => .INTERNAL,
3331 .HIDDEN => .HIDDEN,
3332 .PROTECTED => .PROTECTED,
3333 .DEFAULT => .DEFAULT,
3334 },
3335 };
3336 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3337 inline else => |sym| elf.targetStore(
3338 &sym.other,
3339 .{ .visibility = new_visibility },
3340 ),
3341 }
3342
3343 if (elf.ehdrType() == .REL) {
3344 return false;
3345 }
3346
3347 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or
3348 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to
3349 // a relocatable), then the symbol should be "demoted" to binding `STB_LOCAL` in the output.
3350 const old_demoted: bool = switch (old_visibility) {
3351 .INTERNAL, .HIDDEN => true,
3352 .DEFAULT, .PROTECTED => false,
3353 };
3354 const new_demoted: bool = switch (new_visibility) {
3355 .INTERNAL, .HIDDEN => true,
3356 .DEFAULT, .PROTECTED => false,
3357 };
3358 if (!old_demoted and new_demoted) {
3359 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3360 inline else => |sym| elf.targetStore(&sym.info, .{
3361 .type = elf.targetLoad(&sym.info).type,
3362 .bind = .LOCAL,
3363 }),
3364 }
3365 elf.moveDemotedGlobal(gsi);
3366 }
3367
3368 const old_protected: bool = switch (old_visibility) {
3369 .PROTECTED, .INTERNAL, .HIDDEN => true,
3370 .DEFAULT => false,
3371 };
3372 const new_protected: bool = switch (new_visibility) {
3373 .PROTECTED, .INTERNAL, .HIDDEN => true,
3374 .DEFAULT => false,
3375 };
3376 return old_protected != new_protected;
3377}
2972/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),3378/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
2973/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF3379/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
2974/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.3380/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.
...@@ -2995,8 +3401,7 @@ fn moveDemotedGlobal(elf: *Elf, gsi: Symbol.Global.Index) void {...@@ -2995,8 +3401,7 @@ fn moveDemotedGlobal(elf: *Elf, gsi: Symbol.Global.Index) void {
2995 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));3401 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2996 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));3402 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
29973403
2998 const other_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&dest_sym_ptr.name));3404 const other_gsi = elf.globalBySym(dest_index);
2999 const other_gsi = elf.globalByName(other_name.slice(elf)).?;
3000 assert(other_gsi.ptr(elf).symtab_index == dest_index);3405 assert(other_gsi.ptr(elf).symtab_index == dest_index);
30013406
3002 // First swap the symtab entries...3407 // First swap the symtab entries...
...@@ -3010,15 +3415,40 @@ fn moveDemotedGlobal(elf: *Elf, gsi: Symbol.Global.Index) void {...@@ -3010,15 +3415,40 @@ fn moveDemotedGlobal(elf: *Elf, gsi: Symbol.Global.Index) void {
3010 },3415 },
3011 }3416 }
3012}3417}
3013/// Asserts that the `.dynsym` section exists, and removes the entry at index `dynsym_index`,3418/// If `gsi` owns a dynsym entry, deletes that entry from the `.dynsym` section and marks `gsi` as
3014/// swapping another entry into its place to keep the section compact. Also updates the `.hash`3419/// no longer owning it. All state depending on the dynamic symbol is also deleted. To keep the
3015/// section, and updates `Symbol.Global.dynsym_index` for whichever global gets swapped into3420/// dynamic symbol table compact, the last entry in the table is swapped into the place of the
3016/// `dynsym_index`. However, this does *not* in any way modify the `Symbol.Global` corresponding to3421/// removed one (all associated bookkeeping is handled by this function).
3017/// the old symbol at `dynsym_index`.
3018///3422///
3019/// Asserts that there is at least 1 unused capacity in `elf.changed_symtab_index`.3423/// Returns `true` if a PLT entry or copy relocation for the global symbol was removed, indicating
3020fn deleteDynsym(elf: *Elf, dynsym_index: u32) void {3424/// that the caller must at some point re-apply relocations targeting `gsi`.
3021 // To keep dynsym compact, we'll swap another symbol into `dynsym_index`.3425fn deleteOwnedDynsym(elf: *Elf, gsi: Symbol.Global.Index) std.mem.Allocator.Error!bool {
3426 if (!gsi.ptr(elf).status.owns_dynsym) {
3427 return false;
3428 }
3429
3430 const gpa = elf.base.comp.gpa;
3431
3432 const dynsym_index = gsi.ptr(elf).dynsym_index;
3433 assert(dynsym_index != 0);
3434 gsi.ptr(elf).status.owns_dynsym = false;
3435
3436 var apply_relocs: bool = false;
3437 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
3438 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
3439 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the
3440 // space), we should delete `copied_global_kv.value.node`, which is an
3441 // "orphaned" `copied_global` node.
3442 apply_relocs = true;
3443 }
3444 if (elf.plt.getIndex(gsi)) |plt_index| {
3445 if (!elf.pltEntryIsDead(plt_index)) {
3446 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
3447 assert(elf.pltEntryIsDead(plt_index));
3448 apply_relocs = true;
3449 }
3450 }
3451
3022 switch (elf.shdrPtr(elf.shndx.dynsym)) {3452 switch (elf.shdrPtr(elf.shndx.dynsym)) {
3023 inline else => |dynsym_shdr, class| {3453 inline else => |dynsym_shdr, class| {
3024 const ent_size = @sizeOf(class.ElfN().Sym);3454 const ent_size = @sizeOf(class.ElfN().Sym);
...@@ -3027,39 +3457,66 @@ fn deleteDynsym(elf: *Elf, dynsym_index: u32) void {...@@ -3027,39 +3457,66 @@ fn deleteDynsym(elf: *Elf, dynsym_index: u32) void {
3027 // We're going to decrease the size of `.dynsym`, thereby removing its last index.3457 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
3028 const old_size = elf.targetLoad(&dynsym_shdr.size);3458 const old_size = elf.targetLoad(&dynsym_shdr.size);
3029 const new_size = old_size - ent_size;3459 const new_size = old_size - ent_size;
3030 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));3460 const pop_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
3461
3462 elf.popDynsymHashEntry(pop_dynsym_index);
30313463
3032 elf.popDynsymHashEntry(remove_dynsym_index);3464 _ = elf.versioned_dynsym_owners.swapRemove(dynsym_index);
30333465
3034 if (dynsym_index != remove_dynsym_index) {3466 if (dynsym_index != pop_dynsym_index) {
3035 // The demoted global wasn't the last entry, so move whatever entry we just3467 // The demoted global wasn't the last entry, so move whatever entry we just
3036 // truncated out of dynsym into its place.3468 // truncated out of dynsym into its place.
30373469
3470 const moved_gsi = elf.globalByDynsym(pop_dynsym_index);
3471
3038 elf.clearDynsymHashEntry(dynsym_index);3472 elf.clearDynsymHashEntry(dynsym_index);
30393473
3040 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));3474 const src_dynsym_ptr = @field(elf.dynsymPtr(pop_dynsym_index), @tagName(class));
3041 const dest_dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));3475 const dest_dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
30423476
3043 // MLUGG TODO: this depends on symbol names matching between dynsym and symtab3477 const versym = elf.versymSlice();
3044 const moved_name: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));
3045 const moved_gsi = elf.globalByName(moved_name.slice(elf)).?;
30463478
3047 dest_dynsym_ptr.* = src_dynsym_ptr.*;3479 dest_dynsym_ptr.* = src_dynsym_ptr.*;
3480 versym[dynsym_index] = versym[pop_dynsym_index];
30483481
3049 assert(moved_gsi.ptr(elf).dynsym_index.unwrap().? == remove_dynsym_index);3482 moved_gsi.ptr(elf).dynsym_index = @intCast(dynsym_index);
3050 moved_gsi.ptr(elf).dynsym_index = .wrap(dynsym_index);
30513483
3052 elf.populateDynsymHashEntry(dynsym_index);3484 elf.populateDynsymHashEntry(dynsym_index);
30533485
3486 if (elf.versioned_dynsym_owners.getIndex(pop_dynsym_index)) |map_index| {
3487 elf.versioned_dynsym_owners.setKey(map_index, dynsym_index);
3488 }
3489
3054 // Since that symbol's dynsym index has changed, we'll have to update any3490 // Since that symbol's dynsym index has changed, we'll have to update any
3055 // relocation entries targeting it.3491 // relocation entries targeting it.
3056 elf.changed_symtab_index.putAssumeCapacity(moved_gsi, {});3492 try elf.changed_symtab_index.put(gpa, moved_gsi, {});
3493
3494 // If `moved_gsi` is a versioned symbol, then it may have an unversioned
3495 // alias with the same dynsym index.
3496 if (moved_gsi.version(elf) != null) {
3497 if (elf.globalByName(.{
3498 .name = moved_gsi.name(elf),
3499 .version = null,
3500 })) |unversioned_gsi| {
3501 if (unversioned_gsi.ptr(elf).dynsym_index == pop_dynsym_index) {
3502 assert(!unversioned_gsi.ptr(elf).status.owns_dynsym);
3503 unversioned_gsi.ptr(elf).dynsym_index = @intCast(dynsym_index);
3504 try elf.changed_symtab_index.put(gpa, unversioned_gsi, {});
3505 }
3506 }
3507 }
3057 }3508 }
30583509
3059 // Now that we've given that symbol a new home, actually decrease the section size.3510 // Now that we've given that symbol a new home, actually decrease the section size.
3060 elf.targetStore(&dynsym_shdr.size, new_size);3511 elf.targetStore(&dynsym_shdr.size, new_size);
3512
3513 const versym_shdr = @field(elf.shdrPtr(elf.shndx.gnu_version), @tagName(class));
3514 assert(elf.targetLoad(&versym_shdr.size) == (pop_dynsym_index + 1) * 2);
3515 elf.targetStore(&versym_shdr.size, pop_dynsym_index * 2);
3061 },3516 },
3062 }3517 }
3518
3519 return apply_relocs;
3063}3520}
30643521
3065const Symbol = struct {3522const Symbol = struct {
...@@ -3074,63 +3531,81 @@ const Symbol = struct {...@@ -3074,63 +3531,81 @@ const Symbol = struct {
3074 first_target_reloc: SymbolReloc.Index,3531 first_target_reloc: SymbolReloc.Index,
30753532
3076 const Global = struct {3533 const Global = struct {
3534 status: packed struct(u8) {
3535 /// We cannot reliably recover this information from the symbol table, because symbols
3536 /// with visibility `STV_HIDDEN` are "demoted" to `STB_LOCAL` binding, yet we must still
3537 /// consider them strong or weak for the purposes of the link.
3538 bind: Symbol.Global.Bind,
3539 /// Initially set to `false`. If a relocation is added which targets this symbol and
3540 /// wants it to have a statically-known value, this flag is set to signal to
3541 /// `updateGlobalDynamic` that it may need to create a copy relocation.
3542 want_static_value: bool,
3543 /// Every entry in `.dynsym` is "owned" by exactly one `Global`. If this flag is set,
3544 /// then `Global.dynsym_index` is non-zero and refers to a dynsym entry owned by this
3545 /// global. Otherwise, if `Global.dynsym_index` is non-zero, this `Global` is an "alias"
3546 /// of a default version of this symbol (e.g. 'foo' is an alias of 'foo@@version') and
3547 /// `Global.dynsym_index` refers to that symbol's dynsym entry
3548 owns_dynsym: bool,
3549 any_static_target_relocs: bool,
3550 any_static_relative_target_relocs: bool,
3551 any_dynamic_target_relocs: bool,
3552 _: u2 = 0,
3553 },
3554
3555 dynsym_index: u32,
3556
3077 /// The current index of the symtab entry for this global symbol.3557 /// The current index of the symtab entry for this global symbol.
3078 symtab_index: Symbol.Index,3558 symtab_index: Symbol.Index,
3079 /// As well as the index of this global symbol's `.dynsym` entry, in the event that the
3080 /// symbol is *not* in `.dynsym`, we store a special value here indicating why that is.
3081 dynsym_index: enum(u32) {
3082 /// In theory, this global should have a dynamic symbol index (it is not `STV_HIDDEN`),
3083 /// but there is no `.dynsym` section, so it does not. This can happens when we are
3084 /// emitting either a relocatable or a static executable.
3085 no_section = 0,
3086 /// This global has no dynamic symbol index because it has visibility `STV_HIDDEN`.
3087 ///
3088 /// Due to this `STV_HIDDEN` visibility, we have necessarily "demoted" the symtab entry
3089 /// to `STB_LOCAL` (see logic in `mergeGlobalSymbolVisibility`), but its actual binding
3090 /// is `STB_GLOBAL`.
3091 demoted_strong = std.math.maxInt(u32),
3092 /// Like `.demoted_global`, except indicates that the actual symbol binding is
3093 /// `STB_WEAK`, not `STB_GLOBAL`.
3094 demoted_weak = std.math.maxInt(u32) - 1,
3095 /// An actual index into the `.dynsym` section.
3096 _,
3097
3098 /// If this symbol is actually in `.dynsym`, returns the index as a raw `u32`. This
3099 /// index is always non-zero because index 0 holds the special "null" symbol.
3100 ///
3101 /// Otherwise, returns `null`.
3102 fn unwrap(dsi: @This()) ?u32 {
3103 return switch (dsi) {
3104 .no_section, .demoted_strong, .demoted_weak => null,
3105 _ => @backingInt(dsi),
3106 };
3107 }
3108
3109 /// Takes an index into `.dynsym` as a raw `u32`, and converts it to this type.
3110 ///
3111 /// Asserts that `index` is non-zero because index 0 in `.dynsym` is the "null" symbol.
3112 fn wrap(index: u32) @This() {
3113 assert(index != 0);
3114 return @fromBackingInt(index);
3115 }
3116 },
31173559
3118 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.3560 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
3119 next_in_node: Symbol.Global.Index.Optional,3561 next_in_node: Symbol.Global.Index.Optional,
3120 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.3562 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
3121 prev_in_node: Symbol.Global.Index.Optional,3563 prev_in_node: Symbol.Global.Index.Optional,
31223564
3123 const NameAdapter = struct {3565 /// The logical key of `Elf.globals_by_name`. Although versioned symbol names are stored in
3566 /// the form "foo@version" or "foo@@version" in the symbol table, it is generally convenient
3567 /// to consider the name and version as separate values.
3568 const VersionedName = struct {
3569 name: []const u8,
3570 version: ?[]const u8,
3571
3572 const Adapter = struct {
3573 elf: *Elf,
3574 pub fn eql(ctx: Adapter, lhs_key: VersionedName, _: void, rhs_index: usize) bool {
3575 const elf = ctx.elf;
3576 const rhs_gsi: Symbol.Global.Index = @fromBackingInt(@intCast(rhs_index));
3577
3578 if (!std.mem.eql(u8, lhs_key.name, rhs_gsi.name(elf))) return false;
3579
3580 if (lhs_key.version) |lhs_version| {
3581 const rhs_version = rhs_gsi.version(elf) orelse return false;
3582 return std.mem.eql(u8, lhs_version, rhs_version);
3583 } else {
3584 return rhs_gsi.version(elf) == null;
3585 }
3586 }
3587 pub fn hash(ctx: Adapter, key: VersionedName) u32 {
3588 _ = ctx;
3589 var h: std.hash.Wyhash = .init(0);
3590 h.update(key.name);
3591 if (key.version) |version| {
3592 h.update("@");
3593 h.update(version);
3594 }
3595 return @truncate(h.final());
3596 }
3597 };
3598 };
3599
3600 const DefaultVersionAdapter = struct {
3124 elf: *Elf,3601 elf: *Elf,
3125 pub fn eql(ctx: NameAdapter, lhs_slice: []const u8, _: void, rhs_index: usize) bool {3602 pub fn eql(ctx: DefaultVersionAdapter, lhs_name: []const u8, rhs_gsi: Symbol.Global.Index, _: usize) bool {
3126 const elf = ctx.elf;3603 const elf = ctx.elf;
3127 const rhs_gsi: Symbol.Global.Index = @fromBackingInt(@intCast(rhs_index));3604 return std.mem.eql(u8, lhs_name, rhs_gsi.name(elf));
3128 const rhs_slice = Symbol.Id.global(rhs_gsi).name(elf).slice(elf);
3129 return std.mem.eql(u8, lhs_slice, rhs_slice);
3130 }3605 }
3131 pub fn hash(ctx: NameAdapter, slice: []const u8) u32 {3606 pub fn hash(ctx: DefaultVersionAdapter, name: []const u8) u32 {
3132 _ = ctx;3607 _ = ctx;
3133 return std.array_hash_map.hashString(slice);3608 return std.array_hash_map.hashString(name);
3134 }3609 }
3135 };3610 };
31363611
...@@ -3141,18 +3616,220 @@ const Symbol = struct {...@@ -3141,18 +3616,220 @@ const Symbol = struct {
3141 return &elf.globals.items[@backingInt(gsi)];3616 return &elf.globals.items[@backingInt(gsi)];
3142 }3617 }
31433618
3144 fn bind(gsi: Symbol.Global.Index, elf: *Elf) Symbol.Global.Bind {3619 /// Does not traverse aliases: if 'foo' is an alias for 'foo@@version', then `false`
3145 return switch (gsi.ptr(elf).dynsym_index) {3620 /// will be returned for 'foo', even thought 'foo@@version' is defined.
3146 .demoted_strong => .strong,3621 fn defined(gsi: Symbol.Global.Index, elf: *Elf) bool {
3147 .demoted_weak => .weak,3622 return switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3148 .no_section, _ => switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {3623 inline else => |sym| elf.targetLoad(&sym.shndx) != std.elf.SHN_UNDEF,
3149 inline else => |sym| switch (elf.targetLoad(&sym.info).bind) {3624 };
3150 .GLOBAL => .strong,3625 }
3151 .WEAK => .weak,3626
3152 else => unreachable,3627 fn rawName(gsi: Symbol.Global.Index, elf: *Elf) String(.strtab) {
3153 },3628 return switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3629 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
3630 };
3631 }
3632 fn name(gsi: Symbol.Global.Index, elf: *Elf) []const u8 {
3633 return parseVersionedSymbolName(gsi.rawName(elf).slice(elf)).name;
3634 }
3635 fn version(gsi: Symbol.Global.Index, elf: *Elf) ?[]const u8 {
3636 return parseVersionedSymbolName(gsi.rawName(elf).slice(elf)).version;
3637 }
3638 fn isDefaultDefinition(gsi: Symbol.Global.Index, elf: *Elf) bool {
3639 return parseVersionedSymbolName(gsi.rawName(elf).slice(elf)).is_default_version;
3640 }
3641
3642 fn dynsymIndex(gsi: Symbol.Global.Index, elf: *Elf) ?u32 {
3643 const dynsym_index = gsi.ptr(elf).dynsym_index;
3644 if (dynsym_index == 0) {
3645 return null;
3646 } else {
3647 return dynsym_index;
3648 }
3649 }
3650
3651 /// One global symbol may be an "alias" of another, which means that the global's value
3652 /// is defined to be equal to the value of the other. The original global's symbol table
3653 /// entry is effectively ignored while it is acting as an alias, and it will not own an
3654 /// entry in the dynamic symbol table.
3655 ///
3656 /// This function will return the symbol for which `gsi` is an alias. If `gsi` is not an
3657 /// alias, then `gsi` itself is returned.
3658 ///
3659 /// At the time of writing, there is only one situation in which one global may alias
3660 /// another: if an unversioned symbol "foo" is undefined, and a default-versioned symbol
3661 /// "foo@@version" is defined, then "foo" is considered an alias for "foo@@version". We
3662 /// cannot simply combine the globals, because it is possible that a future incremental
3663 /// update would cause the globals to become separate again (e.g. due to a definition of
3664 /// "foo@@version" being replaced with a definition for "foo@version", which is the same
3665 /// symbol but no longer acting as a default version).
3666 ///
3667 /// See also `findAliaser`, which maps in the opposite direction, i.e. given a global,
3668 /// returns all globals which alias it.
3669 fn resolveAlias(gsi: Symbol.Global.Index, elf: *Elf) Symbol.Global.Index {
3670 if (elf.ehdrType() == .REL) {
3671 // It's too early to combine 'foo@@v' with 'foo'---let the final static linker
3672 // invocation do that.
3673 return gsi;
3674 }
3675
3676 if (gsi.version(elf) != null) {
3677 // An explicitly versioned symbol is not overriden by a default version.
3678 return gsi;
3679 }
3680
3681 const default_gsi: Symbol.Global.Index = default_gsi: {
3682 if (elf.default_version_globals.getIndexAdapted(
3683 gsi.name(elf),
3684 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
3685 )) |index| {
3686 break :default_gsi elf.default_version_globals.keys()[index];
3687 }
3688 if (elf.dso_globals.findDefaultVersion(gsi.name(elf))) |dso_global| {
3689 if (elf.globalByName(.{
3690 .name = gsi.name(elf),
3691 .version = dso_global.version.unwrap().?.slice(&elf.dso_globals),
3692 })) |default_gsi| {
3693 break :default_gsi default_gsi;
3694 }
3695 }
3696 return gsi; // no default symbol version exists
3697 };
3698
3699 if (!gsi.defined(elf)) {
3700 // Always use the default version if the unversioned symbol isn't defined.
3701 return default_gsi;
3702 } else if (!default_gsi.defined(elf)) {
3703 // If the unverisoned symbol is defined and the default version is undefined,
3704 // then ignore the default version: local definitions always take priority.
3705 return gsi;
3706 }
3707
3708 // `gsi` and `default_gsi` are both defined: the winner depends on the symbol binds.
3709 // If both are strong, there will be an error, so the answer doesn't matter. In that
3710 // case, we arbitrarily choose to consider the versioned symbol the winner. This is
3711 // useful because it makes it easier for `updateGlobalDynamic` to detect this case
3712 // and track it in `elf.defined_alias_globals`.
3713 return switch (gsi.ptr(elf).status.bind) {
3714 .strong => switch (default_gsi.ptr(elf).status.bind) {
3715 .strong => default_gsi,
3716 .weak => gsi,
3154 },3717 },
3718 .weak => default_gsi,
3719 };
3720 }
3721
3722 /// Effectively the opposite of `resolveAlias`; given a global symbol, returns the index
3723 /// of the single other global which aliases it, if any. The signature of this function
3724 /// implies that each global may have at most one global which is an alias for it.
3725 fn findAliaser(gsi: Symbol.Global.Index, elf: *Elf) ?Symbol.Global.Index {
3726 if (gsi.version(elf) == null) {
3727 return null;
3728 }
3729 const unversioned_gsi = elf.globalByName(.{
3730 .name = gsi.name(elf),
3731 .version = null,
3732 }) orelse {
3733 return null;
3155 };3734 };
3735 assert(unversioned_gsi != gsi);
3736 if (unversioned_gsi.resolveAlias(elf) == gsi) {
3737 return unversioned_gsi;
3738 }
3739 return null;
3740 }
3741
3742 /// Like `dynsymIndex`, except also returns `null` if `gsi` is the unversioned alias for
3743 /// a default symbol version. This means that for each dynsym entry, there is exactly
3744 /// one `Global` which will return that dynsym index here.
3745 fn ownedDynsymIndex(gsi: Symbol.Global.Index, elf: *Elf) ?u32 {
3746 if (gsi.ptr(elf).status.owns_dynsym) {
3747 const dynsym_index = gsi.ptr(elf).dynsym_index;
3748 assert(dynsym_index != 0);
3749 return dynsym_index;
3750 }
3751 return null;
3752 }
3753
3754 /// Scans through all relocations targeting `gsi`, deletes their dynamic relocation
3755 /// entries, and adds `R_*_RELATIVE` relocation entries as needed.
3756 ///
3757 /// Asserts we are creating a DSO.
3758 fn deleteDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) void {
3759 if (elf.shndx.dynamic == .UNDEF) return;
3760 const sym_id: Symbol.Id = .global(gsi);
3761 switch (elf.classifySymbolValue(sym_id)) {
3762 .static => {
3763 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
3764 !gsi.ptr(elf).status.any_static_relative_target_relocs)
3765 {
3766 return;
3767 }
3768 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
3769 gsi.ptr(elf).status.any_static_relative_target_relocs = false;
3770 gsi.ptr(elf).status.any_static_target_relocs = true;
3771 },
3772 .static_relative => {
3773 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
3774 !gsi.ptr(elf).status.any_static_target_relocs)
3775 {
3776 return;
3777 }
3778 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
3779 gsi.ptr(elf).status.any_static_target_relocs = false;
3780 gsi.ptr(elf).status.any_static_relative_target_relocs = true;
3781 },
3782 // TODO: this function needs to support a symbol becoming dynamic which wasn't
3783 // previously dynamic (and when it does, the function should be renamed to
3784 // `updateTargetOutputRelocs`). But for now, this isn't supported.
3785 .dynamic => unreachable,
3786 }
3787 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3788 while (ri != .none) {
3789 const reloc = ri.get(elf);
3790 assert(reloc.target == sym_id);
3791 reloc.deleteOutputRel(elf);
3792 ri = reloc.next;
3793 }
3794 switch (elf.classifySymbolValue(sym_id)) {
3795 .static => return,
3796 .static_relative => {},
3797 .dynamic => unreachable,
3798 }
3799 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
3800 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3801 while (ri != .none) {
3802 const reloc = ri.get(elf);
3803 ri = reloc.next;
3804 assert(reloc.target == sym_id);
3805 switch (reloc.type.target) {
3806 // Only relocations which resolve to absolute addresses require runtime
3807 // `R_*_RELATIVE` relocations.
3808 .special,
3809 .pltrel,
3810 .rel,
3811 .dtpoff,
3812 .tpoff,
3813 .size,
3814 => continue,
3815
3816 .abs, .pltabs => {},
3817 }
3818 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
3819 const node = reloc.node.unwrap().?;
3820 switch (elf.nodeWantsDsoRelocation(node)) {
3821 .no => continue,
3822 .yes_textrel => elf.textrel_count += 1,
3823 .yes => {},
3824 }
3825 // There is capacity for a relocation because we just deleted one earlier.
3826 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
3827 .type = .relative(elf),
3828 .offset = elf.getNodeVAddr(node) + reloc.offset,
3829 .raw_sym_index = 0,
3830 .addend = 0,
3831 }).toOptional();
3832 }
3156 }3833 }
31573834
3158 const Optional = enum(u32) {3835 const Optional = enum(u32) {
...@@ -3171,7 +3848,19 @@ const Symbol = struct {...@@ -3171,7 +3848,19 @@ const Symbol = struct {
3171 };3848 };
3172 };3849 };
31733850
3174 const Bind = enum { strong, weak };3851 const Bind = enum(u1) { strong, weak };
3852
3853 const AddOptions = struct {
3854 node: MappedFile.Node.Index.Optional,
3855 name: []const u8,
3856 lib_name: ?[]const u8 = null,
3857 value: u64,
3858 size: u64,
3859 type: std.elf.STT,
3860 bind: Symbol.Global.Bind,
3861 visibility: std.elf.STV,
3862 shndx: Section.Index,
3863 };
3175 };3864 };
31763865
3177 /// An index directly into the symtab. These values are not stable (global symbols are sometimes3866 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
...@@ -3245,52 +3934,67 @@ const Symbol = struct {...@@ -3245,52 +3934,67 @@ const Symbol = struct {
3245 };3934 };
3246 }3935 }
32473936
3937 /// Returns the `Symbol.Id` which determines the resolved value, size, etc of `s`.
3938 ///
3939 /// See `Symbol.Global.Index.resolveAlias` for details.
3940 fn resolveAlias(s: Symbol.Id, elf: *Elf) Symbol.Id {
3941 return switch (s.unwrap()) {
3942 .local => |lsi| .local(lsi),
3943 .global => |gsi| .global(gsi.resolveAlias(elf)),
3944 };
3945 }
3946
3248 /// Returns the value of this symbol, or 0 if it is undefined. If the symbol is an undefined3947 /// Returns the value of this symbol, or 0 if it is undefined. If the symbol is an undefined
3249 /// global for which we have emitted a copy relocation, returns the virtual address of that3948 /// global for which we have emitted a copy relocation, returns the virtual address of that
3250 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.3949 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.
3251 fn value(s: Symbol.Id, elf: *Elf) u64 {3950 fn value(s: Symbol.Id, elf: *Elf) u64 {
3252 return switch (elf.symPtr(s.index(elf))) {3951 return switch (elf.symPtr(s.resolveAlias(elf).index(elf))) {
3253 inline else => |sym| elf.targetLoad(&sym.value),3952 inline else => |sym| elf.targetLoad(&sym.value),
3254 };3953 };
3255 }3954 }
32563955
3257 fn name(s: Symbol.Id, elf: *Elf) String(.strtab) {3956 fn size(s: Symbol.Id, elf: *Elf) u64 {
3258 return switch (elf.symPtr(s.index(elf))) {3957 return switch (elf.symPtr(s.resolveAlias(elf).index(elf))) {
3259 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),3958 inline else => |sym| elf.targetLoad(&sym.value),
3260 };
3261 }
3262
3263 fn defined(s: Symbol.Id, elf: *Elf) bool {
3264 return switch (elf.symPtr(s.index(elf))) {
3265 inline else => |sym| elf.targetLoad(&sym.shndx) != std.elf.SHN_UNDEF,
3266 };3959 };
3267 }3960 }
32683961
3269 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {3962 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
3270 // Update the symbol value in `.symtab`
3271 const sym_index = sym_id.index(elf);3963 const sym_index = sym_id.index(elf);
3272 switch (elf.symPtr(sym_index)) {3964 switch (elf.symPtr(sym_index)) {
3273 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),3965 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3274 }3966 }
32753967
3276 // Update the symbol value in `.dynsym` if applicable
3277 switch (sym_id.unwrap()) {3968 switch (sym_id.unwrap()) {
3278 .local => {},3969 .local => {},
3279 .global => |gsi| {3970 .global => |gsi| if (gsi.ownedDynsymIndex(elf)) |dynsym_index| {
3280 if (gsi.ptr(elf).dynsym_index.unwrap()) |dynsym_index| {3971 switch (elf.dynsymPtr(dynsym_index)) {
3281 switch (elf.dynsymPtr(dynsym_index)) {3972 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3282 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3283 }
3284 }3973 }
3285 },3974 },
3286 }3975 }
32873976
3288 // Re-apply relocations targeting this symbol3977 sym_id.applyTargetRelocs(elf);
3978
3979 switch (sym_id.unwrap()) {
3980 .local => {},
3981 .global => |gsi| if (gsi.findAliaser(elf)) |alias_gsi| {
3982 Symbol.Id.global(alias_gsi).applyTargetRelocs(elf);
3983 },
3984 }
3985 }
3986
3987 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
3289 if (elf.ehdrType() != .REL) {3988 if (elf.ehdrType() != .REL) {
3290 sym_id.applyTargetRelocs(elf);3989 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3990 while (ri != .none) {
3991 const reloc = ri.get(elf);
3992 assert(reloc.target == sym_id);
3993 reloc.apply(elf);
3994 ri = reloc.next;
3995 }
3291 }3996 }
32923997
3293 // Update GOT entries targeting this symbol
3294 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {3998 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
3295 elf.updateGotEntry(got_index);3999 elf.updateGotEntry(got_index);
3296 }4000 }
...@@ -3303,79 +4007,14 @@ const Symbol = struct {...@@ -3303,79 +4007,14 @@ const Symbol = struct {
3303 }4007 }
3304 }4008 }
33054009
3306 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
3307 assert(elf.ehdrType() != .REL);
3308 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3309 while (ri != .none) {
3310 const reloc = ri.get(elf);
3311 assert(reloc.target == sym_id);
3312 reloc.apply(elf);
3313 ri = reloc.next;
3314 }
3315 }
3316
3317 /// Scans through all relocations targeting `sym_id` and, for each one with a dynamic
3318 /// relocation entry, either deletes it or converts it to R_*_RELATIVE as required.
3319 ///
3320 /// Asserts we are creating a DSO.
3321 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
3322 assert(elf.ehdrType() != .REL);
3323 assert(elf.shndx.dynamic != .UNDEF);
3324 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3325 while (ri != .none) {
3326 const reloc = ri.get(elf);
3327 assert(reloc.target == sym_id);
3328 reloc.deleteOutputRel(elf);
3329 ri = reloc.next;
3330 }
3331 switch (elf.classifySymbolValue(sym_id)) {
3332 .static => return,
3333 .static_relative => {},
3334 .dynamic => unreachable,
3335 }
3336 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
3337 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3338 while (ri != .none) {
3339 const reloc = ri.get(elf);
3340 ri = reloc.next;
3341 assert(reloc.target == sym_id);
3342 switch (reloc.type.target) {
3343 // Only relocations which resolve to absolute addresses require runtime
3344 // `R_*_RELATIVE` relocations.
3345 .special,
3346 .pltrel,
3347 .rel,
3348 .dtpoff,
3349 .tpoff,
3350 .size,
3351 => continue,
3352
3353 .abs, .pltabs => {},
3354 }
3355 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
3356 const node = reloc.node.unwrap().?;
3357 switch (elf.nodeWantsDsoRelocation(node)) {
3358 .no => continue,
3359 .yes_textrel => elf.textrel_count += 1,
3360 .yes => {},
3361 }
3362 // There is capacity for a relocation because we just deleted one earlier.
3363 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
3364 .type = .relative(elf),
3365 .offset = elf.getNodeVAddr(node) + reloc.offset,
3366 .raw_sym_index = 0,
3367 .addend = 0,
3368 }).toOptional();
3369 }
3370 }
3371
3372 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at4010 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
3373 /// some point due to a call to `flushMoved`.4011 /// some point due to a call to `flushMoved`.
3374 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {4012 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
3375 if (s.index(elf).ptr(elf).node.unwrap()) |node| {4013 const resolved = s.resolveAlias(elf);
4014 if (resolved.index(elf).ptr(elf).node.unwrap()) |node| {
3376 return node.hasMoved(&elf.mf);4015 return node.hasMoved(&elf.mf);
3377 }4016 }
3378 switch (s.unwrap()) {4017 switch (resolved.unwrap()) {
3379 .local => {},4018 .local => {},
3380 .global => |gsi| if (elf.copied_globals.getPtr(gsi)) |copied_global| {4019 .global => |gsi| if (elf.copied_globals.getPtr(gsi)) |copied_global| {
3381 return copied_global.node.hasMoved(&elf.mf);4020 return copied_global.node.hasMoved(&elf.mf);
...@@ -3386,11 +4025,34 @@ const Symbol = struct {...@@ -3386,11 +4025,34 @@ const Symbol = struct {
3386 };4025 };
3387};4026};
33884027
3389fn globalByName(elf: *Elf, name: []const u8) ?Symbol.Global.Index {4028fn globalByName(elf: *Elf, name: Symbol.Global.VersionedName) ?Symbol.Global.Index {
3390 const adapter: Symbol.Global.NameAdapter = .{ .elf = elf };4029 const adapter: Symbol.Global.VersionedName.Adapter = .{ .elf = elf };
3391 const index_raw = elf.globals_by_name.getIndexAdapted(name, adapter) orelse return null;4030 const index_raw = elf.globals_by_name.getIndexAdapted(name, adapter) orelse return null;
3392 return @fromBackingInt(@intCast(index_raw));4031 return @fromBackingInt(@intCast(index_raw));
3393}4032}
4033fn globalBySym(elf: *Elf, sym_index: Symbol.Index) Symbol.Global.Index {
4034 const raw_name: String(.strtab) = switch (elf.symPtr(sym_index)) {
4035 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
4036 };
4037 const name = parseVersionedSymbolName(raw_name.slice(elf));
4038 return elf.globalByName(.{ .name = name.name, .version = name.version }).?;
4039}
4040fn globalByDynsym(elf: *Elf, dynsym_index: u32) Symbol.Global.Index {
4041 // Versioned symbols have different names in the normal symbol table vs the dynamic symbol
4042 // table. In those cases, a hash map holds an "override":
4043 if (elf.versioned_dynsym_owners.get(dynsym_index)) |gsi| {
4044 assert(gsi.ownedDynsymIndex(elf).? == dynsym_index);
4045 return gsi;
4046 }
4047 // For all other symbols, we can just take the name of the dynamic symbol table entry, and look
4048 // up the global with that name.
4049 const name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
4050 inline else => |dynsym| @fromBackingInt(elf.targetLoad(&dynsym.name)),
4051 };
4052 const gsi = elf.globalByName(.{ .name = name.slice(elf), .version = null }).?;
4053 assert(gsi.ownedDynsymIndex(elf).? == dynsym_index);
4054 return gsi;
4055}
33944056
3395fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {4057fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
3396 /// This symbol's value is guaranteed to equal `sym.value(elf)`.4058 /// This symbol's value is guaranteed to equal `sym.value(elf)`.
...@@ -3415,14 +4077,16 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -3415,14 +4077,16 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
3415 return .static;4077 return .static;
3416 }4078 }
34174079
3418 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(sym.index(elf))) {4080 const resolved_sym = sym.resolveAlias(elf);
4081
4082 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(resolved_sym.index(elf))) {
3419 inline else => |sym_ptr| .{4083 inline else => |sym_ptr| .{
3420 .fromSection(elf.targetLoad(&sym_ptr.shndx)),4084 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
3421 elf.targetLoad(&sym_ptr.other).visibility,4085 elf.targetLoad(&sym_ptr.other).visibility,
3422 },4086 },
3423 };4087 };
34244088
3425 switch (sym.unwrap()) {4089 switch (resolved_sym.unwrap()) {
3426 .local => {4090 .local => {
3427 assert(shndx != .UNDEF);4091 assert(shndx != .UNDEF);
3428 assert(visibility == .DEFAULT);4092 assert(visibility == .DEFAULT);
...@@ -3441,7 +4105,9 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -3441,7 +4105,9 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
3441 .UNDEF => switch (visibility) {4105 .UNDEF => switch (visibility) {
3442 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {4106 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
3443 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section4107 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section
3444 // This is a static PIE---the only dynamic relocations are `R_*_RELATIVE`.4108 // This is a static PIE---we'll see a definition at some point, so there'd be no
4109 // point in adding an invalid dynamic relocation (the only valid dynamic relation
4110 // type in a static PIE is `R_*_RELATIVE`).
3445 return .static;4111 return .static;
3446 } else .dynamic, // external symbol4112 } else .dynamic, // external symbol
34474113
...@@ -3516,7 +4182,6 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.Sy...@@ -3516,7 +4182,6 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.Sy
3516fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId {4182fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId {
3517 const gpa = elf.base.comp.gpa;4183 const gpa = elf.base.comp.gpa;
35184184
3519 try elf.ensureUnusedSymbolCapacity(1, .all_local);
3520 try elf.nodes.ensureUnusedCapacity(gpa, 1);4185 try elf.nodes.ensureUnusedCapacity(gpa, 1);
3521 try elf.lazy.getPtr(lazy.kind).map.ensureUnusedCapacity(gpa, 1);4186 try elf.lazy.getPtr(lazy.kind).map.ensureUnusedCapacity(gpa, 1);
35224187
...@@ -3537,9 +4202,9 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol...@@ -3537,9 +4202,9 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol
3537 const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ lazy.kind, gop.index }) catch4202 const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ lazy.kind, gop.index }) catch
3538 unreachable;4203 unreachable;
3539 gop.value_ptr.* = .{4204 gop.value_ptr.* = .{
3540 .lsi = elf.addLocalSymbolAssumeCapacity(.{4205 .lsi = try elf.addLocalSymbol(.{
3541 .node = .wrap(node),4206 .node = .wrap(node),
3542 .name = try elf.string(.strtab, name),4207 .name = name,
3543 .value = 0,4208 .value = 0,
3544 .size = 0,4209 .size = 0,
3545 .type = sym_type,4210 .type = sym_type,
...@@ -3568,8 +4233,7 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb...@@ -3568,8 +4233,7 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb
3568 }).toTypeErased();4233 }).toTypeErased();
3569}4234}
3570fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {4235fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3571 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);4236 const gsi = elf.addGlobalSymbol(.{
3572 const gsi = elf.addGlobalSymbolAssumeCapacity(.{
3573 .node = .none,4237 .node = .none,
3574 .name = opts.name,4238 .name = opts.name,
3575 .lib_name = opts.lib_name,4239 .lib_name = opts.lib_name,
...@@ -3590,6 +4254,11 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {...@@ -3590,6 +4254,11 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3590 .shndx = .UNDEF,4254 .shndx = .UNDEF,
3591 }) catch |err| switch (err) {4255 }) catch |err| switch (err) {
3592 error.MultipleDefinitions => unreachable, // shndx is undef4256 error.MultipleDefinitions => unreachable, // shndx is undef
4257 error.MultipleDefaultVersions => unreachable, // shndx is undef
4258 error.UndefinedDefaultVersion => return elf.base.comp.link_diags.fail(
4259 "symbol '{s}' specifies default version without defining it",
4260 .{opts.name},
4261 ),
3593 else => |e| return e,4262 else => |e| return e,
3594 };4263 };
3595 return .global(gsi);4264 return .global(gsi);
...@@ -3739,8 +4408,21 @@ fn String(section: StringSection) type {...@@ -3739,8 +4408,21 @@ fn String(section: StringSection) type {
3739 };4408 };
3740}4409}
3741fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {4410fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {
4411 if (key.len == 0) {
4412 // Special case to allow calls from `initHeaders` before the strtab is initialized.
4413 return .empty;
4414 }
3742 const st: *StringTable = &@field(elf, @tagName(section));4415 const st: *StringTable = &@field(elf, @tagName(section));
3743 return @fromBackingInt(try st.get(elf, section.shndx(elf), key));4416 try st.ensureAdditionalCapacity(elf, section.shndx(elf), key.len);
4417 return @fromBackingInt(st.getAssumeCapacity(elf, section.shndx(elf), key));
4418}
4419fn ensureAdditionalStringCapacity(elf: *Elf, comptime section: StringSection, string_len: usize) Error!void {
4420 const st: *StringTable = &@field(elf, @tagName(section));
4421 try st.ensureAdditionalCapacity(elf, section.shndx(elf), string_len);
4422}
4423fn stringAssumeCapacity(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
4424 const st: *StringTable = &@field(elf, @tagName(section));
4425 return @fromBackingInt(st.getAssumeCapacity(elf, section.shndx(elf), key));
3744}4426}
3745/// Like `string`, but asserts that the string is already in `section`.4427/// Like `string`, but asserts that the string is already in `section`.
3746fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {4428fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
...@@ -3784,35 +4466,41 @@ const StringTable = struct {...@@ -3784,35 +4466,41 @@ const StringTable = struct {
3784 return st.map.getKeyAdapted(key, adapter).?;4466 return st.map.getKeyAdapted(key, adapter).?;
3785 }4467 }
37864468
3787 fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 {4469 fn ensureAdditionalCapacity(st: *StringTable, elf: *Elf, shndx: Section.Index, string_len: usize) Error!void {
4470 const gpa = elf.base.comp.gpa;
4471 const ni = shndx.get(elf).ni;
4472 const slice_const = ni.sliceConst(&elf.mf);
4473 try st.map.ensureUnusedCapacityContext(gpa, 1, .{ .slice = slice_const });
4474 const size: u64 = switch (elf.shdrPtr(shndx)) {
4475 inline else => |shdr| elf.targetLoad(&shdr.size),
4476 };
4477 try ni.ensureMinimumSize(gpa, &elf.mf, size + string_len + 1);
4478 }
4479
4480 fn getAssumeCapacity(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) u32 {
3788 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special4481 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
3789 // case the empty string.4482 // case the empty string.
3790 if (key.len == 0) return 0;4483 if (key.len == 0) return 0;
37914484
3792 const gpa = elf.base.comp.gpa;
3793 const ni = shndx.get(elf).ni;4485 const ni = shndx.get(elf).ni;
3794 const slice_const = ni.sliceConst(&elf.mf);4486 const slice_const = ni.sliceConst(&elf.mf);
3795 const gop = try st.map.getOrPutContextAdapted(4487 const gop = st.map.getOrPutAssumeCapacityAdapted(
3796 gpa,
3797 key,4488 key,
3798 StringTable.Adapter{ .slice = slice_const },4489 StringTable.Adapter{ .slice = slice_const },
3799 .{ .slice = slice_const },
3800 );4490 );
3801 if (gop.found_existing) return gop.key_ptr.*;4491 if (gop.found_existing) return gop.key_ptr.*;
3802 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {4492 const offset: u32 = switch (elf.shdrPtr(shndx)) {
3803 inline else => |shdr| {4493 inline else => |shdr| offset: {
3804 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));4494 const old_size = elf.targetLoad(&shdr.size);
3805 const new_size: u32 = @intCast(old_size + key.len + 1);4495 elf.targetStore(&shdr.size, @intCast(old_size + key.len + 1));
3806 elf.targetStore(&shdr.size, new_size);4496 break :offset @intCast(old_size);
3807 break :size .{ old_size, new_size };
3808 },4497 },
3809 };4498 };
3810 try ni.ensureMinimumSize(gpa, &elf.mf, new_size);4499 const slice = ni.slice(&elf.mf)[offset..];
3811 const slice = ni.slice(&elf.mf)[old_size..];
3812 @memcpy(slice[0..key.len], key);4500 @memcpy(slice[0..key.len], key);
3813 slice[key.len] = 0;4501 slice[key.len] = 0;
3814 gop.key_ptr.* = old_size;4502 gop.key_ptr.* = offset;
3815 return old_size;4503 return offset;
3816 }4504 }
3817};4505};
38184506
...@@ -3935,6 +4623,9 @@ fn create(...@@ -3935,6 +4623,9 @@ fn create(
3935 .tdata = .UNDEF,4623 .tdata = .UNDEF,
3936 .rela_dyn = .UNDEF,4624 .rela_dyn = .UNDEF,
3937 .rela_plt = .UNDEF,4625 .rela_plt = .UNDEF,
4626 .gnu_version = .UNDEF,
4627 .gnu_version_d = .UNDEF,
4628 .gnu_version_r = .UNDEF,
3938 .debug_abbrev = .UNDEF,4629 .debug_abbrev = .UNDEF,
3939 .debug_addr = .UNDEF,4630 .debug_addr = .UNDEF,
3940 .eh_frame_hdr = .UNDEF,4631 .eh_frame_hdr = .UNDEF,
...@@ -3959,11 +4650,23 @@ fn create(...@@ -3959,11 +4650,23 @@ fn create(
3959 .symtab = .empty,4650 .symtab = .empty,
3960 .globals = .empty,4651 .globals = .empty,
3961 .globals_by_name = .empty,4652 .globals_by_name = .empty,
4653 .default_version_globals = .empty,
3962 .unknown_globals = .empty,4654 .unknown_globals = .empty,
4655 .defined_alias_globals = .empty,
3963 .copied_globals = .empty,4656 .copied_globals = .empty,
3964 .want_copied_globals = .empty,4657 .versioned_dynsym_owners = .empty,
4658 .verdef = .empty,
4659 .verneed = .empty,
4660 .last_verneed_file_index = 0,
4661 // This is initially 2 because that is the first user-defined version index.
4662 // 0 and 1 are reserved for `VER_NDX_LOCAL` and `VER_NDX_GLOBAL`.
4663 .next_version_id = 2,
3965 .node_global_symbols = .empty,4664 .node_global_symbols = .empty,
3966 .dso_globals = .empty,4665 .dso_globals = .{
4666 .string_bytes = .empty,
4667 .symbols = .empty,
4668 .default_sym_vers = .empty,
4669 },
3967 .shstrtab = .{ .map = .empty },4670 .shstrtab = .{ .map = .empty },
3968 .strtab = .{ .map = .empty },4671 .strtab = .{ .map = .empty },
3969 .dynstr = .{ .map = .empty },4672 .dynstr = .{ .map = .empty },
...@@ -4035,11 +4738,17 @@ pub fn deinit(elf: *Elf) void {...@@ -4035,11 +4738,17 @@ pub fn deinit(elf: *Elf) void {
4035 elf.symtab.deinit(gpa);4738 elf.symtab.deinit(gpa);
4036 elf.globals.deinit(gpa);4739 elf.globals.deinit(gpa);
4037 elf.globals_by_name.deinit(gpa);4740 elf.globals_by_name.deinit(gpa);
4741 elf.default_version_globals.deinit(gpa);
4038 elf.unknown_globals.deinit(gpa);4742 elf.unknown_globals.deinit(gpa);
4743 elf.defined_alias_globals.deinit(gpa);
4039 elf.copied_globals.deinit(gpa);4744 elf.copied_globals.deinit(gpa);
4040 elf.want_copied_globals.deinit(gpa);4745 elf.versioned_dynsym_owners.deinit(gpa);
4746 elf.verdef.deinit(gpa);
4747 elf.verneed.deinit(gpa);
4041 elf.node_global_symbols.deinit(gpa);4748 elf.node_global_symbols.deinit(gpa);
4042 elf.dso_globals.deinit(gpa);4749 elf.dso_globals.string_bytes.deinit(gpa);
4750 elf.dso_globals.symbols.deinit(gpa);
4751 elf.dso_globals.default_sym_vers.deinit(gpa);
4043 elf.shstrtab.map.deinit(gpa);4752 elf.shstrtab.map.deinit(gpa);
4044 elf.strtab.map.deinit(gpa);4753 elf.strtab.map.deinit(gpa);
4045 elf.dynstr.map.deinit(gpa);4754 elf.dynstr.map.deinit(gpa);
...@@ -4132,6 +4841,9 @@ fn initHeaders(...@@ -4132,6 +4841,9 @@ fn initHeaders(
4132 shnum += 1; // .dynstr4841 shnum += 1; // .dynstr
4133 shnum += 1; // .dynsym4842 shnum += 1; // .dynsym
4134 shnum += 1; // .hash4843 shnum += 1; // .hash
4844 shnum += 1; // .gnu.version
4845 shnum += 1; // .gnu.version_d
4846 shnum += 1; // .gnu.version_r
4135 shnum += 1; // .rela.dyn4847 shnum += 1; // .rela.dyn
4136 shnum += 1; // .rela.plt4848 shnum += 1; // .rela.plt
4137 }4849 }
...@@ -4905,6 +5617,66 @@ fn initHeaders(...@@ -4905,6 +5617,66 @@ fn initHeaders(
4905 .addralign = addr_align,5617 .addralign = addr_align,
4906 .manual_size = true,5618 .manual_size = true,
4907 });5619 });
5620
5621 elf.shndx.gnu_version = try elf.addSection(elf.ni.rodata, .{
5622 .name = ".gnu.version",
5623 .type = .GNU_VERSYM,
5624 .flags = .{ .ALLOC = true },
5625 .link = elf.shndx.dynsym.toSection().?,
5626 .addralign = .@"2",
5627 .size = 2, // "null" dynsym entry
5628 .manual_size = true,
5629 });
5630 elf.targetStore(&elf.versymSlice()[0], .LOCAL); // "null" dynsym entry
5631
5632 elf.shndx.gnu_version_d = try elf.addSection(elf.ni.rodata, .{
5633 .name = ".gnu.version_d",
5634 .type = .GNU_VERDEF,
5635 .flags = .{ .ALLOC = true },
5636 .link = elf.shndx.dynstr.toSection().?,
5637 // It's completely undocumented outside of source code, but `sh_info` for this
5638 // section must hold the number of `Verdef` entries.
5639 .info = 1,
5640 .addralign = .@"4",
5641 .size = @sizeOf(VerdefEntry), // "file" entry
5642 .manual_size = true,
5643 });
5644 {
5645 const base_version_name: []const u8 = elf.options.soname orelse comp.root_name;
5646 const entry_ptr = &elf.verdefSlice()[0];
5647 entry_ptr.* = .{
5648 .def = .{
5649 .version = 1,
5650 .flags = std.elf.VER_FLG_BASE,
5651 .ndx = .GLOBAL,
5652 .cnt = 1,
5653 .hash = std.elf.hash.calculate(base_version_name),
5654 .aux = @offsetOf(VerdefEntry, "aux"),
5655 .next = @sizeOf(VerdefEntry),
5656 },
5657 .aux = .{
5658 .name = @backingInt(try elf.string(.dynstr, base_version_name)),
5659 .next = 0,
5660 },
5661 };
5662 if (elf.targetEndian() != std.lang.Endian.native) {
5663 std.mem.byteSwapAllFields(VerdefEntry, entry_ptr);
5664 }
5665 }
5666
5667 elf.shndx.gnu_version_r = try elf.addSection(elf.ni.rodata, .{
5668 .name = ".gnu.version_r",
5669 .type = .GNU_VERNEED,
5670 .flags = .{ .ALLOC = true },
5671 .link = elf.shndx.dynstr.toSection().?,
5672 // It's completely undocumented outside of source code, but `sh_info` for this
5673 // section must hold the number of `Verneed` entries.
5674 .info = 0,
5675 .addralign = .@"4",
5676 .size = 0,
5677 .manual_size = true,
5678 });
5679
4908 switch (elf.targetDynsymHashInfo()) {5680 switch (elf.targetDynsymHashInfo()) {
4909 inline else => |info| {5681 inline else => |info| {
4910 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{5682 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{
...@@ -5091,9 +5863,8 @@ fn initHeaders(...@@ -5091,9 +5863,8 @@ fn initHeaders(
5091 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`5863 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
5092 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).5864 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).
50935865
5094 try elf.ensureUnusedSymbolCapacity(10, .maybe_global);
5095 // Despite the name, `__dso_handle` is necessary even in static binaries.5866 // Despite the name, `__dso_handle` is necessary even in static binaries.
5096 _ = elf.addGlobalSymbolAssumeCapacity(.{5867 _ = elf.addGlobalSymbol(.{
5097 .node = .wrap(Section.Index.text.get(elf).ni),5868 .node = .wrap(Section.Index.text.get(elf).ni),
5098 .name = "__dso_handle",5869 .name = "__dso_handle",
5099 .value = Section.Index.text.vaddr(elf),5870 .value = Section.Index.text.vaddr(elf),
...@@ -5104,9 +5875,11 @@ fn initHeaders(...@@ -5104,9 +5875,11 @@ fn initHeaders(
5104 .shndx = .text,5875 .shndx = .text,
5105 }) catch |err| switch (err) {5876 }) catch |err| switch (err) {
5106 error.MultipleDefinitions => unreachable, // no inputs are processed yet5877 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5878 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5879 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5107 else => |e| return e,5880 else => |e| return e,
5108 };5881 };
5109 _ = elf.addGlobalSymbolAssumeCapacity(.{5882 _ = elf.addGlobalSymbol(.{
5110 .node = .wrap(elf.shndx.plt.get(elf).ni),5883 .node = .wrap(elf.shndx.plt.get(elf).ni),
5111 .name = "_PROCEDURE_LINKAGE_TABLE_",5884 .name = "_PROCEDURE_LINKAGE_TABLE_",
5112 .value = elf.shndx.plt.vaddr(elf),5885 .value = elf.shndx.plt.vaddr(elf),
...@@ -5117,9 +5890,11 @@ fn initHeaders(...@@ -5117,9 +5890,11 @@ fn initHeaders(
5117 .shndx = elf.shndx.plt,5890 .shndx = elf.shndx.plt,
5118 }) catch |err| switch (err) {5891 }) catch |err| switch (err) {
5119 error.MultipleDefinitions => unreachable, // no inputs are processed yet5892 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5893 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5894 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5120 else => |e| return e,5895 else => |e| return e,
5121 };5896 };
5122 _ = elf.addGlobalSymbolAssumeCapacity(.{5897 _ = elf.addGlobalSymbol(.{
5123 .node = .wrap(elf.shndx.got.get(elf).ni),5898 .node = .wrap(elf.shndx.got.get(elf).ni),
5124 .name = "_GLOBAL_OFFSET_TABLE_",5899 .name = "_GLOBAL_OFFSET_TABLE_",
5125 .value = switch (machine) {5900 .value = switch (machine) {
...@@ -5142,9 +5917,11 @@ fn initHeaders(...@@ -5142,9 +5917,11 @@ fn initHeaders(
5142 .shndx = elf.shndx.got,5917 .shndx = elf.shndx.got,
5143 }) catch |err| switch (err) {5918 }) catch |err| switch (err) {
5144 error.MultipleDefinitions => unreachable, // no inputs are processed yet5919 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5920 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5921 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5145 else => |e| return e,5922 else => |e| return e,
5146 };5923 };
5147 _ = elf.addGlobalSymbolAssumeCapacity(.{5924 _ = elf.addGlobalSymbol(.{
5148 .node = .none,5925 .node = .none,
5149 .name = "__init_array_start",5926 .name = "__init_array_start",
5150 .value = 0,5927 .value = 0,
...@@ -5155,9 +5932,11 @@ fn initHeaders(...@@ -5155,9 +5932,11 @@ fn initHeaders(
5155 .shndx = .ABS,5932 .shndx = .ABS,
5156 }) catch |err| switch (err) {5933 }) catch |err| switch (err) {
5157 error.MultipleDefinitions => unreachable, // no inputs are processed yet5934 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5935 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5936 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5158 else => |e| return e,5937 else => |e| return e,
5159 };5938 };
5160 _ = elf.addGlobalSymbolAssumeCapacity(.{5939 _ = elf.addGlobalSymbol(.{
5161 .node = .none,5940 .node = .none,
5162 .name = "__init_array_end",5941 .name = "__init_array_end",
5163 .value = 0,5942 .value = 0,
...@@ -5168,9 +5947,11 @@ fn initHeaders(...@@ -5168,9 +5947,11 @@ fn initHeaders(
5168 .shndx = .ABS,5947 .shndx = .ABS,
5169 }) catch |err| switch (err) {5948 }) catch |err| switch (err) {
5170 error.MultipleDefinitions => unreachable, // no inputs are processed yet5949 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5950 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5951 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5171 else => |e| return e,5952 else => |e| return e,
5172 };5953 };
5173 _ = elf.addGlobalSymbolAssumeCapacity(.{5954 _ = elf.addGlobalSymbol(.{
5174 .node = .none,5955 .node = .none,
5175 .name = "__fini_array_start",5956 .name = "__fini_array_start",
5176 .value = 0,5957 .value = 0,
...@@ -5181,9 +5962,11 @@ fn initHeaders(...@@ -5181,9 +5962,11 @@ fn initHeaders(
5181 .shndx = .ABS,5962 .shndx = .ABS,
5182 }) catch |err| switch (err) {5963 }) catch |err| switch (err) {
5183 error.MultipleDefinitions => unreachable, // no inputs are processed yet5964 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5965 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5966 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5184 else => |e| return e,5967 else => |e| return e,
5185 };5968 };
5186 _ = elf.addGlobalSymbolAssumeCapacity(.{5969 _ = elf.addGlobalSymbol(.{
5187 .node = .none,5970 .node = .none,
5188 .name = "__fini_array_end",5971 .name = "__fini_array_end",
5189 .value = 0,5972 .value = 0,
...@@ -5194,9 +5977,11 @@ fn initHeaders(...@@ -5194,9 +5977,11 @@ fn initHeaders(
5194 .shndx = .ABS,5977 .shndx = .ABS,
5195 }) catch |err| switch (err) {5978 }) catch |err| switch (err) {
5196 error.MultipleDefinitions => unreachable, // no inputs are processed yet5979 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5980 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5981 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5197 else => |e| return e,5982 else => |e| return e,
5198 };5983 };
5199 _ = elf.addGlobalSymbolAssumeCapacity(.{5984 _ = elf.addGlobalSymbol(.{
5200 .node = .none,5985 .node = .none,
5201 .name = "__preinit_array_start",5986 .name = "__preinit_array_start",
5202 .value = 0,5987 .value = 0,
...@@ -5207,9 +5992,11 @@ fn initHeaders(...@@ -5207,9 +5992,11 @@ fn initHeaders(
5207 .shndx = .ABS,5992 .shndx = .ABS,
5208 }) catch |err| switch (err) {5993 }) catch |err| switch (err) {
5209 error.MultipleDefinitions => unreachable, // no inputs are processed yet5994 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5995 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5996 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5210 else => |e| return e,5997 else => |e| return e,
5211 };5998 };
5212 _ = elf.addGlobalSymbolAssumeCapacity(.{5999 _ = elf.addGlobalSymbol(.{
5213 .node = .none,6000 .node = .none,
5214 .name = "__preinit_array_end",6001 .name = "__preinit_array_end",
5215 .value = 0,6002 .value = 0,
...@@ -5220,10 +6007,12 @@ fn initHeaders(...@@ -5220,10 +6007,12 @@ fn initHeaders(
5220 .shndx = .ABS,6007 .shndx = .ABS,
5221 }) catch |err| switch (err) {6008 }) catch |err| switch (err) {
5222 error.MultipleDefinitions => unreachable, // no inputs are processed yet6009 error.MultipleDefinitions => unreachable, // no inputs are processed yet
6010 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
6011 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5223 else => |e| return e,6012 else => |e| return e,
5224 };6013 };
5225 if (have_dynamic) {6014 if (have_dynamic) {
5226 _ = elf.addGlobalSymbolAssumeCapacity(.{6015 _ = elf.addGlobalSymbol(.{
5227 .node = .wrap(elf.shndx.dynamic.get(elf).ni),6016 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
5228 .name = "_DYNAMIC",6017 .name = "_DYNAMIC",
5229 .value = elf.shndx.dynamic.vaddr(elf),6018 .value = elf.shndx.dynamic.vaddr(elf),
...@@ -5234,6 +6023,8 @@ fn initHeaders(...@@ -5234,6 +6023,8 @@ fn initHeaders(
5234 .shndx = elf.shndx.dynamic,6023 .shndx = elf.shndx.dynamic,
5235 }) catch |err| switch (err) {6024 }) catch |err| switch (err) {
5236 error.MultipleDefinitions => unreachable, // no inputs are processed yet6025 error.MultipleDefinitions => unreachable, // no inputs are processed yet
6026 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
6027 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
5237 else => |e| return e,6028 else => |e| return e,
5238 };6029 };
5239 }6030 }
...@@ -6025,6 +6816,39 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {...@@ -6025,6 +6816,39 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
6025 }6816 }
6026}6817}
60276818
6819fn versymSlice(elf: *Elf) []std.elf.Versym {
6820 const raw_slice = elf.shndx.gnu_version.get(elf).ni.slice(&elf.mf);
6821 const size = elf.shndx.gnu_version.size(elf);
6822 return @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
6823}
6824
6825/// Although `.gnu.version_d` (the `SHT_VERDEF` section) can have essentially any layout, we always
6826/// organize it as an array of contiguous entries where each entry is a single `std.elf.Verdef` with
6827/// a single `std.elf.Verdaux`. This type represents a single such entry; the section contents is
6828/// then effectively an array of these.
6829const VerdefEntry = extern struct {
6830 def: std.elf.Verdef,
6831 aux: std.elf.Verdaux,
6832};
6833fn verdefSlice(elf: *Elf) []VerdefEntry {
6834 const raw_slice = elf.shndx.gnu_version_d.get(elf).ni.slice(&elf.mf);
6835 const size = elf.shndx.gnu_version_d.size(elf);
6836 return @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
6837}
6838
6839const VerneedEntry = extern union {
6840 verneed: std.elf.Verneed,
6841 vernaux: std.elf.Vernaux,
6842 comptime {
6843 assert(@sizeOf(std.elf.Verneed) == @sizeOf(std.elf.Vernaux));
6844 }
6845};
6846fn verneedSlice(elf: *Elf) []VerneedEntry {
6847 const raw_slice = elf.shndx.gnu_version_r.get(elf).ni.slice(&elf.mf);
6848 const size = elf.shndx.gnu_version_r.size(elf);
6849 return @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
6850}
6851
6028fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {6852fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
6029 const comp = elf.base.comp;6853 const comp = elf.base.comp;
6030 const any_non_single_threaded = comp.config.any_non_single_threaded;6854 const any_non_single_threaded = comp.config.any_non_single_threaded;
...@@ -6146,7 +6970,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -6146,7 +6970,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
6146 const ip = &zcu.intern_pool;6970 const ip = &zcu.intern_pool;
6147 const nav = ip.getNav(nav_index);6971 const nav = ip.getNav(nav_index);
61486972
6149 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6150 try elf.nodes.ensureUnusedCapacity(gpa, 1);6973 try elf.nodes.ensureUnusedCapacity(gpa, 1);
6151 try elf.navs.ensureUnusedCapacity(gpa, 1);6974 try elf.navs.ensureUnusedCapacity(gpa, 1);
61526975
...@@ -6210,9 +7033,9 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -6210,9 +7033,9 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
6210 .alignment = alignment,7033 .alignment = alignment,
6211 }), .{ .nav = nmi });7034 }), .{ .nav = nmi });
6212 nav_gop.value_ptr.* = .{7035 nav_gop.value_ptr.* = .{
6213 .lsi = elf.addLocalSymbolAssumeCapacity(.{7036 .lsi = try elf.addLocalSymbol(.{
6214 .node = .wrap(node),7037 .node = .wrap(node),
6215 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),7038 .name = nav.fqn.toSlice(ip),
6216 .value = 0,7039 .value = 0,
6217 .size = 0,7040 .size = 0,
6218 .type = elf.navType(nav.resolved.?),7041 .type = elf.navType(nav.resolved.?),
...@@ -6233,7 +7056,6 @@ fn uavMapIndex(...@@ -6233,7 +7056,6 @@ fn uavMapIndex(
6233 const gpa = elf.base.comp.gpa;7056 const gpa = elf.base.comp.gpa;
6234 const zcu = elf.base.comp.zcu.?;7057 const zcu = elf.base.comp.zcu.?;
62357058
6236 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6237 try elf.nodes.ensureUnusedCapacity(gpa, 1);7059 try elf.nodes.ensureUnusedCapacity(gpa, 1);
6238 try elf.uavs.ensureUnusedCapacity(gpa, 1);7060 try elf.uavs.ensureUnusedCapacity(gpa, 1);
6239 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);7061 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
...@@ -6256,9 +7078,9 @@ fn uavMapIndex(...@@ -6256,9 +7078,9 @@ fn uavMapIndex(
6256 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;7078 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;
6257 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;7079 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;
6258 uav_gop.value_ptr.* = .{7080 uav_gop.value_ptr.* = .{
6259 .lsi = elf.addLocalSymbolAssumeCapacity(.{7081 .lsi = try elf.addLocalSymbol(.{
6260 .node = .wrap(node),7082 .node = .wrap(node),
6261 .name = try elf.string(.strtab, name),7083 .name = name,
6262 .value = 0,7084 .value = 0,
6263 .size = 0,7085 .size = 0,
6264 .type = .OBJECT,7086 .type = .OBJECT,
...@@ -6537,10 +7359,9 @@ fn loadObject(...@@ -6537,10 +7359,9 @@ fn loadObject(
6537 }7359 }
65387360
6539 elf.input_pending_index += 1;7361 elf.input_pending_index += 1;
6540 try elf.ensureUnusedSymbolCapacity(1, .all_local);7362 input.extra = .{ .file_symbol = try elf.addLocalSymbol(.{
6541 input.extra = .{ .file_symbol = elf.addLocalSymbolAssumeCapacity(.{
6542 .node = .none,7363 .node = .none,
6543 .name = try elf.string(.strtab, std.fs.path.stem(member orelse path.sub_path)),7364 .name = std.fs.path.stem(member orelse path.sub_path),
6544 .value = 0,7365 .value = 0,
6545 .size = 0,7366 .size = 0,
6546 .type = .FILE,7367 .type = .FILE,
...@@ -6768,7 +7589,6 @@ fn loadObject(...@@ -6768,7 +7589,6 @@ fn loadObject(
6768 ), 1) catch continue;7589 ), 1) catch continue;
6769 symmap.clearRetainingCapacity();7590 symmap.clearRetainingCapacity();
6770 try symmap.resize(gpa, symnum);7591 try symmap.resize(gpa, symnum);
6771 try elf.ensureUnusedSymbolCapacity(symnum, .maybe_global);
6772 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);7592 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
6773 for (symmap.items) |*si| {7593 for (symmap.items) |*si| {
6774 si.* = .null;7594 si.* = .null;
...@@ -6792,7 +7612,7 @@ fn loadObject(...@@ -6792,7 +7612,7 @@ fn loadObject(
6792 ),7612 ),
6793 .LOCAL => continue,7613 .LOCAL => continue,
6794 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {7614 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6795 si.* = .global(elf.addGlobalSymbolAssumeCapacity(.{7615 si.* = .global(elf.addGlobalSymbol(.{
6796 .node = .none,7616 .node = .none,
6797 .name = name,7617 .name = name,
6798 .value = input_sym.value,7618 .value = input_sym.value,
...@@ -6807,6 +7627,12 @@ fn loadObject(...@@ -6807,6 +7627,12 @@ fn loadObject(
6807 .shndx = .UNDEF,7627 .shndx = .UNDEF,
6808 }) catch |err| switch (err) {7628 }) catch |err| switch (err) {
6809 error.MultipleDefinitions => unreachable, // shndx is .UNDEF7629 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
7630 error.MultipleDefaultVersions => unreachable, // shndx is .UNDEF
7631 error.UndefinedDefaultVersion => return diags.failParse(
7632 path,
7633 "symbol '{s}' specifies default version without defining it",
7634 .{name},
7635 ),
6810 else => |e| return e,7636 else => |e| return e,
6811 });7637 });
6812 continue;7638 continue;
...@@ -6822,9 +7648,9 @@ fn loadObject(...@@ -6822,9 +7648,9 @@ fn loadObject(
6822 .{ name, bind },7648 .{ name, bind },
6823 ),7649 ),
6824 .LOCAL => {7650 .LOCAL => {
6825 const lsi = elf.addLocalSymbolAssumeCapacity(.{7651 const lsi = try elf.addLocalSymbol(.{
6826 .node = .wrap(input_section_node),7652 .node = .wrap(input_section_node),
6827 .name = try elf.string(.strtab, name),7653 .name = name,
6828 .value = input_sym.value,7654 .value = input_sym.value,
6829 .size = input_sym.size,7655 .size = input_sym.size,
6830 .type = sym_type,7656 .type = sym_type,
...@@ -6833,7 +7659,7 @@ fn loadObject(...@@ -6833,7 +7659,7 @@ fn loadObject(
6833 si.* = .local(lsi);7659 si.* = .local(lsi);
6834 },7660 },
6835 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {7661 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6836 si.* = .global(elf.addGlobalSymbolAssumeCapacity(.{7662 si.* = .global(elf.addGlobalSymbol(.{
6837 .node = .wrap(input_section_node),7663 .node = .wrap(input_section_node),
6838 .name = name,7664 .name = name,
6839 .value = input_sym.value,7665 .value = input_sym.value,
...@@ -6852,6 +7678,12 @@ fn loadObject(...@@ -6852,6 +7678,12 @@ fn loadObject(
6852 "multiple definitions of '{s}'",7678 "multiple definitions of '{s}'",
6853 .{name},7679 .{name},
6854 ),7680 ),
7681 error.MultipleDefaultVersions => return diags.failParse(
7682 path,
7683 "multiple default versions of '{s}'",
7684 .{parseVersionedSymbolName(name).name},
7685 ),
7686 error.UndefinedDefaultVersion => unreachable, // shndx is not `.UNDEF` (i.e. the symbol is defined)
6855 else => |e| return e,7687 else => |e| return e,
6856 });7688 });
6857 },7689 },
...@@ -7006,73 +7838,199 @@ fn loadDso(...@@ -7006,73 +7838,199 @@ fn loadDso(
7006 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});7838 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
7007 if (ehdr.machine != elf.ehdrMachine().toElf())7839 if (ehdr.machine != elf.ehdrMachine().toElf())
7008 return diags.failParse(path, "bad machine", .{});7840 return diags.failParse(path, "bad machine", .{});
7009 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
7010 // We're going to need to know the alignment of every section later.7841 // We're going to need to know the alignment of every section later.
7011 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);7842 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);
7012 defer gpa.free(section_aligns);7843 defer gpa.free(section_aligns);
7013 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {7844 const shdr: struct {
7014 var dynamic_sh: ?ElfN.Shdr = null;7845 dynamic: ElfN.Shdr,
7015 var dynsym_sh: ?ElfN.Shdr = null;7846 dynsym: ElfN.Shdr,
7016 for (section_aligns) |*section_align| {7847 dynstr: ElfN.Shdr,
7848 verdef: ?ElfN.Shdr,
7849 versym: ?ElfN.Shdr,
7850 } = shdr: {
7851 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
7852 const InputSh = struct {
7853 shndx: u32,
7854 sh: ElfN.Shdr,
7855 };
7856 var opt_dynamic: ?InputSh = null;
7857 var opt_dynsym: ?InputSh = null;
7858 var opt_verdef: ?InputSh = null;
7859 var opt_versym: ?InputSh = null;
7860 for (section_aligns, 0..) |*section_align, shndx| {
7017 const sh = try r.peekStruct(ElfN.Shdr, target_endian);7861 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
7018 try r.discardAll(ehdr.shentsize);7862 try r.discardAll(ehdr.shentsize);
7019 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(7863 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
7020 usize,7864 usize,
7021 @intCast(@max(sh.addralign, 1)),7865 @intCast(@max(sh.addralign, 1)),
7022 ));7866 ));
7023 switch (sh.type) {7867 const ptr: *?InputSh = switch (sh.type) {
7024 else => {},7868 else => continue,
7025 .DYNAMIC => dynamic_sh = sh,7869 .DYNAMIC => &opt_dynamic,
7026 .DYNSYM => dynsym_sh = sh,7870 .DYNSYM => &opt_dynsym,
7871 .GNU_VERDEF => &opt_verdef,
7872 .GNU_VERSYM => &opt_versym,
7873 };
7874 if (ptr.* != null) {
7875 return diags.failParse(path, "multiple SHT_{t} sections", .{sh.type});
7027 }7876 }
7877 ptr.* = .{ .shndx = @intCast(shndx), .sh = sh };
7028 }7878 }
7029 break :sh .{7879
7030 dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}),7880 const dynamic = opt_dynamic orelse {
7031 dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}),7881 return diags.failParse(path, "missing SHT_DYNAMIC section", .{});
7032 };7882 };
7033 };7883 const dynsym = opt_dynsym orelse {
7034 const dynstr_sh: ElfN.Shdr = sh: {7884 return diags.failParse(path, "missing SHT_DYNSYM section", .{});
7035 if (dynsym_sh.link >= ehdr.shnum) {7885 };
7036 return diags.failParse(path, "bad dynamic string table section index", .{});7886
7887 const dynstr_shndx = dynamic.sh.link;
7888 if (dynstr_shndx >= ehdr.shnum) {
7889 return diags.failParse(path, "SHT_DYNAMIC section does not link to a valid section", .{});
7890 }
7891 try fr.seekTo(ehdr.shoff + dynstr_shndx * ehdr.shentsize);
7892 const dynstr_sh = try r.peekStruct(ElfN.Shdr, target_endian);
7893 if (dynstr_sh.type != .STRTAB) {
7894 return diags.failParse(path, "SHT_DYNAMIC section does not link to a SHT_STRTAB section", .{});
7037 }7895 }
7038 try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize);7896
7039 break :sh try r.peekStruct(ElfN.Shdr, target_endian);7897 // Validate all the other shdr `link` fields. After this we won't need the shndx
7898 // values for anything else.
7899 if (dynsym.sh.link != dynstr_shndx) {
7900 return diags.failParse(path, "SHT_DYNSYM section does not link to the dynamic string table section", .{});
7901 }
7902 if (opt_verdef) |verdef| if (verdef.sh.link != dynstr_shndx) {
7903 return diags.failParse(path, "SHT_GNU_VERDEF section does not link to the dynamic string table section", .{});
7904 };
7905 if (opt_versym) |versym| if (versym.sh.link != dynsym.shndx) {
7906 return diags.failParse(path, "SHT_GNU_VERSYM section does not link to the dynamic symbol table section", .{});
7907 };
7908
7909 break :shdr .{
7910 .dynamic = dynamic.sh,
7911 .dynsym = dynsym.sh,
7912 .dynstr = dynstr_sh,
7913 .verdef = if (opt_verdef) |verdef| verdef.sh else null,
7914 .versym = if (opt_versym) |versym| versym.sh else null,
7915 };
7040 };7916 };
70417917
7042 if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) {7918 if (shdr.dynamic.entsize != @sizeOf(ElfN.Addr) * 2) {
7043 return diags.failParse(path, "bad dynamic section entsize", .{});7919 return diags.failParse(path, "bad SHT_DYNAMIC section entsize", .{});
7044 }7920 }
7045 const dynnum = std.math.divExact(7921 const dynnum = std.math.divExact(
7046 u32,7922 u32,
7047 @intCast(dynamic_sh.size),7923 @intCast(shdr.dynamic.size),
7048 @sizeOf(ElfN.Addr) * 2,7924 @sizeOf(ElfN.Addr) * 2,
7049 ) catch return diags.failParse(7925 ) catch return diags.failParse(
7050 path,7926 path,
7051 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",7927 "SHT_DYNAMIC section size (0x{x}) is not a multiple of entsize (0x{x})",
7052 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },7928 .{ shdr.dynamic.size, @sizeOf(ElfN.Addr) * 2 },
7053 );7929 );
70547930
7055 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {7931 if (shdr.dynsym.entsize < @sizeOf(ElfN.Sym)) {
7056 return diags.failParse(path, "bad dynsym entsize", .{});7932 return diags.failParse(path, "dynamic symbol table has invalid entsize", .{});
7057 }7933 }
7058 const symnum = std.math.divExact(7934 const symnum = std.math.divExact(
7059 u32,7935 u32,
7060 @intCast(dynsym_sh.size),7936 @intCast(shdr.dynsym.size),
7061 @intCast(dynsym_sh.entsize),7937 @intCast(shdr.dynsym.entsize),
7062 ) catch return diags.failParse(7938 ) catch return diags.failParse(
7063 path,7939 path,
7064 "dynsym size (0x{x}) is not a multiple of entsize (0x{x})",7940 "dynamic symbol table size (0x{x}) is not a multiple of entsize (0x{x})",
7065 .{ dynsym_sh.size, dynsym_sh.entsize },7941 .{ shdr.dynsym.size, shdr.dynsym.entsize },
7066 );7942 );
70677943
7068 const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size));7944 const dynstr = try gpa.alloc(u8, @intCast(shdr.dynstr.size));
7069 defer gpa.free(dynstr);7945 defer gpa.free(dynstr);
7070 try fr.seekTo(dynstr_sh.offset);7946 try fr.seekTo(shdr.dynstr.offset);
7071 try r.readSliceAll(dynstr);7947 try r.readSliceAll(dynstr);
70727948
7949 const versym: []std.elf.Versym = versym: {
7950 const versym_sh = shdr.versym orelse break :versym &.{};
7951 if (versym_sh.size != symnum * 2) return diags.failParse(
7952 path,
7953 "SHT_GNU_VERSYM section has invalid size (expected 0x{x}, got 0x{x})",
7954 .{ symnum * 2, versym_sh.size },
7955 );
7956 const versym = try gpa.alloc(std.elf.Versym, symnum);
7957 errdefer gpa.free(versym);
7958 try fr.seekTo(versym_sh.offset);
7959 try r.readSliceEndian(std.elf.Versym, versym, target_endian);
7960 break :versym versym;
7961 };
7962 defer gpa.free(versym);
7963
7964 const verdef: []DsoGlobals.String.Optional = verdef: {
7965 const verdef_sh = shdr.verdef orelse break :verdef &.{};
7966
7967 // This parsing is a bit of a mess because it appears that at this moment in history
7968 // nobody at GNU was yet aware of the concept of an "array".
7969
7970 const num_versions = verdef_sh.info;
7971 if (num_versions > std.math.maxInt(u16)) {
7972 return diags.failParse(path, "entry count of SHT_GNU_VERDEF section is too large", .{});
7973 }
7974
7975 // First pass: find the largest version index, because they're not actually indices,
7976 // but rather arbitrary IDs. But it's okay to allocate memory proportional to the
7977 // largest ID, because (a) glibc does this which suggests that the values should be
7978 // dense in practice, and more importantly (b) the IDs are only 16 bits long anyway
7979 // so this will never allocate more than 65k entries.
7980 var max_ver_ndx: u16 = 0;
7981 var verdef_offset: u64 = 0;
7982 for (0..num_versions) |_| {
7983 if (verdef_offset + @sizeOf(std.elf.Verdef) > verdef_sh.size) {
7984 return diags.failParse(path, "verdef entry exceeds section bounds", .{});
7985 }
7986 try fr.seekTo(verdef_sh.offset + verdef_offset);
7987 const def = try r.peekStruct(std.elf.Verdef, target_endian);
7988 if (def.cnt != 0 and def.flags & std.elf.VER_FLG_BASE == 0) {
7989 max_ver_ndx = @max(max_ver_ndx, @backingInt(def.ndx));
7990 }
7991 verdef_offset += def.next;
7992 }
7993
7994 const verdef = try gpa.alloc(DsoGlobals.String.Optional, @intCast(max_ver_ndx + 1));
7995 errdefer gpa.free(verdef);
7996
7997 @memset(verdef, .none);
7998
7999 // Second pass: for each version definition, look at its first verdaux entry to get
8000 // the actual version name.
8001 verdef_offset = 0;
8002 for (0..num_versions) |_| {
8003 try fr.seekTo(verdef_sh.offset + verdef_offset);
8004 const def = try r.peekStruct(std.elf.Verdef, target_endian);
8005 if (def.cnt != 0 and def.flags & std.elf.VER_FLG_BASE == 0) {
8006 const ver_ndx = @backingInt(def.ndx);
8007 if (verdef[ver_ndx] != .none) {
8008 return diags.failParse(path, "multiple definitions for symbol version index '{d}'", .{ver_ndx});
8009 }
8010 if (verdef_offset + def.aux + @sizeOf(std.elf.Verdaux) > verdef_sh.size) {
8011 return diags.failParse(path, "verdaux entry exceeds section bounds", .{});
8012 }
8013 try fr.seekTo(verdef_sh.offset + verdef_offset + def.aux);
8014 const aux = try r.peekStruct(std.elf.Verdaux, target_endian);
8015 if (aux.name >= shdr.dynstr.size) {
8016 return diags.failParse(path, "bad verdaux entry name string", .{});
8017 }
8018 const version_name = std.mem.sliceTo(dynstr[aux.name..], 0);
8019 verdef[ver_ndx] = @fromBackingInt(@intCast(elf.dso_globals.string_bytes.items.len));
8020 try elf.dso_globals.string_bytes.ensureUnusedCapacity(gpa, version_name.len + 1);
8021 elf.dso_globals.string_bytes.appendSliceAssumeCapacity(version_name);
8022 elf.dso_globals.string_bytes.appendAssumeCapacity(0);
8023 }
8024 verdef_offset += def.next;
8025 }
8026
8027 break :verdef verdef;
8028 };
8029 defer gpa.free(verdef);
8030
7073 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.8031 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.
7074 try fr.seekTo(dynamic_sh.offset);8032 try fr.seekTo(shdr.dynamic.offset);
7075 const soname: []const u8 = for (0..dynnum) |_| {8033 const soname_slice: []const u8 = for (0..dynnum) |_| {
7076 const tag = try r.takeInt(ElfN.Addr, target_endian);8034 const tag = try r.takeInt(ElfN.Addr, target_endian);
7077 const val = try r.takeInt(ElfN.Addr, target_endian);8035 const val = try r.takeInt(ElfN.Addr, target_endian);
7078 if (tag == std.elf.DT_SONAME) {8036 if (tag == std.elf.DT_SONAME) {
...@@ -7086,16 +8044,16 @@ fn loadDso(...@@ -7086,16 +8044,16 @@ fn loadDso(
7086 .basename => std.fs.path.basename(path.sub_path),8044 .basename => std.fs.path.basename(path.sub_path),
7087 .full_path => try path.toString(comp.arena),8045 .full_path => try path.toString(comp.arena),
7088 };8046 };
7089 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});8047 const soname = try elf.string(.dynstr, soname_slice);
8048 try elf.needed.put(gpa, soname, {});
70908049
7091 // Scan the symbol table and populate `elf.dso_globals`.8050 // Scan the symbol table and populate `elf.dso_globals`.
7092 const first_global = @min(dynsym_sh.info, symnum);8051 const first_global = @min(shdr.dynsym.info, symnum);
7093 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);8052 try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global);
7094 try elf.ensureUnusedPltCapacity(symnum - first_global);8053 try fr.seekTo(shdr.dynsym.offset + first_global * shdr.dynsym.entsize);
7095 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);8054 for (first_global..symnum) |in_dynsym_index| {
7096 for (first_global..symnum) |_| {
7097 const sym = try r.peekStruct(ElfN.Sym, target_endian);8055 const sym = try r.peekStruct(ElfN.Sym, target_endian);
7098 try r.discardAll(@intCast(dynsym_sh.entsize));8056 try r.discardAll(@intCast(shdr.dynsym.entsize));
70998057
7100 switch (sym.info.bind) {8058 switch (sym.info.bind) {
7101 else => continue,8059 else => continue,
...@@ -7115,100 +8073,76 @@ fn loadDso(...@@ -7115,100 +8073,76 @@ fn loadDso(
7115 return diags.failParse(path, "bad symbol name string", .{});8073 return diags.failParse(path, "bad symbol name string", .{});
7116 }8074 }
71178075
8076 const maybe_version: DsoGlobals.String.Optional, const is_default_version: bool = if (versym.len > 0) switch (versym[in_dynsym_index]) {
8077 .LOCAL, .GLOBAL => .{ .none, false },
8078 else => |v| version: {
8079 if (v.VERSION >= verdef.len or verdef[v.VERSION] == .none) {
8080 return diags.failParse(path, "bad symbol version index '{d}'", .{v.VERSION});
8081 }
8082 break :version .{ verdef[v.VERSION], !v.HIDDEN };
8083 },
8084 } else .{ .none, false };
8085
7118 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems8086 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems
7119 // insane---refer to the doc comment on `alignment` in `Elf.dso_globals`.8087 // insane---refer to the doc comment on `DsoGlobals.Symbol.alignment`.
7120 const sym_align: Alignment = switch (sym.value) {8088 const sym_align: Alignment = switch (sym.value) {
7121 0 => section_aligns[sym.shndx],8089 0 => section_aligns[sym.shndx],
7122 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),8090 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),
7123 };8091 };
71248092
7125 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));8093 const name = std.mem.sliceTo(dynstr[sym.name..], 0);
7126 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
71278094
7128 if (gop.found_existing and gop.value_ptr.type != .NOTYPE) {8095 const gop = elf.dso_globals.symbols.getOrPutAssumeCapacityAdapted(@as(DsoGlobals.Symbol.Key, .{
7129 if (sym.size > gop.value_ptr.size or8096 .name = name,
7130 sym_align.compare(.gt, gop.value_ptr.alignment))8097 .version = if (maybe_version.unwrap()) |version| slice: {
8098 break :slice version.slice(&elf.dso_globals);
8099 } else null,
8100 }), @as(DsoGlobals.Symbol.Adapter, .{ .dso_globals = &elf.dso_globals }));
8101
8102 if (gop.found_existing and gop.key_ptr.type != .NOTYPE) {
8103 if (sym.size > gop.key_ptr.size or
8104 sym_align.compare(.gt, gop.key_ptr.alignment))
7131 {8105 {
7132 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);8106 gop.key_ptr.size = @max(gop.key_ptr.size, sym.size);
7133 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);8107 gop.key_ptr.alignment = gop.key_ptr.alignment.max(sym_align);
7134
7135 // If there is already a copy relocation for this symbol, the amount of
7136 // space reserved for it might be too small or underaligned, so update it:
7137 update: {
7138 const gsi = elf.globalByName(name.slice(elf)) orelse break :update;
7139 const copied_global = elf.copied_globals.get(gsi) orelse break :update;
7140
7141 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
7142 try copied_global.node.resizeLeaf(
7143 gpa,
7144 &elf.mf,
7145 gop.value_ptr.alignment.forward(gop.value_ptr.size),
7146 );
7147 try copied_global.node.realign(gpa, &elf.mf, gop.value_ptr.alignment);
7148 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
7149 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
7150 }
7151 switch (elf.dynsymPtr(gsi.ptr(elf).dynsym_index.unwrap().?)) {
7152 inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)),
7153 }
7154 }
7155 }8108 }
7156 continue;8109 } else {
8110 gop.key_ptr.* = .{
8111 .name = name: {
8112 if (gop.found_existing) break :name gop.key_ptr.name;
8113 const name_str_off = elf.dso_globals.string_bytes.items.len;
8114 try elf.dso_globals.string_bytes.ensureUnusedCapacity(gpa, name.len + 1);
8115 elf.dso_globals.string_bytes.appendSliceAssumeCapacity(name);
8116 elf.dso_globals.string_bytes.appendAssumeCapacity(0);
8117 break :name @fromBackingInt(@intCast(name_str_off));
8118 },
8119 .version = maybe_version,
8120 .soname = soname,
8121 .type = sym.info.type,
8122 .size = sym.size,
8123 .alignment = sym_align,
8124 };
7157 }8125 }
71588126
7159 gop.value_ptr.* = .{8127 if (is_default_version) {
7160 .type = sym.info.type,8128 const adapter: DsoGlobals.Symbol.DefaultVersionAdapter = .{
7161 .size = sym.size,8129 .dso_globals = &elf.dso_globals,
7162 .alignment = sym_align,8130 };
7163 };8131 const default_gop = try elf.dso_globals.default_sym_vers.getOrPutAdapted(gpa, name, adapter);
71648132 if (!default_gop.found_existing) {
7165 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate8133 default_gop.key_ptr.* = @intCast(gop.index);
7166 // its type now.8134 }
7167 const gsi = elf.globalByName(name.slice(elf)) orelse continue;
7168 if (Symbol.Id.global(gsi).defined(elf)) continue;
7169
7170 _ = elf.unknown_globals.swapRemove(gsi);
7171
7172 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap() orelse continue;
7173
7174 if (elf.want_copied_globals.swapRemove(gsi)) {
7175 // We just found a DSO definition of a symbol for which we wanted a copy
7176 // relocation, so add one if we can!
7177 _ = try elf.maybeAddCopyRelocation(gsi);
7178 }8135 }
71798136
7180 const sym_ptr = @field(elf.symPtr(gsi.ptr(elf).symtab_index), @tagName(class));8137 // Update any global(s) which may care about this input DSO global.
7181 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`8138 if (maybe_version.unwrap()) |version| {
71828139 if (elf.globalByName(.{ .name = name, .version = version.slice(&elf.dso_globals) })) |gsi| {
7183 switch (elf.targetLoad(&sym_ptr.other).visibility) {8140 try elf.updateGlobalDynamic(gsi, false);
7184 .HIDDEN, .INTERNAL, .PROTECTED => continue,8141 }
7185 .DEFAULT => {},
7186 }8142 }
71878143 if (maybe_version == .none or is_default_version) {
7188 const cur_info = elf.targetLoad(&sym_ptr.info);8144 if (elf.globalByName(.{ .name = name, .version = null })) |gsi| {
7189 if (cur_info.type == .NOTYPE) {8145 try elf.updateGlobalDynamic(gsi, false);
7190 const new_type: std.elf.STT = switch (sym.info.type) {
7191 .GNU_IFUNC => .FUNC,
7192 else => |t| t,
7193 };
7194
7195 elf.targetStore(&sym_ptr.info, .{
7196 .bind = cur_info.bind,
7197 .type = new_type,
7198 });
7199
7200 const dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
7201 elf.targetStore(&dynsym_ptr.info, .{
7202 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
7203 .type = new_type,
7204 });
7205
7206 if (new_type == .FUNC) {
7207 // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry...
7208 elf.addPltEntry(gsi);
7209 // ...and therefore, we need to re-apply that symbol's relocations, as
7210 // some might be targeting its PLT entry.
7211 Symbol.Id.global(gsi).applyTargetRelocs(elf);
7212 }8146 }
7213 }8147 }
7214 }8148 }
...@@ -7309,23 +8243,32 @@ fn createInitFiniArraySection(...@@ -7309,23 +8243,32 @@ fn createInitFiniArraySection(
7309 .manual_size = true,8243 .manual_size = true,
7310 });8244 });
7311 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});8245 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
7312 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
7313 // These symbols definitely already exist with strong definitions, because we added them8246 // These symbols definitely already exist with strong definitions, because we added them
7314 // alongside the other linker-defined symbols, all the way back in `initHeaders`.8247 // alongside the other linker-defined symbols, all the way back in `initHeaders`.
7315 elf.setGlobalSymbolValue(elf.globalByName("__" ++ name ++ "_start").?, .{8248 const start_gsi = elf.globalByName(.{
8249 .name = "__" ++ name ++ "_start",
8250 .version = null,
8251 }).?;
8252 const end_gsi = elf.globalByName(.{
8253 .name = "__" ++ name ++ "_end",
8254 .version = null,
8255 }).?;
8256 try elf.setGlobalSymbolValue(start_gsi, .{
7316 .node = .wrap(shndx.get(elf).ni),8257 .node = .wrap(shndx.get(elf).ni),
7317 .value = shndx.vaddr(elf),8258 .value = shndx.vaddr(elf),
7318 .size = 0,8259 .size = 0,
7319 .type = .NOTYPE,8260 .type = .NOTYPE,
7320 .shndx = shndx.*,8261 .shndx = shndx.*,
7321 });8262 });
7322 elf.setGlobalSymbolValue(elf.globalByName("__" ++ name ++ "_end").?, .{8263 try elf.updateGlobalDynamic(start_gsi, true);
8264 try elf.setGlobalSymbolValue(end_gsi, .{
7323 .node = .wrap(shndx.get(elf).ni),8265 .node = .wrap(shndx.get(elf).ni),
7324 .value = shndx.vaddr(elf),8266 .value = shndx.vaddr(elf),
7325 .size = 0,8267 .size = 0,
7326 .type = .NOTYPE,8268 .type = .NOTYPE,
7327 .shndx = shndx.*,8269 .shndx = shndx.*,
7328 });8270 });
8271 try elf.updateGlobalDynamic(end_gsi, true);
7329}8272}
7330fn updateInitFiniArraySectionSize(8273fn updateInitFiniArraySectionSize(
7331 elf: *Elf,8274 elf: *Elf,
...@@ -7335,7 +8278,10 @@ fn updateInitFiniArraySectionSize(...@@ -7335,7 +8278,10 @@ fn updateInitFiniArraySectionSize(
7335 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {8278 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
7336 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),8279 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
7337 };8280 };
7338 const end_sym_gsi = elf.globalByName("__" ++ name ++ "_end").?;8281 const end_sym_gsi = elf.globalByName(.{
8282 .name = "__" ++ name ++ "_end",
8283 .version = null,
8284 }).?;
7339 Symbol.Id.global(end_sym_gsi).flushMoved(elf, end_vaddr);8285 Symbol.Id.global(end_sym_gsi).flushMoved(elf, end_vaddr);
7340}8286}
73418287
...@@ -7356,13 +8302,12 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -7356,13 +8302,12 @@ fn prelinkInner(elf: *Elf) Error!void {
7356 if (comp.config.use_llvm) break :self_hosted_codegen;8302 if (comp.config.use_llvm) break :self_hosted_codegen;
73578303
7358 // We're using self-hosted codegen---add an input representing the Zig "object".8304 // We're using self-hosted codegen---add an input representing the Zig "object".
7359 try elf.ensureUnusedSymbolCapacity(1, .all_local);
7360 try elf.inputs.ensureUnusedCapacity(gpa, 1);8305 try elf.inputs.ensureUnusedCapacity(gpa, 1);
7361 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});8306 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
7362 defer gpa.free(zcu_name);8307 defer gpa.free(zcu_name);
7363 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{8308 const zcu_file_symbol = try elf.addLocalSymbol(.{
7364 .node = .none,8309 .node = .none,
7365 .name = try elf.string(.strtab, zcu_name),8310 .name = zcu_name,
7366 .value = 0,8311 .value = 0,
7367 .size = 0,8312 .size = 0,
7368 .type = .FILE,8313 .type = .FILE,
...@@ -7653,8 +8598,10 @@ fn prepareDynamic(elf: *Elf) Error!void {...@@ -7653,8 +8598,10 @@ fn prepareDynamic(elf: *Elf) Error!void {
7653 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +8598 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
7654 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +8599 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
7655 @as(usize, @intFromBool(use_plt)) * 4 +8600 @as(usize, @intFromBool(use_plt)) * 4 +
8601 @as(usize, @intFromBool(elf.verneed.count() > 0)) * 2 +
8602 @as(usize, @intFromBool(elf.verdef.count() > 0)) * 2 +
7656 @intFromBool(comp.config.output_mode == .Exe) +8603 @intFromBool(comp.config.output_mode == .Exe) +
7657 @intFromBool(elf.textrel_count > 0) + 9;8604 @intFromBool(elf.textrel_count > 0) + 10;
76588605
7659 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();8606 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
76608607
...@@ -7753,8 +8700,25 @@ fn flushDynamic(elf: *Elf) void {...@@ -7753,8 +8700,25 @@ fn flushDynamic(elf: *Elf) void {
7753 };8700 };
7754 dynamic_index += 4;8701 dynamic_index += 4;
7755 }8702 }
8703 if (elf.verneed.count() > 0) {
8704 dynamic_entries[dynamic_index..][0..2].* = .{
8705 .{ std.elf.DT_VERNEED, @intCast(elf.shndx.gnu_version_r.vaddr(elf)) },
8706 .{ std.elf.DT_VERNEEDNUM, verneed_num: {
8707 const shdr = @field(elf.shdrPtr(elf.shndx.gnu_version_r), @tagName(class));
8708 break :verneed_num elf.targetLoad(&shdr.info);
8709 } },
8710 };
8711 dynamic_index += 2;
8712 }
8713 if (elf.verdef.count() > 0) {
8714 dynamic_entries[dynamic_index..][0..2].* = .{
8715 .{ std.elf.DT_VERDEF, @intCast(elf.shndx.gnu_version_d.vaddr(elf)) },
8716 .{ std.elf.DT_VERDEFNUM, @intCast(elf.verdef.count() + 1) },
8717 };
8718 dynamic_index += 2;
8719 }
77568720
7757 dynamic_entries[dynamic_index..][0..9].* = .{8721 dynamic_entries[dynamic_index..][0..10].* = .{
7758 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },8722 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
7759 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },8723 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
7760 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },8724 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
...@@ -7762,10 +8726,11 @@ fn flushDynamic(elf: *Elf) void {...@@ -7762,10 +8726,11 @@ fn flushDynamic(elf: *Elf) void {
7762 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },8726 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
7763 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },8727 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
7764 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },8728 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
8729 .{ std.elf.DT_VERSYM, @intCast(elf.shndx.gnu_version.vaddr(elf)) },
7765 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },8730 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },
7766 .{ std.elf.DT_NULL, 0 },8731 .{ std.elf.DT_NULL, 0 },
7767 };8732 };
7768 dynamic_index += 9;8733 dynamic_index += 10;
77698734
7770 assert(dynamic_index == dynamic_entries.len);8735 assert(dynamic_index == dynamic_entries.len);
7771 if (elf.targetEndian() != std.lang.Endian.native) for (dynamic_entries) |*dynamic_entry|8736 if (elf.targetEndian() != std.lang.Endian.native) for (dynamic_entries) |*dynamic_entry|
...@@ -7802,7 +8767,6 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -7802,7 +8767,6 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
7802 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,8767 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,
7803 else => false,8768 else => false,
7804 };8769 };
7805 if (want_symbol) try elf.ensureUnusedSymbolCapacity(1, .all_local);
78068770
7807 const shstrtab_entry = try elf.string(.shstrtab, opts.name);8771 const shstrtab_entry = try elf.string(.shstrtab, opts.name);
7808 const shndx: Section.Index, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {8772 const shndx: Section.Index, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {
...@@ -7849,9 +8813,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -7849,9 +8813,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
7849 });8813 });
7850 const addr = elf.computeNodeVAddr(ni);8814 const addr = elf.computeNodeVAddr(ni);
7851 elf.shdrs.appendAssumeCapacity(.{8815 elf.shdrs.appendAssumeCapacity(.{
7852 .lsi = if (want_symbol) elf.addLocalSymbolAssumeCapacity(.{8816 .lsi = if (want_symbol) try elf.addLocalSymbol(.{
7853 .node = ni.toOptional(),8817 .node = ni.toOptional(),
7854 .name = .empty,8818 .name = "",
7855 .value = addr,8819 .value = addr,
7856 .size = 0,8820 .size = 0,
7857 .type = .SECTION,8821 .type = .SECTION,
...@@ -8328,10 +9292,27 @@ fn addSymbolRelocAssumeCapacity(...@@ -8328,10 +9292,27 @@ fn addSymbolRelocAssumeCapacity(
8328 => false,9292 => false,
8329 },9293 },
8330 };9294 };
9295 if (try_copy_reloc) switch (target.unwrap()) {
9296 .local => {},
9297 .global => |gsi| if (!gsi.ptr(elf).status.want_static_value) {
9298 gsi.ptr(elf).status.want_static_value = true;
9299 try elf.updateGlobalDynamic(gsi, false);
9300 },
9301 };
83319302
8332 classify: switch (elf.classifySymbolValue(target)) {9303 classify: switch (elf.classifySymbolValue(target)) {
8333 .static => break :r .none,9304 .static => {
9305 switch (target.unwrap()) {
9306 .local => {},
9307 .global => |gsi| gsi.ptr(elf).status.any_static_target_relocs = true,
9308 }
9309 break :r .none;
9310 },
8334 .static_relative => {9311 .static_relative => {
9312 switch (target.unwrap()) {
9313 .local => {},
9314 .global => |gsi| gsi.ptr(elf).status.any_static_relative_target_relocs = true,
9315 }
8335 switch (@"type".target) {9316 switch (@"type".target) {
8336 // Only relocations which resolve to absolute addresses require runtime9317 // Only relocations which resolve to absolute addresses require runtime
8337 // `R_*_RELATIVE` relocations.9318 // `R_*_RELATIVE` relocations.
...@@ -8358,13 +9339,8 @@ fn addSymbolRelocAssumeCapacity(...@@ -8358,13 +9339,8 @@ fn addSymbolRelocAssumeCapacity(
8358 .addend = 0,9339 .addend = 0,
8359 }).toOptional();9340 }).toOptional();
8360 },9341 },
8361 .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) {9342 .dynamic => {
8362 switch (elf.classifySymbolValue(target)) {9343 target.unwrap().global.ptr(elf).status.any_dynamic_target_relocs = true;
8363 .static => continue :classify .static,
8364 .static_relative => continue :classify .static_relative,
8365 .dynamic => unreachable, // we just added a copy relocation
8366 }
8367 } else {
8368 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {9344 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
8369 // PLT relocations targeting dynamic symbols actually target that symbol's PLT9345 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
8370 // entry, so we should emit an `R_*_RELATIVE` relocation instead.9346 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
...@@ -8396,7 +9372,7 @@ fn addSymbolRelocAssumeCapacity(...@@ -8396,7 +9372,7 @@ fn addSymbolRelocAssumeCapacity(
8396 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{9372 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8397 .type = dynamic_reloc_type,9373 .type = dynamic_reloc_type,
8398 .offset = node_vaddr + offset,9374 .offset = node_vaddr + offset,
8399 .raw_sym_index = target.unwrap().global.ptr(elf).dynsym_index.unwrap().?,9375 .raw_sym_index = target.unwrap().global.dynsymIndex(elf).?,
8400 .addend = addend,9376 .addend = addend,
8401 }).toOptional();9377 }).toOptional();
8402 },9378 },
...@@ -8640,7 +9616,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8640,7 +9616,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8640 break :val .{ .signed = @bitCast(sym_value -% tls_size) };9616 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
8641 }9617 }
8642 switch (sym_id.unwrap()) {9618 switch (sym_id.unwrap()) {
8643 .global => |gsi| if (gsi.ptr(elf).dynsym_index.unwrap()) |dynsym_index| {9619 .global => |gsi| if (gsi.dynsymIndex(elf)) |dynsym_index| {
8644 // When there is a dynsym entry, just target that with no addend.9620 // When there is a dynsym entry, just target that with no addend.
8645 break :val .{ .reloc = .{9621 break :val .{ .reloc = .{
8646 .type = .tpOff(elf),9622 .type = .tpOff(elf),
...@@ -8668,7 +9644,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8668,7 +9644,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8668 } },9644 } },
8669 .dynamic => .{ .reloc = .{9645 .dynamic => .{ .reloc = .{
8670 .type = .globDat(elf),9646 .type = .globDat(elf),
8671 .dynsym_index = sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,9647 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
8672 .addend = 0,9648 .addend = 0,
8673 } },9649 } },
8674 },9650 },
...@@ -8677,7 +9653,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8677,7 +9653,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8677 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`9653 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
8678 .dynamic => .{ .reloc = .{9654 .dynamic => .{ .reloc = .{
8679 .type = .dtpOff(elf),9655 .type = .dtpOff(elf),
8680 .dynsym_index = sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,9656 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
8681 .addend = 0,9657 .addend = 0,
8682 } },9658 } },
8683 },9659 },
...@@ -8690,7 +9666,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8690,7 +9666,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
8690 .type = .dtpMod(elf),9666 .type = .dtpMod(elf),
8691 .dynsym_index = switch (elf.classifySymbolValue(sym)) {9667 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
8692 .static, .static_relative => 0,9668 .static, .static_relative => 0,
8693 .dynamic => sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,9669 .dynamic => sym.unwrap().global.dynsymIndex(elf).?,
8694 },9670 },
8695 .addend = 0,9671 .addend = 0,
8696 } },9672 } },
...@@ -8754,7 +9730,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -8754,7 +9730,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
87549730
8755/// If `node` cannot contain runtime relocations, returns `.no`.9731/// If `node` cannot contain runtime relocations, returns `.no`.
8756///9732///
8757/// If `node` can contain runtime relocations, `returns `.yes_textrel` if such a relocation requires9733/// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires
8758/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.9734/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
8759fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {9735fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
8760 const shndx = elf.getNodeShndx(node);9736 const shndx = elf.getNodeShndx(node);
...@@ -8766,82 +9742,6 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye...@@ -8766,82 +9742,6 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
8766 return .yes;9742 return .yes;
8767}9743}
87689744
8769/// If the given undefined global could have a copy relocation, creates that relocation if it does
8770/// not already exist, and returns `true`.
8771///
8772/// Returns `false` iff a copy relocation cannot currently be created for the global. If it may be
8773/// possible in future, the symbol is added to `elf.want_copied_globals` so that the copy relocation
8774/// will be created if and when we discover a suitable definition in an input DSO.
8775///
8776/// If this function creates a new copy relocation, it will also update relocations targeting the
8777/// global where needed---the caller does not need to do this.
8778///
8779/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `gsi` refers to an *undefined* global.
8780fn maybeAddCopyRelocation(elf: *Elf, gsi: Symbol.Global.Index) Error!bool {
8781 assert(elf.shndx.dynamic != .UNDEF);
8782
8783 // Only dynamic executables may contain `R_*_COPY` relocations.
8784 if (elf.base.comp.config.output_mode != .Exe) return false;
8785
8786 const gpa = elf.base.comp.gpa;
8787
8788 assert(!Symbol.Id.global(gsi).defined(elf));
8789
8790 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;
8791
8792 const dso_global = elf.dso_globals.get(Symbol.Id.global(gsi).name(elf)) orelse {
8793 // We do not have a definition to provide the correct size for the symbol. If a definition
8794 // is discovered in a later DSO, we may at that point be able to add a copy relocation.
8795 try elf.want_copied_globals.put(gpa, gsi, {});
8796 return false;
8797 };
8798
8799 if (dso_global.type != .OBJECT) return false;
8800
8801 const gop = try elf.copied_globals.getOrPut(gpa, gsi);
8802 if (gop.found_existing) return true;
8803 errdefer assert(elf.copied_globals.pop().?.key == gsi);
8804
8805 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
8806
8807 try elf.nodes.ensureUnusedCapacity(gpa, 1);
8808 const node = elf.addNodeAssumeCapacity(
8809 try Section.Index.data.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
8810 .size = dso_global.alignment.forward(dso_global.size),
8811 .alignment = dso_global.alignment,
8812 }),
8813 .{ .copied_global = gsi },
8814 );
8815 errdefer comptime unreachable;
8816
8817 const vaddr = elf.computeNodeVAddr(node);
8818 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8819 .type = .copy(elf),
8820 .offset = vaddr,
8821 .raw_sym_index = dynsym_index,
8822 .addend = 0,
8823 });
8824 gop.value_ptr.* = .{
8825 .node = node,
8826 .rela_index = rela_index,
8827 };
8828
8829 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
8830 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
8831 }
8832 switch (elf.dynsymPtr(dynsym_index)) {
8833 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
8834 }
8835
8836 // Because we now have a copy relocation, any dynamic relocations which target this symbol are
8837 // now incorrect, since we now own the canonical address of the symbol. So delete those relocs
8838 // and then update the symbol's address (and re-apply relocations targeting it of course).
8839 Symbol.Id.global(gsi).deleteDynamicTargetRelocs(elf);
8840 Symbol.Id.global(gsi).flushMoved(elf, vaddr);
8841
8842 return true;
8843}
8844
8845pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {9745pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
8846 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {9746 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
8847 else => |e| return e,9747 else => |e| return e,
...@@ -9378,7 +10278,14 @@ fn flushInner(...@@ -9378,7 +10278,14 @@ fn flushInner(
937810278
9379 if (comp.config.output_mode == .Exe and elf.unknown_globals.count() > 0) {10279 if (comp.config.output_mode == .Exe and elf.unknown_globals.count() > 0) {
9380 for (elf.unknown_globals.keys()) |gsi| {10280 for (elf.unknown_globals.keys()) |gsi| {
9381 diags.addError("undefined global symbol '{s}'", .{Symbol.Id.global(gsi).name(elf).slice(elf)});10281 diags.addError("undefined global symbol '{s}'", .{gsi.rawName(elf).slice(elf)});
10282 }
10283 return error.AlreadyReported;
10284 }
10285
10286 if (elf.defined_alias_globals.count() > 0) {
10287 for (elf.defined_alias_globals.keys()) |gsi| {
10288 diags.addError("multiple definitions of '{s}'", .{gsi.name(elf)});
9382 }10289 }
9383 return error.AlreadyReported;10290 return error.AlreadyReported;
9384 }10291 }
...@@ -9429,7 +10336,10 @@ fn flushInner(...@@ -9429,7 +10336,10 @@ fn flushInner(
9429 .enabled => "_start",10336 .enabled => "_start",
9430 .named => |named| named,10337 .named => |named| named,
9431 };10338 };
9432 const gsi = elf.globalByName(sym_name) orelse break :entry 0;10339 const gsi = elf.globalByName(.{
10340 .name = sym_name,
10341 .version = null,
10342 }) orelse break :entry 0;
9433 break :entry Symbol.Id.global(gsi).value(elf);10343 break :entry Symbol.Id.global(gsi).value(elf);
9434 };10344 };
9435 switch (elf.ehdrPtr()) {10345 switch (elf.ehdrPtr()) {
...@@ -9501,7 +10411,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -9501,7 +10411,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
9501 if (elf.changed_symtab_index.pop()) |kv| {10411 if (elf.changed_symtab_index.pop()) |kv| {
9502 const gsi = kv.key;10412 const gsi = kv.key;
950310413
9504 const idle_prog_node = elf.mf.update_prog_node.start(Symbol.Id.global(gsi).name(elf).slice(elf), 0);10414 const idle_prog_node = elf.mf.update_prog_node.start(gsi.rawName(elf).slice(elf), 0);
9505 defer idle_prog_node.end();10415 defer idle_prog_node.end();
950610416
9507 const sym_id: Symbol.Id = .global(gsi);10417 const sym_id: Symbol.Id = .global(gsi);
...@@ -9531,14 +10441,14 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -9531,14 +10441,14 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
9531 // don't care about the dynsym index). The only exception is a copy reloc10441 // don't care about the dynsym index). The only exception is a copy reloc
9532 // could exist (and be the *reason* the symbol value is statically known).10442 // could exist (and be the *reason* the symbol value is statically known).
9533 if (elf.copied_globals.get(gsi)) |copied| {10443 if (elf.copied_globals.get(gsi)) |copied| {
9534 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;10444 const dynsym_index = gsi.dynsymIndex(elf).?;
9535 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, dynsym_index);10445 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, dynsym_index);
9536 }10446 }
9537 },10447 },
9538 .dynamic => {10448 .dynamic => {
9539 assert(!elf.copied_globals.contains(gsi)); // value would be statically known10449 assert(!elf.copied_globals.contains(gsi)); // value would be statically known
954010450
9541 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;10451 const dynsym_index = gsi.dynsymIndex(elf).?;
954210452
9543 // Update symbol relocs:10453 // Update symbol relocs:
9544 var ri = symtab_index.ptr(elf).first_target_reloc;10454 var ri = symtab_index.ptr(elf).first_target_reloc;
...@@ -9719,6 +10629,7 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {...@@ -9719,6 +10629,7 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
9719 .code_view => unreachable,10629 .code_view => unreachable,
9720 }10630 }
9721 try elf.genPendingDebug(gpa);10631 try elf.genPendingDebug(gpa);
10632 assert(elf.pending_uavs.items.len == 0); // no UAVs added by lazy code/data or by debug info
9722}10633}
9723fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {10634fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {
9724 while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) {10635 while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) {
...@@ -10099,7 +11010,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -10099,7 +11010,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
10099 .copied_global => |global_name| {11010 .copied_global => |global_name| {
10100 const copied_global = elf.copied_globals.getPtr(global_name) orelse {11011 const copied_global = elf.copied_globals.getPtr(global_name) orelse {
10101 // TODO: this node is orphaned, which is possible because `MappedFile` does not yet11012 // TODO: this node is orphaned, which is possible because `MappedFile` does not yet
10102 // support deleting nodes. See logic in `setGlobalSymbolValue`.11013 // support deleting nodes. See logic in `updateGlobalDynamic`.
10103 return;11014 return;
10104 };11015 };
10105 assert(copied_global.node == ni);11016 assert(copied_global.node == ni);
...@@ -10848,40 +11759,75 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -10848,40 +11759,75 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
10848 }11759 }
10849}11760}
1085011761
10851/// Asserts that `gsi` has a `.dynsym` entry.11762/// If `gsi` does not have a PLT entry, adds one and returns `true`. If `gsi` already has a PLT
10852fn addPltEntry(elf: *Elf, gsi: Symbol.Global.Index) void {11763/// entry, does nothing and returns `false`.
11764///
11765/// Asserts that `gsi` owns a `.dynsym` entry.
11766fn ensurePltEntry(elf: *Elf, gsi: Symbol.Global.Index) Error!bool {
10853 const target_endian = elf.targetEndian();11767 const target_endian = elf.targetEndian();
1085411768
11769 const maybe_dead_plt_index: ?u32 = dead_plt_index: {
11770 const old_plt_index = elf.plt.getIndex(gsi) orelse break :dead_plt_index null;
11771 if (!elf.pltEntryIsDead(old_plt_index)) return false;
11772 break :dead_plt_index @intCast(old_plt_index);
11773 };
11774
11775 const plt = elf.targetPltInfo();
11776
11777 const gpa = elf.base.comp.gpa;
11778 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, 1);
11779 try elf.plt.ensureUnusedCapacity(gpa, 1);
11780 try elf.shndx.plt.get(elf).ni.ensureMinimumSize(
11781 gpa,
11782 &elf.mf,
11783 plt.entry_size * (plt.header_entries + elf.plt.count() + 1),
11784 );
11785 if (plt.got_plt) |got_plt| try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(
11786 gpa,
11787 &elf.mf,
11788 elf.targetPtrSize() * (got_plt.header_entries + elf.plt.count() + 1),
11789 );
11790 if (plt.plt_sec) |plt_sec| try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(
11791 gpa,
11792 &elf.mf,
11793 plt_sec.entry_size * (elf.plt.count() + 1),
11794 );
11795
10855 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a11796 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
10856 // free-list for the PLT itself---see `pltEntryIsDead` for details.11797 // free-list for the PLT itself---see `pltEntryIsDead` for details.
10857 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{11798 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
10858 .type = .jumpSlot(elf),11799 .type = .jumpSlot(elf),
10859 .offset = 0, // populated later11800 .offset = 0, // populated later
10860 .raw_sym_index = gsi.ptr(elf).dynsym_index.unwrap().?,11801 .raw_sym_index = gsi.ownedDynsymIndex(elf).?,
10861 .addend = 0,11802 .addend = 0,
10862 }));11803 }));
1086311804
10864 // On architectures without `.got.plt` (e.g. SPARC) these values actually refer to `.plt`.11805 // On architectures without `.got.plt` (e.g. SPARC) these values actually refer to `.plt`.
10865 const got_plt_section: Section.Index, const got_plt_offset: u64 = got_plt: {11806 const got_plt_section: Section.Index, const got_plt_offset: u64 = if (plt.got_plt) |got_plt| .{
10866 const plt = elf.targetPltInfo();11807 elf.shndx.got_plt,
10867 break :got_plt if (plt.got_plt) |got_plt| .{11808 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
10868 elf.shndx.got_plt,11809 } else .{
10869 elf.targetPtrSize() * (got_plt.header_entries + plt_index),11810 elf.shndx.plt,
10870 } else .{11811 plt.entry_size * (plt.header_entries + plt_index),
10871 elf.shndx.plt,
10872 plt.entry_size * (plt.header_entries + plt_index),
10873 };
10874 };11812 };
1087511813
10876 // Now that we know the index, we can set the relocation's offset.11814 // Now that we know the index, we can set the relocation's offset.
10877 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);11815 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
1087811816
10879 if (plt_index < elf.plt.count()) {11817 if (plt_index < elf.plt.count()) {
10880 // We reused a free entry, so we're already done!11818 // We reused a free entry, so we're already done! However, if this global has a dead PLT
11819 // entry, then `gsi` is already a key in `elf.plt`, so we'll need to swap it out for some
11820 // other dead key.
11821 if (maybe_dead_plt_index) |dead_plt_index| {
11822 const dead_gsi = elf.plt.keys()[plt_index];
11823 elf.plt.setKey(dead_plt_index, dead_gsi);
11824 }
10881 elf.plt.setKey(plt_index, gsi);11825 elf.plt.setKey(plt_index, gsi);
10882 return;11826 return true;
10883 }11827 }
1088411828
11829 assert(maybe_dead_plt_index == null); // if there were a dead entry we would have reused it
11830
10885 // We added a new entry, so we now need to extend the PLT sections.11831 // We added a new entry, so we now need to extend the PLT sections.
10886 assert(plt_index == elf.plt.count());11832 assert(plt_index == elf.plt.count());
10887 elf.plt.putAssumeCapacityNoClobber(gsi, {});11833 elf.plt.putAssumeCapacityNoClobber(gsi, {});
...@@ -11043,6 +11989,8 @@ fn addPltEntry(elf: *Elf, gsi: Symbol.Global.Index) void {...@@ -11043,6 +11989,8 @@ fn addPltEntry(elf: *Elf, gsi: Symbol.Global.Index) void {
11043 }11989 }
11044 },11990 },
11045 }11991 }
11992
11993 return true;
11046}11994}
11047fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {11995fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
11048 const target_endian = elf.targetEndian();11996 const target_endian = elf.targetEndian();
...@@ -11206,6 +12154,7 @@ pub fn updateExports(...@@ -11206,6 +12154,7 @@ pub fn updateExports(
11206 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),12154 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
11207 };12155 };
11208 }12156 }
12157 try elf.genPending(pt);
11209}12158}
11210fn updateExportInner(12159fn updateExportInner(
11211 elf: *Elf,12160 elf: *Elf,
...@@ -11224,7 +12173,6 @@ fn updateExportInner(...@@ -11224,7 +12173,6 @@ fn updateExportInner(
11224 Value.fromInterned(uav).fmtValue(pt),12173 Value.fromInterned(uav).fmtValue(pt),
11225 }),12174 }),
11226 }12175 }
11227 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
11228 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {12176 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {
11229 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),12177 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),
11230 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),12178 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
...@@ -11243,7 +12191,7 @@ fn updateExportInner(...@@ -11243,7 +12191,7 @@ fn updateExportInner(
11243 };12191 };
1124412192
11245 const name = @"export".opts.name.toSlice(ip);12193 const name = @"export".opts.name.toSlice(ip);
11246 _ = elf.addGlobalSymbolAssumeCapacity(.{12194 _ = elf.addGlobalSymbol(.{
11247 .node = exported_lsi.index().ptr(elf).node,12195 .node = exported_lsi.index().ptr(elf).node,
11248 .name = name,12196 .name = name,
11249 .value = cur_value,12197 .value = cur_value,
...@@ -11267,7 +12215,11 @@ fn updateExportInner(...@@ -11267,7 +12215,11 @@ fn updateExportInner(
11267 // get these errors on every non-initial incremental update. Hack around that by12215 // get these errors on every non-initial incremental update. Hack around that by
11268 // only emitting this error if the symbol we're conflicting with comes from an input12216 // only emitting this error if the symbol we're conflicting with comes from an input
11269 // section (as opposed to the ZCU).12217 // section (as opposed to the ZCU).
11270 const conflicting_global = elf.globalByName(name).?;12218 const parsed = parseVersionedSymbolName(name);
12219 const conflicting_global = elf.globalByName(.{
12220 .name = parsed.name,
12221 .version = parsed.version,
12222 }).?;
11271 if (conflicting_global.ptr(elf).symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {12223 if (conflicting_global.ptr(elf).symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
11272 if (elf.getNode(conflicting_node) == .input_section) {12224 if (elf.getNode(conflicting_node) == .input_section) {
11273 return elf.base.comp.link_diags.fail(12225 return elf.base.comp.link_diags.fail(
...@@ -11277,6 +12229,11 @@ fn updateExportInner(...@@ -11277,6 +12229,11 @@ fn updateExportInner(
11277 }12229 }
11278 }12230 }
11279 },12231 },
12232 error.MultipleDefaultVersions => return elf.base.comp.link_diags.fail(
12233 "multiple default versions of '{s}'",
12234 .{parseVersionedSymbolName(name).name},
12235 ),
12236 error.UndefinedDefaultVersion => unreachable, // this is an export, so the symbol is defined
11280 else => |e| return e,12237 else => |e| return e,
11281 };12238 };
11282}12239}
...@@ -11342,7 +12299,7 @@ pub fn printNode(...@@ -11342,7 +12299,7 @@ pub fn printNode(
11342 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),12299 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
11343 });12300 });
11344 },12301 },
11345 .copied_global => |gsi| try w.print("(copy:{s})", .{Symbol.Id.global(gsi).name(elf).slice(elf)}),12302 .copied_global => |gsi| try w.print("(copy:{s})", .{gsi.rawName(elf).slice(elf)}),
11346 .nav => |nmi| {12303 .nav => |nmi| {
11347 const zcu = elf.base.comp.zcu.?;12304 const zcu = elf.base.comp.zcu.?;
11348 const ip = &zcu.intern_pool;12305 const ip = &zcu.intern_pool;
...@@ -11530,7 +12487,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato...@@ -11530,7 +12487,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato
11530fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {12487fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
11531 const index = switch (sym.unwrap()) {12488 const index = switch (sym.unwrap()) {
11532 .local => return null,12489 .local => return null,
11533 .global => |gsi| elf.plt.getIndex(gsi) orelse return null,12490 .global => |gsi| elf.plt.getIndex(gsi.resolveAlias(elf)) orelse return null,
11534 };12491 };
11535 if (elf.pltEntryIsDead(index)) return null;12492 if (elf.pltEntryIsDead(index)) return null;
11536 const plt = elf.targetPltInfo();12493 const plt = elf.targetPltInfo();
...@@ -11540,3 +12497,256 @@ fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {...@@ -11540,3 +12497,256 @@ fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
11540 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;12497 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;
11541 }12498 }
11542}12499}
12500
12501fn parseVersionedSymbolName(raw_name: []const u8) struct {
12502 name: []const u8,
12503 version: ?[]const u8,
12504 is_default_version: bool,
12505} {
12506 const split_index = std.mem.findScalar(u8, raw_name, '@') orelse {
12507 return .{ .name = raw_name, .version = null, .is_default_version = false };
12508 };
12509 if (std.mem.startsWith(u8, raw_name[split_index..], "@@")) {
12510 return .{
12511 .name = raw_name[0..split_index],
12512 .version = raw_name[split_index + 2 ..],
12513 .is_default_version = true,
12514 };
12515 } else {
12516 return .{
12517 .name = raw_name[0..split_index],
12518 .version = raw_name[split_index + 1 ..],
12519 .is_default_version = false,
12520 };
12521 }
12522}
12523
12524const VerdefAdapter = struct {
12525 elf: *Elf,
12526 pub fn eql(ctx: VerdefAdapter, lhs_name: String(.dynstr), _: void, rhs_index: usize) bool {
12527 const elf = ctx.elf;
12528 const rhs_entry = &elf.verdefSlice()[rhs_index + 1];
12529 const rhs_name: String(.dynstr) = @fromBackingInt(
12530 elf.targetLoad(&rhs_entry.aux.name),
12531 );
12532 return lhs_name == rhs_name;
12533 }
12534 pub fn hash(ctx: VerdefAdapter, name: String(.dynstr)) u32 {
12535 _ = ctx;
12536 return @truncate(std.hash.int(@backingInt(name)));
12537 }
12538};
12539
12540fn verdefId(elf: *Elf, version: String(.dynstr)) Error!u15 {
12541 const gpa = elf.base.comp.gpa;
12542 const adapter: VerdefAdapter = .{ .elf = elf };
12543 try elf.shndx.gnu_version_d.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, (elf.verdef.count() + 2) * @sizeOf(VerdefEntry));
12544 const gop = try elf.verdef.getOrPutAdapted(gpa, version, adapter);
12545
12546 if (gop.found_existing) {
12547 const verdef_ptr = &elf.verdefSlice()[gop.index + 1].def;
12548 return @intCast(@backingInt(elf.targetLoad(&verdef_ptr.ndx)));
12549 }
12550
12551 const version_id = std.math.cast(u15, elf.next_version_id) orelse {
12552 return elf.base.comp.link_diags.fail(
12553 "symbol version identifier '{d}' out of range",
12554 .{elf.next_version_id},
12555 );
12556 };
12557 elf.next_version_id += 1;
12558
12559 errdefer comptime unreachable;
12560
12561 switch (elf.shdrPtr(elf.shndx.gnu_version_d)) {
12562 inline else => |shdr| {
12563 const old_size = elf.targetLoad(&shdr.size);
12564 const old_info = elf.targetLoad(&shdr.info);
12565 assert(old_info == gop.index + 1);
12566 assert(old_size == old_info * @sizeOf(VerdefEntry));
12567 elf.targetStore(&shdr.info, old_info + 1);
12568 elf.targetStore(&shdr.size, old_size + @sizeOf(VerdefEntry));
12569 },
12570 }
12571 const entry_ptr = &elf.verdefSlice()[gop.index + 1];
12572 entry_ptr.* = .{
12573 .def = .{
12574 .version = 1,
12575 .flags = 0,
12576 .ndx = @fromBackingInt(version_id),
12577 .cnt = 1,
12578 .hash = std.elf.hash.calculate(version.slice(elf)),
12579 .aux = @offsetOf(VerdefEntry, "aux"),
12580 .next = @sizeOf(VerdefEntry),
12581 },
12582 .aux = .{
12583 .name = @backingInt(version),
12584 .next = 0,
12585 },
12586 };
12587 if (elf.targetEndian() != std.lang.Endian.native) {
12588 std.mem.byteSwapAllFields(VerdefEntry, entry_ptr);
12589 }
12590
12591 return version_id;
12592}
12593
12594const VerneedAdapter = struct {
12595 elf: *Elf,
12596 const Key = struct {
12597 file: String(.dynstr),
12598 /// If this is `null`, key refers to a `std.elf.Verneed` entry; otherwise it refers to a
12599 /// `std.elf.Vernaux` entry.
12600 version: ?String(.dynstr),
12601 };
12602 const StoredKey = struct {
12603 kind: enum(u8) { verneed, vernaux },
12604 /// If `kind == .verneed`, this is the index in `.gnu.version_r` of the last
12605 /// `std.elf.Vernaux` in this `std.elf.Verneed`.
12606 ///
12607 /// If `kind == .vernaux`, this is the index in `.gnu.version_r` of the `std.elf.Verneed`
12608 /// associated with this `std.elf.Vernaux`.
12609 last_or_verneed_index: u32,
12610 };
12611 pub fn eql(ctx: VerneedAdapter, lhs: Key, rhs_stored: StoredKey, rhs_index: usize) bool {
12612 const elf = ctx.elf;
12613
12614 const lhs_version = lhs.version orelse {
12615 if (rhs_stored.kind != .verneed) return false;
12616 const rhs_file: String(.dynstr) = @fromBackingInt(elf.targetLoad(
12617 &elf.verneedSlice()[rhs_index].verneed.file,
12618 ));
12619 return lhs.file == rhs_file;
12620 };
12621
12622 if (rhs_stored.kind != .vernaux) return false;
12623 const rhs_file: String(.dynstr) = @fromBackingInt(elf.targetLoad(
12624 &elf.verneedSlice()[rhs_stored.last_or_verneed_index].verneed.file,
12625 ));
12626 const rhs_version: String(.dynstr) = @fromBackingInt(elf.targetLoad(
12627 &elf.verneedSlice()[rhs_index].vernaux.name,
12628 ));
12629
12630 return lhs.file == rhs_file and lhs_version == rhs_version;
12631 }
12632 pub fn hash(ctx: VerneedAdapter, key: Key) u32 {
12633 _ = ctx;
12634 const packed_key: packed struct(u65) {
12635 file: String(.dynstr),
12636 have_version: bool,
12637 version: String(.dynstr),
12638 } = .{
12639 .file = key.file,
12640 .have_version = key.version != null,
12641 .version = key.version orelse .empty,
12642 };
12643 return @truncate(std.hash.int(@backingInt(packed_key)));
12644 }
12645};
12646
12647fn verneedId(elf: *Elf, file: String(.dynstr), version: String(.dynstr)) Error!u15 {
12648 const gpa = elf.base.comp.gpa;
12649 const adapter: VerneedAdapter = .{ .elf = elf };
12650 try elf.shndx.gnu_version_r.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, (elf.verneed.count() + 2) * @sizeOf(VerneedEntry));
12651
12652 if (elf.verneed.getIndexAdapted(@as(VerneedAdapter.Key, .{
12653 .file = file,
12654 .version = version,
12655 }), adapter)) |index| {
12656 const vernaux_ptr = &elf.verneedSlice()[index].vernaux;
12657 return @intCast(elf.targetLoad(&vernaux_ptr.other));
12658 }
12659
12660 const file_gop = try elf.verneed.getOrPutAdapted(gpa, @as(VerneedAdapter.Key, .{
12661 .file = file,
12662 .version = null,
12663 }), adapter);
12664
12665 const predicted_vernaux_index: u32 = @intCast(elf.verneed.count());
12666
12667 if (!file_gop.found_existing) {
12668 switch (elf.shdrPtr(elf.shndx.gnu_version_r)) {
12669 inline else => |shdr| {
12670 const old_size = elf.targetLoad(&shdr.size);
12671 const old_info = elf.targetLoad(&shdr.info);
12672 elf.targetStore(&shdr.info, old_info + 1);
12673 elf.targetStore(&shdr.size, old_size + @sizeOf(VerneedEntry));
12674 },
12675 }
12676 const new_verneed = &elf.verneedSlice()[file_gop.index].verneed;
12677 new_verneed.* = .{
12678 .version = 1,
12679 .cnt = 1,
12680 .file = @backingInt(file),
12681 .aux = @sizeOf(VerneedEntry),
12682 .next = 0,
12683 };
12684 if (elf.targetEndian() != std.lang.Endian.native) {
12685 std.mem.byteSwapAllFields(std.elf.Verneed, new_verneed);
12686 }
12687 file_gop.key_ptr.* = .{
12688 .kind = .verneed,
12689 .last_or_verneed_index = predicted_vernaux_index,
12690 };
12691
12692 if (file_gop.index != 0) {
12693 const prev_verneed = &elf.verneedSlice()[elf.last_verneed_file_index].verneed;
12694 const off = (file_gop.index - elf.last_verneed_file_index) * @sizeOf(VerneedEntry);
12695 assert(off > 0);
12696 elf.targetStore(&prev_verneed.next, @intCast(off));
12697 }
12698 elf.last_verneed_file_index = file_gop.index;
12699 } else {
12700 const verneed_ptr = &elf.verneedSlice()[file_gop.index].verneed;
12701 const old_cnt = elf.targetLoad(&verneed_ptr.cnt);
12702 assert(old_cnt > 0);
12703 elf.targetStore(&verneed_ptr.cnt, old_cnt + 1);
12704
12705 const last_vernaux_index = file_gop.key_ptr.last_or_verneed_index;
12706 const last_vernaux = &elf.verneedSlice()[last_vernaux_index].vernaux;
12707 assert(elf.targetLoad(&last_vernaux.next) == 0);
12708 const off = (predicted_vernaux_index - last_vernaux_index) * @sizeOf(VerneedEntry);
12709 assert(off > 0);
12710 elf.targetStore(&last_vernaux.next, @intCast(off));
12711 file_gop.key_ptr.last_or_verneed_index = predicted_vernaux_index;
12712 }
12713
12714 const version_gop = try elf.verneed.getOrPutAdapted(gpa, @as(VerneedAdapter.Key, .{
12715 .file = file,
12716 .version = version,
12717 }), adapter);
12718 assert(!version_gop.found_existing); // already checked `contains` earlier
12719 assert(version_gop.index == predicted_vernaux_index);
12720 switch (elf.shdrPtr(elf.shndx.gnu_version_r)) {
12721 inline else => |shdr| {
12722 const old_size = elf.targetLoad(&shdr.size);
12723 elf.targetStore(&shdr.size, old_size + @sizeOf(VerneedEntry));
12724 },
12725 }
12726 version_gop.key_ptr.* = .{
12727 .kind = .vernaux,
12728 .last_or_verneed_index = @intCast(file_gop.index),
12729 };
12730
12731 const version_id = std.math.cast(u15, elf.next_version_id) orelse {
12732 return elf.base.comp.link_diags.fail(
12733 "symbol version identifier '{d}' out of range",
12734 .{elf.next_version_id},
12735 );
12736 };
12737 elf.next_version_id += 1;
12738
12739 const new_vernaux = &elf.verneedSlice()[version_gop.index].vernaux;
12740 new_vernaux.* = .{
12741 .hash = std.elf.hash.calculate(version.slice(elf)),
12742 .flags = 0,
12743 .other = version_id,
12744 .name = @backingInt(version),
12745 .next = 0,
12746 };
12747 if (elf.targetEndian() != std.lang.Endian.native) {
12748 std.mem.byteSwapAllFields(std.elf.Vernaux, new_vernaux);
12749 }
12750
12751 return version_id;
12752}