| ... | ... | @@ -34,6 +34,12 @@ shndx: struct { |
| 34 | 34 | dynstr: Section.Index, |
| 35 | 35 | dynamic: Section.Index, |
| 36 | 36 | tdata: Section.Index, |
| 37 | rela_dyn: Section.Index, |
| 38 | rela_plt: Section.Index, |
| 39 | // These sections are created only as needed, and are initially `.UNDEF`. |
| 40 | init_array: Section.Index, |
| 41 | fini_array: Section.Index, |
| 42 | preinit_array: Section.Index, |
| 37 | 43 | }, |
| 38 | 44 | symtab: std.ArrayList(Symbol), |
| 39 | 45 | globals: struct { |
| ... | ... | @@ -50,16 +56,34 @@ globals: struct { |
| 50 | 56 | /// We use a separate hash map for this data rather than storing it in `navs` etc to save memory, |
| 51 | 57 | /// because the vast majority of nodes which can export global symbols actually will not. |
| 52 | 58 | node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)), |
| 59 | /// Contains all globals symbols defined in any needed DSO. This map serves two purposes: |
| 60 | /// |
| 61 | /// * If we discover an undefined reference to one of these symbols, we will know the associated |
| 62 | /// symbol type, which is important because it may cause us to create a PLT entry. |
| 63 | /// |
| 64 | /// * When emitting a dynamic executable, we can detect which undefined references are resolved by a |
| 65 | /// linked DSO, so can emit "undefined global symbol" errors for any other undefined references. |
| 66 | dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT), |
| 53 | 67 | shstrtab: StringTable, |
| 54 | 68 | strtab: StringTable, |
| 55 | 69 | dynstr: StringTable, |
| 56 | | got: struct { |
| 57 | | len: u32, |
| 58 | | tlsld: GotIndex, |
| 59 | | plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void), |
| 60 | | }, |
| 61 | | first_plt_reloc: Reloc.Index, |
| 62 | | first_dynamic_reloc: Reloc.Index, |
| 70 | |
| 71 | /// Indices map 1--1 to indices into the actual `.got` section. |
| 72 | /// |
| 73 | /// Value is the output relocation in `.rela.dyn` for the GOT entry. |
| 74 | got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), |
| 75 | /// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into |
| 76 | /// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime |
| 77 | /// relocation is no longer necessary, then neither is the corresponding PLT entry!). |
| 78 | /// |
| 79 | /// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is |
| 80 | /// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs. |
| 81 | plt: std.array_hash_map.Auto(Symbol.Id, void), |
| 82 | /// The `.plt` section contains zero or more symbol relocations starting at this index. |
| 83 | plt_first_symbol_reloc: SymbolReloc.Index, |
| 84 | /// The `.dynamic` section contains zero or more symbol relocations starting at this index. |
| 85 | dynamic_first_symbol_reloc: SymbolReloc.Index, |
| 86 | |
| 63 | 87 | needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void), |
| 64 | 88 | inputs: std.ArrayList(struct { |
| 65 | 89 | path: std.Build.Cache.Path, |
| ... | ... | @@ -69,29 +93,42 @@ inputs: std.ArrayList(struct { |
| 69 | 93 | input_sections: std.ArrayList(InputSection), |
| 70 | 94 | input_section_pending_index: u32, |
| 71 | 95 | navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct { |
| 72 | | /// The start index of the contiguous sequence of relocations in this NAV. |
| 73 | | first_reloc: Reloc.Index, |
| 74 | 96 | lsi: Symbol.LocalIndex, |
| 97 | /// The start index of the contiguous sequence of symbol relocations in this NAV. |
| 98 | first_symbol_reloc: SymbolReloc.Index, |
| 99 | /// The start index of the contiguous sequence of GOT relocations in this NAV. |
| 100 | first_got_reloc: GotReloc.Index, |
| 75 | 101 | }), |
| 76 | 102 | uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct { |
| 77 | | /// The start index of the contiguous sequence of relocations in this UAV. |
| 78 | | first_reloc: Reloc.Index, |
| 79 | 103 | lsi: Symbol.LocalIndex, |
| 104 | /// The start index of the contiguous sequence of symbol relocations in this UAV. |
| 105 | first_symbol_reloc: SymbolReloc.Index, |
| 106 | // No `first_got_reloc` field because a UAV never contains GOT relocations. |
| 80 | 107 | }), |
| 81 | 108 | lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { |
| 82 | 109 | map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct { |
| 83 | | /// The start index of the contiguous sequence of relocations in this lazy code/data. |
| 84 | | first_reloc: Reloc.Index, |
| 85 | 110 | lsi: Symbol.LocalIndex, |
| 111 | /// The start index of the contiguous sequence of symbol relocations in this lazy code/data. |
| 112 | first_symbol_reloc: SymbolReloc.Index, |
| 113 | /// The start index of the contiguous sequence of GOT relocations in this lazy code/data. |
| 114 | first_got_reloc: GotReloc.Index, |
| 86 | 115 | }), |
| 87 | 116 | pending_index: u32, |
| 88 | 117 | }), |
| 89 | 118 | pending_uavs: std.ArrayList(Node.UavMapIndex), |
| 90 | | relocs: std.ArrayList(Reloc), |
| 91 | | |
| 119 | symbol_relocs: std.ArrayList(SymbolReloc), |
| 120 | got_relocs: std.ArrayList(GotReloc), |
| 121 | /// Set of relocations which must be re-applied if the size of the TLS segment changes. |
| 122 | tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), |
| 123 | /// Index matches the index into `shdrs`. |
| 124 | section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), |
| 92 | 125 | /// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation |
| 93 | 126 | /// entries which target that symbol must be updated to reference the correct symbol index. |
| 94 | 127 | changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void), |
| 128 | /// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL` |
| 129 | /// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic` |
| 130 | /// section in `flush` only when it is actually necessary. See also `nodeRequiresTextrel`. |
| 131 | textrel_count: u32, |
| 95 | 132 | |
| 96 | 133 | const_prog_node: std.Progress.Node, |
| 97 | 134 | synth_prog_node: std.Progress.Node, |
| ... | ... | @@ -106,21 +143,21 @@ const Node = union(enum) { |
| 106 | 143 | shdr, |
| 107 | 144 | /// Cannot contain relocations. |
| 108 | 145 | segment: u32, |
| 109 | | /// The section '.plt' may contain relocations via `elf.first_plt_reloc`. |
| 146 | /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. |
| 110 | 147 | /// |
| 111 | | /// The section '.dynamic' may contain relocations via `elf.first_dynamic_reloc`. |
| 148 | /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`. |
| 112 | 149 | /// |
| 113 | 150 | /// Otherwise, cannot contain relocations. |
| 114 | 151 | section: Section.Index, |
| 115 | | /// May contain relocations through the `first_reloc` field in `elf.input_sections`. |
| 152 | /// May contain relocations. |
| 116 | 153 | input_section: InputSection.Index, |
| 117 | | /// May contain relocations through the `first_reloc` field in `elf.navs`. |
| 154 | /// May contain relocations. |
| 118 | 155 | nav: NavMapIndex, |
| 119 | | /// May contain relocations through the `first_reloc` field in `elf.uavs`. |
| 156 | /// May contain relocations. |
| 120 | 157 | uav: UavMapIndex, |
| 121 | | /// May contain relocations through the `first_reloc` field in `elf.lazy.map`. |
| 158 | /// May contain relocations. |
| 122 | 159 | lazy_code: LazyMapRef.Index(.code), |
| 123 | | /// May contain relocations through the `first_reloc` field in `elf.lazy.map`. |
| 160 | /// May contain relocations. |
| 124 | 161 | lazy_const_data: LazyMapRef.Index(.const_data), |
| 125 | 162 | |
| 126 | 163 | pub const InputIndex = enum(u32) { |
| ... | ... | @@ -162,8 +199,11 @@ const Node = union(enum) { |
| 162 | 199 | return elf.navs.values()[@intFromEnum(nmi)].lsi; |
| 163 | 200 | } |
| 164 | 201 | |
| 165 | | fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index { |
| 166 | | return elf.navs.values()[@intFromEnum(nmi)].first_reloc; |
| 202 | fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 203 | return elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc; |
| 204 | } |
| 205 | fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index { |
| 206 | return elf.navs.values()[@intFromEnum(nmi)].first_got_reloc; |
| 167 | 207 | } |
| 168 | 208 | }; |
| 169 | 209 | |
| ... | ... | @@ -178,8 +218,13 @@ const Node = union(enum) { |
| 178 | 218 | return elf.uavs.values()[@intFromEnum(umi)].lsi; |
| 179 | 219 | } |
| 180 | 220 | |
| 181 | | fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index { |
| 182 | | return elf.uavs.values()[@intFromEnum(umi)].first_reloc; |
| 221 | fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 222 | return elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc; |
| 223 | } |
| 224 | fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index { |
| 225 | _ = umi; |
| 226 | _ = elf; |
| 227 | return .none; |
| 183 | 228 | } |
| 184 | 229 | }; |
| 185 | 230 | |
| ... | ... | @@ -203,8 +248,11 @@ const Node = union(enum) { |
| 203 | 248 | return lmi.ref().symbol(elf); |
| 204 | 249 | } |
| 205 | 250 | |
| 206 | | fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index { |
| 207 | | return lmi.ref().firstReloc(elf); |
| 251 | fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index { |
| 252 | return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_symbol_reloc; |
| 253 | } |
| 254 | fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index { |
| 255 | return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_got_reloc; |
| 208 | 256 | } |
| 209 | 257 | }; |
| 210 | 258 | } |
| ... | ... | @@ -216,10 +264,6 @@ const Node = union(enum) { |
| 216 | 264 | pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex { |
| 217 | 265 | return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi; |
| 218 | 266 | } |
| 219 | | |
| 220 | | fn firstReloc(lmr: LazyMapRef, elf: *const Elf) Reloc.Index { |
| 221 | | return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].first_reloc; |
| 222 | | } |
| 223 | 267 | }; |
| 224 | 268 | |
| 225 | 269 | pub const Known = struct { |
| ... | ... | @@ -255,8 +299,10 @@ const InputSection = struct { |
| 255 | 299 | vaddr: u64, |
| 256 | 300 | /// The node corresponding to this input section. |
| 257 | 301 | node: MappedFile.Node.Index, |
| 258 | | /// The start index of the contiguous sequence of relocations in this input section. |
| 259 | | first_reloc: Reloc.Index, |
| 302 | /// The start index of the contiguous sequence of symbol relocations in this input section. |
| 303 | first_symbol_reloc: SymbolReloc.Index, |
| 304 | /// The start index of the contiguous sequence of GOT relocations in this input section. |
| 305 | first_got_reloc: GotReloc.Index, |
| 260 | 306 | |
| 261 | 307 | const Index = enum(u32) { |
| 262 | 308 | _, |
| ... | ... | @@ -290,21 +336,53 @@ const Section = struct { |
| 290 | 336 | /// |
| 291 | 337 | /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`. |
| 292 | 338 | lsi: Symbol.LocalIndex, |
| 293 | | rela_shndx: Section.Index, |
| 294 | | rela_free: RelIndex, |
| 339 | rela: union { |
| 340 | /// This field is active if and only if this section is *not* a `SHT_RELA` section. |
| 341 | /// |
| 342 | /// This field's value refers to this section's corresponding relocation section, if it |
| 343 | /// currently has one. If this section does not currently have a relocation section, the |
| 344 | /// value is `.UNDEF`. |
| 345 | /// |
| 346 | /// This field is only ever non-`.UNDEF` when emitting a relocatable (`ET_REL`). While there |
| 347 | /// are also output relocations in DSOs, they are all placed in the `.rela.dyn` |
| 348 | /// (`elf.shdnx.rela_dyn`) and `.rela.plt` (`elf.shndx.rela_plt`) sections, rather than |
| 349 | /// having separate relocation sections for each section. |
| 350 | shndx: Section.Index, |
| 351 | |
| 352 | /// This field is active if and only if this section *is* a `SHT_RELA` section. |
| 353 | /// |
| 354 | /// This is the head of a single-linked list of free `ElfN.Rela` entries in this section. |
| 355 | /// Entries in this list have `info.type` set to `R_*_NONE`, have `info.sym` set to 0, and |
| 356 | /// have `offset` set to `@enumFromInt(next)` where `next` is `RelaIndex.Optional`. Also, |
| 357 | /// `addend` is set to the length of the list starting from this point; so the last node in |
| 358 | /// the list has `addend = 1`, the one before it has `addend = 2`, etc. This is so that the |
| 359 | /// head node always contains the current length of the list. |
| 360 | /// |
| 361 | /// It would be okay to store these values (in the `offset` and `addend` fields) in the |
| 362 | /// compiler's host endianness, because they will never be read by other tooling. However, |
| 363 | /// we nonetheless use target endianness, because using host endianness would introduce an |
| 364 | /// unnecessary dependency of the output binary on the compiler's host architecture. |
| 365 | free_head: RelaIndex.Optional, |
| 366 | }, |
| 295 | 367 | |
| 296 | | pub const RelIndex = enum(u32) { |
| 368 | const RelaIndex = enum(u32) { |
| 297 | 369 | none, |
| 298 | 370 | _, |
| 299 | 371 | |
| 300 | | pub fn wrap(i: ?u32) RelIndex { |
| 301 | | return @enumFromInt((i orelse return .none) + 1); |
| 302 | | } |
| 303 | | pub fn unwrap(ri: RelIndex) ?u32 { |
| 304 | | return switch (ri) { |
| 305 | | .none => null, |
| 306 | | _ => @intFromEnum(ri) - 1, |
| 307 | | }; |
| 372 | const Optional = enum(u32) { |
| 373 | none = std.math.maxInt(u32), |
| 374 | _, |
| 375 | |
| 376 | fn unwrap(opt: RelaIndex.Optional) ?RelaIndex { |
| 377 | return switch (opt) { |
| 378 | .none => null, |
| 379 | _ => @enumFromInt(@intFromEnum(opt)), |
| 380 | }; |
| 381 | } |
| 382 | }; |
| 383 | |
| 384 | fn toOptional(i: RelaIndex) RelaIndex.Optional { |
| 385 | return @enumFromInt(@intFromEnum(i)); |
| 308 | 386 | } |
| 309 | 387 | }; |
| 310 | 388 | |
| ... | ... | @@ -357,11 +435,10 @@ const Section = struct { |
| 357 | 435 | return &elf.shdrs.items[@intFromEnum(s)]; |
| 358 | 436 | } |
| 359 | 437 | |
| 360 | | fn name(s: Index, elf: *Elf) [:0]const u8 { |
| 361 | | const str: String(.shstrtab) = switch (elf.shdrPtr(s)) { |
| 438 | fn name(s: Index, elf: *Elf) String(.shstrtab) { |
| 439 | return switch (elf.shdrPtr(s)) { |
| 362 | 440 | inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)), |
| 363 | 441 | }; |
| 364 | | return str.slice(elf); |
| 365 | 442 | } |
| 366 | 443 | |
| 367 | 444 | fn vaddr(s: Index, elf: *Elf) u64 { |
| ... | ... | @@ -377,9 +454,690 @@ const Section = struct { |
| 377 | 454 | inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)), |
| 378 | 455 | } |
| 379 | 456 | } |
| 457 | |
| 458 | /// Asserts that `shndx` is a `SHT_RELA` section and ensures that its node has enough unused |
| 459 | /// space to hold `n` additional `ElfN.Rela` entries. |
| 460 | fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) !void { |
| 461 | const node = rela_shndx.get(elf).ni; |
| 462 | const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) { |
| 463 | inline else => |shdr, class| need_size: { |
| 464 | assert(elf.targetLoad(&shdr.type) == .RELA); |
| 465 | const cur_size = elf.targetLoad(&shdr.size); |
| 466 | const ent_size = @sizeOf(class.ElfN().Rela); |
| 467 | assert(elf.targetLoad(&shdr.entsize) == ent_size); |
| 468 | const free_len: u32 = free_len: { |
| 469 | const opt_free_head = rela_shndx.get(elf).rela.free_head; |
| 470 | const free_head = opt_free_head.unwrap() orelse break :free_len 0; |
| 471 | const relas: []const class.ElfN().Rela = @ptrCast(@alignCast( |
| 472 | node.slice(&elf.mf)[0..@intCast(cur_size)], |
| 473 | )); |
| 474 | const free_len = elf.targetLoad(&relas[@intFromEnum(free_head)].addend); |
| 475 | assert(free_len > 0); |
| 476 | break :free_len @intCast(free_len); |
| 477 | }; |
| 478 | const need_additional = n -| free_len; |
| 479 | break :need_size cur_size + need_additional * ent_size; |
| 480 | }, |
| 481 | }; |
| 482 | _, const cur_node_size = node.location(&elf.mf).resolve(&elf.mf); |
| 483 | if (need_size > cur_node_size) { |
| 484 | const gpa = elf.base.comp.gpa; |
| 485 | try node.resize(&elf.mf, gpa, need_size +| need_size / MappedFile.growth_factor); |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | /// Asserts that `shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at the |
| 490 | /// given `index` in it. The entry is added to the free-list for reuse later. Asserts that |
| 491 | /// the relocation entry at `index` is not already free. |
| 492 | fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void { |
| 493 | switch (elf.shdrPtr(rela_shndx)) { |
| 494 | inline else => |shdr, class| { |
| 495 | assert(elf.targetLoad(&shdr.type) == .RELA); |
| 496 | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); |
| 497 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 498 | rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))], |
| 499 | )); |
| 500 | const opt_free_head = rela_shndx.get(elf).rela.free_head; |
| 501 | const old_free_len: u32 = free_len: { |
| 502 | const free_head = opt_free_head.unwrap() orelse break :free_len 0; |
| 503 | const free_len = elf.targetLoad(&relas[@intFromEnum(free_head)].addend); |
| 504 | assert(free_len > 0); |
| 505 | break :free_len @intCast(free_len); |
| 506 | }; |
| 507 | const none_reloc_type = MachineRelocType.none(elf).unwrap(elf); |
| 508 | { |
| 509 | const old_type = elf.targetLoad(&relas[@intFromEnum(index)].info).type; |
| 510 | assert(old_type != none_reloc_type); // bug: `index` is already in the free-list |
| 511 | } |
| 512 | relas[@intFromEnum(index)] = .{ |
| 513 | .offset = @intFromEnum(opt_free_head), // next |
| 514 | .info = .{ |
| 515 | .type = @intCast(none_reloc_type), |
| 516 | .sym = 0, |
| 517 | }, |
| 518 | .addend = @intCast(old_free_len + 1), // list length |
| 519 | }; |
| 520 | if (elf.targetEndian() != native_endian) { |
| 521 | std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@intFromEnum(index)]); |
| 522 | } |
| 523 | }, |
| 524 | } |
| 525 | rela_shndx.get(elf).rela.free_head = index.toOptional(); |
| 526 | } |
| 527 | |
| 528 | /// Asserts that `shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it with |
| 529 | /// the given field values. Returns the index of the populated entry. Asserts that capacity |
| 530 | /// for this operation was already guaranteed using `relaEnsureAdditionalCapacity`. |
| 531 | fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct { |
| 532 | type: MachineRelocType, |
| 533 | offset: u64, |
| 534 | /// This is a raw `u32` because whether this is an index into `.symtab` (`Symbol.Index`) |
| 535 | /// or an index into `.dynsym` is contextual. |
| 536 | raw_sym_index: u32, |
| 537 | addend: i64, |
| 538 | }) RelaIndex { |
| 539 | switch (elf.shdrPtr(rela_shndx)) { |
| 540 | inline else => |shdr, class| { |
| 541 | assert(elf.targetLoad(&shdr.type) == .RELA); |
| 542 | const ent_size = @sizeOf(class.ElfN().Rela); |
| 543 | assert(elf.targetLoad(&shdr.entsize) == ent_size); |
| 544 | const new_index: RelaIndex = if (rela_shndx.get(elf).rela.free_head.unwrap()) |free_head| new_index: { |
| 545 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 546 | rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))], |
| 547 | )); |
| 548 | const next: RelaIndex.Optional = @enumFromInt(elf.targetLoad( |
| 549 | &relas[@intFromEnum(free_head)].offset, |
| 550 | )); |
| 551 | rela_shndx.get(elf).rela.free_head = next; |
| 552 | |
| 553 | const old_free_len: u32 = @intCast( |
| 554 | elf.targetLoad(&relas[@intFromEnum(free_head)].addend), |
| 555 | ); |
| 556 | const new_free_len: u32 = if (next.unwrap()) |i| @intCast( |
| 557 | elf.targetLoad(&relas[@intFromEnum(i)].addend), |
| 558 | ) else 0; |
| 559 | assert(new_free_len == old_free_len - 1); |
| 560 | |
| 561 | break :new_index free_head; |
| 562 | } else new_index: { |
| 563 | const old_size = elf.targetLoad(&shdr.size); |
| 564 | const new_size = old_size + ent_size; |
| 565 | elf.targetStore(&shdr.size, new_size); |
| 566 | if (rela_shndx == elf.shndx.rela_dyn) { |
| 567 | elf.updateDynamicEntry(std.elf.DT_RELASZ, new_size); |
| 568 | } else if (rela_shndx == elf.shndx.rela_plt) { |
| 569 | elf.updateDynamicEntry(std.elf.DT_PLTRELSZ, new_size); |
| 570 | } |
| 571 | break :new_index @enumFromInt(@divExact(old_size, ent_size)); |
| 572 | }; |
| 573 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 574 | rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))], |
| 575 | )); |
| 576 | relas[@intFromEnum(new_index)] = .{ |
| 577 | .offset = @intCast(opts.offset), |
| 578 | .info = .{ |
| 579 | .type = @intCast(opts.type.unwrap(elf)), |
| 580 | .sym = @intCast(opts.raw_sym_index), |
| 581 | }, |
| 582 | .addend = @intCast(opts.addend), |
| 583 | }; |
| 584 | if (elf.targetEndian() != native_endian) { |
| 585 | std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@intFromEnum(new_index)]); |
| 586 | } |
| 587 | return new_index; |
| 588 | }, |
| 589 | } |
| 590 | } |
| 591 | |
| 592 | /// Asserts that `shndx` is a `SHT_RELA` section and updates the `info.sym` field of the |
| 593 | /// `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol |
| 594 | /// index is a raw `u32`, because it may be an index into `.symtab` or an index into |
| 595 | /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted). |
| 596 | fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void { |
| 597 | switch (elf.shdrPtr(rela_shndx)) { |
| 598 | inline else => |shdr, class| { |
| 599 | assert(elf.targetLoad(&shdr.type) == .RELA); |
| 600 | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); |
| 601 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 602 | rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))], |
| 603 | )); |
| 604 | const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info); |
| 605 | { |
| 606 | const none_reloc_type = MachineRelocType.none(elf).unwrap(elf); |
| 607 | assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list |
| 608 | } |
| 609 | elf.targetStore(&relas[@intFromEnum(index)].info, .{ |
| 610 | .type = rela_info.type, |
| 611 | .sym = @intCast(raw_sym_index), |
| 612 | }); |
| 613 | }, |
| 614 | } |
| 615 | } |
| 616 | |
| 617 | /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the |
| 618 | /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e. |
| 619 | /// it is not deleted). |
| 620 | fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void { |
| 621 | switch (elf.shdrPtr(rela_shndx)) { |
| 622 | inline else => |shdr, class| { |
| 623 | assert(elf.targetLoad(&shdr.type) == .RELA); |
| 624 | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); |
| 625 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 626 | rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))], |
| 627 | )); |
| 628 | { |
| 629 | const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info); |
| 630 | const none_reloc_type = MachineRelocType.none(elf).unwrap(elf); |
| 631 | assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list |
| 632 | } |
| 633 | elf.targetStore(&relas[@intFromEnum(index)].offset, @intCast(new_offset)); |
| 634 | }, |
| 635 | } |
| 636 | } |
| 637 | |
| 638 | /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the |
| 639 | /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`. |
| 640 | /// Asserts that `index` is not in the free-list (i.e. it is not deleted). |
| 641 | fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void { |
| 642 | switch (elf.shdrPtr(rela_shndx)) { |
| 643 | inline else => |shdr, class| { |
| 644 | assert(elf.targetLoad(&shdr.type) == .RELA); |
| 645 | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); |
| 646 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 647 | rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))], |
| 648 | )); |
| 649 | { |
| 650 | const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info); |
| 651 | const none_reloc_type = MachineRelocType.none(elf).unwrap(elf); |
| 652 | assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list |
| 653 | } |
| 654 | const old_offset = elf.targetLoad(&relas[@intFromEnum(index)].offset); |
| 655 | elf.targetStore(&relas[@intFromEnum(index)].offset, @intCast( |
| 656 | old_offset - old_base + new_base, |
| 657 | )); |
| 658 | }, |
| 659 | } |
| 660 | } |
| 380 | 661 | }; |
| 381 | 662 | }; |
| 382 | 663 | |
| 664 | /// Identifies a single entry in the GOT. |
| 665 | const GotKey = union(enum) { |
| 666 | /// The entry is a reserved word, initialized to zero. `initHeaders` will add as many of these |
| 667 | /// as the target machine ABI requires. |
| 668 | /// |
| 669 | /// This `u32` value exists to allow reserving multiple words with distinct keys. |
| 670 | reserved: u32, |
| 671 | |
| 672 | /// Value is the address of the given symbol. |
| 673 | symbol: Symbol.Id, |
| 674 | |
| 675 | /// Value is the signed offset of the given symbol from the TLS pointer. |
| 676 | tpoff: Symbol.Id, |
| 677 | |
| 678 | /// Value is the TLS module ID of the DSO we are creating. |
| 679 | /// |
| 680 | /// Used for the first of the two GOT entries generated by a TLSLD relocation. |
| 681 | tlsld0, |
| 682 | /// Value is always 0. |
| 683 | /// |
| 684 | /// Used for the second of the two GOT entries generated by a TLSLD relocation. |
| 685 | tlsld1, |
| 686 | |
| 687 | /// Value is the TLS module ID for the given STT_TLS symbol. |
| 688 | /// |
| 689 | /// Used for the first of the two GOT entries generated by a TLSGD relocation. |
| 690 | tlsgd0: Symbol.Id, |
| 691 | /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area. |
| 692 | /// |
| 693 | /// Used for the second of the two GOT entries generated by a TLSGD relocation. |
| 694 | tlsgd1: Symbol.Id, |
| 695 | }; |
| 696 | |
| 697 | /// A relocation targeting a particular GOT entry. |
| 698 | const GotReloc = struct { |
| 699 | /// The node containing this relocation. Possible values are: |
| 700 | /// * An input section |
| 701 | /// * A section |
| 702 | /// * A NAV, UAV, or lazy code/data |
| 703 | /// * `.none`, if this relocation was deleted (in which case it should be ignored) |
| 704 | node: MappedFile.Node.Index, |
| 705 | /// The offset of the relocation inside of `node`. |
| 706 | offset: u64, |
| 707 | target: GotKey, |
| 708 | addend: i64, |
| 709 | type: GotReloc.Type, |
| 710 | |
| 711 | const deleted: GotReloc = .{ |
| 712 | .node = .none, |
| 713 | .offset = undefined, |
| 714 | .target = undefined, |
| 715 | .addend = undefined, |
| 716 | .type = undefined, |
| 717 | }; |
| 718 | |
| 719 | const Type = enum(u8) { |
| 720 | offset64, |
| 721 | offset32, |
| 722 | rel64, |
| 723 | rel32, |
| 724 | }; |
| 725 | |
| 726 | const Index = enum(u32) { |
| 727 | none = std.math.maxInt(u32), |
| 728 | _, |
| 729 | |
| 730 | fn get(index: GotReloc.Index, elf: *Elf) *GotReloc { |
| 731 | return &elf.got_relocs.items[@intFromEnum(index)]; |
| 732 | } |
| 733 | }; |
| 734 | |
| 735 | fn apply(reloc: *const GotReloc, elf: *Elf) void { |
| 736 | assert(elf.ehdrField(.type) != .REL); |
| 737 | if (reloc.node == .none) return; // deleted |
| 738 | if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) { |
| 739 | // There's no point applying the relocation now, because it will be re-applied by |
| 740 | // `flushMoved` at some point anyway. |
| 741 | return; |
| 742 | } |
| 743 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 744 | .file => unreachable, |
| 745 | .ehdr => unreachable, |
| 746 | .shdr => unreachable, |
| 747 | .segment => unreachable, |
| 748 | .section => |shndx| shndx.vaddr(elf), |
| 749 | .input_section => |isi| isi.ptrConst(elf).vaddr, |
| 750 | inline .nav, |
| 751 | .uav, |
| 752 | .lazy_code, |
| 753 | .lazy_const_data, |
| 754 | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 755 | }; |
| 756 | const dest_vaddr = node_vaddr + reloc.offset; |
| 757 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 758 | const target_endian = elf.targetEndian(); |
| 759 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 760 | const got_index: u64 = elf.got.getIndex(reloc.target).?; |
| 761 | const got_offset: u64 = switch (elf.identClass()) { |
| 762 | .NONE, _ => unreachable, |
| 763 | inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, |
| 764 | }; |
| 765 | const addend: u64 = @bitCast(reloc.addend); |
| 766 | switch (reloc.type) { |
| 767 | .offset64 => std.mem.writeInt( |
| 768 | u64, |
| 769 | dest_slice[0..8], |
| 770 | got_offset +% addend, |
| 771 | target_endian, |
| 772 | ), |
| 773 | .offset32 => std.mem.writeInt( |
| 774 | u32, |
| 775 | dest_slice[0..4], |
| 776 | @intCast(got_offset +% addend), |
| 777 | target_endian, |
| 778 | ), |
| 779 | .rel64 => std.mem.writeInt( |
| 780 | i64, |
| 781 | dest_slice[0..8], |
| 782 | @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr), |
| 783 | target_endian, |
| 784 | ), |
| 785 | .rel32 => std.mem.writeInt( |
| 786 | i32, |
| 787 | dest_slice[0..4], |
| 788 | @intCast(@as(i64, @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr))), |
| 789 | target_endian, |
| 790 | ), |
| 791 | } |
| 792 | } |
| 793 | }; |
| 794 | |
| 795 | pub const MachineRelocType = union { |
| 796 | X86_64: std.elf.R_X86_64, |
| 797 | AARCH64: std.elf.R_AARCH64, |
| 798 | RISCV: std.elf.R_RISCV, |
| 799 | PPC64: std.elf.R_PPC64, |
| 800 | |
| 801 | pub fn none(elf: *Elf) MachineRelocType { |
| 802 | return switch (elf.ehdrField(.machine)) { |
| 803 | else => unreachable, |
| 804 | .AARCH64 => .{ .AARCH64 = .NONE }, |
| 805 | .PPC64 => .{ .PPC64 = .NONE }, |
| 806 | .RISCV => .{ .RISCV = .NONE }, |
| 807 | .X86_64 => .{ .X86_64 = .NONE }, |
| 808 | }; |
| 809 | } |
| 810 | pub fn jumpSlot(elf: *Elf) MachineRelocType { |
| 811 | return switch (elf.ehdrField(.machine)) { |
| 812 | else => unreachable, |
| 813 | .X86_64 => .{ .X86_64 = .JUMP_SLOT }, |
| 814 | }; |
| 815 | } |
| 816 | pub fn globDat(elf: *Elf) MachineRelocType { |
| 817 | return switch (elf.ehdrField(.machine)) { |
| 818 | else => unreachable, |
| 819 | .X86_64 => .{ .X86_64 = .GLOB_DAT }, |
| 820 | }; |
| 821 | } |
| 822 | pub fn dtpOffAddr(elf: *Elf) MachineRelocType { |
| 823 | return switch (elf.ehdrField(.machine)) { |
| 824 | else => unreachable, |
| 825 | .X86_64 => .{ .X86_64 = .DTPOFF64 }, |
| 826 | }; |
| 827 | } |
| 828 | pub fn absAddr(elf: *Elf) MachineRelocType { |
| 829 | return switch (elf.ehdrField(.machine)) { |
| 830 | else => unreachable, |
| 831 | .AARCH64 => .{ .AARCH64 = .ABS64 }, |
| 832 | .PPC64 => .{ .PPC64 = .ADDR64 }, |
| 833 | .RISCV => .{ .RISCV = .@"64" }, |
| 834 | .X86_64 => .{ .X86_64 = .@"64" }, |
| 835 | }; |
| 836 | } |
| 837 | pub fn sizeAddr(elf: *Elf) MachineRelocType { |
| 838 | return switch (elf.ehdrField(.machine)) { |
| 839 | else => unreachable, |
| 840 | .X86_64 => .{ .X86_64 = .SIZE64 }, |
| 841 | }; |
| 842 | } |
| 843 | |
| 844 | pub fn wrap(int: u32, elf: *Elf) MachineRelocType { |
| 845 | return switch (elf.ehdrField(.machine)) { |
| 846 | else => unreachable, |
| 847 | inline .AARCH64, |
| 848 | .PPC64, |
| 849 | .RISCV, |
| 850 | .X86_64, |
| 851 | => |machine| @unionInit(MachineRelocType, @tagName(machine), @enumFromInt(int)), |
| 852 | }; |
| 853 | } |
| 854 | pub fn unwrap(rt: MachineRelocType, elf: *Elf) u32 { |
| 855 | return switch (elf.ehdrField(.machine)) { |
| 856 | else => unreachable, |
| 857 | inline .AARCH64, |
| 858 | .PPC64, |
| 859 | .RISCV, |
| 860 | .X86_64, |
| 861 | => |machine| @intFromEnum(@field(rt, @tagName(machine))), |
| 862 | }; |
| 863 | } |
| 864 | }; |
| 865 | |
| 866 | /// A relocation targeting an arbitrary symbol with a fixed addend. |
| 867 | const SymbolReloc = struct { |
| 868 | /// The node containing this relocation. Possible values are: |
| 869 | /// * An input section |
| 870 | /// * A section |
| 871 | /// * A NAV, UAV, or lazy code/data |
| 872 | node: MappedFile.Node.Index, |
| 873 | /// The offset of the relocation inside of `node`. |
| 874 | offset: u64, |
| 875 | /// A symbol used to compute the relocated value. Precise meaning depends on `@"type"`. |
| 876 | target: Symbol.Id, |
| 877 | /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`. |
| 878 | addend: i64, |
| 879 | /// Specifies how to apply the relocation. |
| 880 | type: SymbolReloc.Type, |
| 881 | /// Forms a linked list of all symbol relocations with the same `target`. This list exists so |
| 882 | /// that all relocations targeting a particular symbol can be re-applied if that symbol moves. |
| 883 | /// Doubly-linked so that relocations can be removed. |
| 884 | next: SymbolReloc.Index, |
| 885 | /// Back-reference in a doubly-linked list---see `next`. |
| 886 | prev: SymbolReloc.Index, |
| 887 | /// If this relocation has a corresponding output relocation, this is its index within the |
| 888 | /// appropriate SHT_RELA section (see `relaSection`). If there is no output relocation |
| 889 | /// corresponding to this relocation, this is `.none`. |
| 890 | /// |
| 891 | /// If we are producing a relocatable, this field is always populated, because all relocations |
| 892 | /// are emitted as output relocations. |
| 893 | /// |
| 894 | /// If we are producing a DSO, this field is populated if this relocation requires a runtime |
| 895 | /// relocation entry. The entry will be removed if we discover a definition which allows us to |
| 896 | /// statically resolve the relocation. |
| 897 | rela_index: Section.RelaIndex.Optional, |
| 898 | |
| 899 | /// Determines the section in which this relocation will be placed if it is outstanding. |
| 900 | /// |
| 901 | /// When producing a relocatable (ET_REL), the relocation section is `Section.rela.shndx` for |
| 902 | /// the section of `node`, and this function asserts that the aforementioned `rela.shndx` field |
| 903 | /// is populated. |
| 904 | /// |
| 905 | /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt` |
| 906 | /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries. |
| 907 | fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index { |
| 908 | const shndx = switch (elf.ehdrField(.type)) { |
| 909 | .NONE, .CORE, _ => unreachable, |
| 910 | .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx, |
| 911 | .EXEC, .DYN => elf.shndx.rela_dyn, |
| 912 | }; |
| 913 | assert(shndx != .UNDEF); |
| 914 | return shndx; |
| 915 | } |
| 916 | |
| 917 | const Index = enum(u32) { |
| 918 | none = std.math.maxInt(u32), |
| 919 | _, |
| 920 | |
| 921 | fn get(index: SymbolReloc.Index, elf: *Elf) *SymbolReloc { |
| 922 | return &elf.symbol_relocs.items[@intFromEnum(index)]; |
| 923 | } |
| 924 | }; |
| 925 | |
| 926 | const Type = enum { |
| 927 | /// This input relocation is being directly forwarded to an `ElfN.Rela` entry in the output |
| 928 | /// file. `rela_index` is guaranteed to be populated. The ELF relocation type is available |
| 929 | /// in the `ElfN.Rela` entry. |
| 930 | /// |
| 931 | /// If we are emitting a relocatable (`ET_REL`), all symbol relocs use this type (since we |
| 932 | /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type. |
| 933 | write_rela, |
| 934 | |
| 935 | abs64, |
| 936 | abs32, |
| 937 | abs32s, |
| 938 | rel64, |
| 939 | rel32, |
| 940 | pltrel64, |
| 941 | pltrel32, |
| 942 | dtpoff64, |
| 943 | dtpoff32, |
| 944 | tpoff64, |
| 945 | tpoff32, |
| 946 | size64, |
| 947 | size32, |
| 948 | |
| 949 | fn dependsOnTlsSize(t: SymbolReloc.Type) bool { |
| 950 | return switch (t) { |
| 951 | .tpoff32, .tpoff64 => true, |
| 952 | else => false, |
| 953 | }; |
| 954 | } |
| 955 | }; |
| 956 | |
| 957 | fn apply(reloc: *const SymbolReloc, elf: *Elf) void { |
| 958 | assert(elf.ehdrField(.type) != .REL); |
| 959 | assert(reloc.node != .none); |
| 960 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { |
| 961 | // There's no point applying the relocation now, because it will be re-applied by |
| 962 | // `flushMoved` at some point anyway. |
| 963 | return; |
| 964 | } |
| 965 | if (reloc.rela_index != .none) { |
| 966 | // This relocation has been lowered to a runtime relocation. Until that changes, it is |
| 967 | // not our job to apply it. |
| 968 | return; |
| 969 | } |
| 970 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 971 | .file => unreachable, |
| 972 | .ehdr => unreachable, |
| 973 | .shdr => unreachable, |
| 974 | .segment => unreachable, |
| 975 | .section => |shndx| shndx.vaddr(elf), |
| 976 | .input_section => |isi| isi.ptrConst(elf).vaddr, |
| 977 | inline .nav, |
| 978 | .uav, |
| 979 | .lazy_code, |
| 980 | .lazy_const_data, |
| 981 | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 982 | }; |
| 983 | const dest_vaddr = node_vaddr + reloc.offset; |
| 984 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 985 | const target_endian = elf.targetEndian(); |
| 986 | const sym_value: u64, const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) { |
| 987 | inline else => |target_sym| .{ |
| 988 | elf.targetLoad(&target_sym.value), |
| 989 | elf.targetLoad(&target_sym.size), |
| 990 | }, |
| 991 | }; |
| 992 | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); |
| 993 | type: switch (reloc.type) { |
| 994 | .write_rela => unreachable, |
| 995 | .abs64 => std.mem.writeInt( |
| 996 | u64, |
| 997 | dest_slice[0..8], |
| 998 | target_value, |
| 999 | target_endian, |
| 1000 | ), |
| 1001 | .abs32 => std.mem.writeInt( |
| 1002 | u32, |
| 1003 | dest_slice[0..4], |
| 1004 | @intCast(target_value), |
| 1005 | target_endian, |
| 1006 | ), |
| 1007 | .abs32s => std.mem.writeInt( |
| 1008 | i32, |
| 1009 | dest_slice[0..4], |
| 1010 | @intCast(@as(i64, @bitCast(target_value))), |
| 1011 | target_endian, |
| 1012 | ), |
| 1013 | .rel64 => std.mem.writeInt( |
| 1014 | i64, |
| 1015 | dest_slice[0..8], |
| 1016 | @bitCast(target_value -% dest_vaddr), |
| 1017 | target_endian, |
| 1018 | ), |
| 1019 | .rel32 => std.mem.writeInt( |
| 1020 | i32, |
| 1021 | dest_slice[0..4], |
| 1022 | @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))), |
| 1023 | target_endian, |
| 1024 | ), |
| 1025 | .pltrel64 => { |
| 1026 | const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel64; |
| 1027 | if (elf.pltEntryIsDead(plt_index)) continue :type .rel64; |
| 1028 | const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1029 | else => |machine| @panic(@tagName(machine)), |
| 1030 | .X86_64 => .{ elf.shndx.plt_sec, 16 }, |
| 1031 | }; |
| 1032 | const plt_entry = plt_shndx.vaddr(elf) +% plt_index * plt_entry_size; |
| 1033 | std.mem.writeInt( |
| 1034 | i64, |
| 1035 | dest_slice[0..8], |
| 1036 | @bitCast(plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr), |
| 1037 | target_endian, |
| 1038 | ); |
| 1039 | }, |
| 1040 | .pltrel32 => { |
| 1041 | const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel32; |
| 1042 | if (elf.pltEntryIsDead(plt_index)) continue :type .rel32; |
| 1043 | const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1044 | else => |machine| @panic(@tagName(machine)), |
| 1045 | .X86_64 => .{ elf.shndx.plt_sec, 16 }, |
| 1046 | }; |
| 1047 | const plt_entry = plt_shndx.vaddr(elf) +% plt_index * plt_entry_size; |
| 1048 | std.mem.writeInt( |
| 1049 | i32, |
| 1050 | dest_slice[0..4], |
| 1051 | @intCast(@as(i64, @bitCast( |
| 1052 | plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr, |
| 1053 | ))), |
| 1054 | target_endian, |
| 1055 | ); |
| 1056 | }, |
| 1057 | .size64 => std.mem.writeInt( |
| 1058 | u64, |
| 1059 | dest_slice[0..8], |
| 1060 | sym_size +% @as(u64, @bitCast(reloc.addend)), |
| 1061 | target_endian, |
| 1062 | ), |
| 1063 | .size32 => std.mem.writeInt( |
| 1064 | u32, |
| 1065 | dest_slice[0..4], |
| 1066 | @intCast(sym_size +% @as(u64, @bitCast(reloc.addend))), |
| 1067 | target_endian, |
| 1068 | ), |
| 1069 | .dtpoff64 => std.mem.writeInt( |
| 1070 | i64, |
| 1071 | dest_slice[0..8], |
| 1072 | @bitCast(target_value), |
| 1073 | target_endian, |
| 1074 | ), |
| 1075 | .dtpoff32 => std.mem.writeInt( |
| 1076 | i32, |
| 1077 | dest_slice[0..4], |
| 1078 | @intCast(@as(i64, @bitCast(target_value))), |
| 1079 | target_endian, |
| 1080 | ), |
| 1081 | .tpoff64 => { |
| 1082 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1083 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1084 | inline else => |phdr| tls_size: { |
| 1085 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1086 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1087 | }, |
| 1088 | }; |
| 1089 | std.mem.writeInt( |
| 1090 | i64, |
| 1091 | dest_slice[0..8], |
| 1092 | @bitCast(target_value -% tls_size), |
| 1093 | target_endian, |
| 1094 | ); |
| 1095 | }, |
| 1096 | .tpoff32 => { |
| 1097 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1098 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1099 | inline else => |phdr| tls_size: { |
| 1100 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1101 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1102 | }, |
| 1103 | }; |
| 1104 | std.mem.writeInt( |
| 1105 | i32, |
| 1106 | dest_slice[0..4], |
| 1107 | @intCast(@as(i64, @bitCast(target_value -% tls_size))), |
| 1108 | target_endian, |
| 1109 | ); |
| 1110 | }, |
| 1111 | } |
| 1112 | } |
| 1113 | |
| 1114 | fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void { |
| 1115 | assert(index.get(elf) == reloc); |
| 1116 | switch (reloc.prev) { |
| 1117 | .none => { |
| 1118 | const target_ptr = reloc.target.index(elf).ptr(elf); |
| 1119 | assert(target_ptr.first_target_reloc == index); |
| 1120 | target_ptr.first_target_reloc = reloc.next; |
| 1121 | }, |
| 1122 | else => |prev| prev.get(elf).next = reloc.next, |
| 1123 | } |
| 1124 | switch (reloc.next) { |
| 1125 | .none => {}, |
| 1126 | else => |next| next.get(elf).prev = reloc.prev, |
| 1127 | } |
| 1128 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 1129 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); |
| 1130 | if (elf.nodeRequiresTextrel(reloc.node)) { |
| 1131 | elf.textrel_count -= 1; |
| 1132 | } |
| 1133 | } |
| 1134 | if (reloc.type.dependsOnTlsSize()) { |
| 1135 | assert(elf.tls_size_symbol_relocs.swapRemove(index)); |
| 1136 | } |
| 1137 | reloc.* = undefined; |
| 1138 | } |
| 1139 | }; |
| 1140 | |
| 383 | 1141 | fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void { |
| 384 | 1142 | const gpa = elf.base.comp.gpa; |
| 385 | 1143 | |
| ... | ... | @@ -426,59 +1184,70 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe |
| 426 | 1184 | try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 427 | 1185 | } |
| 428 | 1186 | |
| 429 | | try elf.got.plt.ensureUnusedCapacity(gpa, len); |
| 430 | | const need_plt_capacity = elf.got.plt.count() + len; |
| 1187 | try elf.ensureUnusedPltCapacity(len); |
| 1188 | } |
| 1189 | }, |
| 1190 | } |
| 1191 | } |
| 1192 | fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { |
| 1193 | const gpa = elf.base.comp.gpa; |
| 431 | 1194 | |
| 432 | | switch (elf.ehdrField(.machine)) { |
| 433 | | else => |machine| @panic(@tagName(machine)), |
| 434 | | .X86_64 => { |
| 435 | | // Ensure the `.plt` section's node is big enough |
| 436 | | const plt_need_size: usize = 16 * (1 + need_plt_capacity); |
| 437 | | _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 438 | | if (plt_cur_size < plt_need_size) { |
| 439 | | const new_size = plt_need_size +| plt_need_size / MappedFile.growth_factor; |
| 440 | | try elf.shndx.plt.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 441 | | } |
| 1195 | try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len); |
| 442 | 1196 | |
| 443 | | // Ensure the `.got.plt` section's node is big enough |
| 444 | | const got_plt_need_size: usize = switch (elf.identClass()) { |
| 445 | | .NONE, _ => unreachable, |
| 446 | | inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity), |
| 447 | | }; |
| 448 | | _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 449 | | if (got_plt_cur_size < got_plt_need_size) { |
| 450 | | const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor; |
| 451 | | try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 452 | | } |
| 1197 | try elf.plt.ensureUnusedCapacity(gpa, len); |
| 1198 | const need_plt_capacity = elf.plt.count() + len; |
| 453 | 1199 | |
| 454 | | // Ensure the `.plt.sec` section's node is big enough |
| 455 | | const plt_sec_need_size: usize = 16 * need_plt_capacity; |
| 456 | | _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 457 | | if (plt_sec_cur_size < plt_sec_need_size) { |
| 458 | | const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor; |
| 459 | | try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 460 | | } |
| 1200 | switch (elf.ehdrField(.machine)) { |
| 1201 | else => |machine| @panic(@tagName(machine)), |
| 1202 | .X86_64 => { |
| 1203 | // Ensure the `.plt` section's node is big enough |
| 1204 | const plt_need_size: usize = 16 * (1 + need_plt_capacity); |
| 1205 | _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 1206 | if (plt_cur_size < plt_need_size) { |
| 1207 | const new_size = plt_need_size +| plt_need_size / MappedFile.growth_factor; |
| 1208 | try elf.shndx.plt.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 1209 | } |
| 461 | 1210 | |
| 462 | | // Ensure the `.rela.plt` section's node is big enough |
| 463 | | const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx; |
| 464 | | const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) { |
| 465 | | inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity), |
| 466 | | }; |
| 467 | | _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 468 | | if (rela_plt_cur_size < rela_plt_need_size) { |
| 469 | | const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor; |
| 470 | | try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 471 | | } else { |
| 472 | | // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can |
| 473 | | // be updated if we do indeed add a PLT entry. |
| 474 | | try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf); |
| 475 | | } |
| 476 | | }, |
| 477 | | } |
| 1211 | // Ensure the `.got.plt` section's node is big enough |
| 1212 | const got_plt_need_size: usize = switch (elf.identClass()) { |
| 1213 | .NONE, _ => unreachable, |
| 1214 | inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity), |
| 1215 | }; |
| 1216 | _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 1217 | if (got_plt_cur_size < got_plt_need_size) { |
| 1218 | const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor; |
| 1219 | try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 1220 | } |
| 1221 | |
| 1222 | // Ensure the `.plt.sec` section's node is big enough |
| 1223 | const plt_sec_need_size: usize = 16 * need_plt_capacity; |
| 1224 | _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 1225 | if (plt_sec_cur_size < plt_sec_need_size) { |
| 1226 | const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor; |
| 1227 | try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 478 | 1228 | } |
| 479 | 1229 | }, |
| 480 | 1230 | } |
| 481 | 1231 | } |
| 1232 | /// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at |
| 1233 | /// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`. |
| 1234 | fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool { |
| 1235 | assert(elf.shndx.plt != .UNDEF); |
| 1236 | assert(plt_index <= elf.plt.count()); |
| 1237 | // We track which PLT entries are alive based on the relocation entries, since there is a 1-1 |
| 1238 | // mapping between PLT entries and `.rela.plt` entries and the relocation entries already have |
| 1239 | // a free-list mechanism. |
| 1240 | switch (elf.shdrPtr(elf.shndx.rela_plt)) { |
| 1241 | inline else => |rela_shdr, class| { |
| 1242 | const size = elf.targetLoad(&rela_shdr.size); |
| 1243 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 1244 | elf.shndx.rela_plt.get(elf).ni.slice(&elf.mf)[0..@intCast(size)], |
| 1245 | )); |
| 1246 | const rel_type = elf.targetLoad(&relas[plt_index].info).type; |
| 1247 | return rel_type == MachineRelocType.none(elf).unwrap(elf); |
| 1248 | }, |
| 1249 | } |
| 1250 | } |
| 482 | 1251 | |
| 483 | 1252 | const AddLocalSymbolOptions = struct { |
| 484 | 1253 | node: MappedFile.Node.Index, |
| ... | ... | @@ -695,6 +1464,11 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 695 | 1464 | .weak => .WEAK, |
| 696 | 1465 | }; |
| 697 | 1466 | |
| 1467 | const @"type": std.elf.STT = switch (opts.type) { |
| 1468 | .NOTYPE => elf.dso_globals.get(opts.name.strtab) orelse .NOTYPE, |
| 1469 | else => |t| t, |
| 1470 | }; |
| 1471 | |
| 698 | 1472 | const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len); |
| 699 | 1473 | elf.symtab.appendAssumeCapacity(.{ |
| 700 | 1474 | .node = opts.node, |
| ... | ... | @@ -713,7 +1487,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 713 | 1487 | .name = @intFromEnum(opts.name.strtab), |
| 714 | 1488 | .value = @intCast(opts.value), |
| 715 | 1489 | .size = @intCast(opts.size), |
| 716 | | .info = .{ .type = opts.type, .bind = bind }, |
| 1490 | .info = .{ .type = @"type", .bind = bind }, |
| 717 | 1491 | .other = .{ .visibility = opts.visibility }, |
| 718 | 1492 | .shndx = opts.shndx.toSection().?, |
| 719 | 1493 | }; |
| ... | ... | @@ -749,7 +1523,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 749 | 1523 | .name = @intFromEnum(opts.name.dynstr), |
| 750 | 1524 | .value = @intCast(opts.value), |
| 751 | 1525 | .size = @intCast(opts.size), |
| 752 | | .info = .{ .type = opts.type, .bind = bind }, |
| 1526 | .info = .{ .type = @"type", .bind = bind }, |
| 753 | 1527 | .other = .{ .visibility = opts.visibility }, |
| 754 | 1528 | .shndx = opts.shndx.toSection().?, |
| 755 | 1529 | }; |
| ... | ... | @@ -778,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 778 | 1552 | if (new_global_ptr.dynsym_index != 0 and |
| 779 | 1553 | opts.visibility == .DEFAULT and |
| 780 | 1554 | opts.shndx == .UNDEF and |
| 781 | | opts.type == .FUNC) |
| 1555 | (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC)) |
| 782 | 1556 | { |
| 783 | 1557 | // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO. |
| 784 | | // We therefore might need a PLT entry, so let's add one now. TODO: it'd be good to remove |
| 785 | | // the PLT entry if we later discover a link inpu which resolves this reference. |
| 1558 | // We therefore might need a PLT entry, so let's add one now. |
| 786 | 1559 | elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index); |
| 1560 | // TODO: we also need to emit a PLT entry if the symbol could be preempted/interposed! By |
| 1561 | // not doing that we're basically implementing the behavior of `-Bsymbolic-functions`. |
| 787 | 1562 | } |
| 788 | 1563 | |
| 789 | 1564 | return .global(opts.name.strtab); |
| ... | ... | @@ -800,6 +1575,7 @@ fn setGlobalSymbolValue( |
| 800 | 1575 | shndx: Section.Index, |
| 801 | 1576 | }, |
| 802 | 1577 | ) void { |
| 1578 | assert(new.shndx != .UNDEF); |
| 803 | 1579 | const old_node = global_ptr.symtab_index.ptr(elf).node; |
| 804 | 1580 | if (old_node != .none) { |
| 805 | 1581 | if (global_ptr.next_in_node != .empty) { |
| ... | ... | @@ -874,7 +1650,40 @@ fn setGlobalSymbolValue( |
| 874 | 1650 | }, |
| 875 | 1651 | }; |
| 876 | 1652 | |
| 877 | | global_ptr.flushMoved(elf, new.value); |
| 1653 | // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to |
| 1654 | // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error. |
| 1655 | // This also allows the PLT entry to be reused---see `pltEntryIsDead`. |
| 1656 | if (elf.plt.getIndex(.global(global_name))) |plt_index| { |
| 1657 | // TODO: we might still need the PLT entry if the symbol could be preempted/interposed! See |
| 1658 | // matching comment at the end of `addGlobalSymbolAssumeCapacity`. |
| 1659 | if (!elf.pltEntryIsDead(plt_index)) { |
| 1660 | elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index)); |
| 1661 | assert(elf.pltEntryIsDead(plt_index)); |
| 1662 | } |
| 1663 | } |
| 1664 | |
| 1665 | // If this symbol was previously undefined, relocations targeting it may have been lowered to |
| 1666 | // runtime relocations which we have now discovered we do not need, so delete those. |
| 1667 | if (elf.shndx.dynamic != .UNDEF) { |
| 1668 | var ri = global_ptr.symtab_index.ptr(elf).first_target_reloc; |
| 1669 | while (ri != .none) { |
| 1670 | const reloc = ri.get(elf); |
| 1671 | assert(reloc.target == Symbol.Id.global(global_name)); |
| 1672 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 1673 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); |
| 1674 | if (elf.nodeRequiresTextrel(reloc.node)) { |
| 1675 | elf.textrel_count -= 1; |
| 1676 | } |
| 1677 | reloc.rela_index = .none; |
| 1678 | } |
| 1679 | ri = reloc.next; |
| 1680 | } |
| 1681 | } |
| 1682 | |
| 1683 | // Finally, update the symbol value, re-applying target relocations. Also note that because we |
| 1684 | // possibly removed the PLT entry above, some relocations which were previously targeting the |
| 1685 | // PLT will now instead target the symbol itself. |
| 1686 | Symbol.Id.global(global_name).flushMoved(elf, new.value); |
| 878 | 1687 | } |
| 879 | 1688 | /// When the same global symbol appears in two inputs---even if one symbol is defined and the other |
| 880 | 1689 | /// undefined---their visibility values are combined to determine the resulting visibility, which |
| ... | ... | @@ -1009,14 +1818,45 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 1009 | 1818 | if (elf.targetEndian() != native_endian) { |
| 1010 | 1819 | std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym); |
| 1011 | 1820 | } |
| 1821 | global_ptr.dynsym_index = 0; |
| 1012 | 1822 | } |
| 1013 | 1823 | }, |
| 1014 | 1824 | } |
| 1015 | 1825 | } |
| 1016 | 1826 | fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void { |
| 1017 | 1827 | const target_endian = elf.targetEndian(); |
| 1018 | | const plt_index: u32 = @intCast(elf.got.plt.count()); |
| 1019 | | elf.got.plt.putAssumeCapacityNoClobber(.global(global_name), {}); |
| 1828 | |
| 1829 | // We use the existing free-list tracking of the `.rela.plt` section to also behave as a |
| 1830 | // free-list for the PLT itself---see `pltEntryIsDead` for details. |
| 1831 | const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{ |
| 1832 | .type = .jumpSlot(elf), |
| 1833 | .offset = 0, // populated later |
| 1834 | .raw_sym_index = dynsym_index, |
| 1835 | .addend = 0, |
| 1836 | })); |
| 1837 | |
| 1838 | // Now that we know the index, we can set the relocation's offset. |
| 1839 | const got_plt_addr = switch (elf.shdrPtr(elf.shndx.got_plt)) { |
| 1840 | inline else => |shdr, class| got_plt_addr: { |
| 1841 | const ent_size = @sizeOf(class.ElfN().Addr); |
| 1842 | assert(elf.targetLoad(&shdr.entsize) == ent_size); |
| 1843 | const offset = ent_size * @as(u64, 3 + plt_index); |
| 1844 | assert(offset <= elf.targetLoad(&shdr.size)); |
| 1845 | break :got_plt_addr elf.targetLoad(&shdr.addr) + offset; |
| 1846 | }, |
| 1847 | }; |
| 1848 | elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_addr); |
| 1849 | |
| 1850 | if (plt_index < elf.plt.count()) { |
| 1851 | // We reused a free entry, so we're already done! |
| 1852 | elf.plt.setKey(plt_index, .global(global_name)); |
| 1853 | return; |
| 1854 | } |
| 1855 | |
| 1856 | // We added a new entry, so we now need to extend the PLT sections. |
| 1857 | assert(plt_index == elf.plt.count()); |
| 1858 | elf.plt.putAssumeCapacityNoClobber(.global(global_name), {}); |
| 1859 | |
| 1020 | 1860 | switch (elf.ehdrField(.machine)) { |
| 1021 | 1861 | else => |machine| @panic(@tagName(machine)), |
| 1022 | 1862 | .X86_64 => { |
| ... | ... | @@ -1044,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1044 | 1884 | }, |
| 1045 | 1885 | }; |
| 1046 | 1886 | |
| 1047 | | const got_plt_shndx = elf.shndx.got_plt; |
| 1048 | 1887 | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; |
| 1049 | | const got_plt_addr = got_plt_addr: switch (elf.shdrPtr(got_plt_shndx)) { |
| 1888 | switch (elf.shdrPtr(elf.shndx.got_plt)) { |
| 1050 | 1889 | inline else => |shdr, class| { |
| 1051 | 1890 | const ent_size = @sizeOf(class.ElfN().Addr); |
| 1052 | 1891 | const old_size = ent_size * (3 + plt_index); |
| 1892 | assert(elf.targetLoad(&shdr.size) == old_size); |
| 1053 | 1893 | elf.targetStore(&shdr.size, old_size + ent_size); |
| 1054 | 1894 | std.mem.writeInt( |
| 1055 | 1895 | class.ElfN().Addr, |
| ... | ... | @@ -1057,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1057 | 1897 | @intCast(plt_addr), |
| 1058 | 1898 | target_endian, |
| 1059 | 1899 | ); |
| 1060 | | break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size; |
| 1061 | 1900 | }, |
| 1062 | | }; |
| 1901 | } |
| 1063 | 1902 | |
| 1064 | 1903 | const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni; |
| 1065 | 1904 | switch (elf.shdrPtr(elf.shndx.plt_sec)) { |
| ... | ... | @@ -1082,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1082 | 1921 | ); |
| 1083 | 1922 | }, |
| 1084 | 1923 | } |
| 1085 | | |
| 1086 | | const rela_plt_shndx = got_plt_shndx.get(elf).rela_shndx; |
| 1087 | | const rela_plt_ni = rela_plt_shndx.get(elf).ni; |
| 1088 | | switch (elf.shdrPtr(rela_plt_shndx)) { |
| 1089 | | inline else => |shdr, class| { |
| 1090 | | const Rela = class.ElfN().Rela; |
| 1091 | | const rela_size = elf.targetLoad(&shdr.entsize); |
| 1092 | | const old_size = rela_size * plt_index; |
| 1093 | | const new_size = old_size + rela_size; |
| 1094 | | elf.targetStore(&shdr.size, new_size); |
| 1095 | | const rela: *Rela = @ptrCast(@alignCast( |
| 1096 | | rela_plt_ni.slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)], |
| 1097 | | )); |
| 1098 | | rela.* = .{ |
| 1099 | | .offset = @intCast(got_plt_addr), |
| 1100 | | .info = .{ |
| 1101 | | .type = @intFromEnum(std.elf.R_X86_64.JUMP_SLOT), |
| 1102 | | .sym = @intCast(dynsym_index), |
| 1103 | | }, |
| 1104 | | .addend = 0, |
| 1105 | | }; |
| 1106 | | if (target_endian != native_endian) std.mem.byteSwapAllFields(Rela, rela); |
| 1107 | | }, |
| 1108 | | } |
| 1109 | 1924 | }, |
| 1110 | 1925 | } |
| 1111 | 1926 | } |
| ... | ... | @@ -1113,13 +1928,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1113 | 1928 | const Symbol = struct { |
| 1114 | 1929 | /// The node which this symbol's value is defined relative to. Possible values are: |
| 1115 | 1930 | /// * `.none` for a SHN_ABS or SHN_UNDEF symbol |
| 1116 | | /// * A section (the symbol's value is that section's vaddr) |
| 1931 | /// * A section (the symbol's value is some vaddr in that section) |
| 1117 | 1932 | /// * An input section (the symbol's value is some vaddr in that input section) |
| 1118 | 1933 | /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node) |
| 1119 | 1934 | node: MappedFile.Node.Index, |
| 1120 | 1935 | |
| 1121 | 1936 | /// The head of a linked list of relocations targeting this symbol. |
| 1122 | | first_target_reloc: Reloc.Index, |
| 1937 | first_target_reloc: SymbolReloc.Index, |
| 1123 | 1938 | |
| 1124 | 1939 | const Global = struct { |
| 1125 | 1940 | /// The current index of the symtab entry for this global symbol. |
| ... | ... | @@ -1136,16 +1951,6 @@ const Symbol = struct { |
| 1136 | 1951 | /// |
| 1137 | 1952 | /// If `node` is `.none`, this is `.empty`. |
| 1138 | 1953 | prev_in_node: String(.strtab), |
| 1139 | | |
| 1140 | | /// Like `Symbol.Index.flushMoved`, but also updates the dynamic symbol table if necessary. |
| 1141 | | fn flushMoved(g: *const Global, elf: *Elf, value: u64) void { |
| 1142 | | g.symtab_index.flushMoved(elf, value); |
| 1143 | | if (g.dynsym_index != 0) { |
| 1144 | | switch (elf.dynsymPtr(g.dynsym_index)) { |
| 1145 | | inline else => |sym| elf.targetStore(&sym.value, @intCast(value)), |
| 1146 | | } |
| 1147 | | } |
| 1148 | | } |
| 1149 | 1954 | }; |
| 1150 | 1955 | |
| 1151 | 1956 | /// An index directly into the symtab. These values are not stable (global symbols are sometimes |
| ... | ... | @@ -1158,23 +1963,20 @@ const Symbol = struct { |
| 1158 | 1963 | null = 0, |
| 1159 | 1964 | _, |
| 1160 | 1965 | |
| 1161 | | fn flushMoved(si: Symbol.Index, elf: *Elf, value: u64) void { |
| 1162 | | switch (elf.symPtr(si)) { |
| 1163 | | inline else => |sym| elf.targetStore(&sym.value, @intCast(value)), |
| 1164 | | } |
| 1165 | | if (elf.ehdrField(.type) != .REL) { |
| 1166 | | var ri = si.ptr(elf).first_target_reloc; |
| 1167 | | while (ri != .none) { |
| 1168 | | const reloc = ri.get(elf); |
| 1169 | | assert(reloc.target.index(elf) == si); |
| 1170 | | reloc.apply(elf); |
| 1171 | | ri = reloc.next; |
| 1172 | | } |
| 1173 | | } |
| 1174 | | } |
| 1175 | 1966 | fn ptr(si: Symbol.Index, elf: *Elf) *Symbol { |
| 1176 | 1967 | return &elf.symtab.items[@intFromEnum(si)]; |
| 1177 | 1968 | } |
| 1969 | |
| 1970 | fn applyTargetRelocs(si: Symbol.Index, elf: *Elf) void { |
| 1971 | assert(elf.ehdrField(.type) != .REL); |
| 1972 | var ri = si.ptr(elf).first_target_reloc; |
| 1973 | while (ri != .none) { |
| 1974 | const reloc = ri.get(elf); |
| 1975 | assert(reloc.target.index(elf) == si); |
| 1976 | reloc.apply(elf); |
| 1977 | ri = reloc.next; |
| 1978 | } |
| 1979 | } |
| 1178 | 1980 | }; |
| 1179 | 1981 | |
| 1180 | 1982 | /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the |
| ... | ... | @@ -1239,6 +2041,44 @@ const Symbol = struct { |
| 1239 | 2041 | }; |
| 1240 | 2042 | } |
| 1241 | 2043 | |
| 2044 | fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void { |
| 2045 | // Update the symbol value in `.symtab` |
| 2046 | const sym_index = sym_id.index(elf); |
| 2047 | switch (elf.symPtr(sym_index)) { |
| 2048 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), |
| 2049 | } |
| 2050 | |
| 2051 | // Update the symbol value in `.dynsym` if applicable |
| 2052 | switch (sym_id.unwrap()) { |
| 2053 | .local => {}, |
| 2054 | .global => |name| { |
| 2055 | const g = elf.globalByName(name).?; |
| 2056 | if (g.dynsym_index != 0) { |
| 2057 | switch (elf.dynsymPtr(g.dynsym_index)) { |
| 2058 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), |
| 2059 | } |
| 2060 | } |
| 2061 | }, |
| 2062 | } |
| 2063 | |
| 2064 | // Re-apply relocations targeting this symbol |
| 2065 | if (elf.ehdrField(.type) != .REL) { |
| 2066 | sym_index.applyTargetRelocs(elf); |
| 2067 | } |
| 2068 | |
| 2069 | // Update GOT entries targeting this symbol |
| 2070 | if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| { |
| 2071 | elf.updateGotEntry(got_index); |
| 2072 | } |
| 2073 | if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| { |
| 2074 | elf.updateGotEntry(got_index); |
| 2075 | } |
| 2076 | if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| { |
| 2077 | elf.updateGotEntry(got_index); |
| 2078 | elf.updateGotEntry(got_index + 1); // tlsgd1 |
| 2079 | } |
| 2080 | } |
| 2081 | |
| 1242 | 2082 | /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at |
| 1243 | 2083 | /// some point due to a call to `flushMoved`. |
| 1244 | 2084 | fn hasMoved(s: Symbol.Id, elf: *Elf) bool { |
| ... | ... | @@ -1305,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId { |
| 1305 | 2145 | .type = sym_type, |
| 1306 | 2146 | .shndx = shndx, |
| 1307 | 2147 | }), |
| 1308 | | .first_reloc = .none, |
| 2148 | .first_symbol_reloc = .none, |
| 2149 | .first_got_reloc = .none, |
| 1309 | 2150 | }; |
| 1310 | 2151 | elf.nodes.appendAssumeCapacity(switch (lazy.kind) { |
| 1311 | 2152 | .code => .{ .lazy_code = @enumFromInt(gop.index) }, |
| ... | ... | @@ -1354,7 +2195,7 @@ pub fn addReloc( |
| 1354 | 2195 | offset: u64, |
| 1355 | 2196 | target: link.File.SymbolId, |
| 1356 | 2197 | addend: i64, |
| 1357 | | @"type": Reloc.Type, |
| 2198 | @"type": MachineRelocType, |
| 1358 | 2199 | ) !void { |
| 1359 | 2200 | const node: MappedFile.Node.Index = Node.fromAtom(atom); |
| 1360 | 2201 | try elf.ensureUnusedRelocCapacity(node, 1); |
| ... | ... | @@ -1368,7 +2209,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId |
| 1368 | 2209 | return elf.externSymbol(.{ |
| 1369 | 2210 | .name = @"extern".name.toSlice(ip), |
| 1370 | 2211 | .lib_name = @"extern".lib_name.toSlice(ip), |
| 1371 | | .type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded), |
| 2212 | .type = elf.navType(nav.resolved.?), |
| 1372 | 2213 | .linkage = @"extern".linkage, |
| 1373 | 2214 | .visibility = @"extern".visibility, |
| 1374 | 2215 | }); |
| ... | ... | @@ -1507,310 +2348,44 @@ const StringTable = struct { |
| 1507 | 2348 | key, |
| 1508 | 2349 | StringTable.Adapter{ .slice = slice_const }, |
| 1509 | 2350 | .{ .slice = slice_const }, |
| 1510 | | ); |
| 1511 | | if (gop.found_existing) return gop.key_ptr.*; |
| 1512 | | try ni.resized(gpa, &elf.mf); |
| 1513 | | const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) { |
| 1514 | | inline else => |shdr| { |
| 1515 | | const old_size: u32 = @intCast(elf.targetLoad(&shdr.size)); |
| 1516 | | const new_size: u32 = @intCast(old_size + key.len + 1); |
| 1517 | | elf.targetStore(&shdr.size, new_size); |
| 1518 | | break :size .{ old_size, new_size }; |
| 1519 | | }, |
| 1520 | | }; |
| 1521 | | _, const node_size = ni.location(&elf.mf).resolve(&elf.mf); |
| 1522 | | if (new_size > node_size) |
| 1523 | | try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor); |
| 1524 | | const slice = ni.slice(&elf.mf)[old_size..]; |
| 1525 | | @memcpy(slice[0..key.len], key); |
| 1526 | | slice[key.len] = 0; |
| 1527 | | gop.key_ptr.* = old_size; |
| 1528 | | return old_size; |
| 1529 | | } |
| 1530 | | }; |
| 1531 | | |
| 1532 | | const GotIndex = enum(u32) { |
| 1533 | | none = std.math.maxInt(u32), |
| 1534 | | _, |
| 1535 | | |
| 1536 | | pub fn wrap(i: ?u32) GotIndex { |
| 1537 | | const gi: GotIndex = @enumFromInt(i orelse return .none); |
| 1538 | | assert(gi != .none); |
| 1539 | | return gi; |
| 1540 | | } |
| 1541 | | pub fn unwrap(gi: GotIndex) ?u32 { |
| 1542 | | return switch (gi) { |
| 1543 | | _ => @intFromEnum(gi), |
| 1544 | | .none => null, |
| 1545 | | }; |
| 1546 | | } |
| 1547 | | }; |
| 1548 | | |
| 1549 | | const Reloc = extern struct { |
| 1550 | | type: Reloc.Type, |
| 1551 | | prev: Reloc.Index, |
| 1552 | | next: Reloc.Index, |
| 1553 | | node: MappedFile.Node.Index, |
| 1554 | | target: Symbol.Id, |
| 1555 | | index: Section.RelIndex, |
| 1556 | | offset: u64, |
| 1557 | | addend: i64, |
| 1558 | | |
| 1559 | | pub const Type = extern union { |
| 1560 | | X86_64: std.elf.R_X86_64, |
| 1561 | | AARCH64: std.elf.R_AARCH64, |
| 1562 | | RISCV: std.elf.R_RISCV, |
| 1563 | | PPC64: std.elf.R_PPC64, |
| 1564 | | |
| 1565 | | pub fn none(elf: *Elf) Reloc.Type { |
| 1566 | | return switch (elf.ehdrField(.machine)) { |
| 1567 | | else => unreachable, |
| 1568 | | .AARCH64 => .{ .AARCH64 = .NONE }, |
| 1569 | | .PPC64 => .{ .PPC64 = .NONE }, |
| 1570 | | .RISCV => .{ .RISCV = .NONE }, |
| 1571 | | .X86_64 => .{ .X86_64 = .NONE }, |
| 1572 | | }; |
| 1573 | | } |
| 1574 | | pub fn absAddr(elf: *Elf) Reloc.Type { |
| 1575 | | return switch (elf.ehdrField(.machine)) { |
| 1576 | | else => unreachable, |
| 1577 | | .AARCH64 => .{ .AARCH64 = .ABS64 }, |
| 1578 | | .PPC64 => .{ .PPC64 = .ADDR64 }, |
| 1579 | | .RISCV => .{ .RISCV = .@"64" }, |
| 1580 | | .X86_64 => .{ .X86_64 = .@"64" }, |
| 1581 | | }; |
| 1582 | | } |
| 1583 | | pub fn sizeAddr(elf: *Elf) Reloc.Type { |
| 1584 | | return switch (elf.ehdrField(.machine)) { |
| 1585 | | else => unreachable, |
| 1586 | | .X86_64 => .{ .X86_64 = .SIZE64 }, |
| 1587 | | }; |
| 1588 | | } |
| 1589 | | |
| 1590 | | pub fn wrap(int: u32, elf: *Elf) Reloc.Type { |
| 1591 | | return switch (elf.ehdrField(.machine)) { |
| 1592 | | else => unreachable, |
| 1593 | | inline .AARCH64, |
| 1594 | | .PPC64, |
| 1595 | | .RISCV, |
| 1596 | | .X86_64, |
| 1597 | | => |machine| @unionInit(Reloc.Type, @tagName(machine), @enumFromInt(int)), |
| 1598 | | }; |
| 1599 | | } |
| 1600 | | pub fn unwrap(rt: Reloc.Type, elf: *Elf) u32 { |
| 1601 | | return switch (elf.ehdrField(.machine)) { |
| 1602 | | else => unreachable, |
| 1603 | | inline .AARCH64, |
| 1604 | | .PPC64, |
| 1605 | | .RISCV, |
| 1606 | | .X86_64, |
| 1607 | | => |machine| @intFromEnum(@field(rt, @tagName(machine))), |
| 1608 | | }; |
| 1609 | | } |
| 1610 | | }; |
| 1611 | | |
| 1612 | | pub const Index = enum(u32) { |
| 1613 | | none = std.math.maxInt(u32), |
| 1614 | | _, |
| 1615 | | |
| 1616 | | pub fn get(si: Reloc.Index, elf: *Elf) *Reloc { |
| 1617 | | return &elf.relocs.items[@intFromEnum(si)]; |
| 1618 | | } |
| 1619 | | }; |
| 1620 | | |
| 1621 | | pub fn apply(reloc: *const Reloc, elf: *Elf) void { |
| 1622 | | assert(elf.ehdrField(.type) != .REL); |
| 1623 | | assert(reloc.node != .none); |
| 1624 | | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { |
| 1625 | | // There's no point applying the relocation now, because it will be re-applied by |
| 1626 | | // `flushMoved` at some point anyway. |
| 1627 | | return; |
| 1628 | | } |
| 1629 | | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 1630 | | .file => unreachable, |
| 1631 | | .ehdr => unreachable, |
| 1632 | | .shdr => unreachable, |
| 1633 | | .segment => unreachable, |
| 1634 | | .section => |shndx| shndx.vaddr(elf), |
| 1635 | | .input_section => |isi| isi.ptrConst(elf).vaddr, |
| 1636 | | inline .nav, |
| 1637 | | .uav, |
| 1638 | | .lazy_code, |
| 1639 | | .lazy_const_data, |
| 1640 | | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 1641 | | }; |
| 1642 | | const dest_vaddr = node_vaddr + reloc.offset; |
| 1643 | | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 1644 | | const target_endian = elf.targetEndian(); |
| 1645 | | switch (elf.symPtr(reloc.target.index(elf))) { |
| 1646 | | inline else => |target_sym, class| { |
| 1647 | | const target_value = elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend)); |
| 1648 | | switch (elf.ehdrField(.machine)) { |
| 1649 | | else => |machine| @panic(@tagName(machine)), |
| 1650 | | .X86_64 => switch (reloc.type.X86_64) { |
| 1651 | | else => |kind| @panic(@tagName(kind)), |
| 1652 | | .@"64" => std.mem.writeInt( |
| 1653 | | u64, |
| 1654 | | dest_slice[0..8], |
| 1655 | | target_value, |
| 1656 | | target_endian, |
| 1657 | | ), |
| 1658 | | .PC32 => std.mem.writeInt( |
| 1659 | | i32, |
| 1660 | | dest_slice[0..4], |
| 1661 | | @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))), |
| 1662 | | target_endian, |
| 1663 | | ), |
| 1664 | | .PLT32 => std.mem.writeInt( |
| 1665 | | i32, |
| 1666 | | dest_slice[0..4], |
| 1667 | | @intCast(@as(i64, @bitCast(if (elf.got.plt.getIndex(reloc.target)) |plt_index| |
| 1668 | | elf.targetLoad(&@field( |
| 1669 | | elf.shdrPtr(elf.shndx.plt_sec), |
| 1670 | | @tagName(class), |
| 1671 | | ).addr) +% 16 * plt_index +% |
| 1672 | | @as(u64, @bitCast(reloc.addend)) -% dest_vaddr |
| 1673 | | else |
| 1674 | | target_value -% dest_vaddr))), |
| 1675 | | target_endian, |
| 1676 | | ), |
| 1677 | | .@"32" => std.mem.writeInt( |
| 1678 | | u32, |
| 1679 | | dest_slice[0..4], |
| 1680 | | @intCast(target_value), |
| 1681 | | target_endian, |
| 1682 | | ), |
| 1683 | | .@"32S" => std.mem.writeInt( |
| 1684 | | i32, |
| 1685 | | dest_slice[0..4], |
| 1686 | | @intCast(@as(i64, @bitCast(target_value))), |
| 1687 | | target_endian, |
| 1688 | | ), |
| 1689 | | .TLSLD => std.mem.writeInt( |
| 1690 | | i32, |
| 1691 | | dest_slice[0..4], |
| 1692 | | @intCast(@as(i64, @bitCast( |
| 1693 | | elf.shndx.got.vaddr(elf) +% |
| 1694 | | @as(u64, @bitCast(reloc.addend)) +% |
| 1695 | | @as(u64, 8) * elf.got.tlsld.unwrap().? -% |
| 1696 | | dest_vaddr, |
| 1697 | | ))), |
| 1698 | | target_endian, |
| 1699 | | ), |
| 1700 | | .DTPOFF32 => std.mem.writeInt( |
| 1701 | | i32, |
| 1702 | | dest_slice[0..4], |
| 1703 | | @intCast(@as(i64, @bitCast(target_value))), |
| 1704 | | target_endian, |
| 1705 | | ), |
| 1706 | | .TPOFF32 => { |
| 1707 | | const phdr = @field(elf.phdrSlice(), @tagName(class)); |
| 1708 | | const ph = &phdr[elf.getNode(elf.ni.tls).segment]; |
| 1709 | | assert(elf.targetLoad(&ph.type) == .TLS); |
| 1710 | | std.mem.writeInt( |
| 1711 | | i32, |
| 1712 | | dest_slice[0..4], |
| 1713 | | @intCast(@as(i64, @bitCast(target_value -% elf.targetLoad(&ph.memsz)))), |
| 1714 | | target_endian, |
| 1715 | | ); |
| 1716 | | }, |
| 1717 | | .SIZE32 => std.mem.writeInt( |
| 1718 | | u32, |
| 1719 | | dest_slice[0..4], |
| 1720 | | @intCast( |
| 1721 | | elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)), |
| 1722 | | ), |
| 1723 | | target_endian, |
| 1724 | | ), |
| 1725 | | .SIZE64 => std.mem.writeInt( |
| 1726 | | u64, |
| 1727 | | dest_slice[0..8], |
| 1728 | | elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)), |
| 1729 | | target_endian, |
| 1730 | | ), |
| 1731 | | }, |
| 1732 | | } |
| 1733 | | }, |
| 1734 | | } |
| 1735 | | } |
| 1736 | | |
| 1737 | | pub fn delete(reloc: *Reloc, elf: *Elf) void { |
| 1738 | | switch (reloc.prev) { |
| 1739 | | .none => { |
| 1740 | | const target_ptr = reloc.target.index(elf).ptr(elf); |
| 1741 | | assert(target_ptr.first_target_reloc.get(elf) == reloc); |
| 1742 | | target_ptr.first_target_reloc = reloc.next; |
| 1743 | | }, |
| 1744 | | else => |prev| prev.get(elf).next = reloc.next, |
| 1745 | | } |
| 1746 | | switch (reloc.next) { |
| 1747 | | .none => {}, |
| 1748 | | else => |next| next.get(elf).prev = reloc.prev, |
| 1749 | | } |
| 1750 | | switch (elf.ehdrField(.type)) { |
| 1751 | | .NONE, .CORE, _ => unreachable, |
| 1752 | | .REL => { |
| 1753 | | const sh = elf.getNodeShndx(reloc.node).get(elf); |
| 1754 | | switch (elf.shdrPtr(sh.rela_shndx)) { |
| 1755 | | inline else => |shdr, class| { |
| 1756 | | const Rela = class.ElfN().Rela; |
| 1757 | | const ent_size = elf.targetLoad(&shdr.entsize); |
| 1758 | | const start = ent_size * reloc.index.unwrap().?; |
| 1759 | | const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); |
| 1760 | | const rela: *Rela = @ptrCast(@alignCast( |
| 1761 | | rela_slice[@intCast(start)..][0..@intCast(ent_size)], |
| 1762 | | )); |
| 1763 | | rela.* = .{ |
| 1764 | | .offset = @intFromEnum(sh.rela_free), |
| 1765 | | .info = .{ |
| 1766 | | .type = @intCast(Reloc.Type.none(elf).unwrap(elf)), |
| 1767 | | .sym = 0, |
| 1768 | | }, |
| 1769 | | .addend = 0, |
| 1770 | | }; |
| 1771 | | }, |
| 1772 | | } |
| 1773 | | sh.rela_free = reloc.index; |
| 2351 | ); |
| 2352 | if (gop.found_existing) return gop.key_ptr.*; |
| 2353 | const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) { |
| 2354 | inline else => |shdr| { |
| 2355 | const old_size: u32 = @intCast(elf.targetLoad(&shdr.size)); |
| 2356 | const new_size: u32 = @intCast(old_size + key.len + 1); |
| 2357 | elf.targetStore(&shdr.size, new_size); |
| 2358 | break :size .{ old_size, new_size }; |
| 1774 | 2359 | }, |
| 1775 | | .EXEC, .DYN => assert(reloc.index == .none), |
| 2360 | }; |
| 2361 | if (shndx == elf.shndx.dynstr) { |
| 2362 | elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size); |
| 1776 | 2363 | } |
| 1777 | | reloc.* = undefined; |
| 2364 | _, const node_size = ni.location(&elf.mf).resolve(&elf.mf); |
| 2365 | if (new_size > node_size) |
| 2366 | try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor); |
| 2367 | const slice = ni.slice(&elf.mf)[old_size..]; |
| 2368 | @memcpy(slice[0..key.len], key); |
| 2369 | slice[key.len] = 0; |
| 2370 | gop.key_ptr.* = old_size; |
| 2371 | return old_size; |
| 1778 | 2372 | } |
| 2373 | }; |
| 1779 | 2374 | |
| 1780 | | fn updateTargetIndex(reloc: *const Reloc, elf: *Elf) void { |
| 1781 | | assert(elf.ehdrField(.type) == .REL); |
| 1782 | | const sh = elf.getNodeShndx(reloc.node).get(elf); |
| 1783 | | switch (elf.shdrPtr(sh.rela_shndx)) { |
| 1784 | | inline else => |shdr, class| { |
| 1785 | | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); |
| 1786 | | const size = elf.targetLoad(&shdr.size); |
| 1787 | | const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); |
| 1788 | | const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)])); |
| 1789 | | elf.targetStore(&rela_slice[reloc.index.unwrap().?].info, .{ |
| 1790 | | .type = @intCast(reloc.type.unwrap(elf)), |
| 1791 | | .sym = @intCast(@intFromEnum(reloc.target.index(elf))), |
| 1792 | | }); |
| 1793 | | }, |
| 1794 | | } |
| 1795 | | } |
| 2375 | const GotIndex = enum(u32) { |
| 2376 | none = std.math.maxInt(u32), |
| 2377 | _, |
| 1796 | 2378 | |
| 1797 | | fn updateNodeOffset(reloc: *const Reloc, elf: *Elf, node_offset: u64) void { |
| 1798 | | assert(elf.ehdrField(.type) == .REL); |
| 1799 | | const total_offset = node_offset + reloc.offset; |
| 1800 | | const sh = elf.getNodeShndx(reloc.node).get(elf); |
| 1801 | | switch (elf.shdrPtr(sh.rela_shndx)) { |
| 1802 | | inline else => |shdr, class| { |
| 1803 | | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); |
| 1804 | | const size = elf.targetLoad(&shdr.size); |
| 1805 | | const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); |
| 1806 | | const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)])); |
| 1807 | | elf.targetStore(&rela_slice[reloc.index.unwrap().?].offset, @intCast(total_offset)); |
| 1808 | | }, |
| 1809 | | } |
| 2379 | pub fn wrap(i: ?u32) GotIndex { |
| 2380 | const gi: GotIndex = @enumFromInt(i orelse return .none); |
| 2381 | assert(gi != .none); |
| 2382 | return gi; |
| 1810 | 2383 | } |
| 1811 | | |
| 1812 | | comptime { |
| 1813 | | if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40); |
| 2384 | pub fn unwrap(gi: GotIndex) ?u32 { |
| 2385 | return switch (gi) { |
| 2386 | _ => @intFromEnum(gi), |
| 2387 | .none => null, |
| 2388 | }; |
| 1814 | 2389 | } |
| 1815 | 2390 | }; |
| 1816 | 2391 | |
| ... | ... | @@ -1918,6 +2493,11 @@ fn create( |
| 1918 | 2493 | .dynstr = .UNDEF, |
| 1919 | 2494 | .dynamic = .UNDEF, |
| 1920 | 2495 | .tdata = .UNDEF, |
| 2496 | .rela_dyn = .UNDEF, |
| 2497 | .rela_plt = .UNDEF, |
| 2498 | .init_array = .UNDEF, |
| 2499 | .fini_array = .UNDEF, |
| 2500 | .preinit_array = .UNDEF, |
| 1921 | 2501 | }, |
| 1922 | 2502 | .symtab = .empty, |
| 1923 | 2503 | .globals = .{ |
| ... | ... | @@ -1927,16 +2507,14 @@ fn create( |
| 1927 | 2507 | .weak_undef = .empty, |
| 1928 | 2508 | }, |
| 1929 | 2509 | .node_global_symbols = .empty, |
| 2510 | .dso_globals = .empty, |
| 1930 | 2511 | .shstrtab = .{ .map = .empty }, |
| 1931 | 2512 | .strtab = .{ .map = .empty }, |
| 1932 | 2513 | .dynstr = .{ .map = .empty }, |
| 1933 | | .got = .{ |
| 1934 | | .len = 0, |
| 1935 | | .tlsld = .none, |
| 1936 | | .plt = .empty, |
| 1937 | | }, |
| 1938 | | .first_plt_reloc = .none, |
| 1939 | | .first_dynamic_reloc = .none, |
| 2514 | .got = .empty, |
| 2515 | .plt = .empty, |
| 2516 | .plt_first_symbol_reloc = .none, |
| 2517 | .dynamic_first_symbol_reloc = .none, |
| 1940 | 2518 | .needed = .empty, |
| 1941 | 2519 | .inputs = .empty, |
| 1942 | 2520 | .input_sections = .empty, |
| ... | ... | @@ -1948,11 +2526,15 @@ fn create( |
| 1948 | 2526 | .pending_index = 0, |
| 1949 | 2527 | }), |
| 1950 | 2528 | .pending_uavs = .empty, |
| 1951 | | .relocs = .empty, |
| 2529 | .symbol_relocs = .empty, |
| 2530 | .got_relocs = .empty, |
| 2531 | .tls_size_symbol_relocs = .empty, |
| 2532 | .section_by_name = .empty, |
| 1952 | 2533 | .changed_symtab_index = .empty, |
| 1953 | 2534 | .const_prog_node = .none, |
| 1954 | 2535 | .synth_prog_node = .none, |
| 1955 | 2536 | .input_prog_node = .none, |
| 2537 | .textrel_count = 0, |
| 1956 | 2538 | }; |
| 1957 | 2539 | errdefer elf.deinit(); |
| 1958 | 2540 | |
| ... | ... | @@ -1972,10 +2554,12 @@ pub fn deinit(elf: *Elf) void { |
| 1972 | 2554 | elf.globals.strong_undef.deinit(gpa); |
| 1973 | 2555 | elf.globals.weak_undef.deinit(gpa); |
| 1974 | 2556 | elf.node_global_symbols.deinit(gpa); |
| 2557 | elf.dso_globals.deinit(gpa); |
| 1975 | 2558 | elf.shstrtab.map.deinit(gpa); |
| 1976 | 2559 | elf.strtab.map.deinit(gpa); |
| 1977 | 2560 | elf.dynstr.map.deinit(gpa); |
| 1978 | | elf.got.plt.deinit(gpa); |
| 2561 | elf.got.deinit(gpa); |
| 2562 | elf.plt.deinit(gpa); |
| 1979 | 2563 | elf.needed.deinit(gpa); |
| 1980 | 2564 | for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m); |
| 1981 | 2565 | elf.inputs.deinit(gpa); |
| ... | ... | @@ -1984,7 +2568,10 @@ pub fn deinit(elf: *Elf) void { |
| 1984 | 2568 | elf.uavs.deinit(gpa); |
| 1985 | 2569 | for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); |
| 1986 | 2570 | elf.pending_uavs.deinit(gpa); |
| 1987 | | elf.relocs.deinit(gpa); |
| 2571 | elf.symbol_relocs.deinit(gpa); |
| 2572 | elf.got_relocs.deinit(gpa); |
| 2573 | elf.tls_size_symbol_relocs.deinit(gpa); |
| 2574 | elf.section_by_name.deinit(gpa); |
| 1988 | 2575 | elf.changed_symtab_index.deinit(gpa); |
| 1989 | 2576 | elf.* = undefined; |
| 1990 | 2577 | } |
| ... | ... | @@ -2293,7 +2880,7 @@ fn initHeaders( |
| 2293 | 2880 | .entsize = 0, |
| 2294 | 2881 | }; |
| 2295 | 2882 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef); |
| 2296 | | elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela_shndx = .UNDEF, .rela_free = .none }); |
| 2883 | elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela = .{ .shndx = .UNDEF } }); |
| 2297 | 2884 | |
| 2298 | 2885 | elf.symtab.addOneAssumeCapacity().* = .{ |
| 2299 | 2886 | .node = .none, |
| ... | ... | @@ -2368,8 +2955,15 @@ fn initHeaders( |
| 2368 | 2955 | if (@"type" != .REL) { |
| 2369 | 2956 | elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{ |
| 2370 | 2957 | .name = ".got", |
| 2958 | .type = .PROGBITS, |
| 2959 | // Reserve space for the reserved words, populated later. |
| 2960 | .size = switch (machine) { |
| 2961 | else => @panic(@tagName(machine)), |
| 2962 | .X86_64 => 3 * 8, |
| 2963 | }, |
| 2371 | 2964 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 2372 | 2965 | .addralign = addr_align, |
| 2966 | .entsize = @intCast(addr_align.toByteUnits()), |
| 2373 | 2967 | }); |
| 2374 | 2968 | elf.shndx.got_plt = try elf.addSection( |
| 2375 | 2969 | if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data, |
| ... | ... | @@ -2383,6 +2977,7 @@ fn initHeaders( |
| 2383 | 2977 | .X86_64 => 3 * 8, |
| 2384 | 2978 | }, |
| 2385 | 2979 | .addralign = addr_align, |
| 2980 | .entsize = @intCast(addr_align.toByteUnits()), |
| 2386 | 2981 | }, |
| 2387 | 2982 | ); |
| 2388 | 2983 | const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec = |
| ... | ... | @@ -2478,7 +3073,7 @@ fn initHeaders( |
| 2478 | 3073 | .NONE, _ => unreachable, |
| 2479 | 3074 | inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela), |
| 2480 | 3075 | }; |
| 2481 | | elf.shndx.got.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{ |
| 3076 | elf.shndx.rela_dyn = try elf.addSection(elf.ni.rodata, .{ |
| 2482 | 3077 | .name = ".rela.dyn", |
| 2483 | 3078 | .type = .RELA, |
| 2484 | 3079 | .flags = .{ .ALLOC = true }, |
| ... | ... | @@ -2487,13 +3082,12 @@ fn initHeaders( |
| 2487 | 3082 | .entsize = rela_size, |
| 2488 | 3083 | .node_align = elf.mf.flags.block_size, |
| 2489 | 3084 | }); |
| 2490 | | const got_plt_shndx = elf.shndx.got_plt; |
| 2491 | | got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{ |
| 3085 | elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{ |
| 2492 | 3086 | .name = ".rela.plt", |
| 2493 | 3087 | .type = .RELA, |
| 2494 | 3088 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, |
| 2495 | 3089 | .link = elf.shndx.dynsym.toSection().?, |
| 2496 | | .info = got_plt_shndx.toSection().?, |
| 3090 | .info = elf.shndx.got_plt.toSection().?, |
| 2497 | 3091 | .addralign = addr_align, |
| 2498 | 3092 | .entsize = rela_size, |
| 2499 | 3093 | .node_align = elf.mf.flags.block_size, |
| ... | ... | @@ -2516,7 +3110,7 @@ fn initHeaders( |
| 2516 | 3110 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip) |
| 2517 | 3111 | 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax) |
| 2518 | 3112 | }); |
| 2519 | | elf.first_plt_reloc = @enumFromInt(elf.relocs.items.len); |
| 3113 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 2520 | 3114 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); |
| 2521 | 3115 | elf.addRelocAssumeCapacity( |
| 2522 | 3116 | plt_ni, |
| ... | ... | @@ -2544,6 +3138,83 @@ fn initHeaders( |
| 2544 | 3138 | elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx }); |
| 2545 | 3139 | elf.phdrs.items[tls_phndx] = elf.ni.tls; |
| 2546 | 3140 | } |
| 3141 | |
| 3142 | // Populate reserved GOT words. |
| 3143 | switch (machine) { |
| 3144 | else => @panic(@tagName(machine)), |
| 3145 | .X86_64 => { |
| 3146 | try elf.got.ensureUnusedCapacity(gpa, 3); |
| 3147 | elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { |
| 3148 | true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) }, |
| 3149 | false => .{ .reserved = 0 }, |
| 3150 | }, .none); |
| 3151 | elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none); |
| 3152 | elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none); |
| 3153 | }, |
| 3154 | } |
| 3155 | switch (elf.shdrPtr(elf.shndx.got)) { |
| 3156 | inline else => |shdr, ct_class| { |
| 3157 | const Addr = ct_class.ElfN().Addr; |
| 3158 | assert(elf.targetLoad(&shdr.size) == elf.got.count() * @sizeOf(Addr)); |
| 3159 | }, |
| 3160 | } |
| 3161 | |
| 3162 | // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/ |
| 3163 | // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection` |
| 3164 | // when needed (it seems to be legal to leave those undefined if the section doesn't exist). |
| 3165 | |
| 3166 | try elf.ensureUnusedSymbolCapacity(4, .maybe_global); |
| 3167 | // Despite the name, `__dso_handle` is necessary even in static binaries. |
| 3168 | _ = elf.addGlobalSymbolAssumeCapacity(.{ |
| 3169 | .node = Section.Index.text.get(elf).ni, |
| 3170 | .name = try .string(elf, "__dso_handle"), |
| 3171 | .value = Section.Index.text.vaddr(elf), |
| 3172 | .size = 0, |
| 3173 | .type = .NOTYPE, |
| 3174 | .bind = .strong, |
| 3175 | .visibility = .HIDDEN, |
| 3176 | .shndx = .text, |
| 3177 | }) catch |err| switch (err) { |
| 3178 | error.MultipleDefinitions => unreachable, // no inputs are processed yet |
| 3179 | }; |
| 3180 | _ = elf.addGlobalSymbolAssumeCapacity(.{ |
| 3181 | .node = elf.shndx.plt.get(elf).ni, |
| 3182 | .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"), |
| 3183 | .value = elf.shndx.plt.vaddr(elf), |
| 3184 | .size = 0, |
| 3185 | .type = .NOTYPE, |
| 3186 | .bind = .strong, |
| 3187 | .visibility = .HIDDEN, |
| 3188 | .shndx = elf.shndx.plt, |
| 3189 | }) catch |err| switch (err) { |
| 3190 | error.MultipleDefinitions => unreachable, // no inputs are processed yet |
| 3191 | }; |
| 3192 | _ = elf.addGlobalSymbolAssumeCapacity(.{ |
| 3193 | .node = elf.shndx.got.get(elf).ni, |
| 3194 | .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"), |
| 3195 | .value = elf.shndx.got.vaddr(elf), |
| 3196 | .size = 0, |
| 3197 | .type = .NOTYPE, |
| 3198 | .bind = .strong, |
| 3199 | .visibility = .HIDDEN, |
| 3200 | .shndx = elf.shndx.got, |
| 3201 | }) catch |err| switch (err) { |
| 3202 | error.MultipleDefinitions => unreachable, // no inputs are processed yet |
| 3203 | }; |
| 3204 | if (have_dynamic_section) { |
| 3205 | _ = elf.addGlobalSymbolAssumeCapacity(.{ |
| 3206 | .node = elf.shndx.dynamic.get(elf).ni, |
| 3207 | .name = try .string(elf, "_DYNAMIC"), |
| 3208 | .value = elf.shndx.dynamic.vaddr(elf), |
| 3209 | .size = 0, |
| 3210 | .type = .NOTYPE, |
| 3211 | .bind = .strong, |
| 3212 | .visibility = .HIDDEN, |
| 3213 | .shndx = elf.shndx.dynamic, |
| 3214 | }) catch |err| switch (err) { |
| 3215 | error.MultipleDefinitions => unreachable, // no inputs are processed yet |
| 3216 | }; |
| 3217 | } |
| 2547 | 3218 | } else { |
| 2548 | 3219 | assert(maybe_interp == null); |
| 2549 | 3220 | assert(!have_dynamic_section); |
| ... | ... | @@ -2554,6 +3225,12 @@ fn initHeaders( |
| 2554 | 3225 | .addralign = elf.mf.flags.block_size, |
| 2555 | 3226 | }); |
| 2556 | 3227 | assert(elf.nodes.len == expected_nodes_len); |
| 3228 | |
| 3229 | try elf.section_by_name.ensureUnusedCapacity(gpa, elf.shdrs.items.len); |
| 3230 | for (0..elf.shdrs.items.len) |shndx_raw| { |
| 3231 | const shndx: Section.Index = @enumFromInt(shndx_raw); |
| 3232 | elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); |
| 3233 | } |
| 2557 | 3234 | } |
| 2558 | 3235 | |
| 2559 | 3236 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { |
| ... | ... | @@ -2586,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node { |
| 2586 | 3263 | return elf.nodes.get(@intFromEnum(ni)); |
| 2587 | 3264 | } |
| 2588 | 3265 | /// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data. |
| 2589 | | fn getNodeShndx(elf: *Elf, ni: MappedFile.Node.Index) Section.Index { |
| 3266 | fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { |
| 2590 | 3267 | return switch (elf.getNode(ni)) { |
| 2591 | 3268 | .file => unreachable, |
| 2592 | 3269 | .ehdr => unreachable, |
| ... | ... | @@ -2624,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 2624 | 3301 | /// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support |
| 2625 | 3302 | /// the special-case sections '.plt' and '.dynamic'. |
| 2626 | 3303 | fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 2627 | | const first_reloc_ptr: *Reloc.Index = switch (elf.getNode(ni)) { |
| 3304 | const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) { |
| 2628 | 3305 | .file => unreachable, // cannot contain relocs |
| 2629 | 3306 | .ehdr => unreachable, // cannot contain relocs |
| 2630 | 3307 | .shdr => unreachable, // cannot contain relocs |
| 2631 | 3308 | .segment => unreachable, // cannot contain relocs |
| 2632 | 3309 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) |
| 2633 | | .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc, |
| 2634 | | .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc, |
| 2635 | | .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc, |
| 2636 | | inline .lazy_code, .lazy_const_data => |lmi| &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_reloc, |
| 3310 | .input_section => |isi| .{ |
| 3311 | &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc, |
| 3312 | &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc, |
| 3313 | }, |
| 3314 | .nav => |nmi| .{ |
| 3315 | &elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc, |
| 3316 | &elf.navs.values()[@intFromEnum(nmi)].first_got_reloc, |
| 3317 | }, |
| 3318 | .uav => |umi| .{ |
| 3319 | &elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc, |
| 3320 | null, |
| 3321 | }, |
| 3322 | inline .lazy_code, .lazy_const_data => |lmi| .{ |
| 3323 | &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc, |
| 3324 | &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc, |
| 3325 | }, |
| 2637 | 3326 | }; |
| 2638 | | if (first_reloc_ptr.* != .none) { |
| 2639 | | for (elf.relocs.items[@intFromEnum(first_reloc_ptr.*)..]) |*reloc| { |
| 3327 | |
| 3328 | if (symbol_relocs.* != .none) { |
| 3329 | for ( |
| 3330 | elf.symbol_relocs.items[@intFromEnum(symbol_relocs.*)..], |
| 3331 | @intFromEnum(symbol_relocs.*).., |
| 3332 | ) |*reloc, index| { |
| 2640 | 3333 | if (reloc.node != ni) break; |
| 2641 | | reloc.delete(elf); |
| 3334 | reloc.delete(elf, @enumFromInt(index)); |
| 3335 | } |
| 3336 | } |
| 3337 | symbol_relocs.* = @enumFromInt(elf.symbol_relocs.items.len); |
| 3338 | |
| 3339 | if (got_relocs) |ptr| { |
| 3340 | if (ptr.* != .none) { |
| 3341 | for (elf.got_relocs.items[@intFromEnum(ptr.*)..]) |*reloc| { |
| 3342 | if (reloc.node != ni) break; |
| 3343 | reloc.* = .deleted; |
| 3344 | } |
| 2642 | 3345 | } |
| 3346 | ptr.* = @enumFromInt(elf.got_relocs.items.len); |
| 2643 | 3347 | } |
| 2644 | | first_reloc_ptr.* = @enumFromInt(elf.relocs.items.len); |
| 2645 | 3348 | } |
| 2646 | 3349 | |
| 2647 | | /// Given that `node` has moved, updates all relocations in `node` (starting from `first_reloc`) as |
| 2648 | | /// needed. In relocatables, this means updating the offsets of those relocations. In ELF modules, |
| 2649 | | /// this means applying the relocations. |
| 3350 | /// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this |
| 3351 | /// means updating the relocations' offsets. In ELF modules, this means applying the relocations. |
| 2650 | 3352 | fn flushMovedNodeRelocs( |
| 2651 | 3353 | elf: *Elf, |
| 2652 | 3354 | node: MappedFile.Node.Index, |
| 2653 | 3355 | node_vaddr: u64, |
| 2654 | | first_reloc: Reloc.Index, |
| 3356 | first_symbol_reloc: SymbolReloc.Index, |
| 3357 | first_got_reloc: GotReloc.Index, |
| 2655 | 3358 | ) void { |
| 2656 | | if (first_reloc == .none) return; |
| 2657 | | switch (elf.ehdrField(.type)) { |
| 2658 | | .NONE, .CORE, _ => unreachable, |
| 2659 | | .REL => { |
| 2660 | | // In a relocatable, we're not actually applying any relocations ourselves, but we need |
| 2661 | | // to update the offsets of the relocation entries since the node they're in has moved. |
| 2662 | | for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| { |
| 2663 | | if (reloc.node != node) break; |
| 2664 | | reloc.updateNodeOffset(elf, node_vaddr); |
| 2665 | | } |
| 2666 | | }, |
| 2667 | | .EXEC, .DYN => { |
| 2668 | | // For an ELF module, we just need to apply relocations. |
| 2669 | | for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| { |
| 2670 | | if (reloc.node != node) break; |
| 3359 | if (first_symbol_reloc != .none) { |
| 3360 | for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| { |
| 3361 | if (reloc.node != node) break; |
| 3362 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 3363 | // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're |
| 3364 | // in has moved, so their offset within the section might also have moved. |
| 3365 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 3366 | } else { |
| 3367 | // We've applied this relocation ourselves! Just re-apply it now. |
| 2671 | 3368 | reloc.apply(elf); |
| 2672 | 3369 | } |
| 2673 | | // TODO: once we're emitting runtime relocation entries, we need to update their offsets |
| 2674 | | // too, like the logic for relocatables above. |
| 2675 | | }, |
| 3370 | } |
| 3371 | } |
| 3372 | |
| 3373 | if (first_got_reloc != .none) { |
| 3374 | for (elf.got_relocs.items[@intFromEnum(first_got_reloc)..]) |*reloc| { |
| 3375 | if (reloc.node != node) break; |
| 3376 | reloc.apply(elf); |
| 3377 | } |
| 2676 | 3378 | } |
| 2677 | 3379 | } |
| 2678 | 3380 | |
| ... | ... | @@ -2803,28 +3505,137 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr { |
| 2803 | 3505 | } |
| 2804 | 3506 | } |
| 2805 | 3507 | |
| 2806 | | fn navType( |
| 2807 | | ip: *const InternPool, |
| 2808 | | nav_resolved: @typeInfo(@FieldType(InternPool.Nav, "resolved")).optional.child, |
| 2809 | | any_non_single_threaded: bool, |
| 2810 | | ) std.elf.STT { |
| 3508 | fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT { |
| 3509 | const any_non_single_threaded = elf.base.comp.config.any_non_single_threaded; |
| 2811 | 3510 | return if (any_non_single_threaded and nav_resolved.@"threadlocal") |
| 2812 | 3511 | .TLS |
| 2813 | | else if (ip.isFunctionType(nav_resolved.type)) |
| 3512 | else if (elf.base.comp.zcu.?.intern_pool.isFunctionType(nav_resolved.type)) |
| 2814 | 3513 | .FUNC |
| 2815 | 3514 | else |
| 2816 | 3515 | .OBJECT; |
| 2817 | 3516 | } |
| 2818 | | fn namedSection(elf: *const Elf, name: []const u8) ?Section.Index { |
| 2819 | | if (std.mem.eql(u8, name, ".rodata") or |
| 2820 | | std.mem.startsWith(u8, name, ".rodata.")) return .rodata; |
| 2821 | | if (std.mem.eql(u8, name, ".text") or |
| 2822 | | std.mem.startsWith(u8, name, ".text.")) return .text; |
| 2823 | | if (std.mem.eql(u8, name, ".data") or |
| 2824 | | std.mem.startsWith(u8, name, ".data.")) return .data; |
| 2825 | | if (std.mem.eql(u8, name, ".tdata") or |
| 2826 | | std.mem.startsWith(u8, name, ".tdata.")) return elf.shndx.tdata; |
| 2827 | | return null; |
| 3517 | fn mapInputSection(elf: *Elf, opts: struct { |
| 3518 | name: []const u8, |
| 3519 | flags: std.elf.SHF, |
| 3520 | addralign: std.elf.Xword, |
| 3521 | entsize: std.elf.Xword, |
| 3522 | }) !Section.Index { |
| 3523 | const gpa = elf.base.comp.gpa; |
| 3524 | if (opts.flags.INFO_LINK or |
| 3525 | opts.flags.LINK_ORDER or |
| 3526 | opts.flags.OS_NONCONFORMING or |
| 3527 | (opts.flags.EXECINSTR and opts.flags.WRITE) or |
| 3528 | (opts.flags.EXECINSTR and opts.flags.TLS)) |
| 3529 | { |
| 3530 | return error.UnsupportedSectionFlags; |
| 3531 | } |
| 3532 | if (opts.flags.TLS and elf.ni.tls == .none) { |
| 3533 | assert(!elf.base.comp.config.any_non_single_threaded); |
| 3534 | return error.TlsSectionUnavailable; |
| 3535 | } |
| 3536 | |
| 3537 | if (elf.base.comp.config.debug_format == .strip and |
| 3538 | std.mem.startsWith(u8, opts.name, ".debug_") and |
| 3539 | !opts.flags.ALLOC) |
| 3540 | { |
| 3541 | return error.StripSection; |
| 3542 | } |
| 3543 | |
| 3544 | const name: []const u8 = switch (elf.ehdrField(.type)) { |
| 3545 | .NONE, .CORE, _ => unreachable, |
| 3546 | .REL => opts.name, |
| 3547 | .EXEC, .DYN => name: { |
| 3548 | if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text"; |
| 3549 | if (std.mem.startsWith(u8, opts.name, ".rodata.")) break :name ".rodata"; |
| 3550 | if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data"; |
| 3551 | if (std.mem.startsWith(u8, opts.name, ".data.rel.ro.")) break :name ".data.rel.ro"; |
| 3552 | if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata"; |
| 3553 | if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table"; |
| 3554 | // TODO: actually generate a bss section! |
| 3555 | if (std.mem.eql(u8, opts.name, ".bss")) break :name ".data"; |
| 3556 | if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".data"; |
| 3557 | // TODO: actually generate a tbss section! |
| 3558 | if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tdata"; |
| 3559 | if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tdata"; |
| 3560 | break :name opts.name; |
| 3561 | }, |
| 3562 | }; |
| 3563 | const existing_shndx: Section.Index = existing: { |
| 3564 | const name_shstrtab = try elf.string(.shstrtab, name); |
| 3565 | const gop = try elf.section_by_name.getOrPut(gpa, name_shstrtab); |
| 3566 | if (gop.found_existing) { |
| 3567 | break :existing @enumFromInt(gop.index); |
| 3568 | } |
| 3569 | errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab); |
| 3570 | const parent_node: MappedFile.Node.Index = parent: { |
| 3571 | if (!opts.flags.ALLOC) break :parent elf.ni.file; |
| 3572 | if (opts.flags.EXECINSTR) break :parent elf.ni.text; |
| 3573 | if (opts.flags.TLS) break :parent elf.ni.tls; |
| 3574 | if (opts.flags.WRITE) break :parent elf.ni.data; |
| 3575 | break :parent elf.ni.rodata; |
| 3576 | }; |
| 3577 | assert(gop.index == elf.shdrs.items.len); |
| 3578 | return elf.addSection(parent_node, .{ |
| 3579 | .name = name, |
| 3580 | .type = .NULL, // because initial size is 0 |
| 3581 | .flags = flags: { |
| 3582 | // We need to decompress the section for linking. |
| 3583 | var flags = opts.flags; |
| 3584 | flags.COMPRESSED = false; |
| 3585 | break :flags flags; |
| 3586 | }, |
| 3587 | .node_align = .fromByteUnits(std.math.ceilPowerOfTwoAssert( |
| 3588 | usize, |
| 3589 | @intCast(@max(opts.addralign, 1)), |
| 3590 | )), |
| 3591 | .entsize = std.math.lossyCast(u32, opts.entsize), |
| 3592 | }); |
| 3593 | }; |
| 3594 | // Validate that the input is compatible with this section... |
| 3595 | switch (elf.shdrPtr(existing_shndx)) { |
| 3596 | inline else => |shdr| { |
| 3597 | const cur_flags = elf.targetLoad(&shdr.flags).shf; |
| 3598 | if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or |
| 3599 | cur_flags.WRITE != opts.flags.WRITE or |
| 3600 | cur_flags.TLS != opts.flags.TLS) |
| 3601 | { |
| 3602 | return error.SectionFlagsConflict; |
| 3603 | } |
| 3604 | |
| 3605 | switch (elf.targetLoad(&shdr.type)) { |
| 3606 | .NULL, .PROGBITS => {}, |
| 3607 | else => return error.SectionTypeConflict, |
| 3608 | } |
| 3609 | }, |
| 3610 | } |
| 3611 | // ...then realign the section's node if necessary... |
| 3612 | if (opts.addralign > existing_shndx.get(elf).ni.alignment(&elf.mf).toByteUnits()) { |
| 3613 | const new_alignment: std.mem.Alignment = .fromByteUnits( |
| 3614 | std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)), |
| 3615 | ); |
| 3616 | try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment); |
| 3617 | } |
| 3618 | // ...and update the shdr as needed. |
| 3619 | switch (elf.shdrPtr(existing_shndx)) { |
| 3620 | inline else => |shdr| { |
| 3621 | // Combine the section flags. |
| 3622 | const cur_flags = elf.targetLoad(&shdr.flags).shf; |
| 3623 | elf.targetStore(&shdr.flags, .{ .shf = .{ |
| 3624 | .EXECINSTR = cur_flags.EXECINSTR, |
| 3625 | .WRITE = cur_flags.WRITE, |
| 3626 | .TLS = cur_flags.TLS, |
| 3627 | .ALLOC = cur_flags.ALLOC or opts.flags.ALLOC, |
| 3628 | .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS, |
| 3629 | .MERGE = cur_flags.MERGE and opts.flags.MERGE, |
| 3630 | } }); |
| 3631 | // Increase addralign to the maximum of the current value and the new value---the node |
| 3632 | // alignment was already increased above. |
| 3633 | if (opts.addralign > elf.targetLoad(&shdr.addralign)) { |
| 3634 | elf.targetStore(&shdr.addralign, @intCast(opts.addralign)); |
| 3635 | } |
| 3636 | }, |
| 3637 | } |
| 3638 | return existing_shndx; |
| 2828 | 3639 | } |
| 2829 | 3640 | fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex { |
| 2830 | 3641 | const gpa = zcu.gpa; |
| ... | ... | @@ -2838,17 +3649,41 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM |
| 2838 | 3649 | const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index); |
| 2839 | 3650 | const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index); |
| 2840 | 3651 | if (!nav_gop.found_existing) { |
| 2841 | | const sym_type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded); |
| 2842 | 3652 | const shndx: Section.Index = section: { |
| 2843 | 3653 | if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| { |
| 2844 | | if (elf.namedSection(@"linksection")) |shndx| break :section shndx; |
| 3654 | if (elf.mapInputSection(.{ |
| 3655 | .name = @"linksection", |
| 3656 | .flags = .{ |
| 3657 | .ALLOC = true, |
| 3658 | .EXECINSTR = ip.isFunctionType(nav.resolved.?.type), |
| 3659 | .WRITE = !nav.resolved.?.@"const", |
| 3660 | .TLS = elf.base.comp.config.any_non_single_threaded and |
| 3661 | nav.resolved.?.@"threadlocal", |
| 3662 | }, |
| 3663 | .addralign = 1, |
| 3664 | .entsize = 0, |
| 3665 | })) |shndx| { |
| 3666 | break :section shndx; |
| 3667 | } else |err| switch (err) { |
| 3668 | error.StripSection, |
| 3669 | error.TlsSectionUnavailable, |
| 3670 | error.UnsupportedSectionFlags, |
| 3671 | error.SectionTypeConflict, |
| 3672 | error.SectionFlagsConflict, |
| 3673 | => {}, // fall back to default behavior below |
| 3674 | |
| 3675 | else => |e| return e, |
| 3676 | } |
| 3677 | } |
| 3678 | if (elf.base.comp.config.any_non_single_threaded and nav.resolved.?.@"threadlocal") { |
| 3679 | break :section elf.shndx.tdata; |
| 3680 | } else if (!nav.resolved.?.@"const") { |
| 3681 | break :section .data; |
| 3682 | } else if (ip.isFunctionType(nav.resolved.?.type)) { |
| 3683 | break :section .text; |
| 3684 | } else { |
| 3685 | break :section .rodata; |
| 2845 | 3686 | } |
| 2846 | | break :section switch (sym_type) { |
| 2847 | | else => unreachable, |
| 2848 | | .FUNC => .text, |
| 2849 | | .OBJECT => .data, |
| 2850 | | .TLS => elf.shndx.tdata, |
| 2851 | | }; |
| 2852 | 3687 | }; |
| 2853 | 3688 | const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) { |
| 2854 | 3689 | .@"fn" => a: { |
| ... | ... | @@ -2880,10 +3715,11 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM |
| 2880 | 3715 | .name = try elf.string(.strtab, nav.fqn.toSlice(ip)), |
| 2881 | 3716 | .value = 0, |
| 2882 | 3717 | .size = 0, |
| 2883 | | .type = sym_type, |
| 3718 | .type = elf.navType(nav.resolved.?), |
| 2884 | 3719 | .shndx = shndx, |
| 2885 | 3720 | }), |
| 2886 | | .first_reloc = .none, |
| 3721 | .first_symbol_reloc = .none, |
| 3722 | .first_got_reloc = .none, |
| 2887 | 3723 | }; |
| 2888 | 3724 | elf.nodes.appendAssumeCapacity(.{ .nav = nmi }); |
| 2889 | 3725 | } |
| ... | ... | @@ -2932,7 +3768,7 @@ fn uavMapIndex( |
| 2932 | 3768 | .type = .OBJECT, |
| 2933 | 3769 | .shndx = shndx, |
| 2934 | 3770 | }), |
| 2935 | | .first_reloc = .none, |
| 3771 | .first_symbol_reloc = .none, |
| 2936 | 3772 | }; |
| 2937 | 3773 | elf.nodes.appendAssumeCapacity(.{ .uav = umi }); |
| 2938 | 3774 | elf.const_prog_node.increaseEstimatedTotalItems(1); |
| ... | ... | @@ -2940,7 +3776,7 @@ fn uavMapIndex( |
| 2940 | 3776 | } else { |
| 2941 | 3777 | const node = uav_gop.value_ptr.lsi.index().ptr(elf).node; |
| 2942 | 3778 | if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) { |
| 2943 | | node.realign(&elf.mf, resolved_align.toStdMem()); |
| 3779 | try node.realign(&elf.mf, gpa, resolved_align.toStdMem()); |
| 2944 | 3780 | } |
| 2945 | 3781 | } |
| 2946 | 3782 | return umi; |
| ... | ... | @@ -2977,7 +3813,14 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError || |
| 2977 | 3813 | else => |e| return e, |
| 2978 | 3814 | }; |
| 2979 | 3815 | }, |
| 2980 | | .dso_exact => |dso_exact| try elf.loadDsoExact(dso_exact.name), |
| 3816 | .dso_exact => |dso_exact| { |
| 3817 | log.debug("load dso_exact '{f}'", .{std.zig.fmtString(dso_exact.name)}); |
| 3818 | if (elf.shndx.dynamic != .UNDEF) { |
| 3819 | try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, dso_exact.name), {}); |
| 3820 | } |
| 3821 | // TODO: we need to get a resolved file path from the frontend, because we need to read |
| 3822 | // the shared object to discover symbol types. |
| 3823 | }, |
| 2981 | 3824 | } |
| 2982 | 3825 | } |
| 2983 | 3826 | fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| ... | ... | @@ -3006,6 +3849,13 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void |
| 3006 | 3849 | continue; |
| 3007 | 3850 | } |
| 3008 | 3851 | load_object: { |
| 3852 | if (std.mem.eql(u8, &header.ar_name, std.elf.SYMNAME) or |
| 3853 | std.mem.eql(u8, &header.ar_name, std.elf.SYM64NAME) or |
| 3854 | std.mem.eql(u8, &header.ar_name, std.elf.SYMDEFNAME) or |
| 3855 | std.mem.eql(u8, &header.ar_name, std.elf.SYMDEFSORTEDNAME)) |
| 3856 | { |
| 3857 | break :load_object; |
| 3858 | } |
| 3009 | 3859 | const member = header.name() orelse member: { |
| 3010 | 3860 | const strtab_offset = header.nameOffset() catch |err| switch (err) { |
| 3011 | 3861 | error.Overflow => break :member error.Overflow, |
| ... | ... | @@ -3021,7 +3871,6 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void |
| 3021 | 3871 | } catch |err| switch (err) { |
| 3022 | 3872 | error.Overflow => return diags.failParse(path, "bad member name offset", .{}), |
| 3023 | 3873 | }; |
| 3024 | | if (!std.mem.endsWith(u8, member, ".o")) break :load_object; |
| 3025 | 3874 | try elf.loadObject(path, member, fr, .{ .offset = offset, .size = size }); |
| 3026 | 3875 | } |
| 3027 | 3876 | try fr.seekTo(std.mem.alignForward(u64, offset + size, 2)); |
| ... | ... | @@ -3109,47 +3958,146 @@ fn loadObject( |
| 3109 | 3958 | defer gpa.free(shstrtab); |
| 3110 | 3959 | try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 3111 | 3960 | try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 3112 | | for (sections[1..]) |*section| switch (section.shdr.type) { |
| 3113 | | else => {}, |
| 3114 | | .PROGBITS, .NOBITS => { |
| 3115 | | if (section.shdr.name >= shstrtab.len) continue; |
| 3116 | | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 3117 | | const shndx: Section.Index = elf.namedSection(name) orelse shndx: { |
| 3118 | | // TODO: actually generate a .bss section. For now, just throw it into `.data`. |
| 3119 | | if (std.mem.eql(u8, name, ".bss") or |
| 3120 | | std.mem.startsWith(u8, name, ".bss.")) break :shndx .data; |
| 3121 | | if (std.mem.eql(u8, name, ".tbss") or |
| 3122 | | std.mem.startsWith(u8, name, ".tbss.")) break :shndx elf.shndx.tdata; |
| 3123 | | break :shndx .UNDEF; |
| 3124 | | }; |
| 3125 | | if (shndx == .UNDEF) continue; |
| 3126 | | const ni = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{ |
| 3127 | | .size = section.shdr.size, |
| 3128 | | .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( |
| 3129 | | usize, |
| 3130 | | @intCast(@max(section.shdr.addralign, 1)), |
| 3131 | | )), |
| 3132 | | .moved = true, // see assert at end of `flushInputSection` |
| 3133 | | }); |
| 3134 | | elf.nodes.appendAssumeCapacity(.{ |
| 3135 | | .input_section = @enumFromInt(elf.input_sections.items.len), |
| 3136 | | }); |
| 3137 | | section.isi = @enumFromInt(elf.input_sections.items.len); |
| 3138 | | elf.input_sections.addOneAssumeCapacity().* = .{ |
| 3139 | | .input = input_index, |
| 3140 | | .file_location = .{ |
| 3141 | | .offset = fl.offset + section.shdr.offset, |
| 3142 | | .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size, |
| 3961 | for (sections[1..]) |*section| { |
| 3962 | if (section.shdr.name >= shstrtab.len) continue; |
| 3963 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 3964 | const opts: struct { |
| 3965 | shndx: Section.Index, |
| 3966 | has_file_bits: bool, |
| 3967 | node_fixed: bool, |
| 3968 | } = switch (section.shdr.type) { |
| 3969 | else => continue, |
| 3970 | .PROGBITS, .NOBITS => opts: { |
| 3971 | const shndx = elf.mapInputSection(.{ |
| 3972 | .name = name, |
| 3973 | .flags = section.shdr.flags.shf, |
| 3974 | .addralign = section.shdr.addralign, |
| 3975 | .entsize = section.shdr.entsize, |
| 3976 | }) catch |err| switch (err) { |
| 3977 | error.StripSection => continue, |
| 3978 | error.TlsSectionUnavailable => return diags.failParse( |
| 3979 | path, |
| 3980 | "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'", |
| 3981 | .{name}, |
| 3982 | ), |
| 3983 | error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) { |
| 3984 | // It probably doesn't matter, just skip this section. |
| 3985 | continue; |
| 3986 | } else return diags.failParse( |
| 3987 | path, |
| 3988 | "unsupported flags for section '{s}'", |
| 3989 | .{name}, |
| 3990 | ), |
| 3991 | error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) { |
| 3992 | // It probably doesn't matter, just skip this section. |
| 3993 | continue; |
| 3994 | } else return diags.failParse( |
| 3995 | path, |
| 3996 | "type of section '{s}' conflicts with other inputs", |
| 3997 | .{name}, |
| 3998 | ), |
| 3999 | error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) { |
| 4000 | // It probably doesn't matter, just skip this section. |
| 4001 | continue; |
| 4002 | } else return diags.failParse( |
| 4003 | path, |
| 4004 | "flags of section '{s}' conflict with other inputs", |
| 4005 | .{name}, |
| 4006 | ), |
| 4007 | else => |e| return e, |
| 4008 | }; |
| 4009 | if (section.shdr.flags.shf.COMPRESSED) { |
| 4010 | // SHF_COMPRESSED is only allowed on non-alloc sections. |
| 4011 | if (section.shdr.flags.shf.ALLOC) return diags.failParse( |
| 4012 | path, |
| 4013 | "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED", |
| 4014 | .{name}, |
| 4015 | ); |
| 4016 | // TODO: handle compressed input sections. We'll need to set a flag to |
| 4017 | // indicate that `flushInputSection` needs to decompress the section. |
| 4018 | // But because this section isn't SHF_ALLOC, it's probably okay to just |
| 4019 | // skip it for now. |
| 4020 | continue; |
| 4021 | } |
| 4022 | break :opts .{ |
| 4023 | .shndx = shndx, |
| 4024 | .has_file_bits = section.shdr.type == .PROGBITS, |
| 4025 | // For well-known sections, we know that it's fine to have e.g. random |
| 4026 | // padding, so there's no need to make the sections fixed. For custom |
| 4027 | // sections, however, we do want fixed nodes to avoid padding. |
| 4028 | .node_fixed = shndx != .text and |
| 4029 | shndx != .rodata and |
| 4030 | shndx != .data and |
| 4031 | shndx != .data_rel_ro and |
| 4032 | shndx != elf.shndx.tdata, |
| 4033 | }; |
| 4034 | }, |
| 4035 | inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{ |
| 4036 | .shndx = shndx: { |
| 4037 | // TODO: the input section name may include a "priority" value between 1 |
| 4038 | // and 65535 which should affect the order we assemble input sections in |
| 4039 | const init_fini_section_name: []const u8 = switch (@"type") { |
| 4040 | .INIT_ARRAY => "init_array", |
| 4041 | .FINI_ARRAY => "fini_array", |
| 4042 | .PREINIT_ARRAY => "preinit_array", |
| 4043 | else => comptime unreachable, |
| 4044 | }; |
| 4045 | const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name); |
| 4046 | const need_addralign: u8 = switch (class) { |
| 4047 | .NONE, _ => unreachable, |
| 4048 | .@"32" => 4, |
| 4049 | .@"64" => 8, |
| 4050 | }; |
| 4051 | if (section.shdr.addralign != need_addralign) { |
| 4052 | return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"}); |
| 4053 | } |
| 4054 | if (shndx.* == .UNDEF) { |
| 4055 | try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type"); |
| 4056 | } |
| 4057 | switch (elf.shdrPtr(shndx.*)) { |
| 4058 | inline else => |shdr| { |
| 4059 | const old_size = elf.targetLoad(&shdr.size); |
| 4060 | const new_size = old_size + section.shdr.size; |
| 4061 | elf.targetStore(&shdr.size, @intCast(new_size)); |
| 4062 | elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name, @"type", new_size); |
| 4063 | }, |
| 4064 | } |
| 4065 | break :shndx shndx.*; |
| 3143 | 4066 | }, |
| 3144 | | // The section vaddr is initially 0, because the symbol addresses are |
| 3145 | | // zero-based. This will eventually be updated by `flushMoved`. |
| 3146 | | .vaddr = 0, |
| 3147 | | .node = ni, |
| 3148 | | .first_reloc = .none, |
| 3149 | | }; |
| 3150 | | elf.synth_prog_node.increaseEstimatedTotalItems(1); |
| 3151 | | }, |
| 3152 | | }; |
| 4067 | .has_file_bits = true, |
| 4068 | // This node must be fixed to prevent padding from being added between different |
| 4069 | // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections. |
| 4070 | .node_fixed = true, |
| 4071 | }, |
| 4072 | }; |
| 4073 | const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{ |
| 4074 | .size = section.shdr.size, |
| 4075 | .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( |
| 4076 | usize, |
| 4077 | @intCast(@max(section.shdr.addralign, 1)), |
| 4078 | )), |
| 4079 | .moved = true, // see assert at end of `flushInputSection` |
| 4080 | .fixed = opts.node_fixed, |
| 4081 | }); |
| 4082 | elf.nodes.appendAssumeCapacity(.{ |
| 4083 | .input_section = @enumFromInt(elf.input_sections.items.len), |
| 4084 | }); |
| 4085 | section.isi = @enumFromInt(elf.input_sections.items.len); |
| 4086 | elf.input_sections.addOneAssumeCapacity().* = .{ |
| 4087 | .input = input_index, |
| 4088 | .file_location = .{ |
| 4089 | .offset = fl.offset + section.shdr.offset, |
| 4090 | .size = if (opts.has_file_bits) section.shdr.size else 0, |
| 4091 | }, |
| 4092 | // The section vaddr is initially 0, because the symbol addresses are |
| 4093 | // zero-based. This will eventually be updated by `flushMoved`. |
| 4094 | .vaddr = 0, |
| 4095 | .node = ni, |
| 4096 | .first_symbol_reloc = .none, |
| 4097 | .first_got_reloc = .none, |
| 4098 | }; |
| 4099 | elf.synth_prog_node.increaseEstimatedTotalItems(1); |
| 4100 | } |
| 3153 | 4101 | var symmap: std.ArrayList(Symbol.Id) = .empty; |
| 3154 | 4102 | defer symmap.deinit(gpa); |
| 3155 | 4103 | for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) { |
| ... | ... | @@ -3198,20 +4146,24 @@ fn loadObject( |
| 3198 | 4146 | }; |
| 3199 | 4147 | |
| 3200 | 4148 | if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) { |
| 3201 | | _ => |bind| return diags.failParse( |
| 4149 | else => |bind| return diags.failParse( |
| 3202 | 4150 | path, |
| 3203 | 4151 | "symbol '{s}' has unsupported binding (0x{x})", |
| 3204 | 4152 | .{ name, bind }, |
| 3205 | 4153 | ), |
| 3206 | 4154 | .LOCAL => continue, |
| 3207 | | .GLOBAL, .WEAK => |bind| { |
| 4155 | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { |
| 3208 | 4156 | si.* = elf.addGlobalSymbolAssumeCapacity(.{ |
| 3209 | 4157 | .node = .none, |
| 3210 | 4158 | .name = try .string(elf, name), |
| 3211 | 4159 | .value = input_sym.value, |
| 3212 | 4160 | .size = input_sym.size, |
| 3213 | 4161 | .type = sym_type, |
| 3214 | | .bind = if (bind == .WEAK) .weak else .strong, |
| 4162 | .bind = switch (bind) { |
| 4163 | .WEAK, .GNU_UNIQUE => .weak, |
| 4164 | .GLOBAL => .strong, |
| 4165 | else => unreachable, |
| 4166 | }, |
| 3215 | 4167 | .visibility = input_sym.other.visibility, |
| 3216 | 4168 | .shndx = .UNDEF, |
| 3217 | 4169 | }) catch |err| switch (err) { |
| ... | ... | @@ -3224,7 +4176,7 @@ fn loadObject( |
| 3224 | 4176 | const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf); |
| 3225 | 4177 | |
| 3226 | 4178 | switch (input_sym.info.bind) { |
| 3227 | | _ => |bind| return diags.failParse( |
| 4179 | else => |bind| return diags.failParse( |
| 3228 | 4180 | path, |
| 3229 | 4181 | "symbol '{s}' has unsupported binding (0x{x})", |
| 3230 | 4182 | .{ name, bind }, |
| ... | ... | @@ -3240,14 +4192,18 @@ fn loadObject( |
| 3240 | 4192 | }); |
| 3241 | 4193 | si.* = .local(lsi); |
| 3242 | 4194 | }, |
| 3243 | | .GLOBAL, .WEAK => |bind| { |
| 4195 | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { |
| 3244 | 4196 | si.* = elf.addGlobalSymbolAssumeCapacity(.{ |
| 3245 | 4197 | .node = input_section_node, |
| 3246 | 4198 | .name = try .string(elf, name), |
| 3247 | 4199 | .value = input_sym.value, |
| 3248 | 4200 | .size = input_sym.size, |
| 3249 | 4201 | .type = sym_type, |
| 3250 | | .bind = if (bind == .WEAK) .weak else .strong, |
| 4202 | .bind = switch (bind) { |
| 4203 | .WEAK, .GNU_UNIQUE => .weak, |
| 4204 | .GLOBAL => .strong, |
| 4205 | else => unreachable, |
| 4206 | }, |
| 3251 | 4207 | .visibility = input_sym.other.visibility, |
| 3252 | 4208 | .shndx = elf.getNodeShndx(input_section_node), |
| 3253 | 4209 | }) catch |err| switch (err) { |
| ... | ... | @@ -3298,11 +4254,17 @@ fn loadObject( |
| 3298 | 4254 | .{rel.info.sym}, |
| 3299 | 4255 | ); |
| 3300 | 4256 | const target = symmap.items[rel.info.sym - 1]; |
| 3301 | | if (target == Symbol.Id.null) return diags.failParse( |
| 3302 | | path, |
| 3303 | | "unsupported symbol at index {d} required for relocation", |
| 3304 | | .{rel.info.sym}, |
| 3305 | | ); |
| 4257 | if (target == Symbol.Id.null) { |
| 4258 | // If this is not an SHF_ALLOC section, then let's let this |
| 4259 | // slide for now, because it probably doesn't affect the final |
| 4260 | // binary's functionality for this section to be a bit broken. |
| 4261 | if (!loc_sec.shdr.flags.shf.ALLOC) continue; |
| 4262 | return diags.failParse( |
| 4263 | path, |
| 4264 | "unsupported symbol at index {d} required for relocation", |
| 4265 | .{rel.info.sym}, |
| 4266 | ); |
| 4267 | } |
| 3306 | 4268 | elf.addRelocAssumeCapacity( |
| 3307 | 4269 | loc_node, |
| 3308 | 4270 | rel.offset - loc_sec.shdr.addr, |
| ... | ... | @@ -3320,6 +4282,7 @@ fn loadObject( |
| 3320 | 4282 | } |
| 3321 | 4283 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 3322 | 4284 | const comp = elf.base.comp; |
| 4285 | const gpa = comp.gpa; |
| 3323 | 4286 | const diags = &comp.link_diags; |
| 3324 | 4287 | const r = &fr.interface; |
| 3325 | 4288 | |
| ... | ... | @@ -3334,68 +4297,157 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 3334 | 4297 | if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{}); |
| 3335 | 4298 | if (ehdr.machine != elf.ehdrField(.machine)) |
| 3336 | 4299 | return diags.failParse(path, "bad machine", .{}); |
| 3337 | | if (ehdr.phoff == 0 or ehdr.phnum <= 1) |
| 3338 | | return diags.failParse(path, "no program headers", .{}); |
| 3339 | | try fr.seekTo(ehdr.phoff); |
| 3340 | | const dynamic_ph = for (0..ehdr.phnum) |_| { |
| 3341 | | const ph = try r.peekStruct(ElfN.Phdr, target_endian); |
| 3342 | | try r.discardAll(ehdr.phentsize); |
| 3343 | | switch (ph.type) { |
| 3344 | | else => {}, |
| 3345 | | .DYNAMIC => break ph, |
| 4300 | if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff); |
| 4301 | const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: { |
| 4302 | var dynamic_sh: ?ElfN.Shdr = null; |
| 4303 | var dynsym_sh: ?ElfN.Shdr = null; |
| 4304 | for (0..ehdr.shnum) |_| { |
| 4305 | const sh = try r.peekStruct(ElfN.Shdr, target_endian); |
| 4306 | try r.discardAll(ehdr.shentsize); |
| 4307 | switch (sh.type) { |
| 4308 | else => {}, |
| 4309 | .DYNAMIC => dynamic_sh = sh, |
| 4310 | .DYNSYM => dynsym_sh = sh, |
| 4311 | } |
| 4312 | } |
| 4313 | break :sh .{ |
| 4314 | dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}), |
| 4315 | dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}), |
| 4316 | }; |
| 4317 | }; |
| 4318 | const dynstr_sh: ElfN.Shdr = sh: { |
| 4319 | if (dynsym_sh.link >= ehdr.shnum) { |
| 4320 | return diags.failParse(path, "bad dynamic string table section index", .{}); |
| 3346 | 4321 | } |
| 3347 | | } else return diags.failParse(path, "no dynamic segment", .{}); |
| 4322 | try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize); |
| 4323 | break :sh try r.peekStruct(ElfN.Shdr, target_endian); |
| 4324 | }; |
| 4325 | |
| 4326 | if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) { |
| 4327 | return diags.failParse(path, "bad dynamic section entsize", .{}); |
| 4328 | } |
| 3348 | 4329 | const dynnum = std.math.divExact( |
| 3349 | 4330 | u32, |
| 3350 | | @intCast(dynamic_ph.filesz), |
| 4331 | @intCast(dynamic_sh.size), |
| 3351 | 4332 | @sizeOf(ElfN.Addr) * 2, |
| 3352 | 4333 | ) catch return diags.failParse( |
| 3353 | 4334 | path, |
| 3354 | | "dynamic segment filesz (0x{x}) is not a multiple of entsize (0x{x})", |
| 3355 | | .{ dynamic_ph.filesz, @sizeOf(ElfN.Addr) * 2 }, |
| 4335 | "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})", |
| 4336 | .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 }, |
| 3356 | 4337 | ); |
| 3357 | | var strtab: ?ElfN.Addr = null; |
| 3358 | | var strsz: ?ElfN.Addr = null; |
| 3359 | | var soname: ?ElfN.Addr = null; |
| 3360 | | try fr.seekTo(dynamic_ph.offset); |
| 3361 | | for (0..dynnum) |_| { |
| 4338 | |
| 4339 | if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) { |
| 4340 | return diags.failParse(path, "bad dynsym entsize", .{}); |
| 4341 | } |
| 4342 | const symnum = std.math.divExact( |
| 4343 | u32, |
| 4344 | @intCast(dynsym_sh.size), |
| 4345 | @intCast(dynsym_sh.entsize), |
| 4346 | ) catch return diags.failParse( |
| 4347 | path, |
| 4348 | "dynsym size (0x{x}) is not a multiple of entsize (0x{x})", |
| 4349 | .{ dynsym_sh.size, dynsym_sh.entsize }, |
| 4350 | ); |
| 4351 | |
| 4352 | const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size)); |
| 4353 | defer gpa.free(dynstr); |
| 4354 | try fr.seekTo(dynstr_sh.offset); |
| 4355 | try r.readSliceAll(dynstr); |
| 4356 | |
| 4357 | // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry. |
| 4358 | try fr.seekTo(dynamic_sh.offset); |
| 4359 | const soname: []const u8 = for (0..dynnum) |_| { |
| 3362 | 4360 | const tag = try r.takeInt(ElfN.Addr, target_endian); |
| 3363 | 4361 | const val = try r.takeInt(ElfN.Addr, target_endian); |
| 3364 | | switch (tag) { |
| 3365 | | else => {}, |
| 3366 | | std.elf.DT_STRTAB => strtab = val, |
| 3367 | | std.elf.DT_STRSZ => strsz = val, |
| 3368 | | std.elf.DT_SONAME => soname = val, |
| 4362 | if (tag == std.elf.DT_SONAME) { |
| 4363 | // val is a dynstr index |
| 4364 | if (val >= dynstr.len) { |
| 4365 | return diags.failParse(path, "bad soname string", .{}); |
| 4366 | } |
| 4367 | break std.mem.sliceTo(dynstr[@intCast(val)..], 0); |
| 3369 | 4368 | } |
| 3370 | | } |
| 3371 | | if (strtab == null or soname == null) |
| 3372 | | return elf.loadDsoExact(std.fs.path.basename(path.sub_path)); |
| 3373 | | if (strsz) |size| if (soname.? >= size) |
| 3374 | | return diags.failParse(path, "bad soname string", .{}); |
| 3375 | | try fr.seekTo(ehdr.phoff); |
| 3376 | | const ph = for (0..ehdr.phnum) |_| { |
| 3377 | | const ph = try r.peekStruct(ElfN.Phdr, target_endian); |
| 3378 | | try r.discardAll(ehdr.phentsize); |
| 3379 | | switch (ph.type) { |
| 3380 | | else => {}, |
| 3381 | | .LOAD => if (strtab.? >= ph.vaddr and |
| 3382 | | strtab.? + (strsz orelse 0) <= ph.vaddr + ph.filesz) break ph, |
| 4369 | } else std.fs.path.basename(path.sub_path); |
| 4370 | try elf.needed.put(gpa, try elf.string(.dynstr, soname), {}); |
| 4371 | |
| 4372 | // Scan the symbol table and populate `elf.dso_globals`. |
| 4373 | const first_global = @min(dynsym_sh.info, symnum); |
| 4374 | try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global); |
| 4375 | try elf.ensureUnusedPltCapacity(symnum - first_global); |
| 4376 | try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize); |
| 4377 | for (first_global..symnum) |_| { |
| 4378 | const sym = try r.peekStruct(ElfN.Sym, target_endian); |
| 4379 | try r.discardAll(@intCast(dynsym_sh.entsize)); |
| 4380 | |
| 4381 | switch (sym.info.bind) { |
| 4382 | else => continue, |
| 4383 | .GLOBAL, .WEAK, .GNU_UNIQUE => {}, |
| 3383 | 4384 | } |
| 3384 | | } else return diags.failParse(path, "strtab not part of a loaded segment", .{}); |
| 3385 | | try fr.seekTo(strtab.? + soname.? - ph.vaddr + ph.offset); |
| 3386 | | return elf.loadDsoExact(r.peekSentinel(0) catch |err| switch (err) { |
| 3387 | | error.StreamTooLong => return diags.failParse(path, "soname too lang", .{}), |
| 3388 | | else => |e| return e, |
| 3389 | | }); |
| 4385 | // STV_HIDDEN/STV_INTERNAL symbols should be marked as STB_LOCAL and hence skipped |
| 4386 | // above, but we might as well double-check. |
| 4387 | switch (sym.other.visibility) { |
| 4388 | .HIDDEN, .INTERNAL => continue, |
| 4389 | .DEFAULT, .PROTECTED => {}, |
| 4390 | } |
| 4391 | |
| 4392 | if (sym.shndx == std.elf.SHN_UNDEF) continue; |
| 4393 | |
| 4394 | if (sym.name >= dynstr.len) { |
| 4395 | return diags.failParse(path, "bad symbol name string", .{}); |
| 4396 | } |
| 4397 | |
| 4398 | const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0)); |
| 4399 | const gop = elf.dso_globals.getOrPutAssumeCapacity(name); |
| 4400 | if (!gop.found_existing or gop.value_ptr.* == .NOTYPE) { |
| 4401 | gop.value_ptr.* = sym.info.type; |
| 4402 | } |
| 4403 | |
| 4404 | // If there's already an undefined symbol by this name of type STT_NOTYPE, populate |
| 4405 | // its type now. |
| 4406 | const global_ptr = elf.globals.strong_undef.getPtr(name) orelse |
| 4407 | elf.globals.weak_undef.getPtr(name) orelse |
| 4408 | continue; |
| 4409 | |
| 4410 | if (global_ptr.dynsym_index == 0) continue; |
| 4411 | |
| 4412 | const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class)); |
| 4413 | switch (elf.targetLoad(&sym_ptr.other).visibility) { |
| 4414 | .HIDDEN, .INTERNAL, .PROTECTED => continue, |
| 4415 | .DEFAULT => {}, |
| 4416 | } |
| 4417 | |
| 4418 | if (elf.targetLoad(&sym_ptr.shndx) != std.elf.SHN_UNDEF) continue; |
| 4419 | |
| 4420 | const cur_info = elf.targetLoad(&sym_ptr.info); |
| 4421 | if (cur_info.type == .NOTYPE) { |
| 4422 | const new_type: std.elf.STT = switch (sym.info.type) { |
| 4423 | .GNU_IFUNC => .FUNC, |
| 4424 | else => |t| t, |
| 4425 | }; |
| 4426 | |
| 4427 | elf.targetStore(&sym_ptr.info, .{ |
| 4428 | .bind = cur_info.bind, |
| 4429 | .type = new_type, |
| 4430 | }); |
| 4431 | |
| 4432 | const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class)); |
| 4433 | elf.targetStore(&dynsym_ptr.info, .{ |
| 4434 | .bind = elf.targetLoad(&dynsym_ptr.info).bind, |
| 4435 | .type = new_type, |
| 4436 | }); |
| 4437 | |
| 4438 | // If we just turned this into an STT_FUNC symbol, then we have determined |
| 4439 | // that it needs a PLT entry. |
| 4440 | if (new_type == .FUNC) { |
| 4441 | elf.addPltEntry(name, global_ptr.dynsym_index); |
| 4442 | // ...and therefore, we need to re-apply that symbol's relocations, as |
| 4443 | // some might be targeting its PLT entry. |
| 4444 | global_ptr.symtab_index.applyTargetRelocs(elf); |
| 4445 | } |
| 4446 | } |
| 4447 | } |
| 3390 | 4448 | }, |
| 3391 | 4449 | } |
| 3392 | 4450 | } |
| 3393 | | fn loadDsoExact(elf: *Elf, name: []const u8) !void { |
| 3394 | | log.debug("loadDsoExact({f})", .{std.zig.fmtString(name)}); |
| 3395 | | if (elf.shndx.dynamic != .UNDEF) { |
| 3396 | | try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, name), {}); |
| 3397 | | } |
| 3398 | | } |
| 3399 | 4451 | |
| 3400 | 4452 | /// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input. |
| 3401 | 4453 | /// |
| ... | ... | @@ -3432,22 +4484,100 @@ fn checkInputIdent( |
| 3432 | 4484 | .{ident.version}, |
| 3433 | 4485 | ); |
| 3434 | 4486 | |
| 3435 | | // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common. |
| 3436 | | // It sounds reasonable to allow the value we chose *and* allow `.NONE`. |
| 3437 | | const expect_abiversion: u8 = abiver: { |
| 3438 | | if (ident.osabi == .NONE) break :abiver 0; |
| 3439 | | if (ident.osabi == target.osabi) break :abiver target.abiversion; |
| 3440 | | return diags.failParse( |
| 3441 | | path, |
| 3442 | | "bad ELF OS/ABI ({?s})", |
| 3443 | | .{std.enums.tagName(std.elf.OSABI, ident.osabi)}, |
| 3444 | | ); |
| 4487 | // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common. |
| 4488 | // It sounds reasonable to allow the value we chose *and* allow `.NONE`. |
| 4489 | const expect_abiversion: u8 = abiver: { |
| 4490 | if (ident.osabi == .NONE) break :abiver 0; |
| 4491 | if (ident.osabi == target.osabi) break :abiver target.abiversion; |
| 4492 | return diags.failParse( |
| 4493 | path, |
| 4494 | "bad ELF OS/ABI ({?s})", |
| 4495 | .{std.enums.tagName(std.elf.OSABI, ident.osabi)}, |
| 4496 | ); |
| 4497 | }; |
| 4498 | if (ident.abiversion != expect_abiversion) return diags.failParse( |
| 4499 | path, |
| 4500 | "bad ELF ABI version ({d})", |
| 4501 | .{ident.abiversion}, |
| 4502 | ); |
| 4503 | } |
| 4504 | |
| 4505 | fn createInitFiniArraySection( |
| 4506 | elf: *Elf, |
| 4507 | shndx: *Section.Index, |
| 4508 | comptime name: []const u8, |
| 4509 | @"type": std.elf.SHT, |
| 4510 | ) !void { |
| 4511 | assert(shndx.* == .UNDEF); |
| 4512 | const gpa = elf.base.comp.gpa; |
| 4513 | const addr_align: std.mem.Alignment = switch (elf.identClass()) { |
| 4514 | .NONE, _ => unreachable, |
| 4515 | .@"32" => .@"4", |
| 4516 | .@"64" => .@"8", |
| 4517 | }; |
| 4518 | assert(elf.section_by_name.count() == elf.shdrs.items.len); |
| 4519 | try elf.section_by_name.ensureUnusedCapacity(gpa, 1); |
| 4520 | shndx.* = try elf.addSection(elf.ni.data_rel_ro, .{ |
| 4521 | .name = "." ++ name, |
| 4522 | .type = @"type", |
| 4523 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 4524 | .node_align = addr_align, |
| 4525 | }); |
| 4526 | elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); |
| 4527 | try elf.ensureUnusedSymbolCapacity(2, .maybe_global); |
| 4528 | _ = elf.addGlobalSymbolAssumeCapacity(.{ |
| 4529 | .node = shndx.get(elf).ni, |
| 4530 | .name = try .string(elf, "__" ++ name ++ "_start"), |
| 4531 | .value = shndx.vaddr(elf), |
| 4532 | .size = 0, |
| 4533 | .type = .NOTYPE, |
| 4534 | .bind = .strong, |
| 4535 | .visibility = .HIDDEN, |
| 4536 | .shndx = shndx.*, |
| 4537 | }) catch |err| switch (err) { |
| 4538 | error.MultipleDefinitions => return elf.base.comp.link_diags.fail( |
| 4539 | "multiple definitions of '{s}'", |
| 4540 | .{"__" ++ name ++ "_start"}, |
| 4541 | ), |
| 4542 | }; |
| 4543 | _ = elf.addGlobalSymbolAssumeCapacity(.{ |
| 4544 | .node = shndx.get(elf).ni, |
| 4545 | .name = try .string(elf, "__" ++ name ++ "_end"), |
| 4546 | .value = shndx.vaddr(elf), |
| 4547 | .size = 0, |
| 4548 | .type = .NOTYPE, |
| 4549 | .bind = .strong, |
| 4550 | .visibility = .HIDDEN, |
| 4551 | .shndx = shndx.*, |
| 4552 | }) catch |err| switch (err) { |
| 4553 | error.MultipleDefinitions => return elf.base.comp.link_diags.fail( |
| 4554 | "multiple definitions of '{s}'", |
| 4555 | .{"__" ++ name ++ "_end"}, |
| 4556 | ), |
| 4557 | }; |
| 4558 | } |
| 4559 | fn updateInitFiniArraySectionSize( |
| 4560 | elf: *Elf, |
| 4561 | shndx: Section.Index, |
| 4562 | comptime name: []const u8, |
| 4563 | @"type": std.elf.SHT, |
| 4564 | new_size: u64, |
| 4565 | ) void { |
| 4566 | if (elf.shndx.dynamic != .UNDEF) { |
| 4567 | const arraysz_dyn_key: u32 = switch (@"type") { |
| 4568 | .INIT_ARRAY => std.elf.DT_INIT_ARRAYSZ, |
| 4569 | .FINI_ARRAY => std.elf.DT_FINI_ARRAYSZ, |
| 4570 | .PREINIT_ARRAY => std.elf.DT_PREINIT_ARRAYSZ, |
| 4571 | else => unreachable, |
| 4572 | }; |
| 4573 | elf.updateDynamicEntry(arraysz_dyn_key, new_size); |
| 4574 | } |
| 4575 | |
| 4576 | const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) { |
| 4577 | inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size), |
| 3445 | 4578 | }; |
| 3446 | | if (ident.abiversion != expect_abiversion) return diags.failParse( |
| 3447 | | path, |
| 3448 | | "bad ELF ABI version ({d})", |
| 3449 | | .{ident.abiversion}, |
| 3450 | | ); |
| 4579 | const end_sym_name = elf.string(.strtab, "__" ++ name ++ "_end") catch unreachable; // string definitely already exists |
| 4580 | Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr); |
| 3451 | 4581 | } |
| 3452 | 4582 | |
| 3453 | 4583 | pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { |
| ... | ... | @@ -3486,9 +4616,22 @@ fn prelinkInner(elf: *Elf) !void { |
| 3486 | 4616 | const ElfN = ct_class.ElfN(); |
| 3487 | 4617 | const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0; |
| 3488 | 4618 | const flags_1: ElfN.Addr = if (elf.options.z_now) std.elf.DF_1_NOW else 0; |
| 4619 | const rpath: String(.dynstr) = rpath: { |
| 4620 | var buf: std.ArrayList(u8) = .empty; |
| 4621 | defer buf.deinit(gpa); |
| 4622 | for (elf.options.rpath_list, 0..) |path, i| { |
| 4623 | if (i > 0) try buf.append(gpa, ':'); |
| 4624 | try buf.appendSlice(gpa, path); |
| 4625 | } |
| 4626 | break :rpath try elf.string(.dynstr, buf.items); |
| 4627 | }; |
| 3489 | 4628 | const needed_len = elf.needed.count(); |
| 3490 | 4629 | const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) + |
| 4630 | @intFromBool(rpath != .empty) + |
| 3491 | 4631 | @intFromBool(flags != 0) + @intFromBool(flags_1 != 0) + |
| 4632 | @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 + |
| 4633 | @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 + |
| 4634 | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + |
| 3492 | 4635 | @intFromBool(comp.config.output_mode == .Exe) + 12; |
| 3493 | 4636 | const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len); |
| 3494 | 4637 | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; |
| ... | ... | @@ -3508,6 +4651,10 @@ fn prelinkInner(elf: *Elf) !void { |
| 3508 | 4651 | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) }; |
| 3509 | 4652 | dynamic_index += 1; |
| 3510 | 4653 | } |
| 4654 | if (rpath != .empty) { |
| 4655 | dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) }; |
| 4656 | dynamic_index += 1; |
| 4657 | } |
| 3511 | 4658 | if (flags != 0) { |
| 3512 | 4659 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags }; |
| 3513 | 4660 | dynamic_index += 1; |
| ... | ... | @@ -3520,23 +4667,72 @@ fn prelinkInner(elf: *Elf) !void { |
| 3520 | 4667 | dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 }; |
| 3521 | 4668 | dynamic_index += 1; |
| 3522 | 4669 | } |
| 3523 | | const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx; |
| 3524 | | const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx; |
| 4670 | if (elf.shndx.init_array != .UNDEF) { |
| 4671 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 4672 | .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) }, |
| 4673 | .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad( |
| 4674 | &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size, |
| 4675 | ) }, |
| 4676 | }; |
| 4677 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 1); |
| 4678 | elf.addRelocAssumeCapacity( |
| 4679 | dynamic_ni, |
| 4680 | @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1), |
| 4681 | .local(elf.shndx.init_array.get(elf).lsi), |
| 4682 | 0, |
| 4683 | .absAddr(elf), |
| 4684 | ); |
| 4685 | dynamic_index += 2; |
| 4686 | } |
| 4687 | if (elf.shndx.fini_array != .UNDEF) { |
| 4688 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 4689 | .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) }, |
| 4690 | .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad( |
| 4691 | &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size, |
| 4692 | ) }, |
| 4693 | }; |
| 4694 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 1); |
| 4695 | elf.addRelocAssumeCapacity( |
| 4696 | dynamic_ni, |
| 4697 | @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1), |
| 4698 | .local(elf.shndx.fini_array.get(elf).lsi), |
| 4699 | 0, |
| 4700 | .absAddr(elf), |
| 4701 | ); |
| 4702 | dynamic_index += 2; |
| 4703 | } |
| 4704 | if (elf.shndx.preinit_array != .UNDEF) { |
| 4705 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 4706 | .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) }, |
| 4707 | .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad( |
| 4708 | &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size, |
| 4709 | ) }, |
| 4710 | }; |
| 4711 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 1); |
| 4712 | elf.addRelocAssumeCapacity( |
| 4713 | dynamic_ni, |
| 4714 | @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1), |
| 4715 | .local(elf.shndx.preinit_array.get(elf).lsi), |
| 4716 | 0, |
| 4717 | .absAddr(elf), |
| 4718 | ); |
| 4719 | dynamic_index += 2; |
| 4720 | } |
| 3525 | 4721 | dynamic_entries[dynamic_index..][0..12].* = .{ |
| 3526 | | .{ std.elf.DT_RELA, @intCast(elf.computeNodeVAddr(rela_dyn_shndx.get(elf).ni)) }, |
| 4722 | .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) }, |
| 3527 | 4723 | .{ std.elf.DT_RELASZ, elf.targetLoad( |
| 3528 | | &@field(elf.shdrPtr(rela_dyn_shndx), @tagName(ct_class)).size, |
| 4724 | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, |
| 3529 | 4725 | ) }, |
| 3530 | 4726 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, |
| 3531 | | .{ std.elf.DT_JMPREL, @intCast(elf.computeNodeVAddr(rela_plt_shndx.get(elf).ni)) }, |
| 4727 | .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) }, |
| 3532 | 4728 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( |
| 3533 | | &@field(elf.shdrPtr(rela_plt_shndx), @tagName(ct_class)).size, |
| 4729 | &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size, |
| 3534 | 4730 | ) }, |
| 3535 | | .{ std.elf.DT_PLTGOT, @intCast(elf.computeNodeVAddr(elf.shndx.got_plt.get(elf).ni)) }, |
| 4731 | .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) }, |
| 3536 | 4732 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, |
| 3537 | | .{ std.elf.DT_SYMTAB, @intCast(elf.computeNodeVAddr(elf.shndx.dynsym.get(elf).ni)) }, |
| 4733 | .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) }, |
| 3538 | 4734 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, |
| 3539 | | .{ std.elf.DT_STRTAB, @intCast(elf.computeNodeVAddr(elf.shndx.dynstr.get(elf).ni)) }, |
| 4735 | .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) }, |
| 3540 | 4736 | .{ std.elf.DT_STRSZ, elf.targetLoad( |
| 3541 | 4737 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, |
| 3542 | 4738 | ) }, |
| ... | ... | @@ -3547,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void { |
| 3547 | 4743 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 3548 | 4744 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); |
| 3549 | 4745 | |
| 3550 | | elf.first_dynamic_reloc = @enumFromInt(elf.relocs.items.len); |
| 4746 | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 3551 | 4747 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 5); |
| 3552 | 4748 | elf.addRelocAssumeCapacity( |
| 3553 | 4749 | dynamic_ni, |
| 3554 | 4750 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1), |
| 3555 | | .local(rela_dyn_shndx.get(elf).lsi), |
| 4751 | .local(elf.shndx.rela_dyn.get(elf).lsi), |
| 3556 | 4752 | 0, |
| 3557 | 4753 | .absAddr(elf), |
| 3558 | 4754 | ); |
| 3559 | 4755 | elf.addRelocAssumeCapacity( |
| 3560 | 4756 | dynamic_ni, |
| 3561 | 4757 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1), |
| 3562 | | .local(rela_plt_shndx.get(elf).lsi), |
| 4758 | .local(elf.shndx.rela_plt.get(elf).lsi), |
| 3563 | 4759 | 0, |
| 3564 | 4760 | .absAddr(elf), |
| 3565 | 4761 | ); |
| ... | ... | @@ -3663,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 3663 | 4859 | .type = .SECTION, |
| 3664 | 4860 | .shndx = shndx, |
| 3665 | 4861 | }) else .null; |
| 3666 | | elf.shdrs.appendAssumeCapacity(.{ .lsi = lsi, .ni = ni, .rela_shndx = .UNDEF, .rela_free = .none }); |
| 4862 | elf.shdrs.appendAssumeCapacity(.{ .lsi = lsi, .ni = ni, .rela = switch (opts.type) { |
| 4863 | .REL => unreachable, |
| 4864 | .RELA => .{ .free_head = .none }, |
| 4865 | else => .{ .shndx = .UNDEF }, |
| 4866 | } }); |
| 3667 | 4867 | elf.nodes.appendAssumeCapacity(.{ .section = shndx }); |
| 3668 | 4868 | const offset = ni.fileLocation(&elf.mf, false).offset; |
| 3669 | 4869 | switch (elf.shdrPtr(shndx)) { |
| ... | ... | @@ -3689,20 +4889,23 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 3689 | 4889 | fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void { |
| 3690 | 4890 | if (len == 0) return; |
| 3691 | 4891 | const gpa = elf.base.comp.gpa; |
| 3692 | | try elf.relocs.ensureUnusedCapacity(gpa, len); |
| 4892 | try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 4893 | try elf.got_relocs.ensureUnusedCapacity(gpa, len); |
| 3693 | 4894 | const class = elf.identClass(); |
| 3694 | | const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) { |
| 4895 | switch (elf.ehdrField(.type)) { |
| 3695 | 4896 | .NONE, .CORE, _ => unreachable, |
| 3696 | 4897 | .REL => { |
| 3697 | 4898 | const shndx = elf.getNodeShndx(node); |
| 3698 | | if (shndx.get(elf).rela_shndx == .UNDEF) { |
| 4899 | if (shndx.get(elf).rela.shndx == .UNDEF) { |
| 3699 | 4900 | var bfa_buf: [32]u8 = undefined; |
| 3700 | 4901 | var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa); |
| 3701 | 4902 | const allocator = bfa.allocator(); |
| 3702 | 4903 | |
| 3703 | | const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf)}); |
| 4904 | const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf).slice(elf)}); |
| 3704 | 4905 | defer allocator.free(rela_name); |
| 3705 | 4906 | |
| 4907 | assert(elf.section_by_name.count() == elf.shdrs.items.len); |
| 4908 | try elf.section_by_name.ensureUnusedCapacity(gpa, 1); |
| 3706 | 4909 | const rela_shndx = try elf.addSection(.none, .{ |
| 3707 | 4910 | .name = rela_name, |
| 3708 | 4911 | .type = .RELA, |
| ... | ... | @@ -3719,33 +4922,29 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) |
| 3719 | 4922 | }, |
| 3720 | 4923 | .node_align = elf.mf.flags.block_size, |
| 3721 | 4924 | }); |
| 3722 | | shndx.get(elf).rela_shndx = rela_shndx; |
| 4925 | elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {}); |
| 4926 | shndx.get(elf).rela.shndx = rela_shndx; |
| 3723 | 4927 | } |
| 3724 | | break :rela .{ shndx.get(elf).rela_shndx, len }; |
| 4928 | try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len); |
| 3725 | 4929 | }, |
| 3726 | | .EXEC, .DYN => switch (elf.got.tlsld) { |
| 3727 | | _ => return, |
| 3728 | | .none => if (elf.shndx.dynamic != .UNDEF) { |
| 3729 | | try elf.mf.updates.ensureUnusedCapacity(gpa, 1); |
| 3730 | | const got_ni = elf.shndx.got.get(elf).ni; |
| 3731 | | _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf); |
| 3732 | | const got_size = switch (class) { |
| 3733 | | .NONE, _ => unreachable, |
| 3734 | | inline else => |ct_class| (elf.got.len + 2) * @sizeOf(ct_class.ElfN().Addr), |
| 3735 | | }; |
| 3736 | | if (got_size > got_node_size) |
| 3737 | | try got_ni.resize(&elf.mf, gpa, got_size +| got_size / MappedFile.growth_factor); |
| 3738 | | break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 }; |
| 3739 | | } else return, |
| 4930 | .EXEC, .DYN => { |
| 4931 | try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 4932 | const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD |
| 4933 | try elf.got.ensureUnusedCapacity(gpa, new_got_entries); |
| 4934 | const got_ni = elf.shndx.got.get(elf).ni; |
| 4935 | _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf); |
| 4936 | const need_got_size = switch (class) { |
| 4937 | .NONE, _ => unreachable, |
| 4938 | inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr), |
| 4939 | }; |
| 4940 | if (need_got_size > got_node_size) |
| 4941 | try got_ni.resize(&elf.mf, gpa, need_got_size +| need_got_size / MappedFile.growth_factor); |
| 4942 | |
| 4943 | if (elf.shndx.dynamic != .UNDEF) { |
| 4944 | try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries); |
| 4945 | } |
| 3740 | 4946 | }, |
| 3741 | | }; |
| 3742 | | const rela_ni = rela_shndx.get(elf).ni; |
| 3743 | | _, const rela_node_size = rela_ni.location(&elf.mf).resolve(&elf.mf); |
| 3744 | | const rela_size = switch (elf.shdrPtr(rela_shndx)) { |
| 3745 | | inline else => |shdr| elf.targetLoad(&shdr.size) + elf.targetLoad(&shdr.entsize) * rela_len, |
| 3746 | | }; |
| 3747 | | if (rela_size > rela_node_size) |
| 3748 | | try rela_ni.resize(&elf.mf, gpa, rela_size +| rela_size / MappedFile.growth_factor); |
| 4947 | } |
| 3749 | 4948 | } |
| 3750 | 4949 | fn addRelocAssumeCapacity( |
| 3751 | 4950 | elf: *Elf, |
| ... | ... | @@ -3753,123 +4952,443 @@ fn addRelocAssumeCapacity( |
| 3753 | 4952 | offset: u64, |
| 3754 | 4953 | target: Symbol.Id, |
| 3755 | 4954 | addend: i64, |
| 3756 | | @"type": Reloc.Type, |
| 4955 | @"type": MachineRelocType, |
| 3757 | 4956 | ) void { |
| 3758 | 4957 | assert(node != .none); |
| 3759 | | const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len); |
| 3760 | | const next: Reloc.Index = next: { |
| 3761 | | const target_ptr = target.index(elf).ptr(elf); |
| 3762 | | const next = target_ptr.first_target_reloc; |
| 3763 | | target_ptr.first_target_reloc = ri; |
| 3764 | | break :next next; |
| 4958 | switch (elf.ehdrField(.type)) { |
| 4959 | .NONE, .CORE, _ => unreachable, |
| 4960 | .REL => { |
| 4961 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| 4962 | const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ |
| 4963 | .type = @"type", |
| 4964 | // This field needs to equal the offset into the section, which is *not* necessarily |
| 4965 | // the same thing as our `offset`, which is the offset into `node`. We could compute |
| 4966 | // the section offset now, but there's no point, because `flushMovedNodeRelocs` will |
| 4967 | // eventually do it for us anyway, so just init to 0. |
| 4968 | .offset = 0, |
| 4969 | .raw_sym_index = @intFromEnum(target.index(elf)), |
| 4970 | .addend = addend, |
| 4971 | }); |
| 4972 | const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len); |
| 4973 | const next: SymbolReloc.Index = next: { |
| 4974 | const target_ptr = target.index(elf).ptr(elf); |
| 4975 | const next = target_ptr.first_target_reloc; |
| 4976 | target_ptr.first_target_reloc = ri; |
| 4977 | break :next next; |
| 4978 | }; |
| 4979 | if (next != .none) { |
| 4980 | next.get(elf).prev = ri; |
| 4981 | } |
| 4982 | elf.symbol_relocs.appendAssumeCapacity(.{ |
| 4983 | .node = node, |
| 4984 | .offset = offset, |
| 4985 | .type = .write_rela, |
| 4986 | .target = target, |
| 4987 | .addend = addend, |
| 4988 | .next = next, |
| 4989 | .prev = .none, |
| 4990 | .rela_index = rela_index.toOptional(), |
| 4991 | }); |
| 4992 | }, |
| 4993 | |
| 4994 | .DYN, .EXEC => switch (elf.ehdrField(.machine)) { |
| 4995 | else => |machine| @panic(@tagName(machine)), |
| 4996 | .X86_64 => switch (@"type".X86_64) { |
| 4997 | _, |
| 4998 | .NONE, |
| 4999 | .COPY, |
| 5000 | .GLOB_DAT, |
| 5001 | .JUMP_SLOT, |
| 5002 | .RELATIVE64, |
| 5003 | .RELATIVE, |
| 5004 | .IRELATIVE, |
| 5005 | .@"16", |
| 5006 | .PC16, |
| 5007 | .@"8", |
| 5008 | .PC8, |
| 5009 | .DTPMOD64, |
| 5010 | .GOTPLT64, |
| 5011 | => @panic("TODO: error for illegal or unsupported input relocation"), |
| 5012 | |
| 5013 | // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt |
| 5014 | .GOTPC32_TLSDESC => @panic("TODO: R_X86_64_GOTPC32_TLSDESC"), |
| 5015 | .TLSDESC_CALL => @panic("TODO: R_X86_64_TLSDESC_CALL"), |
| 5016 | .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"), |
| 5017 | |
| 5018 | // Relocations targeting a symbol |
| 5019 | .@"64" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), |
| 5020 | .@"32" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), |
| 5021 | .@"32S" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s), |
| 5022 | .PC64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64), |
| 5023 | .PC32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), |
| 5024 | .PLT32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32), |
| 5025 | .SIZE64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64), |
| 5026 | .SIZE32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32), |
| 5027 | .DTPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64), |
| 5028 | .DTPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32), |
| 5029 | .TPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64), |
| 5030 | .TPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32), |
| 5031 | .GOTPC64 => { |
| 5032 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 5033 | return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64); |
| 5034 | }, |
| 5035 | .GOTPC32 => { |
| 5036 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 5037 | return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32); |
| 5038 | }, |
| 5039 | |
| 5040 | // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the |
| 5041 | // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm |
| 5042 | // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which |
| 5043 | // need to be re-applied whenever the GOT moves. |
| 5044 | .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base |
| 5045 | .PLTOFF64 => @panic("TODO: R_X86_64_PLTOFF64"), // offset of PLT entry from GOT base (yes, I know, the name is stupid) |
| 5046 | |
| 5047 | // Relocations targeting a GOT entry |
| 5048 | .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset64), |
| 5049 | .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset32), |
| 5050 | .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64), |
| 5051 | .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 5052 | // TODO: the next two are relaxable to non-GOT relocations, but I haven't figured |
| 5053 | // out how to represent relaxations yet. If we want to remove a `GotReloc` and add a |
| 5054 | // `SymbolReloc` at some point, we can't do that in `GotReloc.apply`, because that |
| 5055 | // function must be idempotent to ensure reproducible binaries. I think we would |
| 5056 | // need to do that as soon as the operation is known to be relaxable (e.g. because |
| 5057 | // we found a defininition for a non-preemptible symbol). |
| 5058 | .GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 5059 | .REX_GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 5060 | |
| 5061 | .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .rel32), |
| 5062 | .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32), |
| 5063 | .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32), |
| 5064 | }, |
| 5065 | }, |
| 5066 | } |
| 5067 | } |
| 5068 | fn addSymbolRelocAssumeCapacity( |
| 5069 | elf: *Elf, |
| 5070 | node: MappedFile.Node.Index, |
| 5071 | offset: u64, |
| 5072 | target: Symbol.Id, |
| 5073 | addend: i64, |
| 5074 | @"type": SymbolReloc.Type, |
| 5075 | ) void { |
| 5076 | assert(elf.ehdrField(.type) != .REL); |
| 5077 | |
| 5078 | const rela_index: Section.RelaIndex.Optional = r: { |
| 5079 | if (elf.shndx.dynamic == .UNDEF) break :r .none; |
| 5080 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 5081 | else => |machine| @panic(@tagName(machine)), |
| 5082 | .X86_64 => .{ .X86_64 = switch (@"type") { |
| 5083 | .write_rela => unreachable, |
| 5084 | .abs64 => .@"64", |
| 5085 | .abs32 => .@"32", |
| 5086 | .abs32s => .@"32S", |
| 5087 | .rel64 => .PC64, |
| 5088 | .rel32 => .PC32, |
| 5089 | .pltrel64 => break :r .none, |
| 5090 | .pltrel32 => break :r .none, |
| 5091 | .dtpoff64 => .DTPOFF64, |
| 5092 | .dtpoff32 => .DTPOFF32, |
| 5093 | .tpoff64 => .TPOFF64, |
| 5094 | .tpoff32 => .TPOFF32, |
| 5095 | .size64 => .SIZE64, |
| 5096 | .size32 => .SIZE32, |
| 5097 | } }, |
| 5098 | }; |
| 5099 | const dynsym_index: u32 = switch (target.unwrap()) { |
| 5100 | .local => break :r .none, |
| 5101 | // TODO: even if the symbol is locally defined, preemption/interposition is a |
| 5102 | // possibility, which this condition does not currently consider! |
| 5103 | .global => |name| if (elf.globals.strong_def.contains(name) or |
| 5104 | elf.globals.weak_def.contains(name)) |
| 5105 | { |
| 5106 | break :r .none; |
| 5107 | } else elf.globalByName(name).?.dynsym_index, |
| 5108 | }; |
| 5109 | |
| 5110 | if (elf.nodeRequiresTextrel(node)) { |
| 5111 | elf.textrel_count += 1; |
| 5112 | } |
| 5113 | |
| 5114 | // It currently looks like we need a runtime relocation for this. |
| 5115 | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 5116 | .type = rela_type, |
| 5117 | // This field needs to equal the offset into the section, which is *not* necessarily |
| 5118 | // the same thing as our `offset`, which is the offset into `node`. We could compute |
| 5119 | // the section offset now, but there's no point, because `flushMovedNodeRelocs` will |
| 5120 | // eventually do it for us anyway, so just init to 0. |
| 5121 | .offset = 0, |
| 5122 | .raw_sym_index = dynsym_index, |
| 5123 | .addend = addend, |
| 5124 | }).toOptional(); |
| 3765 | 5125 | }; |
| 5126 | |
| 5127 | const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len); |
| 5128 | const target_ptr = target.index(elf).ptr(elf); |
| 5129 | const next = target_ptr.first_target_reloc; |
| 5130 | target_ptr.first_target_reloc = ri; |
| 3766 | 5131 | if (next != .none) { |
| 3767 | 5132 | next.get(elf).prev = ri; |
| 3768 | 5133 | } |
| 3769 | | elf.relocs.addOneAssumeCapacity().* = .{ |
| 5134 | elf.symbol_relocs.appendAssumeCapacity(.{ |
| 5135 | .node = node, |
| 5136 | .offset = offset, |
| 5137 | .target = target, |
| 5138 | .addend = addend, |
| 3770 | 5139 | .type = @"type", |
| 3771 | | .prev = .none, |
| 3772 | 5140 | .next = next, |
| 5141 | .prev = .none, |
| 5142 | .rela_index = rela_index, |
| 5143 | }); |
| 5144 | if (@"type".dependsOnTlsSize()) { |
| 5145 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); |
| 5146 | } |
| 5147 | } |
| 5148 | fn addGotRelocAssumeCapacity( |
| 5149 | elf: *Elf, |
| 5150 | node: MappedFile.Node.Index, |
| 5151 | offset: u64, |
| 5152 | target: GotKey, |
| 5153 | addend: i64, |
| 5154 | @"type": GotReloc.Type, |
| 5155 | ) void { |
| 5156 | assert(elf.ehdrField(.type) != .REL); |
| 5157 | switch (elf.getNode(node)) { |
| 5158 | .input_section, |
| 5159 | .nav, |
| 5160 | .lazy_code, |
| 5161 | .lazy_const_data, |
| 5162 | => {}, |
| 5163 | |
| 5164 | .section => unreachable, // cannot contain GOT relocs |
| 5165 | .uav => unreachable, // cannot contain GOT relocs |
| 5166 | |
| 5167 | .file => unreachable, // cannot contain relocs |
| 5168 | .ehdr => unreachable, // cannot contain relocs |
| 5169 | .shdr => unreachable, // cannot contain relocs |
| 5170 | .segment => unreachable, // cannot contain relocs |
| 5171 | } |
| 5172 | |
| 5173 | const gop = elf.got.getOrPutAssumeCapacity(target); |
| 5174 | if (!gop.found_existing) { |
| 5175 | gop.value_ptr.* = .none; |
| 5176 | const maybe_next_key: ?GotKey = switch (target) { |
| 5177 | .reserved => null, |
| 5178 | .tpoff => null, |
| 5179 | .symbol => null, |
| 5180 | .tlsld0 => .tlsld1, |
| 5181 | .tlsgd0 => |sym| .{ .tlsgd1 = sym }, |
| 5182 | .tlsld1 => unreachable, |
| 5183 | .tlsgd1 => unreachable, |
| 5184 | }; |
| 5185 | switch (elf.shdrPtr(elf.shndx.got)) { |
| 5186 | inline else => |got_shdr, class| { |
| 5187 | const Addr = class.ElfN().Addr; |
| 5188 | const old_size = elf.targetLoad(&got_shdr.size); |
| 5189 | const new_entry_count = @as(u32, 1) + @intFromBool(maybe_next_key != null); |
| 5190 | elf.targetStore(&got_shdr.size, @intCast(old_size + @sizeOf(Addr) * new_entry_count)); |
| 5191 | }, |
| 5192 | } |
| 5193 | if (maybe_next_key) |next_key| { |
| 5194 | elf.got.putAssumeCapacityNoClobber(next_key, .none); |
| 5195 | elf.updateGotEntry(gop.index); |
| 5196 | elf.updateGotEntry(gop.index + 1); |
| 5197 | } else { |
| 5198 | elf.updateGotEntry(gop.index); |
| 5199 | } |
| 5200 | } |
| 5201 | |
| 5202 | elf.got_relocs.appendAssumeCapacity(.{ |
| 3773 | 5203 | .node = node, |
| 5204 | .offset = offset, |
| 3774 | 5205 | .target = target, |
| 3775 | | .index = index: switch (elf.ehdrField(.type)) { |
| 3776 | | .NONE, .CORE, _ => unreachable, |
| 3777 | | .REL => { |
| 3778 | | const sh = elf.getNodeShndx(node).get(elf); |
| 3779 | | switch (elf.shdrPtr(sh.rela_shndx)) { |
| 3780 | | inline else => |shdr, class| { |
| 3781 | | const Rela = class.ElfN().Rela; |
| 3782 | | const ent_size = elf.targetLoad(&shdr.entsize); |
| 3783 | | const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); |
| 3784 | | const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: { |
| 3785 | | const rela: *Rela = @ptrCast(@alignCast( |
| 3786 | | rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], |
| 3787 | | )); |
| 3788 | | sh.rela_free = @enumFromInt(rela.offset); |
| 3789 | | break :alloc_index index; |
| 3790 | | } else alloc_index: { |
| 3791 | | const old_size = elf.targetLoad(&shdr.size); |
| 3792 | | const new_size = old_size + ent_size; |
| 3793 | | elf.targetStore(&shdr.size, @intCast(new_size)); |
| 3794 | | break :alloc_index @intCast(@divExact(old_size, ent_size)); |
| 3795 | | }; |
| 3796 | | const rela: *Rela = @ptrCast(@alignCast( |
| 3797 | | rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], |
| 3798 | | )); |
| 3799 | | // The `offset` field here needs to equal the offset into the section, which |
| 3800 | | // is *not* the same as our `offset` which is the offset into `node`. We |
| 3801 | | // could calculate it now, but there's no point since `flushMovedNodeRelocs` |
| 3802 | | // will eventually do that for us anyway. So for now, just set offset to 0. |
| 3803 | | rela.* = .{ |
| 3804 | | .offset = 0, |
| 3805 | | .info = .{ |
| 3806 | | .type = @intCast(@"type".unwrap(elf)), |
| 3807 | | .sym = @intCast(@intFromEnum(target.index(elf))), |
| 3808 | | }, |
| 3809 | | .addend = @intCast(addend), |
| 3810 | | }; |
| 3811 | | if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(Rela, rela); |
| 3812 | | break :index .wrap(index); |
| 5206 | .addend = addend, |
| 5207 | .type = @"type", |
| 5208 | }); |
| 5209 | } |
| 5210 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 5211 | const entry_value: union(enum) { |
| 5212 | unsigned: u64, |
| 5213 | signed: i64, |
| 5214 | reloc: struct { |
| 5215 | type: MachineRelocType, |
| 5216 | dynsym_index: u32, |
| 5217 | }, |
| 5218 | } = switch (elf.got.keys()[got_index]) { |
| 5219 | .reserved => .{ .unsigned = 0 }, |
| 5220 | .tpoff => |sym_id| val: { |
| 5221 | // We will break from this block if we require a relocation. |
| 5222 | known: { |
| 5223 | if (elf.base.comp.config.output_mode != .Exe) { |
| 5224 | // Only the executable's per-module TLS block is at a known offset from the |
| 5225 | // general TLS pointer. |
| 5226 | break :known; |
| 5227 | } |
| 5228 | switch (sym_id.unwrap()) { |
| 5229 | .local => {}, |
| 5230 | .global => |name| if (elf.globals.strong_undef.contains(name) or |
| 5231 | elf.globals.weak_undef.contains(name)) |
| 5232 | { |
| 5233 | // This is an external TLS symbol, so we don't know its offset. |
| 5234 | break :known; |
| 3813 | 5235 | }, |
| 3814 | 5236 | } |
| 3815 | | }, |
| 3816 | | .EXEC, .DYN => { |
| 3817 | | switch (elf.ehdrField(.machine)) { |
| 3818 | | else => |machine| @panic(@tagName(machine)), |
| 3819 | | .AARCH64, .PPC64, .RISCV => {}, |
| 3820 | | .X86_64 => switch (@"type".X86_64) { |
| 3821 | | else => {}, |
| 3822 | | .TLSLD => switch (elf.got.tlsld) { |
| 3823 | | _ => {}, |
| 3824 | | .none => if (elf.shndx.dynamic != .UNDEF) { |
| 3825 | | const tlsld_index = elf.got.len; |
| 3826 | | elf.got.tlsld = .wrap(tlsld_index); |
| 3827 | | elf.got.len = tlsld_index + 2; |
| 3828 | | const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) { |
| 3829 | | inline else => |shdr, class| { |
| 3830 | | const addr_size = @sizeOf(class.ElfN().Addr); |
| 3831 | | const old_size = addr_size * tlsld_index; |
| 3832 | | const new_size = old_size + addr_size * 2; |
| 3833 | | @memset( |
| 3834 | | elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size], |
| 3835 | | 0, |
| 3836 | | ); |
| 3837 | | break :got_addr elf.targetLoad(&shdr.addr) + old_size; |
| 3838 | | }, |
| 5237 | // It's a symbol which we define, the symbol is not interposable because we're the |
| 5238 | // executable, and we know our per-module TLS block's offset because we're the |
| 5239 | // executable. We therefore know this value! |
| 5240 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 5241 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 5242 | inline else => |phdr| tls_size: { |
| 5243 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 5244 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 5245 | }, |
| 5246 | }; |
| 5247 | const sym_value = sym_id.value(elf); |
| 5248 | break :val .{ .signed = @bitCast(sym_value -% tls_size) }; |
| 5249 | } |
| 5250 | break :val .{ |
| 5251 | .reloc = .{ |
| 5252 | .type = switch (elf.ehdrField(.machine)) { |
| 5253 | else => |machine| @panic(@tagName(machine)), |
| 5254 | .X86_64 => .{ .X86_64 = .TPOFF64 }, |
| 5255 | }, |
| 5256 | .dynsym_index = switch (sym_id.unwrap()) { |
| 5257 | .global => |name| elf.globalByName(name).?.dynsym_index, |
| 5258 | // TODO: I have no idea if compilers are even allowed to emit this, but if they |
| 5259 | // are then I guess we need to add this local symbol to `.dynsym`? |
| 5260 | .local => @panic("TODO(Elf2): GOT tpoff entry referencing local symbol"), |
| 5261 | }, |
| 5262 | }, |
| 5263 | }; |
| 5264 | }, |
| 5265 | .symbol, .tlsgd1 => |sym_id, tag| val: { |
| 5266 | const name = switch (sym_id.unwrap()) { |
| 5267 | .local => break :val .{ .unsigned = sym_id.value(elf) }, |
| 5268 | .global => |name| name, |
| 5269 | }; |
| 5270 | // If the symbol is *defined* in this module, we might be able to avoid the relocation. |
| 5271 | if (elf.globals.strong_def.getPtr(name) orelse |
| 5272 | elf.globals.weak_def.getPtr(name)) |global| |
| 5273 | { |
| 5274 | // We have a definition, but it might be interposable (aka preemptible). There |
| 5275 | // are two cases where it is not and so we can (and, in fact, must) elide the |
| 5276 | // runtime relocation: |
| 5277 | // * We are the executable. Symbols from executables cannot be interposed. |
| 5278 | // * The symbol's visibility disallows interposition. |
| 5279 | if (elf.base.comp.config.output_mode == .Exe) { |
| 5280 | // No relocation needed. |
| 5281 | break :val .{ .unsigned = sym_id.value(elf) }; |
| 5282 | } |
| 5283 | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { |
| 5284 | inline else => |sym| elf.targetLoad(&sym.other).visibility, |
| 5285 | }; |
| 5286 | switch (visibility) { |
| 5287 | .DEFAULT => {}, |
| 5288 | .INTERNAL, .HIDDEN, .PROTECTED => { |
| 5289 | // No relocation needed. |
| 5290 | break :val .{ .unsigned = sym_id.value(elf) }; |
| 5291 | }, |
| 5292 | } |
| 5293 | } |
| 5294 | break :val .{ .reloc = .{ |
| 5295 | .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf), |
| 5296 | .dynsym_index = elf.globalByName(name).?.dynsym_index, |
| 5297 | } }; |
| 5298 | }, |
| 5299 | .tlsgd0 => |sym| switch (elf.shndx.dynamic) { |
| 5300 | .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable |
| 5301 | else => .{ |
| 5302 | .reloc = .{ |
| 5303 | .type = .{ .X86_64 = .DTPMOD64 }, |
| 5304 | .dynsym_index = switch (sym.unwrap()) { |
| 5305 | .local => 0, |
| 5306 | .global => |name| dsi: { |
| 5307 | // Like in the `.tlsgd1` case, we need to check for a non-interposable definition. |
| 5308 | if (elf.globals.strong_def.getPtr(name) orelse |
| 5309 | elf.globals.weak_def.getPtr(name)) |global| |
| 5310 | { |
| 5311 | if (elf.base.comp.config.output_mode == .Exe) { |
| 5312 | break :dsi 0; // non-interposable definition |
| 5313 | } |
| 5314 | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { |
| 5315 | inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility, |
| 3839 | 5316 | }; |
| 3840 | | const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx; |
| 3841 | | const rela_dyn_ni = rela_dyn_shndx.get(elf).ni; |
| 3842 | | switch (elf.shdrPtr(rela_dyn_shndx)) { |
| 3843 | | inline else => |shdr, class| { |
| 3844 | | const Rela = class.ElfN().Rela; |
| 3845 | | const old_size = elf.targetLoad(&shdr.size); |
| 3846 | | const new_size = old_size + elf.targetLoad(&shdr.entsize); |
| 3847 | | elf.targetStore(&shdr.size, new_size); |
| 3848 | | const rela: *Rela = @ptrCast(@alignCast(rela_dyn_ni |
| 3849 | | .slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)])); |
| 3850 | | rela.* = .{ |
| 3851 | | .offset = @intCast(got_addr), |
| 3852 | | .info = .{ |
| 3853 | | .type = @intFromEnum(std.elf.R_X86_64.DTPMOD64), |
| 3854 | | .sym = 0, |
| 3855 | | }, |
| 3856 | | .addend = 0, |
| 3857 | | }; |
| 3858 | | if (elf.targetEndian() != native_endian) |
| 3859 | | std.mem.byteSwapAllFields(Rela, rela); |
| 5317 | switch (visibility) { |
| 5318 | .DEFAULT => {}, |
| 5319 | .INTERNAL, .HIDDEN, .PROTECTED => { |
| 5320 | break :dsi 0; // non-interposable definition |
| 3860 | 5321 | }, |
| 3861 | 5322 | } |
| 3862 | | rela_dyn_ni.resizedAssumeCapacity(&elf.mf); |
| 3863 | | }, |
| 5323 | } |
| 5324 | // `sym` is either undefined or an interposable definition, so use its |
| 5325 | // actual dynsym index. |
| 5326 | break :dsi elf.globalByName(name).?.dynsym_index; |
| 3864 | 5327 | }, |
| 3865 | 5328 | }, |
| 3866 | | } |
| 3867 | | break :index .none; |
| 5329 | }, |
| 3868 | 5330 | }, |
| 3869 | 5331 | }, |
| 3870 | | .offset = offset, |
| 3871 | | .addend = addend, |
| 5332 | .tlsld0 => switch (elf.shndx.dynamic) { |
| 5333 | .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable |
| 5334 | else => .{ .reloc = .{ |
| 5335 | .type = .{ .X86_64 = .DTPMOD64 }, |
| 5336 | .dynsym_index = 0, |
| 5337 | } }, |
| 5338 | }, |
| 5339 | .tlsld1 => .{ .unsigned = 0 }, |
| 5340 | }; |
| 5341 | |
| 5342 | // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes. |
| 5343 | const got_entry_addr: u64 = switch (elf.shdrPtr(elf.shndx.got)) { |
| 5344 | inline else => |got_shdr, class| got_entry_addr: { |
| 5345 | const addr_size = @sizeOf(class.ElfN().Addr); |
| 5346 | const offset = got_index * addr_size; |
| 5347 | const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast( |
| 5348 | elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size], |
| 5349 | )); |
| 5350 | entry_ptr.* = switch (entry_value) { |
| 5351 | .unsigned => |x| @intCast(x), |
| 5352 | .signed => |x| switch (class) { |
| 5353 | .NONE, _ => comptime unreachable, |
| 5354 | .@"32" => @bitCast(@as(i32, @intCast(x))), |
| 5355 | .@"64" => @bitCast(x), |
| 5356 | }, |
| 5357 | .reloc => 0, |
| 5358 | }; |
| 5359 | break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset; |
| 5360 | }, |
| 5361 | }; |
| 5362 | |
| 5363 | // Then, add or remove the relocation entry if needed. |
| 5364 | if (elf.shndx.dynamic == .UNDEF) { |
| 5365 | // There are no relocations in the output file, so there's no reloc to delete and we can't |
| 5366 | // add a reloc in any case. (If we *are* requesting a reloc, it'll be because the value of |
| 5367 | // this GOT entry is not yet known, e.g. because a symbol is currently undefined.) |
| 5368 | return; |
| 5369 | } |
| 5370 | if (elf.got.values()[got_index].unwrap()) |rela_index| { |
| 5371 | // Clear the old relocation entry (although we might immediately re-use it below). |
| 5372 | elf.shndx.rela_dyn.relaDeleteOne(elf, rela_index); |
| 5373 | } |
| 5374 | elf.got.values()[got_index] = switch (entry_value) { |
| 5375 | .unsigned, .signed => .none, // no relocation needed |
| 5376 | .reloc => |reloc| elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 5377 | .type = reloc.type, |
| 5378 | .offset = got_entry_addr, |
| 5379 | .raw_sym_index = reloc.dynsym_index, |
| 5380 | .addend = 0, |
| 5381 | }).toOptional(), |
| 5382 | }; |
| 5383 | } |
| 5384 | |
| 5385 | /// Returns whether a `DT_TEXTREL` dynamic entry is needed to have a runtime relocation in `node`. |
| 5386 | fn nodeRequiresTextrel(elf: *Elf, node: MappedFile.Node.Index) bool { |
| 5387 | const shndx = elf.getNodeShndx(node); |
| 5388 | const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) { |
| 5389 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, |
| 3872 | 5390 | }; |
| 5391 | return shf.ALLOC and !shf.WRITE; |
| 3873 | 5392 | } |
| 3874 | 5393 | |
| 3875 | 5394 | pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| ... | ... | @@ -3999,16 +5518,21 @@ pub fn flush( |
| 3999 | 5518 | _ = arena; |
| 4000 | 5519 | _ = prog_node; |
| 4001 | 5520 | |
| 4002 | | if (elf.ehdrField(.type) != .REL and |
| 4003 | | elf.shndx.dynamic == .UNDEF and |
| 4004 | | elf.globals.strong_undef.count() > 0) |
| 4005 | | { |
| 5521 | if (comp.config.output_mode == .Exe) { |
| 5522 | var any_undef = false; |
| 4006 | 5523 | for (elf.globals.strong_undef.keys()) |name| { |
| 5524 | if (elf.dso_globals.contains(name)) continue; |
| 5525 | any_undef = true; |
| 4007 | 5526 | comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)}); |
| 4008 | 5527 | } |
| 4009 | | return error.LinkFailure; |
| 5528 | if (any_undef) return error.LinkFailure; |
| 4010 | 5529 | } |
| 4011 | 5530 | |
| 5531 | elf.updateDynamicTextrel() catch |err| switch (err) { |
| 5532 | error.OutOfMemory => |e| return e, |
| 5533 | else => |e| return elf.base.comp.link_diags.fail("updateDynamicTextrel failed: {t}", .{e}), |
| 5534 | }; |
| 5535 | |
| 4012 | 5536 | while (try elf.idle(tid)) {} |
| 4013 | 5537 | |
| 4014 | 5538 | const entry_addr: u64 = entry: { |
| ... | ... | @@ -4039,6 +5563,47 @@ pub fn flush( |
| 4039 | 5563 | else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}), |
| 4040 | 5564 | }; |
| 4041 | 5565 | } |
| 5566 | fn updateDynamicTextrel(elf: *Elf) !void { |
| 5567 | if (elf.shndx.dynamic == .UNDEF) return; |
| 5568 | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; |
| 5569 | switch (elf.shdrPtr(elf.shndx.dynamic)) { |
| 5570 | inline else => |shdr, class| if (elf.textrel_count > 0) { |
| 5571 | const cur_size = elf.targetLoad(&shdr.size); |
| 5572 | const cur_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 5573 | dynamic_ni.slice(&elf.mf)[0..@intCast(cur_size)], |
| 5574 | )); |
| 5575 | const has_textrel: bool = for (cur_entries) |*entry| { |
| 5576 | if (elf.targetLoad(&entry[0]) == std.elf.DT_TEXTREL) { |
| 5577 | break true; |
| 5578 | } |
| 5579 | } else false; |
| 5580 | if (!has_textrel) { |
| 5581 | // Add a DT_TEXTREL entry before the final DT_NULL entry. |
| 5582 | const new_size = cur_size + @sizeOf([2]class.ElfN().Addr); |
| 5583 | _, const node_size = dynamic_ni.location(&elf.mf).resolve(&elf.mf); |
| 5584 | if (node_size < new_size) { |
| 5585 | try dynamic_ni.resize(&elf.mf, elf.base.comp.gpa, new_size); |
| 5586 | } |
| 5587 | elf.targetStore(&shdr.size, new_size); |
| 5588 | const new_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 5589 | dynamic_ni.slice(&elf.mf)[0..@intCast(new_size)], |
| 5590 | )); |
| 5591 | const write_entries = new_entries[new_entries.len - 2 ..][0..2]; |
| 5592 | assert(elf.targetLoad(&write_entries[0][0]) == std.elf.DT_NULL); |
| 5593 | write_entries.* = .{ |
| 5594 | .{ std.elf.DT_TEXTREL, 0 }, |
| 5595 | .{ std.elf.DT_NULL, 0 }, |
| 5596 | }; |
| 5597 | if (elf.targetEndian() != native_endian) { |
| 5598 | std.mem.byteSwapAllElements([2]class.ElfN().Addr, write_entries); |
| 5599 | } |
| 5600 | } |
| 5601 | } else { |
| 5602 | // TODO: remove the DT_TEXTREL entry if there is one, because it's not necessary any |
| 5603 | // more. It won't cause any issues having it there, it's just inefficient. |
| 5604 | }, |
| 5605 | } |
| 5606 | } |
| 4042 | 5607 | |
| 4043 | 5608 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4044 | 5609 | const comp = elf.base.comp; |
| ... | ... | @@ -4093,7 +5658,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4093 | 5658 | return comp.link_diags.fail( |
| 4094 | 5659 | "linker failed to read input section '{s}' from \"{f}{f}\": {t}", |
| 4095 | 5660 | .{ |
| 4096 | | elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf), |
| 5661 | elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), |
| 4097 | 5662 | ii.path(elf).fmtEscapeString(), |
| 4098 | 5663 | fmtMemberString(ii.member(elf)), |
| 4099 | 5664 | e, |
| ... | ... | @@ -4104,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4104 | 5669 | break :task; |
| 4105 | 5670 | } |
| 4106 | 5671 | if (elf.changed_symtab_index.pop()) |kv| { |
| 5672 | // We only need to do work in relocatables, because in ELF modules (non-relocatables) |
| 5673 | // our `ElfN.Rela` entries use `.dynsym` indices rather than `.symtab` indices, and |
| 5674 | // `.dynsym` indices are (at the time of writing) always immutable. |
| 4107 | 5675 | if (elf.ehdrField(.type) == .REL) { |
| 4108 | 5676 | const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0); |
| 4109 | 5677 | defer sub_prog_node.end(); |
| ... | ... | @@ -4111,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4111 | 5679 | var ri = sym.first_target_reloc; |
| 4112 | 5680 | while (ri != .none) { |
| 4113 | 5681 | const reloc = ri.get(elf); |
| 4114 | | reloc.updateTargetIndex(elf); |
| 5682 | reloc.relaSection(elf).relaUpdateSym( |
| 5683 | elf, |
| 5684 | reloc.rela_index.unwrap().?, |
| 5685 | @intFromEnum(reloc.target.index(elf)), |
| 5686 | ); |
| 4115 | 5687 | ri = reloc.next; |
| 4116 | 5688 | } |
| 4117 | 5689 | break :task; |
| ... | ... | @@ -4146,13 +5718,13 @@ fn idleProgNode( |
| 4146 | 5718 | var name: [std.Progress.Node.max_name_len]u8 = undefined; |
| 4147 | 5719 | return prog_node.start(name: switch (node) { |
| 4148 | 5720 | else => |tag| @tagName(tag), |
| 4149 | | .section => |shndx| shndx.name(elf), |
| 5721 | .section => |shndx| shndx.name(elf).slice(elf), |
| 4150 | 5722 | .input_section => |isi| { |
| 4151 | 5723 | const ii = isi.input(elf); |
| 4152 | 5724 | break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{ |
| 4153 | 5725 | ii.path(elf).fmtEscapeString(), |
| 4154 | 5726 | fmtMemberString(ii.member(elf)), |
| 4155 | | elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf), |
| 5727 | elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), |
| 4156 | 5728 | }) catch &name; |
| 4157 | 5729 | }, |
| 4158 | 5730 | .nav => |nmi| { |
| ... | ... | @@ -4320,110 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4320 | 5892 | .section => |shndx| { |
| 4321 | 5893 | try elf.flushFileOffset(ni); |
| 4322 | 5894 | const addr = elf.computeNodeVAddr(ni); |
| 4323 | | switch (elf.shdrPtr(shndx)) { |
| 4324 | | inline else => |shdr, class| { |
| 4325 | | const flags = elf.targetLoad(&shdr.flags).shf; |
| 4326 | | if (flags.ALLOC) { |
| 4327 | | if (elf.shndx.dynamic != .UNDEF) { |
| 4328 | | if (shndx == elf.shndx.got) { |
| 4329 | | const old_addr = elf.targetLoad(&shdr.addr); |
| 4330 | | const rela_dyn_shndx = shndx.get(elf).rela_shndx; |
| 4331 | | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 4332 | | rela_dyn_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast( |
| 4333 | | elf.targetLoad(&@field( |
| 4334 | | elf.shdrPtr(rela_dyn_shndx), |
| 4335 | | @tagName(class), |
| 4336 | | ).size), |
| 4337 | | )], |
| 4338 | | )); |
| 4339 | | switch (elf.ehdrField(.machine)) { |
| 4340 | | else => |machine| @panic(@tagName(machine)), |
| 4341 | | .AARCH64, .PPC64, .RISCV => {}, |
| 4342 | | .X86_64 => for (relas) |*rela| switch (@as( |
| 4343 | | std.elf.R_X86_64, |
| 4344 | | @enumFromInt(elf.targetLoad(&rela.info).type), |
| 4345 | | )) { |
| 4346 | | else => |@"type"| @panic(@tagName(@"type")), |
| 4347 | | .RELATIVE => {}, |
| 4348 | | .GLOB_DAT, .DTPMOD64, .DTPOFF64 => elf.targetStore( |
| 4349 | | &rela.offset, |
| 4350 | | @intCast(elf.targetLoad(&rela.offset) - old_addr + addr), |
| 4351 | | ), |
| 4352 | | }, |
| 4353 | | } |
| 4354 | | } else if (shndx == elf.shndx.got_plt) { |
| 4355 | | const target_endian = elf.targetEndian(); |
| 4356 | | const old_addr = elf.targetLoad(&shdr.addr); |
| 4357 | | const rela_plt_shndx = shndx.get(elf).rela_shndx; |
| 4358 | | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 4359 | | rela_plt_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast( |
| 4360 | | elf.targetLoad(&@field( |
| 4361 | | elf.shdrPtr(rela_plt_shndx), |
| 4362 | | @tagName(class), |
| 4363 | | ).size), |
| 4364 | | )], |
| 4365 | | )); |
| 4366 | | const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf); |
| 4367 | | switch (elf.ehdrField(.machine)) { |
| 4368 | | else => |machine| @panic(@tagName(machine)), |
| 4369 | | .AARCH64, .PPC64, .RISCV => {}, |
| 4370 | | .X86_64 => { |
| 4371 | | for (relas) |*rela| switch (@as( |
| 4372 | | std.elf.R_X86_64, |
| 4373 | | @enumFromInt(elf.targetLoad(&rela.info).type), |
| 4374 | | )) { |
| 4375 | | else => |@"type"| @panic(@tagName(@"type")), |
| 4376 | | .JUMP_SLOT => elf.targetStore( |
| 4377 | | &rela.offset, |
| 4378 | | @intCast(elf.targetLoad(&rela.offset) - old_addr + addr), |
| 4379 | | ), |
| 4380 | | }; |
| 4381 | | for (0..elf.got.plt.count()) |plt_index| { |
| 4382 | | const slice = plt_sec_slice[16 * plt_index + 6 ..][0..4]; |
| 4383 | | std.mem.writeInt( |
| 4384 | | i32, |
| 4385 | | slice, |
| 4386 | | @intCast(@as(i64, @bitCast(@as(u64, @bitCast(@as( |
| 4387 | | i64, |
| 4388 | | std.mem.readInt(i32, slice, target_endian), |
| 4389 | | ))) -% old_addr +% addr))), |
| 4390 | | target_endian, |
| 4391 | | ); |
| 4392 | | } |
| 4393 | | }, |
| 4394 | | } |
| 4395 | | } else if (shndx == elf.shndx.plt_sec) { |
| 4396 | | const target_endian = elf.targetEndian(); |
| 4397 | | const old_addr = elf.targetLoad(&shdr.addr); |
| 4398 | | const plt_sec_slice = ni.slice(&elf.mf); |
| 4399 | | switch (elf.ehdrField(.machine)) { |
| 4400 | | else => |machine| @panic(@tagName(machine)), |
| 4401 | | .AARCH64, .PPC64, .RISCV => {}, |
| 4402 | | .X86_64 => for (0..elf.got.plt.count()) |plt_index| { |
| 4403 | | const slice = plt_sec_slice[16 * plt_index + 6 ..][0..4]; |
| 4404 | | std.mem.writeInt( |
| 4405 | | i32, |
| 4406 | | slice, |
| 4407 | | @intCast(@as(i64, @bitCast(@as(u64, @bitCast(@as( |
| 4408 | | i64, |
| 4409 | | std.mem.readInt(i32, slice, target_endian), |
| 4410 | | ))) -% addr +% old_addr))), |
| 4411 | | target_endian, |
| 4412 | | ); |
| 4413 | | }, |
| 4414 | | } |
| 4415 | | } |
| 4416 | | } |
| 4417 | | elf.targetStore(&shdr.addr, @intCast(addr)); |
| 4418 | | shndx.get(elf).lsi.index().flushMoved(elf, addr); |
| 4419 | | } |
| 5895 | const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) { |
| 5896 | inline else => |shdr| .{ |
| 5897 | elf.targetLoad(&shdr.addr), |
| 5898 | elf.targetLoad(&shdr.flags).shf, |
| 5899 | }, |
| 5900 | }; |
| 5901 | |
| 5902 | if (flags.ALLOC) { |
| 5903 | switch (elf.shdrPtr(shndx)) { |
| 5904 | inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)), |
| 5905 | } |
| 4420 | 5906 | |
| 4421 | | if (shndx == elf.shndx.plt) { |
| 4422 | | elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc); |
| 4423 | | } else if (shndx == elf.shndx.dynamic) { |
| 4424 | | elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc); |
| 5907 | // Update global symbols targeting this section |
| 5908 | if (elf.node_global_symbols.get(ni)) |first_name| { |
| 5909 | assert(first_name != .empty); |
| 5910 | var name = first_name; |
| 5911 | while (name != .empty) { |
| 5912 | const global = elf.globalByName(name).?; |
| 5913 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { |
| 5914 | inline else => |sym| elf.targetLoad(&sym.value), |
| 5915 | }; |
| 5916 | Symbol.Id.global(name).flushMoved( |
| 5917 | elf, |
| 5918 | old_sym_addr - old_addr + addr, |
| 5919 | ); |
| 5920 | name = global.next_in_node; |
| 4425 | 5921 | } |
| 4426 | | }, |
| 5922 | } |
| 5923 | |
| 5924 | Symbol.Id.local(shndx.get(elf).lsi).flushMoved(elf, addr); |
| 5925 | } |
| 5926 | |
| 5927 | if (shndx == elf.shndx.got) { |
| 5928 | const rela_dyn_shndx = elf.shndx.rela_dyn; |
| 5929 | for (elf.got.values()) |opt_rela_index| { |
| 5930 | const rela_index = opt_rela_index.unwrap() orelse continue; |
| 5931 | rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr); |
| 5932 | } |
| 5933 | for (elf.got_relocs.items) |*reloc| { |
| 5934 | reloc.apply(elf); |
| 5935 | } |
| 5936 | } else if (shndx == elf.shndx.plt) { |
| 5937 | elf.flushMovedNodeRelocs(ni, addr, elf.plt_first_symbol_reloc, .none); |
| 5938 | elf.flushMovedPltSection(.plt, old_addr, addr); |
| 5939 | } else if (shndx == elf.shndx.got_plt) { |
| 5940 | elf.flushMovedPltSection(.got_plt, old_addr, addr); |
| 5941 | } else if (shndx == elf.shndx.plt_sec) { |
| 5942 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); |
| 5943 | } else if (shndx == elf.shndx.dynamic) { |
| 5944 | elf.flushMovedNodeRelocs(ni, addr, elf.dynamic_first_symbol_reloc, .none); |
| 4427 | 5945 | } |
| 4428 | 5946 | }, |
| 4429 | 5947 | .input_section => |isi| { |
| ... | ... | @@ -4448,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4448 | 5966 | .DEFAULT => elf.targetLoad(&sym.value), |
| 4449 | 5967 | }, |
| 4450 | 5968 | }; |
| 4451 | | lsi.index().flushMoved(elf, old_sym_addr - old_section_addr + new_section_addr); |
| 5969 | Symbol.Id.local(lsi).flushMoved( |
| 5970 | elf, |
| 5971 | old_sym_addr - old_section_addr + new_section_addr, |
| 5972 | ); |
| 4452 | 5973 | } |
| 4453 | 5974 | |
| 4454 | 5975 | // Update global symbols |
| ... | ... | @@ -4460,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4460 | 5981 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { |
| 4461 | 5982 | inline else => |sym| elf.targetLoad(&sym.value), |
| 4462 | 5983 | }; |
| 4463 | | global.flushMoved(elf, old_sym_addr - old_section_addr + new_section_addr); |
| 5984 | Symbol.Id.global(name).flushMoved( |
| 5985 | elf, |
| 5986 | old_sym_addr - old_section_addr + new_section_addr, |
| 5987 | ); |
| 4464 | 5988 | name = global.next_in_node; |
| 4465 | 5989 | } |
| 4466 | 5990 | } |
| 4467 | 5991 | |
| 4468 | | elf.flushMovedNodeRelocs(ni, new_section_addr, isi.ptrConst(elf).first_reloc); |
| 5992 | elf.flushMovedNodeRelocs( |
| 5993 | ni, |
| 5994 | new_section_addr, |
| 5995 | isi.ptrConst(elf).first_symbol_reloc, |
| 5996 | isi.ptrConst(elf).first_got_reloc, |
| 5997 | ); |
| 4469 | 5998 | }, |
| 4470 | 5999 | inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { |
| 4471 | 6000 | const new_addr = elf.computeNodeVAddr(ni); |
| 4472 | | mi.symbol(elf).index().flushMoved(elf, new_addr); |
| 6001 | Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr); |
| 4473 | 6002 | if (elf.node_global_symbols.get(ni)) |first_name| { |
| 4474 | 6003 | assert(first_name != .empty); |
| 4475 | 6004 | var name = first_name; |
| 4476 | 6005 | while (name != .empty) { |
| 4477 | | const global = elf.globalByName(name).?; |
| 4478 | | global.flushMoved(elf, new_addr); |
| 4479 | | name = global.next_in_node; |
| 6006 | Symbol.Id.global(name).flushMoved(elf, new_addr); |
| 6007 | name = elf.globalByName(name).?.next_in_node; |
| 4480 | 6008 | } |
| 4481 | 6009 | } |
| 4482 | | elf.flushMovedNodeRelocs(ni, new_addr, mi.firstReloc(elf)); |
| 6010 | elf.flushMovedNodeRelocs( |
| 6011 | ni, |
| 6012 | new_addr, |
| 6013 | mi.firstSymbolReloc(elf), |
| 6014 | mi.firstGotReloc(elf), |
| 6015 | ); |
| 4483 | 6016 | }, |
| 4484 | 6017 | } |
| 4485 | 6018 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| ... | ... | @@ -4507,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4507 | 6040 | }, |
| 4508 | 6041 | .TLS => { |
| 4509 | 6042 | elf.targetStore(&ph.memsz, @intCast(size)); |
| 6043 | // TPOFF relocations care about the size of the TLS segment. Re-apply |
| 6044 | // those, and also update any GOT entries from GOTTPOFF relocations. |
| 6045 | for (elf.tls_size_symbol_relocs.keys()) |reloc| { |
| 6046 | reloc.get(elf).apply(elf); |
| 6047 | } |
| 6048 | for (elf.got.keys(), 0..) |got_key, got_index| { |
| 6049 | switch (got_key) { |
| 6050 | .reserved, |
| 6051 | .symbol, |
| 6052 | .tlsld0, |
| 6053 | .tlsld1, |
| 6054 | .tlsgd0, |
| 6055 | .tlsgd1, |
| 6056 | => { |
| 6057 | @branchHint(.likely); |
| 6058 | continue; |
| 6059 | }, |
| 6060 | |
| 6061 | .tpoff => elf.updateGotEntry(got_index), |
| 6062 | } |
| 6063 | } |
| 4510 | 6064 | return ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 4511 | 6065 | }, |
| 4512 | 6066 | } |
| ... | ... | @@ -4541,53 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4541 | 6095 | }, |
| 4542 | 6096 | }, |
| 4543 | 6097 | .section => |shndx| switch (elf.shdrPtr(shndx)) { |
| 4544 | | inline else => |shdr, class| { |
| 6098 | inline else => |shdr| { |
| 4545 | 6099 | switch (elf.targetLoad(&shdr.type)) { |
| 4546 | 6100 | else => unreachable, |
| 6101 | |
| 4547 | 6102 | .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), |
| 4548 | 6103 | .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), |
| 4549 | | .SYMTAB, .DYNAMIC, .REL, .DYNSYM => return, |
| 4550 | | .STRTAB => { |
| 4551 | | if (elf.shndx.dynamic != .UNDEF) { |
| 4552 | | if (shndx == elf.shndx.dynstr) { |
| 4553 | | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 4554 | | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), |
| 4555 | | )); |
| 4556 | | for (dynamic_entries) |*dynamic_entry| |
| 4557 | | switch (elf.targetLoad(&dynamic_entry[0])) { |
| 4558 | | else => {}, |
| 4559 | | std.elf.DT_STRSZ => dynamic_entry[1] = shdr.size, |
| 4560 | | }; |
| 4561 | | } |
| 4562 | | } |
| 4563 | | return; |
| 4564 | | }, |
| 4565 | | .RELA => { |
| 4566 | | if (elf.shndx.dynamic != .UNDEF) { |
| 4567 | | if (shndx == elf.shndx.got.get(elf).rela_shndx) { |
| 4568 | | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 4569 | | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), |
| 4570 | | )); |
| 4571 | | for (dynamic_entries) |*dynamic_entry| |
| 4572 | | switch (elf.targetLoad(&dynamic_entry[0])) { |
| 4573 | | else => {}, |
| 4574 | | std.elf.DT_RELASZ => dynamic_entry[1] = shdr.size, |
| 4575 | | }; |
| 4576 | | } else if (shndx == elf.shndx.got_plt.get(elf).rela_shndx) { |
| 4577 | | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 4578 | | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), |
| 4579 | | )); |
| 4580 | | for (dynamic_entries) |*dynamic_entry| |
| 4581 | | switch (elf.targetLoad(&dynamic_entry[0])) { |
| 4582 | | else => {}, |
| 4583 | | std.elf.DT_PLTRELSZ => dynamic_entry[1] = shdr.size, |
| 4584 | | }; |
| 4585 | | } |
| 4586 | | } |
| 4587 | | return; |
| 4588 | | }, |
| 6104 | |
| 6105 | .INIT_ARRAY, |
| 6106 | .FINI_ARRAY, |
| 6107 | .PREINIT_ARRAY, |
| 6108 | .STRTAB, |
| 6109 | .SYMTAB, |
| 6110 | .DYNAMIC, |
| 6111 | .REL, |
| 6112 | .RELA, |
| 6113 | .DYNSYM, |
| 6114 | => return, |
| 4589 | 6115 | } |
| 4590 | | if (shndx != elf.shndx.plt) { |
| 6116 | if (shndx != elf.shndx.plt and |
| 6117 | shndx != elf.shndx.got and |
| 6118 | shndx != elf.shndx.got_plt) |
| 6119 | { |
| 4591 | 6120 | elf.targetStore(&shdr.size, @intCast(size)); |
| 4592 | 6121 | } |
| 4593 | 6122 | }, |
| ... | ... | @@ -4595,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4595 | 6124 | .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {}, |
| 4596 | 6125 | } |
| 4597 | 6126 | } |
| 6127 | fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void { |
| 6128 | switch (elf.shdrPtr(elf.shndx.dynamic)) { |
| 6129 | inline else => |shdr, class| { |
| 6130 | const dynamic_size = elf.targetLoad(&shdr.size); |
| 6131 | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 6132 | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)], |
| 6133 | )); |
| 6134 | for (dynamic_entries) |*dynamic_entry| { |
| 6135 | if (elf.targetLoad(&dynamic_entry[0]) == key) { |
| 6136 | elf.targetStore(&dynamic_entry[1], @intCast(new_val)); |
| 6137 | } |
| 6138 | } |
| 6139 | }, |
| 6140 | } |
| 6141 | } |
| 6142 | fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void { |
| 6143 | const target_endian = elf.targetEndian(); |
| 6144 | switch (elf.ehdrField(.machine)) { |
| 6145 | else => |machine| @panic(@tagName(machine)), |
| 6146 | .X86_64 => { |
| 6147 | switch (which) { |
| 6148 | .plt => return, |
| 6149 | .plt_sec => { |
| 6150 | // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of |
| 6151 | // its relocations are probably going through the PLT, so we don't bother with |
| 6152 | // specific tracking for PLT relocations---instead just re-apply all relocations |
| 6153 | // targeting symbols with PLT entries. |
| 6154 | for (elf.plt.keys()) |sym| { |
| 6155 | sym.index(elf).applyTargetRelocs(elf); |
| 6156 | } |
| 6157 | // We also need to update all of the references from `.plt.sec` to `.got.plt`. |
| 6158 | // However, if there's also a flush pending for `.got.plt`, don't bother doing |
| 6159 | // this now, because we'll do it when `.got.plt` is flushed anyway. |
| 6160 | if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) { |
| 6161 | return; |
| 6162 | } |
| 6163 | // Exit this `switch` to update those references. |
| 6164 | }, |
| 6165 | .got_plt => { |
| 6166 | // Update the offsets of the relocation entries in `.rela.plt`. |
| 6167 | const rela_plt_shndx = elf.shndx.rela_plt; |
| 6168 | for (0..elf.plt.count()) |plt_index| { |
| 6169 | if (elf.pltEntryIsDead(plt_index)) continue; |
| 6170 | rela_plt_shndx.relaAdjustOffset(elf, @enumFromInt(plt_index), old_addr, addr); |
| 6171 | } |
| 6172 | // We also need to update all of the references from `.plt.sec` to `.got.plt`. |
| 6173 | // However, if there's also a flush pending for `.plt.sec`, don't bother doing |
| 6174 | // this now, because we'll do it when `.plt.sec` is flushed anyway. |
| 6175 | if (elf.shndx.plt_sec.get(elf).ni.hasMoved(&elf.mf)) { |
| 6176 | return; |
| 6177 | } |
| 6178 | // Exit this `switch` to update those references. |
| 6179 | }, |
| 6180 | } |
| 6181 | // We are updating the references from `.plt.sec` to `.got.plt`. |
| 6182 | const got_plt_addr = elf.shndx.got_plt.vaddr(elf); |
| 6183 | const plt_sec_addr = elf.shndx.plt_sec.vaddr(elf); |
| 6184 | const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf); |
| 6185 | switch (elf.identClass()) { |
| 6186 | .NONE, _ => unreachable, |
| 6187 | inline else => |class| { |
| 6188 | const Addr = class.ElfN().Addr; |
| 6189 | for (0..elf.plt.count()) |plt_index| { |
| 6190 | const plt_sec_offset = 16 * plt_index; |
| 6191 | const got_plt_offset = @sizeOf(Addr) * (3 + plt_index); |
| 6192 | std.mem.writeInt( |
| 6193 | i32, |
| 6194 | plt_sec_slice[plt_sec_offset + 6 ..][0..4], |
| 6195 | @intCast(@as(i64, @bitCast( |
| 6196 | (got_plt_addr + got_plt_offset) -% (plt_sec_addr + plt_sec_offset + 10), |
| 6197 | ))), |
| 6198 | target_endian, |
| 6199 | ); |
| 6200 | } |
| 6201 | }, |
| 6202 | } |
| 6203 | }, |
| 6204 | } |
| 6205 | } |
| 4598 | 6206 | |
| 4599 | 6207 | pub fn updateExports( |
| 4600 | 6208 | elf: *Elf, |
| ... | ... | @@ -4633,7 +6241,7 @@ fn updateExportsInner( |
| 4633 | 6241 | const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) { |
| 4634 | 6242 | .nav => |nav| .{ |
| 4635 | 6243 | (try elf.navMapIndex(zcu, nav)).symbol(elf), |
| 4636 | | navType(ip, ip.getNav(nav).resolved.?, elf.base.comp.config.any_non_single_threaded), |
| 6244 | elf.navType(ip.getNav(nav).resolved.?), |
| 4637 | 6245 | }, |
| 4638 | 6246 | .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT }, |
| 4639 | 6247 | }; |
| ... | ... | @@ -4735,13 +6343,13 @@ pub fn printNode( |
| 4735 | 6343 | try w.writeByte(')'); |
| 4736 | 6344 | }, |
| 4737 | 6345 | }, |
| 4738 | | .section => |shndx| try w.print("({s})", .{shndx.name(elf)}), |
| 6346 | .section => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}), |
| 4739 | 6347 | .input_section => |isi| { |
| 4740 | 6348 | const ii = isi.input(elf); |
| 4741 | 6349 | try w.print("({f}{f}, {s})", .{ |
| 4742 | 6350 | ii.path(elf).fmtEscapeString(), |
| 4743 | 6351 | fmtMemberString(ii.member(elf)), |
| 4744 | | elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf), |
| 6352 | elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), |
| 4745 | 6353 | }); |
| 4746 | 6354 | }, |
| 4747 | 6355 | .nav => |nmi| { |