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 {
15651565 name: Word,
15661566 next: Word,
15671567};
1568/// Deprecated, use `std.elf.Verneed`
15681569pub const Elf32_Verneed = extern struct {
15691570 vn_version: Half,
15701571 vn_cnt: Half,
......@@ -1572,6 +1573,7 @@ pub const Elf32_Verneed = extern struct {
15721573 vn_aux: Word,
15731574 vn_next: Word,
15741575};
1576/// Deprecated, use `std.elf.Verneed`
15751577pub const Elf64_Verneed = extern struct {
15761578 vn_version: Half,
15771579 vn_cnt: Half,
......@@ -1579,6 +1581,13 @@ pub const Elf64_Verneed = extern struct {
15791581 vn_aux: Word,
15801582 vn_next: Word,
15811583};
1584pub const Verneed = extern struct {
1585 version: Half,
1586 cnt: Half,
1587 file: Word,
1588 aux: Word,
1589 next: Word,
1590};
15821591pub const Vernaux = extern struct {
15831592 hash: Word,
15841593 flags: Half,
src/link/Elf2.zig+2111-901
......@@ -52,6 +52,9 @@ shndx: struct {
5252 tdata: Section.Index,
5353 rela_dyn: Section.Index,
5454 rela_plt: Section.Index,
55 gnu_version: Section.Index,
56 gnu_version_d: Section.Index,
57 gnu_version_r: Section.Index,
5558 debug_abbrev: Section.Index,
5659 debug_addr: Section.Index,
5760 eh_frame_hdr: Section.Index,
......@@ -76,52 +79,62 @@ dynamic: struct {
7679},
7780symtab: std.ArrayList(Symbol),
7881globals: 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`.
8083globals_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),
8186/// Set of all strong undef globals which we have also not yet seen defined in any input DSO. We
8287/// maintain this set to allow efficient reporting of "undefined global symbol" errors.
8388unknown_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),
8492/// Key is an undef global for which we have created a "copy relocation" (`R_*_COPY`).
8593copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, struct {
8694 node: MappedFile.Node.Index,
8795 /// The index of this global's runtime relocation in `.rela.dyn`.
8896 rela_index: Section.RelaIndex,
8997}),
90/// Key is an undef global for which we would *like* to create a copy relocation (`R_*_COPY`), but
91/// cannot because we have not seen an appropriate definition in a linked DSO yet.
98/// When rearranging entries in the dynamic symbol table, we need to map a dynamic symbol index to
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.
92104///
93/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
94/// will remove it from this map and call `maybeAddCopyRelocation`.
95want_copied_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
105/// Key is index into `.dynsym`.
106versioned_dynsym_owners: std.array_hash_map.Auto(u32, Symbol.Global.Index),
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,
96129/// Key is a node which is a valid `Symbol.node` value, value is the first global symbol in that
97130/// node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
98131///
99132/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
100133/// because the vast majority of nodes which can export global symbols actually will not.
101134node_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:
103///
104/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has
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}),
135/// See the definition of `DsoGlobals`.
136dso_globals: DsoGlobals,
137
125138shstrtab: StringTable,
126139strtab: StringTable,
127140dynstr: StringTable,
......@@ -276,7 +289,7 @@ const Node = union(enum) {
276289 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
277290 ///
278291 /// 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`.
280293 copied_global: Symbol.Global.Index,
281294 /// May contain relocations.
282295 nav: NavMapIndex,
......@@ -1725,9 +1738,7 @@ const SymbolReloc = struct {
17251738 break :type reloc.target.value(elf) +% addend -% tls_size;
17261739 },
17271740 },
1728 .size => switch (elf.symPtr(reloc.target.index(elf))) {
1729 inline else => |sym| elf.targetLoad(&sym.size),
1730 },
1741 .size => reloc.target.size(elf),
17311742 .special => return reloc.type.action.special.applyInner(
17321743 elf,
17331744 reloc.target,
......@@ -2178,52 +2189,152 @@ const GotReloc = struct {
21782189 }
21792190};
21802191
2181fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {
2182 const gpa = elf.base.comp.gpa;
2192/// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version,
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 adding
2187 // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals
2188 // around to keep `.dynsym` compact. Either way, the maximum is N.
2189 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len);
2220 /// Contains all default-version global symbols defined in any needed DSO.
2221 ///
2222 /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`.
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 {
2192 // Ensure the symtab section's node is big enough
2193 const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {
2194 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),
2227 /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because
2228 /// these string bytes are in a `std.ArrayList` rather than a section of the output binary.
2229 const String = enum(u32) {
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 }
21952247 };
2196 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_node_size);
2197 }
2248 };
21982249
2199 switch (kind) {
2200 .all_local => {},
2201 .maybe_global => {
2202 try elf.globals.ensureUnusedCapacity(gpa, len);
2203 try elf.globals_by_name.ensureUnusedCapacity(gpa, len);
2204 try elf.unknown_globals.ensureUnusedCapacity(gpa, len);
2250 const Symbol = struct {
2251 name: DsoGlobals.String,
2252 /// `.none` if the symbol is unversioned.
2253 version: DsoGlobals.String.Optional,
2254 type: std.elf.STT,
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) {
2209 const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2210 inline else => |shdr, class| .{
2211 elf.targetLoad(&shdr.size),
2212 @sizeOf(class.ElfN().Sym),
2213 },
2214 };
2215 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));
2286 pub fn hash(ctx: Adapter, key: Key) u32 {
2287 _ = ctx;
2288 var h: std.hash.Wyhash = .init(key.name.len);
2289 h.update(key.name);
2290 if (key.version) |version| {
2291 h.update(&.{1});
2292 h.update(version);
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;
2218 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, dynsym_need_size);
2300 const DefaultVersionAdapter = struct {
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);
2223 }
2224 },
2326 fn findDefaultVersion(dso_globals: *const DsoGlobals, name: []const u8) ?*const DsoGlobals.Symbol {
2327 const adapter: DsoGlobals.Symbol.DefaultVersionAdapter = .{
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 }
22252336 }
2226}
2337};
22272338
22282339fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
22292340 const gpa = elf.base.comp.gpa;
......@@ -2391,34 +2502,6 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
23912502 }
23922503}
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}
24222505/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
24232506/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
24242507fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
......@@ -2441,13 +2524,23 @@ fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
24412524
24422525const AddLocalSymbolOptions = struct {
24432526 node: MappedFile.Node.Index.Optional,
2444 name: String(.strtab),
2527 name: []const u8,
24452528 value: u64,
24462529 size: u64,
24472530 type: std.elf.STT,
24482531 shndx: Section.Index,
24492532};
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
24512544 switch (elf.shdrPtr(.symtab)) {
24522545 inline else => |shdr, class| {
24532546 const ent_size = @sizeOf(class.ElfN().Sym);
......@@ -2476,8 +2569,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
24762569 // ...then the `elf.symtab` metadata...
24772570 new_index.ptr(elf).* = target_index.ptr(elf).*;
24782571 // ...then update the `elf.globals` tracking.
2479 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));
2480 const gsi = elf.globalByName(global_name.slice(elf)).?;
2572 const gsi = elf.globalBySym(new_index);
24812573 gsi.ptr(elf).symtab_index = new_index;
24822574
24832575 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
24922584 };
24932585
24942586 target_sym.* = .{
2495 .name = @backingInt(opts.name),
2587 .name = @backingInt(name),
24962588 .value = @intCast(opts.value),
24972589 .size = @intCast(opts.size),
24982590 .info = .{ .type = opts.type, .bind = .LOCAL },
......@@ -2508,77 +2600,105 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
25082600 }
25092601}
25102602
2511fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
2512 node: MappedFile.Node.Index.Optional,
2513 name: []const u8,
2514 lib_name: ?[]const u8 = null,
2515 value: u64,
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 {
2603fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2604 MultipleDefinitions,
2605 MultipleDefaultVersions,
2606 UndefinedDefaultVersion,
2607} || Error)!Symbol.Global.Index {
25222608 _ = opts.lib_name; // TODO
25232609
2524 const adapter: Symbol.Global.NameAdapter = .{ .elf = elf };
2525 const gop = elf.globals_by_name.getOrPutAssumeCapacityAdapted(opts.name, adapter);
2610 const gpa = elf.base.comp.gpa;
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 }));
25262633 const gsi: Symbol.Global.Index = @fromBackingInt(@intCast(gop.index));
25272634
25282635 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
25292661 // There's already a symbol with this name, we just need to potentially set its value, and
25302662 // to update its bind and visibility.
2531 const new_bind: Symbol.Global.Bind = new_bind: {
2532 if (opts.shndx == .UNDEF) {
2533 // We are not defining the symbol, so we won't set its value and we should only
2534 // update its bind if it is undefined and we won't weaken it.
2535 if (!Symbol.Id.global(gsi).defined(elf) and
2536 gsi.bind(elf) == .weak and
2537 opts.bind == .strong)
2538 {
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 }
2663 if (opts.shndx == .UNDEF) {
2664 // We are not defining the symbol, so we won't set its value and we should only
2665 // update its bind if it is undefined and we won't weaken it.
2666 if (!gsi.defined(elf) and
2667 gsi.ptr(elf).status.bind == .weak and
2668 opts.bind == .strong)
2669 {
2670 elf.setGlobalSymbolBind(gsi, .strong);
25482671 }
2549 // We're providing a definition. If there is no other definition yet, that's easy, but
2550 // if there is, we need to figure out which to keep.
2551 if (!Symbol.Id.global(gsi).defined(elf)) {
2552 _ = elf.unknown_globals.swapRemove(gsi); // previously undef so might be here
2553 elf.setGlobalSymbolValue(gsi, .{
2554 .node = opts.node,
2555 .value = opts.value,
2556 .size = opts.size,
2557 .type = opts.type,
2558 .shndx = opts.shndx,
2559 });
2560 break :new_bind opts.bind;
2561 }
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 },
2672 } else if (!gsi.defined(elf)) {
2673 elf.setGlobalSymbolBind(gsi, opts.bind);
2674 try elf.setGlobalSymbolValue(gsi, .{
2675 .node = opts.node,
2676 .value = opts.value,
2677 .size = opts.size,
2678 .type = opts.type,
2679 .shndx = opts.shndx,
2680 });
2681 force_apply_relocs = true;
2682 } else switch (opts.bind) {
2683 .strong => switch (gsi.ptr(elf).status.bind) {
2684 .strong => return error.MultipleDefinitions,
25762685 .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;
25782695 },
2579 }
2580 };
2581 elf.mergeGlobalSymbolVisibility(gsi, opts.visibility, new_bind);
2696 },
2697 .weak => {},
2698 }
2699
2700 try elf.updateGlobalDynamic(gsi, force_apply_relocs);
2701
25822702 return gsi;
25832703 }
25842704
......@@ -2599,13 +2719,6 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
25992719 .weak => .WEAK,
26002720 };
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
26092722 const sym_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));
26102723 elf.symtab.appendAssumeCapacity(.{
26112724 .node = opts.node,
......@@ -2624,7 +2737,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
26242737 .name = @backingInt(strtab_name),
26252738 .value = @intCast(opts.value),
26262739 .size = @intCast(opts.size),
2627 .info = .{ .type = @"type", .bind = bind },
2740 .info = .{ .type = opts.type, .bind = bind },
26282741 .other = .{ .visibility = opts.visibility },
26292742 .shndx = opts.shndx.toSection().?,
26302743 };
......@@ -2646,43 +2759,16 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
26462759 };
26472760
26482761 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,
26492771 .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 },
26862772 .prev_in_node = .none,
26872773 .next_in_node = old_head,
26882774 };
......@@ -2694,281 +2780,601 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: struct {
26942780 old_head_ptr.prev_in_node = .wrap(gsi);
26952781 }
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
27042783 if (force_local_bind) {
27052784 elf.moveDemotedGlobal(gsi);
27062785 }
27072786
2708 switch (@"type") {
2709 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and
2710 elf.classifySymbolValue(.global(gsi)) == .dynamic)
2711 {
2712 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
2713 elf.addPltEntry(gsi);
2714 },
2715 else => {},
2787 if (name.version != null and name.is_default_version) {
2788 const default_gop = try elf.default_version_globals.getOrPutAdapted(
2789 elf.base.comp.gpa,
2790 name.name,
2791 @as(Symbol.Global.DefaultVersionAdapter, .{ .elf = elf }),
2792 );
2793 if (!default_gop.found_existing) {
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 }
27162799 }
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
27182807 return gsi;
27192808}
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);
2730 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
2731 if (global_ptr.next_in_node.unwrap()) |next_gsi| {
2732 assert(next_gsi.ptr(elf).prev_in_node == Symbol.Global.Index.Optional.wrap(gsi));
2733 next_gsi.ptr(elf).prev_in_node = global_ptr.prev_in_node;
2734 }
2735 if (global_ptr.prev_in_node.unwrap()) |prev_gsi| {
2736 assert(prev_gsi.ptr(elf).next_in_node == Symbol.Global.Index.Optional.wrap(gsi));
2737 prev_gsi.ptr(elf).next_in_node = global_ptr.next_in_node;
2810/// Updates the dynsym index associated with the given global symbol; populates that dynsym entry if
2811/// necessary; removes unnecessary dynamic relocations; creates/removes PLT entries as needed; and
2812/// creates/removes copy relocations as needed. Put simply, updates all state related to dynamic
2813/// linking for the given symbol.
2814///
2815/// As well as on creation, this function should be called whenever any of the following properties
2816/// of a global symbol changes:
2817///
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, {});
27382850 } else {
2739 // We're the start of the linked list, so we need to change the head.
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 }
2851 _ = elf.defined_alias_globals.swapRemove(alias_gsi);
27452852 }
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| {
2752 // This is a quite rare case: there was a definition for this symbol in a shared library
2753 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*
2754 // definition which replaces it. We know that our definition cannot be preempted because we
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);
2854 _ = elf.unknown_globals.swapRemove(alias_gsi);
2855 if (try elf.deleteOwnedDynsym(alias_gsi)) {
2856 apply_relocs = true;
2857 }
27662858 }
27672859
2768 global_ptr.symtab_index.ptr(elf).node = new.node;
2769
2770 const old_head: Symbol.Global.Index.Optional = old_head: {
2771 const new_node = new.node.unwrap() orelse break :old_head .none;
2772 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
2773 const old_head: Symbol.Global.Index.Optional = if (gop.found_existing) .wrap(gop.value_ptr.*) else .none;
2774 gop.value_ptr.* = gsi;
2775 break :old_head old_head;
2860 const maybe_dso_global: ?*const DsoGlobals.Symbol = dso_global: {
2861 if (gsi.defined(elf)) {
2862 break :dso_global null;
2863 }
2864 if (elf.dso_globals.find(gsi.name(elf), gsi.version(elf))) |dso_global| {
2865 break :dso_global dso_global;
2866 }
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;
27762873 };
27772874
2778 global_ptr.prev_in_node = .none;
2779 global_ptr.next_in_node = old_head;
2780
2781 if (old_head.unwrap()) |old_head_gsi| {
2782 assert(old_head_gsi.ptr(elf).prev_in_node == .none);
2783 old_head_gsi.ptr(elf).prev_in_node = .wrap(gsi);
2784 }
2875 const visibility: std.elf.STV, const sym_type: std.elf.STT = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
2876 inline else => |sym| info: {
2877 // If the symbol is undefined with type `STT_NOTYPE`, but there is a type available in
2878 // an input DSO, then use that symbol type!
2879 if (maybe_dso_global) |dso_global| {
2880 if (elf.targetLoad(&sym.shndx) == std.elf.SHN_UNDEF and
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.
2787 switch (elf.symPtr(global_ptr.symtab_index)) {
2788 inline else => |sym| {
2789 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
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 });
2891 break :info .{
2892 elf.targetLoad(&sym.other).visibility,
2893 elf.targetLoad(&sym.info).type,
2894 };
27972895 },
2798 }
2896 };
27992897
2800 // ...and also the dynsym entry if there is one.
2801 if (global_ptr.dynsym_index.unwrap()) |dynsym_index| switch (elf.dynsymPtr(dynsym_index)) {
2802 inline else => |sym| {
2803 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
2804 elf.targetStore(&sym.size, @intCast(new.size));
2805 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
2806 const old_bind = elf.targetLoad(&sym.info).bind;
2807 elf.targetStore(&sym.info, .{
2808 .type = new.type,
2809 .bind = old_bind,
2810 });
2811 },
2898 const omit_dynsym: bool = omit: {
2899 if (elf.shndx.dynsym == .UNDEF) break :omit true;
2900 break :omit switch (visibility) {
2901 .HIDDEN, .INTERNAL => true,
2902 .DEFAULT, .PROTECTED => false,
2903 };
28122904 };
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 to
2815 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
2816 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
2817 if (elf.plt.getIndex(gsi)) |plt_index| {
2818 if (!elf.pltEntryIsDead(plt_index) and
2819 elf.classifySymbolValue(.global(gsi)) != .dynamic)
2920 if (!gsi.defined(elf) and
2921 gsi.ptr(elf).status.bind == .strong and
2922 maybe_dso_global == null)
28202923 {
2821 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
2822 assert(elf.pltEntryIsDead(plt_index));
2924 try elf.unknown_globals.put(gpa, gsi, {});
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);
28232933 }
2824 }
28252934
2826 // If this symbol was previously undefined, relocations targeting it may have been lowered to
2827 // runtime relocations which we have now discovered we do not need, so delete those. This does
2828 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.
2829 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(gsi)) != .dynamic) {
2830 Symbol.Id.global(gsi).deleteDynamicTargetRelocs(elf);
2935 if (apply_relocs) {
2936 Symbol.Id.global(gsi).applyTargetRelocs(elf);
2937 if (maybe_alias_gsi) |alias_gsi| {
2938 Symbol.Id.global(alias_gsi).applyTargetRelocs(elf);
2939 }
2940 }
2941 return;
28312942 }
28322943
2833 // Finally, update the symbol value, re-applying target relocations. Also note that because we
2834 // possibly removed the PLT entry above, some relocations which were previously targeting the
2835 // PLT will now instead target the symbol itself.
2836 Symbol.Id.global(gsi).flushMoved(elf, new.value);
2837}
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);
2944 // We need an owned dynsym entry. We can either reuse an existing one or create a new one.
2945 const new_dynsym_index: u32 = owned_dynsym_index: {
2946 if (gsi.ptr(elf).status.owns_dynsym) {
2947 break :owned_dynsym_index gsi.ptr(elf).dynsym_index;
2948 }
28482949
2849 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {
2850 inline else => |sym| elf.targetLoad(&sym.other).visibility,
2851 };
2950 // We're going to add a new entry to dynsym; ensure there's space in the relevant sections.
2951 const dynsym_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
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 whichever
2854 // is "stricter", with most strict being INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
2959 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_dynsym_len * dynsym_ent_size);
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` or
2857 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to
2858 // a relocatable), then the symbol should have binding `STB_LOCAL` in the output. Therefore, if
2859 // we are putting the global in this state for the first time---let's call it "demoting" the
2860 // global to `STB_LOCAL`---we'll need to update its bind in the symtab.
2861 const Demote = enum { no, yes_already, yes_new };
2862 const new_visibility: std.elf.STV, const demote_if_module: Demote = switch (old_visibility) {
2863 .INTERNAL => .{ .INTERNAL, .yes_already },
2864 .HIDDEN => switch (other_visibility) {
2865 .INTERNAL => .{ .INTERNAL, .yes_already },
2866 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, .yes_already },
2867 },
2868 .PROTECTED => switch (other_visibility) {
2869 .INTERNAL => .{ .INTERNAL, .yes_new },
2870 .HIDDEN => .{ .HIDDEN, .yes_new },
2871 .PROTECTED, .DEFAULT => .{ .PROTECTED, .no },
2872 },
2873 .DEFAULT => switch (other_visibility) {
2874 .INTERNAL => .{ .INTERNAL, .yes_new },
2875 .HIDDEN => .{ .HIDDEN, .yes_new },
2876 .PROTECTED => .{ .PROTECTED, .no },
2877 .DEFAULT => .{ .DEFAULT, .no },
2878 },
2879 };
2880 const demote: Demote = switch (elf.ehdrType()) {
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 }
2963 const dynstr_name: String(.dynstr) = name: {
2964 // Explicitly reserve space first because that reservation could invalidate the name slice.
2965 try elf.ensureAdditionalStringCapacity(.dynstr, gsi.name(elf).len);
2966 break :name elf.stringAssumeCapacity(.dynstr, gsi.name(elf));
2967 };
2968
2969 const dynsym_index: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2970 inline else => |shdr, class| new_dynsym_index: {
2971 const Sym = class.ElfN().Sym;
2972
2973 // Increase the dynamic symbol table size...
2974 const old_size = elf.targetLoad(&shdr.size);
2975 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2976 const dynsym_index: u32 = @intCast(@divExact(old_size, @sizeOf(Sym)));
2977
2978 // ...and the same for the symbol version table.
2979 const versym_shdr = @field(elf.shdrPtr(elf.shndx.gnu_version), @tagName(class));
2980 assert(elf.targetLoad(&versym_shdr.size) == dynsym_index * 2);
2981 elf.targetStore(&versym_shdr.size, (dynsym_index + 1) * 2);
2982
2983 // Populate the symbol name (the other fields are set later).
2984 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2985 elf.targetStore(&sym.name, @backingInt(dynstr_name));
2986
2987 break :new_dynsym_index dynsym_index;
29092988 },
2910 },
2989 };
29112990
2912 .yes_already => {
2913 // Update the visibility in the symtab entry...
2914 switch (elf.symPtr(global_ptr.symtab_index)) {
2915 inline else => |sym| {
2916 assert(elf.targetLoad(&sym.info).bind == .LOCAL); // symbol should be demoted
2917 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2918 },
2991 elf.appendDynsymHashEntry(dynsym_index);
2992
2993 if (gsi.version(elf) != null) {
2994 try elf.versioned_dynsym_owners.putNoClobber(gpa, dynsym_index, gsi);
2995 }
2996
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;
29193028 }
2920 // ...and update the binding tracked in `global_ptr.dynsym_index`.
2921 switch (global_ptr.dynsym_index) {
2922 .demoted_strong, .demoted_weak => {},
2923 .no_section => unreachable, // `.demoted_strong` or `.demoted_weak` should take priority
2924 _ => unreachable, // demoted symbol should not have a dynsym index
3029
3030 // ...but if it's undefined, we need to consult the matching input DSO global.
3031 if (maybe_dso_global) |dso_global| {
3032 if (dso_global.version.unwrap()) |version| {
3033 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 }
29253039 }
2926 global_ptr.dynsym_index = switch (bind) {
2927 .strong => .demoted_strong,
2928 .weak => .demoted_weak,
3040
3041 break :versym .LOCAL;
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,
29293059 };
29303060 },
3061 }
29313062
2932 .yes_new => {
2933 // Update the binding and visibility in the symtab, but don't bother with dynsym because
2934 // we're going to delete the dynsym entry in a moment.
2935 switch (elf.symPtr(global_ptr.symtab_index)) {
2936 inline else => |sym| {
2937 const old_info = elf.targetLoad(&sym.info);
2938 assert(old_info.bind != .LOCAL); // symbol should not be already demoted
2939 elf.targetStore(&sym.info, .{ .type = old_info.type, .bind = .LOCAL });
2940 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2941 },
3063 const need_plt_entry: bool = plt: {
3064 switch (sym_type) {
3065 .FUNC, .GNU_IFUNC => {},
3066 else => {
3067 // Only functions can have PLT entries.
3068 break :plt false;
3069 },
3070 }
3071 if (visibility == .PROTECTED) {
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;
29423091 }
3092 }
3093 }
29433094
2944 // If there was a dynsym entry, it is no longer necessary.
2945 if (global_ptr.dynsym_index.unwrap()) |dynsym_index| {
2946 elf.deleteDynsym(dynsym_index);
2947 } else {
2948 assert(global_ptr.dynsym_index == .no_section); // symbol should not be already demoted
2949 }
3095 const want_copy_rel: bool = copy: {
3096 if (elf.base.comp.config.output_mode != .Exe) {
3097 // Only executables can contain copy relocations.
3098 break :copy false;
3099 }
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.
2952 global_ptr.dynsym_index = switch (bind) {
2953 .strong => .demoted_strong,
2954 .weak => .demoted_weak,
3129 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3130 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
3131 }
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,
29553162 };
29563163
2957 // If there was a copy relocation for this symbol, it is now unnecessary. See equivalent
2958 // logic in `setGlobalSymbolValue` for an explanation of this case.
2959 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
2960 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
2961 // TODO: once we can delete `MappedFile` nodes, delete `copied_global_kv.value.node`
3164 // We have added a copy relocation so we now own the canonical address of the symbol.
3165 switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3166 inline else => |sym| elf.targetStore(&sym.value, @intCast(vaddr)),
3167 }
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;
29623234 } else {
2963 _ = elf.want_copied_globals.swapRemove(gsi);
3235 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == gsi);
29643236 }
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 the
2967 // other `STB_LOCAL` symbols instead of the global symbols.
2968 elf.moveDemotedGlobal(gsi);
3261 // Now for the easy bit where we actually update the symtab entry.
3262 switch (elf.symPtr(global_ptr.symtab_index)) {
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 });
29693295 },
29703296 }
29713297}
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}
29723378/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
29733379/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
29743380/// 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 {
29953401 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
29963402 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));
2999 const other_gsi = elf.globalByName(other_name.slice(elf)).?;
3404 const other_gsi = elf.globalBySym(dest_index);
30003405 assert(other_gsi.ptr(elf).symtab_index == dest_index);
30013406
30023407 // First swap the symtab entries...
......@@ -3010,15 +3415,40 @@ fn moveDemotedGlobal(elf: *Elf, gsi: Symbol.Global.Index) void {
30103415 },
30113416 }
30123417}
3013/// Asserts that the `.dynsym` section exists, and removes the entry at index `dynsym_index`,
3014/// swapping another entry into its place to keep the section compact. Also updates the `.hash`
3015/// section, and updates `Symbol.Global.dynsym_index` for whichever global gets swapped into
3016/// `dynsym_index`. However, this does *not* in any way modify the `Symbol.Global` corresponding to
3017/// the old symbol at `dynsym_index`.
3418/// If `gsi` owns a dynsym entry, deletes that entry from the `.dynsym` section and marks `gsi` as
3419/// no longer owning it. All state depending on the dynamic symbol is also deleted. To keep the
3420/// dynamic symbol table compact, the last entry in the table is swapped into the place of the
3421/// removed one (all associated bookkeeping is handled by this function).
30183422///
3019/// Asserts that there is at least 1 unused capacity in `elf.changed_symtab_index`.
3020fn deleteDynsym(elf: *Elf, dynsym_index: u32) void {
3021 // To keep dynsym compact, we'll swap another symbol into `dynsym_index`.
3423/// Returns `true` if a PLT entry or copy relocation for the global symbol was removed, indicating
3424/// that the caller must at some point re-apply relocations targeting `gsi`.
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
30223452 switch (elf.shdrPtr(elf.shndx.dynsym)) {
30233453 inline else => |dynsym_shdr, class| {
30243454 const ent_size = @sizeOf(class.ElfN().Sym);
......@@ -3027,39 +3457,66 @@ fn deleteDynsym(elf: *Elf, dynsym_index: u32) void {
30273457 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
30283458 const old_size = elf.targetLoad(&dynsym_shdr.size);
30293459 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) {
30353467 // The demoted global wasn't the last entry, so move whatever entry we just
30363468 // truncated out of dynsym into its place.
30373469
3470 const moved_gsi = elf.globalByDynsym(pop_dynsym_index);
3471
30383472 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));
30413475 const dest_dynsym_ptr = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
30423476
3043 // MLUGG TODO: this depends on symbol names matching between dynsym and symtab
3044 const moved_name: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));
3045 const moved_gsi = elf.globalByName(moved_name.slice(elf)).?;
3477 const versym = elf.versymSlice();
30463478
30473479 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);
3050 moved_gsi.ptr(elf).dynsym_index = .wrap(dynsym_index);
3482 moved_gsi.ptr(elf).dynsym_index = @intCast(dynsym_index);
30513483
30523484 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
30543490 // Since that symbol's dynsym index has changed, we'll have to update any
30553491 // 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 }
30573508 }
30583509
30593510 // Now that we've given that symbol a new home, actually decrease the section size.
30603511 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);
30613516 },
30623517 }
3518
3519 return apply_relocs;
30633520}
30643521
30653522const Symbol = struct {
......@@ -3074,63 +3531,81 @@ const Symbol = struct {
30743531 first_target_reloc: SymbolReloc.Index,
30753532
30763533 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
30773557 /// The current index of the symtab entry for this global symbol.
30783558 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
31183560 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
31193561 next_in_node: Symbol.Global.Index.Optional,
31203562 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
31213563 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 {
31243601 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 {
31263603 const elf = ctx.elf;
3127 const rhs_gsi: Symbol.Global.Index = @fromBackingInt(@intCast(rhs_index));
3128 const rhs_slice = Symbol.Id.global(rhs_gsi).name(elf).slice(elf);
3129 return std.mem.eql(u8, lhs_slice, rhs_slice);
3604 return std.mem.eql(u8, lhs_name, rhs_gsi.name(elf));
31303605 }
3131 pub fn hash(ctx: NameAdapter, slice: []const u8) u32 {
3606 pub fn hash(ctx: DefaultVersionAdapter, name: []const u8) u32 {
31323607 _ = ctx;
3133 return std.array_hash_map.hashString(slice);
3608 return std.array_hash_map.hashString(name);
31343609 }
31353610 };
31363611
......@@ -3141,18 +3616,220 @@ const Symbol = struct {
31413616 return &elf.globals.items[@backingInt(gsi)];
31423617 }
31433618
3144 fn bind(gsi: Symbol.Global.Index, elf: *Elf) Symbol.Global.Bind {
3145 return switch (gsi.ptr(elf).dynsym_index) {
3146 .demoted_strong => .strong,
3147 .demoted_weak => .weak,
3148 .no_section, _ => switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3149 inline else => |sym| switch (elf.targetLoad(&sym.info).bind) {
3150 .GLOBAL => .strong,
3151 .WEAK => .weak,
3152 else => unreachable,
3153 },
3619 /// Does not traverse aliases: if 'foo' is an alias for 'foo@@version', then `false`
3620 /// will be returned for 'foo', even thought 'foo@@version' is defined.
3621 fn defined(gsi: Symbol.Global.Index, elf: *Elf) bool {
3622 return switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3623 inline else => |sym| elf.targetLoad(&sym.shndx) != std.elf.SHN_UNDEF,
3624 };
3625 }
3626
3627 fn rawName(gsi: Symbol.Global.Index, elf: *Elf) String(.strtab) {
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,
31543717 },
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;
31553734 };
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 }
31563833 }
31573834
31583835 const Optional = enum(u32) {
......@@ -3171,7 +3848,19 @@ const Symbol = struct {
31713848 };
31723849 };
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 };
31753864 };
31763865
31773866 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
......@@ -3245,52 +3934,67 @@ const Symbol = struct {
32453934 };
32463935 }
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
32483947 /// Returns the value of this symbol, or 0 if it is undefined. If the symbol is an undefined
32493948 /// global for which we have emitted a copy relocation, returns the virtual address of that
32503949 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.
32513950 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))) {
32533952 inline else => |sym| elf.targetLoad(&sym.value),
32543953 };
32553954 }
32563955
3257 fn name(s: Symbol.Id, elf: *Elf) String(.strtab) {
3258 return switch (elf.symPtr(s.index(elf))) {
3259 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
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,
3956 fn size(s: Symbol.Id, elf: *Elf) u64 {
3957 return switch (elf.symPtr(s.resolveAlias(elf).index(elf))) {
3958 inline else => |sym| elf.targetLoad(&sym.value),
32663959 };
32673960 }
32683961
32693962 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
3270 // Update the symbol value in `.symtab`
32713963 const sym_index = sym_id.index(elf);
32723964 switch (elf.symPtr(sym_index)) {
32733965 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
32743966 }
32753967
3276 // Update the symbol value in `.dynsym` if applicable
32773968 switch (sym_id.unwrap()) {
32783969 .local => {},
3279 .global => |gsi| {
3280 if (gsi.ptr(elf).dynsym_index.unwrap()) |dynsym_index| {
3281 switch (elf.dynsymPtr(dynsym_index)) {
3282 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3283 }
3970 .global => |gsi| if (gsi.ownedDynsymIndex(elf)) |dynsym_index| {
3971 switch (elf.dynsymPtr(dynsym_index)) {
3972 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
32843973 }
32853974 },
32863975 }
32873976
3288 // Re-apply relocations targeting this symbol
3977 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 {
32893988 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 }
32913996 }
32923997
3293 // Update GOT entries targeting this symbol
32943998 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
32953999 elf.updateGotEntry(got_index);
32964000 }
......@@ -3303,79 +4007,14 @@ const Symbol = struct {
33034007 }
33044008 }
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
33724010 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
33734011 /// some point due to a call to `flushMoved`.
33744012 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| {
33764015 return node.hasMoved(&elf.mf);
33774016 }
3378 switch (s.unwrap()) {
4017 switch (resolved.unwrap()) {
33794018 .local => {},
33804019 .global => |gsi| if (elf.copied_globals.getPtr(gsi)) |copied_global| {
33814020 return copied_global.node.hasMoved(&elf.mf);
......@@ -3386,11 +4025,34 @@ const Symbol = struct {
33864025 };
33874026};
33884027
3389fn globalByName(elf: *Elf, name: []const u8) ?Symbol.Global.Index {
3390 const adapter: Symbol.Global.NameAdapter = .{ .elf = elf };
4028fn globalByName(elf: *Elf, name: Symbol.Global.VersionedName) ?Symbol.Global.Index {
4029 const adapter: Symbol.Global.VersionedName.Adapter = .{ .elf = elf };
33914030 const index_raw = elf.globals_by_name.getIndexAdapted(name, adapter) orelse return null;
33924031 return @fromBackingInt(@intCast(index_raw));
33934032}
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
33954057fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
33964058 /// This symbol's value is guaranteed to equal `sym.value(elf)`.
......@@ -3415,14 +4077,16 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
34154077 return .static;
34164078 }
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))) {
34194083 inline else => |sym_ptr| .{
34204084 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
34214085 elf.targetLoad(&sym_ptr.other).visibility,
34224086 },
34234087 };
34244088
3425 switch (sym.unwrap()) {
4089 switch (resolved_sym.unwrap()) {
34264090 .local => {
34274091 assert(shndx != .UNDEF);
34284092 assert(visibility == .DEFAULT);
......@@ -3441,7 +4105,9 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
34414105 .UNDEF => switch (visibility) {
34424106 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
34434107 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`).
34454111 return .static;
34464112 } else .dynamic, // external symbol
34474113
......@@ -3516,7 +4182,6 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.Sy
35164182fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId {
35174183 const gpa = elf.base.comp.gpa;
35184184
3519 try elf.ensureUnusedSymbolCapacity(1, .all_local);
35204185 try elf.nodes.ensureUnusedCapacity(gpa, 1);
35214186 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
35374202 const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ lazy.kind, gop.index }) catch
35384203 unreachable;
35394204 gop.value_ptr.* = .{
3540 .lsi = elf.addLocalSymbolAssumeCapacity(.{
4205 .lsi = try elf.addLocalSymbol(.{
35414206 .node = .wrap(node),
3542 .name = try elf.string(.strtab, name),
4207 .name = name,
35434208 .value = 0,
35444209 .size = 0,
35454210 .type = sym_type,
......@@ -3568,8 +4233,7 @@ pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.Symb
35684233 }).toTypeErased();
35694234}
35704235fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3571 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
3572 const gsi = elf.addGlobalSymbolAssumeCapacity(.{
4236 const gsi = elf.addGlobalSymbol(.{
35734237 .node = .none,
35744238 .name = opts.name,
35754239 .lib_name = opts.lib_name,
......@@ -3590,6 +4254,11 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
35904254 .shndx = .UNDEF,
35914255 }) catch |err| switch (err) {
35924256 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 ),
35934262 else => |e| return e,
35944263 };
35954264 return .global(gsi);
......@@ -3739,8 +4408,21 @@ fn String(section: StringSection) type {
37394408 };
37404409}
37414410fn 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 }
37424415 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));
37444426}
37454427/// Like `string`, but asserts that the string is already in `section`.
37464428fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
......@@ -3784,35 +4466,41 @@ const StringTable = struct {
37844466 return st.map.getKeyAdapted(key, adapter).?;
37854467 }
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 {
37884481 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
37894482 // case the empty string.
37904483 if (key.len == 0) return 0;
37914484
3792 const gpa = elf.base.comp.gpa;
37934485 const ni = shndx.get(elf).ni;
37944486 const slice_const = ni.sliceConst(&elf.mf);
3795 const gop = try st.map.getOrPutContextAdapted(
3796 gpa,
4487 const gop = st.map.getOrPutAssumeCapacityAdapted(
37974488 key,
37984489 StringTable.Adapter{ .slice = slice_const },
3799 .{ .slice = slice_const },
38004490 );
38014491 if (gop.found_existing) return gop.key_ptr.*;
3802 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
3803 inline else => |shdr| {
3804 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
3805 const new_size: u32 = @intCast(old_size + key.len + 1);
3806 elf.targetStore(&shdr.size, new_size);
3807 break :size .{ old_size, new_size };
4492 const offset: u32 = switch (elf.shdrPtr(shndx)) {
4493 inline else => |shdr| offset: {
4494 const old_size = elf.targetLoad(&shdr.size);
4495 elf.targetStore(&shdr.size, @intCast(old_size + key.len + 1));
4496 break :offset @intCast(old_size);
38084497 },
38094498 };
3810 try ni.ensureMinimumSize(gpa, &elf.mf, new_size);
3811 const slice = ni.slice(&elf.mf)[old_size..];
4499 const slice = ni.slice(&elf.mf)[offset..];
38124500 @memcpy(slice[0..key.len], key);
38134501 slice[key.len] = 0;
3814 gop.key_ptr.* = old_size;
3815 return old_size;
4502 gop.key_ptr.* = offset;
4503 return offset;
38164504 }
38174505};
38184506
......@@ -3935,6 +4623,9 @@ fn create(
39354623 .tdata = .UNDEF,
39364624 .rela_dyn = .UNDEF,
39374625 .rela_plt = .UNDEF,
4626 .gnu_version = .UNDEF,
4627 .gnu_version_d = .UNDEF,
4628 .gnu_version_r = .UNDEF,
39384629 .debug_abbrev = .UNDEF,
39394630 .debug_addr = .UNDEF,
39404631 .eh_frame_hdr = .UNDEF,
......@@ -3959,11 +4650,23 @@ fn create(
39594650 .symtab = .empty,
39604651 .globals = .empty,
39614652 .globals_by_name = .empty,
4653 .default_version_globals = .empty,
39624654 .unknown_globals = .empty,
4655 .defined_alias_globals = .empty,
39634656 .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,
39654664 .node_global_symbols = .empty,
3966 .dso_globals = .empty,
4665 .dso_globals = .{
4666 .string_bytes = .empty,
4667 .symbols = .empty,
4668 .default_sym_vers = .empty,
4669 },
39674670 .shstrtab = .{ .map = .empty },
39684671 .strtab = .{ .map = .empty },
39694672 .dynstr = .{ .map = .empty },
......@@ -4035,11 +4738,17 @@ pub fn deinit(elf: *Elf) void {
40354738 elf.symtab.deinit(gpa);
40364739 elf.globals.deinit(gpa);
40374740 elf.globals_by_name.deinit(gpa);
4741 elf.default_version_globals.deinit(gpa);
40384742 elf.unknown_globals.deinit(gpa);
4743 elf.defined_alias_globals.deinit(gpa);
40394744 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);
40414748 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);
40434752 elf.shstrtab.map.deinit(gpa);
40444753 elf.strtab.map.deinit(gpa);
40454754 elf.dynstr.map.deinit(gpa);
......@@ -4132,6 +4841,9 @@ fn initHeaders(
41324841 shnum += 1; // .dynstr
41334842 shnum += 1; // .dynsym
41344843 shnum += 1; // .hash
4844 shnum += 1; // .gnu.version
4845 shnum += 1; // .gnu.version_d
4846 shnum += 1; // .gnu.version_r
41354847 shnum += 1; // .rela.dyn
41364848 shnum += 1; // .rela.plt
41374849 }
......@@ -4905,6 +5617,66 @@ fn initHeaders(
49055617 .addralign = addr_align,
49065618 .manual_size = true,
49075619 });
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
49085680 switch (elf.targetDynsymHashInfo()) {
49095681 inline else => |info| {
49105682 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{
......@@ -5091,9 +5863,8 @@ fn initHeaders(
50915863 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
50925864 // 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);
50955866 // Despite the name, `__dso_handle` is necessary even in static binaries.
5096 _ = elf.addGlobalSymbolAssumeCapacity(.{
5867 _ = elf.addGlobalSymbol(.{
50975868 .node = .wrap(Section.Index.text.get(elf).ni),
50985869 .name = "__dso_handle",
50995870 .value = Section.Index.text.vaddr(elf),
......@@ -5104,9 +5875,11 @@ fn initHeaders(
51045875 .shndx = .text,
51055876 }) catch |err| switch (err) {
51065877 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5878 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5879 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
51075880 else => |e| return e,
51085881 };
5109 _ = elf.addGlobalSymbolAssumeCapacity(.{
5882 _ = elf.addGlobalSymbol(.{
51105883 .node = .wrap(elf.shndx.plt.get(elf).ni),
51115884 .name = "_PROCEDURE_LINKAGE_TABLE_",
51125885 .value = elf.shndx.plt.vaddr(elf),
......@@ -5117,9 +5890,11 @@ fn initHeaders(
51175890 .shndx = elf.shndx.plt,
51185891 }) catch |err| switch (err) {
51195892 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5893 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5894 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
51205895 else => |e| return e,
51215896 };
5122 _ = elf.addGlobalSymbolAssumeCapacity(.{
5897 _ = elf.addGlobalSymbol(.{
51235898 .node = .wrap(elf.shndx.got.get(elf).ni),
51245899 .name = "_GLOBAL_OFFSET_TABLE_",
51255900 .value = switch (machine) {
......@@ -5142,9 +5917,11 @@ fn initHeaders(
51425917 .shndx = elf.shndx.got,
51435918 }) catch |err| switch (err) {
51445919 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5920 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5921 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
51455922 else => |e| return e,
51465923 };
5147 _ = elf.addGlobalSymbolAssumeCapacity(.{
5924 _ = elf.addGlobalSymbol(.{
51485925 .node = .none,
51495926 .name = "__init_array_start",
51505927 .value = 0,
......@@ -5155,9 +5932,11 @@ fn initHeaders(
51555932 .shndx = .ABS,
51565933 }) catch |err| switch (err) {
51575934 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5935 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5936 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
51585937 else => |e| return e,
51595938 };
5160 _ = elf.addGlobalSymbolAssumeCapacity(.{
5939 _ = elf.addGlobalSymbol(.{
51615940 .node = .none,
51625941 .name = "__init_array_end",
51635942 .value = 0,
......@@ -5168,9 +5947,11 @@ fn initHeaders(
51685947 .shndx = .ABS,
51695948 }) catch |err| switch (err) {
51705949 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5950 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5951 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
51715952 else => |e| return e,
51725953 };
5173 _ = elf.addGlobalSymbolAssumeCapacity(.{
5954 _ = elf.addGlobalSymbol(.{
51745955 .node = .none,
51755956 .name = "__fini_array_start",
51765957 .value = 0,
......@@ -5181,9 +5962,11 @@ fn initHeaders(
51815962 .shndx = .ABS,
51825963 }) catch |err| switch (err) {
51835964 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5965 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5966 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
51845967 else => |e| return e,
51855968 };
5186 _ = elf.addGlobalSymbolAssumeCapacity(.{
5969 _ = elf.addGlobalSymbol(.{
51875970 .node = .none,
51885971 .name = "__fini_array_end",
51895972 .value = 0,
......@@ -5194,9 +5977,11 @@ fn initHeaders(
51945977 .shndx = .ABS,
51955978 }) catch |err| switch (err) {
51965979 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5980 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5981 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
51975982 else => |e| return e,
51985983 };
5199 _ = elf.addGlobalSymbolAssumeCapacity(.{
5984 _ = elf.addGlobalSymbol(.{
52005985 .node = .none,
52015986 .name = "__preinit_array_start",
52025987 .value = 0,
......@@ -5207,9 +5992,11 @@ fn initHeaders(
52075992 .shndx = .ABS,
52085993 }) catch |err| switch (err) {
52095994 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5995 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
5996 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
52105997 else => |e| return e,
52115998 };
5212 _ = elf.addGlobalSymbolAssumeCapacity(.{
5999 _ = elf.addGlobalSymbol(.{
52136000 .node = .none,
52146001 .name = "__preinit_array_end",
52156002 .value = 0,
......@@ -5220,10 +6007,12 @@ fn initHeaders(
52206007 .shndx = .ABS,
52216008 }) catch |err| switch (err) {
52226009 error.MultipleDefinitions => unreachable, // no inputs are processed yet
6010 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
6011 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
52236012 else => |e| return e,
52246013 };
52256014 if (have_dynamic) {
5226 _ = elf.addGlobalSymbolAssumeCapacity(.{
6015 _ = elf.addGlobalSymbol(.{
52276016 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
52286017 .name = "_DYNAMIC",
52296018 .value = elf.shndx.dynamic.vaddr(elf),
......@@ -5234,6 +6023,8 @@ fn initHeaders(
52346023 .shndx = elf.shndx.dynamic,
52356024 }) catch |err| switch (err) {
52366025 error.MultipleDefinitions => unreachable, // no inputs are processed yet
6026 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
6027 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
52376028 else => |e| return e,
52386029 };
52396030 }
......@@ -6025,6 +6816,39 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
60256816 }
60266817}
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
60286852fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
60296853 const comp = elf.base.comp;
60306854 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
61466970 const ip = &zcu.intern_pool;
61476971 const nav = ip.getNav(nav_index);
61486972
6149 try elf.ensureUnusedSymbolCapacity(1, .all_local);
61506973 try elf.nodes.ensureUnusedCapacity(gpa, 1);
61516974 try elf.navs.ensureUnusedCapacity(gpa, 1);
61526975
......@@ -6210,9 +7033,9 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
62107033 .alignment = alignment,
62117034 }), .{ .nav = nmi });
62127035 nav_gop.value_ptr.* = .{
6213 .lsi = elf.addLocalSymbolAssumeCapacity(.{
7036 .lsi = try elf.addLocalSymbol(.{
62147037 .node = .wrap(node),
6215 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
7038 .name = nav.fqn.toSlice(ip),
62167039 .value = 0,
62177040 .size = 0,
62187041 .type = elf.navType(nav.resolved.?),
......@@ -6233,7 +7056,6 @@ fn uavMapIndex(
62337056 const gpa = elf.base.comp.gpa;
62347057 const zcu = elf.base.comp.zcu.?;
62357058
6236 try elf.ensureUnusedSymbolCapacity(1, .all_local);
62377059 try elf.nodes.ensureUnusedCapacity(gpa, 1);
62387060 try elf.uavs.ensureUnusedCapacity(gpa, 1);
62397061 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
......@@ -6256,9 +7078,9 @@ fn uavMapIndex(
62567078 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;
62577079 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;
62587080 uav_gop.value_ptr.* = .{
6259 .lsi = elf.addLocalSymbolAssumeCapacity(.{
7081 .lsi = try elf.addLocalSymbol(.{
62607082 .node = .wrap(node),
6261 .name = try elf.string(.strtab, name),
7083 .name = name,
62627084 .value = 0,
62637085 .size = 0,
62647086 .type = .OBJECT,
......@@ -6537,10 +7359,9 @@ fn loadObject(
65377359 }
65387360
65397361 elf.input_pending_index += 1;
6540 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6541 input.extra = .{ .file_symbol = elf.addLocalSymbolAssumeCapacity(.{
7362 input.extra = .{ .file_symbol = try elf.addLocalSymbol(.{
65427363 .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),
65447365 .value = 0,
65457366 .size = 0,
65467367 .type = .FILE,
......@@ -6768,7 +7589,6 @@ fn loadObject(
67687589 ), 1) catch continue;
67697590 symmap.clearRetainingCapacity();
67707591 try symmap.resize(gpa, symnum);
6771 try elf.ensureUnusedSymbolCapacity(symnum, .maybe_global);
67727592 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
67737593 for (symmap.items) |*si| {
67747594 si.* = .null;
......@@ -6792,7 +7612,7 @@ fn loadObject(
67927612 ),
67937613 .LOCAL => continue,
67947614 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6795 si.* = .global(elf.addGlobalSymbolAssumeCapacity(.{
7615 si.* = .global(elf.addGlobalSymbol(.{
67967616 .node = .none,
67977617 .name = name,
67987618 .value = input_sym.value,
......@@ -6807,6 +7627,12 @@ fn loadObject(
68077627 .shndx = .UNDEF,
68087628 }) catch |err| switch (err) {
68097629 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 ),
68107636 else => |e| return e,
68117637 });
68127638 continue;
......@@ -6822,9 +7648,9 @@ fn loadObject(
68227648 .{ name, bind },
68237649 ),
68247650 .LOCAL => {
6825 const lsi = elf.addLocalSymbolAssumeCapacity(.{
7651 const lsi = try elf.addLocalSymbol(.{
68267652 .node = .wrap(input_section_node),
6827 .name = try elf.string(.strtab, name),
7653 .name = name,
68287654 .value = input_sym.value,
68297655 .size = input_sym.size,
68307656 .type = sym_type,
......@@ -6833,7 +7659,7 @@ fn loadObject(
68337659 si.* = .local(lsi);
68347660 },
68357661 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6836 si.* = .global(elf.addGlobalSymbolAssumeCapacity(.{
7662 si.* = .global(elf.addGlobalSymbol(.{
68377663 .node = .wrap(input_section_node),
68387664 .name = name,
68397665 .value = input_sym.value,
......@@ -6852,6 +7678,12 @@ fn loadObject(
68527678 "multiple definitions of '{s}'",
68537679 .{name},
68547680 ),
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)
68557687 else => |e| return e,
68567688 });
68577689 },
......@@ -7006,73 +7838,199 @@ fn loadDso(
70067838 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
70077839 if (ehdr.machine != elf.ehdrMachine().toElf())
70087840 return diags.failParse(path, "bad machine", .{});
7009 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
70107841 // We're going to need to know the alignment of every section later.
70117842 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);
70127843 defer gpa.free(section_aligns);
7013 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
7014 var dynamic_sh: ?ElfN.Shdr = null;
7015 var dynsym_sh: ?ElfN.Shdr = null;
7016 for (section_aligns) |*section_align| {
7844 const shdr: struct {
7845 dynamic: ElfN.Shdr,
7846 dynsym: ElfN.Shdr,
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| {
70177861 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
70187862 try r.discardAll(ehdr.shentsize);
70197863 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
70207864 usize,
70217865 @intCast(@max(sh.addralign, 1)),
70227866 ));
7023 switch (sh.type) {
7024 else => {},
7025 .DYNAMIC => dynamic_sh = sh,
7026 .DYNSYM => dynsym_sh = sh,
7867 const ptr: *?InputSh = switch (sh.type) {
7868 else => continue,
7869 .DYNAMIC => &opt_dynamic,
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});
70277876 }
7877 ptr.* = .{ .shndx = @intCast(shndx), .sh = sh };
70287878 }
7029 break :sh .{
7030 dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}),
7031 dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}),
7879
7880 const dynamic = opt_dynamic orelse {
7881 return diags.failParse(path, "missing SHT_DYNAMIC section", .{});
70327882 };
7033 };
7034 const dynstr_sh: ElfN.Shdr = sh: {
7035 if (dynsym_sh.link >= ehdr.shnum) {
7036 return diags.failParse(path, "bad dynamic string table section index", .{});
7883 const dynsym = opt_dynsym orelse {
7884 return diags.failParse(path, "missing SHT_DYNSYM section", .{});
7885 };
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", .{});
70377895 }
7038 try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize);
7039 break :sh try r.peekStruct(ElfN.Shdr, target_endian);
7896
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 };
70407916 };
70417917
7042 if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) {
7043 return diags.failParse(path, "bad dynamic section entsize", .{});
7918 if (shdr.dynamic.entsize != @sizeOf(ElfN.Addr) * 2) {
7919 return diags.failParse(path, "bad SHT_DYNAMIC section entsize", .{});
70447920 }
70457921 const dynnum = std.math.divExact(
70467922 u32,
7047 @intCast(dynamic_sh.size),
7923 @intCast(shdr.dynamic.size),
70487924 @sizeOf(ElfN.Addr) * 2,
70497925 ) catch return diags.failParse(
70507926 path,
7051 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",
7052 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },
7927 "SHT_DYNAMIC section size (0x{x}) is not a multiple of entsize (0x{x})",
7928 .{ shdr.dynamic.size, @sizeOf(ElfN.Addr) * 2 },
70537929 );
70547930
7055 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {
7056 return diags.failParse(path, "bad dynsym entsize", .{});
7931 if (shdr.dynsym.entsize < @sizeOf(ElfN.Sym)) {
7932 return diags.failParse(path, "dynamic symbol table has invalid entsize", .{});
70577933 }
70587934 const symnum = std.math.divExact(
70597935 u32,
7060 @intCast(dynsym_sh.size),
7061 @intCast(dynsym_sh.entsize),
7936 @intCast(shdr.dynsym.size),
7937 @intCast(shdr.dynsym.entsize),
70627938 ) catch return diags.failParse(
70637939 path,
7064 "dynsym size (0x{x}) is not a multiple of entsize (0x{x})",
7065 .{ dynsym_sh.size, dynsym_sh.entsize },
7940 "dynamic symbol table size (0x{x}) is not a multiple of entsize (0x{x})",
7941 .{ shdr.dynsym.size, shdr.dynsym.entsize },
70667942 );
70677943
7068 const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size));
7944 const dynstr = try gpa.alloc(u8, @intCast(shdr.dynstr.size));
70697945 defer gpa.free(dynstr);
7070 try fr.seekTo(dynstr_sh.offset);
7946 try fr.seekTo(shdr.dynstr.offset);
70717947 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
70738031 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.
7074 try fr.seekTo(dynamic_sh.offset);
7075 const soname: []const u8 = for (0..dynnum) |_| {
8032 try fr.seekTo(shdr.dynamic.offset);
8033 const soname_slice: []const u8 = for (0..dynnum) |_| {
70768034 const tag = try r.takeInt(ElfN.Addr, target_endian);
70778035 const val = try r.takeInt(ElfN.Addr, target_endian);
70788036 if (tag == std.elf.DT_SONAME) {
......@@ -7086,16 +8044,16 @@ fn loadDso(
70868044 .basename => std.fs.path.basename(path.sub_path),
70878045 .full_path => try path.toString(comp.arena),
70888046 };
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
70918050 // Scan the symbol table and populate `elf.dso_globals`.
7092 const first_global = @min(dynsym_sh.info, symnum);
7093 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);
7094 try elf.ensureUnusedPltCapacity(symnum - first_global);
7095 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);
7096 for (first_global..symnum) |_| {
8051 const first_global = @min(shdr.dynsym.info, symnum);
8052 try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global);
8053 try fr.seekTo(shdr.dynsym.offset + first_global * shdr.dynsym.entsize);
8054 for (first_global..symnum) |in_dynsym_index| {
70978055 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
71008058 switch (sym.info.bind) {
71018059 else => continue,
......@@ -7115,100 +8073,76 @@ fn loadDso(
71158073 return diags.failParse(path, "bad symbol name string", .{});
71168074 }
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
71188086 // 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`.
71208088 const sym_align: Alignment = switch (sym.value) {
71218089 0 => section_aligns[sym.shndx],
71228090 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),
71238091 };
71248092
7125 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));
7126 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
8093 const name = std.mem.sliceTo(dynstr[sym.name..], 0);
71278094
7128 if (gop.found_existing and gop.value_ptr.type != .NOTYPE) {
7129 if (sym.size > gop.value_ptr.size or
7130 sym_align.compare(.gt, gop.value_ptr.alignment))
8095 const gop = elf.dso_globals.symbols.getOrPutAssumeCapacityAdapted(@as(DsoGlobals.Symbol.Key, .{
8096 .name = name,
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))
71318105 {
7132 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);
7133 gop.value_ptr.alignment = gop.value_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 }
8106 gop.key_ptr.size = @max(gop.key_ptr.size, sym.size);
8107 gop.key_ptr.alignment = gop.key_ptr.alignment.max(sym_align);
71558108 }
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 };
71578125 }
71588126
7159 gop.value_ptr.* = .{
7160 .type = sym.info.type,
7161 .size = sym.size,
7162 .alignment = sym_align,
7163 };
7164
7165 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
7166 // its type now.
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);
8127 if (is_default_version) {
8128 const adapter: DsoGlobals.Symbol.DefaultVersionAdapter = .{
8129 .dso_globals = &elf.dso_globals,
8130 };
8131 const default_gop = try elf.dso_globals.default_sym_vers.getOrPutAdapted(gpa, name, adapter);
8132 if (!default_gop.found_existing) {
8133 default_gop.key_ptr.* = @intCast(gop.index);
8134 }
71788135 }
71798136
7180 const sym_ptr = @field(elf.symPtr(gsi.ptr(elf).symtab_index), @tagName(class));
7181 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`
7182
7183 switch (elf.targetLoad(&sym_ptr.other).visibility) {
7184 .HIDDEN, .INTERNAL, .PROTECTED => continue,
7185 .DEFAULT => {},
8137 // Update any global(s) which may care about this input DSO global.
8138 if (maybe_version.unwrap()) |version| {
8139 if (elf.globalByName(.{ .name = name, .version = version.slice(&elf.dso_globals) })) |gsi| {
8140 try elf.updateGlobalDynamic(gsi, false);
8141 }
71868142 }
7187
7188 const cur_info = elf.targetLoad(&sym_ptr.info);
7189 if (cur_info.type == .NOTYPE) {
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);
8143 if (maybe_version == .none or is_default_version) {
8144 if (elf.globalByName(.{ .name = name, .version = null })) |gsi| {
8145 try elf.updateGlobalDynamic(gsi, false);
72128146 }
72138147 }
72148148 }
......@@ -7309,23 +8243,32 @@ fn createInitFiniArraySection(
73098243 .manual_size = true,
73108244 });
73118245 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
7312 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
73138246 // These symbols definitely already exist with strong definitions, because we added them
73148247 // 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, .{
73168257 .node = .wrap(shndx.get(elf).ni),
73178258 .value = shndx.vaddr(elf),
73188259 .size = 0,
73198260 .type = .NOTYPE,
73208261 .shndx = shndx.*,
73218262 });
7322 elf.setGlobalSymbolValue(elf.globalByName("__" ++ name ++ "_end").?, .{
8263 try elf.updateGlobalDynamic(start_gsi, true);
8264 try elf.setGlobalSymbolValue(end_gsi, .{
73238265 .node = .wrap(shndx.get(elf).ni),
73248266 .value = shndx.vaddr(elf),
73258267 .size = 0,
73268268 .type = .NOTYPE,
73278269 .shndx = shndx.*,
73288270 });
8271 try elf.updateGlobalDynamic(end_gsi, true);
73298272}
73308273fn updateInitFiniArraySectionSize(
73318274 elf: *Elf,
......@@ -7335,7 +8278,10 @@ fn updateInitFiniArraySectionSize(
73358278 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
73368279 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
73378280 };
7338 const end_sym_gsi = elf.globalByName("__" ++ name ++ "_end").?;
8281 const end_sym_gsi = elf.globalByName(.{
8282 .name = "__" ++ name ++ "_end",
8283 .version = null,
8284 }).?;
73398285 Symbol.Id.global(end_sym_gsi).flushMoved(elf, end_vaddr);
73408286}
73418287
......@@ -7356,13 +8302,12 @@ fn prelinkInner(elf: *Elf) Error!void {
73568302 if (comp.config.use_llvm) break :self_hosted_codegen;
73578303
73588304 // We're using self-hosted codegen---add an input representing the Zig "object".
7359 try elf.ensureUnusedSymbolCapacity(1, .all_local);
73608305 try elf.inputs.ensureUnusedCapacity(gpa, 1);
73618306 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
73628307 defer gpa.free(zcu_name);
7363 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
8308 const zcu_file_symbol = try elf.addLocalSymbol(.{
73648309 .node = .none,
7365 .name = try elf.string(.strtab, zcu_name),
8310 .name = zcu_name,
73668311 .value = 0,
73678312 .size = 0,
73688313 .type = .FILE,
......@@ -7653,8 +8598,10 @@ fn prepareDynamic(elf: *Elf) Error!void {
76538598 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
76548599 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
76558600 @as(usize, @intFromBool(use_plt)) * 4 +
8601 @as(usize, @intFromBool(elf.verneed.count() > 0)) * 2 +
8602 @as(usize, @intFromBool(elf.verdef.count() > 0)) * 2 +
76568603 @intFromBool(comp.config.output_mode == .Exe) +
7657 @intFromBool(elf.textrel_count > 0) + 9;
8604 @intFromBool(elf.textrel_count > 0) + 10;
76588605
76598606 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
76608607
......@@ -7753,8 +8700,25 @@ fn flushDynamic(elf: *Elf) void {
77538700 };
77548701 dynamic_index += 4;
77558702 }
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].* = .{
77588722 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
77598723 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
77608724 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
......@@ -7762,10 +8726,11 @@ fn flushDynamic(elf: *Elf) void {
77628726 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
77638727 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
77648728 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
8729 .{ std.elf.DT_VERSYM, @intCast(elf.shndx.gnu_version.vaddr(elf)) },
77658730 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },
77668731 .{ std.elf.DT_NULL, 0 },
77678732 };
7768 dynamic_index += 9;
8733 dynamic_index += 10;
77698734
77708735 assert(dynamic_index == dynamic_entries.len);
77718736 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 {
78028767 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,
78038768 else => false,
78048769 };
7805 if (want_symbol) try elf.ensureUnusedSymbolCapacity(1, .all_local);
78068770
78078771 const shstrtab_entry = try elf.string(.shstrtab, opts.name);
78088772 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 {
78498813 });
78508814 const addr = elf.computeNodeVAddr(ni);
78518815 elf.shdrs.appendAssumeCapacity(.{
7852 .lsi = if (want_symbol) elf.addLocalSymbolAssumeCapacity(.{
8816 .lsi = if (want_symbol) try elf.addLocalSymbol(.{
78538817 .node = ni.toOptional(),
7854 .name = .empty,
8818 .name = "",
78558819 .value = addr,
78568820 .size = 0,
78578821 .type = .SECTION,
......@@ -8328,10 +9292,27 @@ fn addSymbolRelocAssumeCapacity(
83289292 => false,
83299293 },
83309294 };
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
83329303 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 },
83349311 .static_relative => {
9312 switch (target.unwrap()) {
9313 .local => {},
9314 .global => |gsi| gsi.ptr(elf).status.any_static_relative_target_relocs = true,
9315 }
83359316 switch (@"type".target) {
83369317 // Only relocations which resolve to absolute addresses require runtime
83379318 // `R_*_RELATIVE` relocations.
......@@ -8358,13 +9339,8 @@ fn addSymbolRelocAssumeCapacity(
83589339 .addend = 0,
83599340 }).toOptional();
83609341 },
8361 .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) {
8362 switch (elf.classifySymbolValue(target)) {
8363 .static => continue :classify .static,
8364 .static_relative => continue :classify .static_relative,
8365 .dynamic => unreachable, // we just added a copy relocation
8366 }
8367 } else {
9342 .dynamic => {
9343 target.unwrap().global.ptr(elf).status.any_dynamic_target_relocs = true;
83689344 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
83699345 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
83709346 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
......@@ -8396,7 +9372,7 @@ fn addSymbolRelocAssumeCapacity(
83969372 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
83979373 .type = dynamic_reloc_type,
83989374 .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).?,
84009376 .addend = addend,
84019377 }).toOptional();
84029378 },
......@@ -8640,7 +9616,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
86409616 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
86419617 }
86429618 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| {
86449620 // When there is a dynsym entry, just target that with no addend.
86459621 break :val .{ .reloc = .{
86469622 .type = .tpOff(elf),
......@@ -8668,7 +9644,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
86689644 } },
86699645 .dynamic => .{ .reloc = .{
86709646 .type = .globDat(elf),
8671 .dynsym_index = sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
9647 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
86729648 .addend = 0,
86739649 } },
86749650 },
......@@ -8677,7 +9653,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
86779653 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
86789654 .dynamic => .{ .reloc = .{
86799655 .type = .dtpOff(elf),
8680 .dynsym_index = sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
9656 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
86819657 .addend = 0,
86829658 } },
86839659 },
......@@ -8690,7 +9666,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
86909666 .type = .dtpMod(elf),
86919667 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
86929668 .static, .static_relative => 0,
8693 .dynamic => sym.unwrap().global.ptr(elf).dynsym_index.unwrap().?,
9669 .dynamic => sym.unwrap().global.dynsymIndex(elf).?,
86949670 },
86959671 .addend = 0,
86969672 } },
......@@ -8754,7 +9730,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
87549730
87559731/// If `node` cannot contain runtime relocations, returns `.no`.
87569732///
8757/// If `node` can contain runtime relocations, `returns `.yes_textrel` if such a relocation requires
9733/// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires
87589734/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
87599735fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
87609736 const shndx = elf.getNodeShndx(node);
......@@ -8766,82 +9742,6 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
87669742 return .yes;
87679743}
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
88459745pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
88469746 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
88479747 else => |e| return e,
......@@ -9378,7 +10278,14 @@ fn flushInner(
937810278
937910279 if (comp.config.output_mode == .Exe and elf.unknown_globals.count() > 0) {
938010280 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)});
938210289 }
938310290 return error.AlreadyReported;
938410291 }
......@@ -9429,7 +10336,10 @@ fn flushInner(
942910336 .enabled => "_start",
943010337 .named => |named| named,
943110338 };
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;
943310343 break :entry Symbol.Id.global(gsi).value(elf);
943410344 };
943510345 switch (elf.ehdrPtr()) {
......@@ -9501,7 +10411,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
950110411 if (elf.changed_symtab_index.pop()) |kv| {
950210412 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);
950510415 defer idle_prog_node.end();
950610416
950710417 const sym_id: Symbol.Id = .global(gsi);
......@@ -9531,14 +10441,14 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
953110441 // don't care about the dynsym index). The only exception is a copy reloc
953210442 // could exist (and be the *reason* the symbol value is statically known).
953310443 if (elf.copied_globals.get(gsi)) |copied| {
9534 const dynsym_index = gsi.ptr(elf).dynsym_index.unwrap().?;
10444 const dynsym_index = gsi.dynsymIndex(elf).?;
953510445 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, dynsym_index);
953610446 }
953710447 },
953810448 .dynamic => {
953910449 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
954310453 // Update symbol relocs:
954410454 var ri = symtab_index.ptr(elf).first_target_reloc;
......@@ -9719,6 +10629,7 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
971910629 .code_view => unreachable,
972010630 }
972110631 try elf.genPendingDebug(gpa);
10632 assert(elf.pending_uavs.items.len == 0); // no UAVs added by lazy code/data or by debug info
972210633}
972310634fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {
972410635 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
1009911010 .copied_global => |global_name| {
1010011011 const copied_global = elf.copied_globals.getPtr(global_name) orelse {
1010111012 // 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`.
1010311014 return;
1010411015 };
1010511016 assert(copied_global.node == ni);
......@@ -10848,40 +11759,75 @@ fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1084811759 }
1084911760}
1085011761
10851/// Asserts that `gsi` has a `.dynsym` entry.
10852fn addPltEntry(elf: *Elf, gsi: Symbol.Global.Index) void {
11762/// If `gsi` does not have a PLT entry, adds one and returns `true`. If `gsi` already has a PLT
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 {
1085311767 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
1085511796 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
1085611797 // free-list for the PLT itself---see `pltEntryIsDead` for details.
1085711798 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
1085811799 .type = .jumpSlot(elf),
1085911800 .offset = 0, // populated later
10860 .raw_sym_index = gsi.ptr(elf).dynsym_index.unwrap().?,
11801 .raw_sym_index = gsi.ownedDynsymIndex(elf).?,
1086111802 .addend = 0,
1086211803 }));
1086311804
1086411805 // 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: {
10866 const plt = elf.targetPltInfo();
10867 break :got_plt if (plt.got_plt) |got_plt| .{
10868 elf.shndx.got_plt,
10869 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
10870 } else .{
10871 elf.shndx.plt,
10872 plt.entry_size * (plt.header_entries + plt_index),
10873 };
11806 const got_plt_section: Section.Index, const got_plt_offset: u64 = if (plt.got_plt) |got_plt| .{
11807 elf.shndx.got_plt,
11808 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
11809 } else .{
11810 elf.shndx.plt,
11811 plt.entry_size * (plt.header_entries + plt_index),
1087411812 };
1087511813
1087611814 // Now that we know the index, we can set the relocation's offset.
1087711815 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
1087811816
1087911817 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 }
1088111825 elf.plt.setKey(plt_index, gsi);
10882 return;
11826 return true;
1088311827 }
1088411828
11829 assert(maybe_dead_plt_index == null); // if there were a dead entry we would have reused it
11830
1088511831 // We added a new entry, so we now need to extend the PLT sections.
1088611832 assert(plt_index == elf.plt.count());
1088711833 elf.plt.putAssumeCapacityNoClobber(gsi, {});
......@@ -11043,6 +11989,8 @@ fn addPltEntry(elf: *Elf, gsi: Symbol.Global.Index) void {
1104311989 }
1104411990 },
1104511991 }
11992
11993 return true;
1104611994}
1104711995fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
1104811996 const target_endian = elf.targetEndian();
......@@ -11206,6 +12154,7 @@ pub fn updateExports(
1120612154 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
1120712155 };
1120812156 }
12157 try elf.genPending(pt);
1120912158}
1121012159fn updateExportInner(
1121112160 elf: *Elf,
......@@ -11224,7 +12173,6 @@ fn updateExportInner(
1122412173 Value.fromInterned(uav).fmtValue(pt),
1122512174 }),
1122612175 }
11227 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
1122812176 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {
1122912177 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),
1123012178 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
......@@ -11243,7 +12191,7 @@ fn updateExportInner(
1124312191 };
1124412192
1124512193 const name = @"export".opts.name.toSlice(ip);
11246 _ = elf.addGlobalSymbolAssumeCapacity(.{
12194 _ = elf.addGlobalSymbol(.{
1124712195 .node = exported_lsi.index().ptr(elf).node,
1124812196 .name = name,
1124912197 .value = cur_value,
......@@ -11267,7 +12215,11 @@ fn updateExportInner(
1126712215 // get these errors on every non-initial incremental update. Hack around that by
1126812216 // only emitting this error if the symbol we're conflicting with comes from an input
1126912217 // 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 }).?;
1127112223 if (conflicting_global.ptr(elf).symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
1127212224 if (elf.getNode(conflicting_node) == .input_section) {
1127312225 return elf.base.comp.link_diags.fail(
......@@ -11277,6 +12229,11 @@ fn updateExportInner(
1127712229 }
1127812230 }
1127912231 },
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
1128012237 else => |e| return e,
1128112238 };
1128212239}
......@@ -11342,7 +12299,7 @@ pub fn printNode(
1134212299 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
1134312300 });
1134412301 },
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)}),
1134612303 .nav => |nmi| {
1134712304 const zcu = elf.base.comp.zcu.?;
1134812305 const ip = &zcu.intern_pool;
......@@ -11530,7 +12487,7 @@ fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocato
1153012487fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
1153112488 const index = switch (sym.unwrap()) {
1153212489 .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,
1153412491 };
1153512492 if (elf.pltEntryIsDead(index)) return null;
1153612493 const plt = elf.targetPltInfo();
......@@ -11540,3 +12497,256 @@ fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
1154012497 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;
1154112498 }
1154212499}
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}