| ... | @@ -34,6 +34,12 @@ shndx: struct { | ... | @@ -34,6 +34,12 @@ shndx: struct { |
| 34 | dynstr: Section.Index, | 34 | dynstr: Section.Index, |
| 35 | dynamic: Section.Index, | 35 | dynamic: Section.Index, |
| 36 | tdata: Section.Index, | 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 | symtab: std.ArrayList(Symbol), | 44 | symtab: std.ArrayList(Symbol), |
| 39 | globals: struct { | 45 | globals: struct { |
| ... | @@ -50,16 +56,34 @@ globals: struct { | ... | @@ -50,16 +56,34 @@ globals: struct { |
| 50 | /// We use a separate hash map for this data rather than storing it in `navs` etc to save memory, | 56 | /// We use a separate hash map for this data rather than storing it in `navs` etc to save memory, |
| 51 | /// because the vast majority of nodes which can export global symbols actually will not. | 57 | /// because the vast majority of nodes which can export global symbols actually will not. |
| 52 | node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)), | 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 | shstrtab: StringTable, | 67 | shstrtab: StringTable, |
| 54 | strtab: StringTable, | 68 | strtab: StringTable, |
| 55 | dynstr: StringTable, | 69 | dynstr: StringTable, |
| 56 | got: struct { | 70 | |
| 57 | len: u32, | 71 | /// Indices map 1--1 to indices into the actual `.got` section. |
| 58 | tlsld: GotIndex, | 72 | /// |
| 59 | plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void), | 73 | /// Value is the output relocation in `.rela.dyn` for the GOT entry. |
| 60 | }, | 74 | got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), |
| 61 | first_plt_reloc: Reloc.Index, | 75 | /// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into |
| 62 | first_dynamic_reloc: Reloc.Index, | 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 | needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void), | 87 | needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void), |
| 64 | inputs: std.ArrayList(struct { | 88 | inputs: std.ArrayList(struct { |
| 65 | path: std.Build.Cache.Path, | 89 | path: std.Build.Cache.Path, |
| ... | @@ -69,29 +93,42 @@ inputs: std.ArrayList(struct { | ... | @@ -69,29 +93,42 @@ inputs: std.ArrayList(struct { |
| 69 | input_sections: std.ArrayList(InputSection), | 93 | input_sections: std.ArrayList(InputSection), |
| 70 | input_section_pending_index: u32, | 94 | input_section_pending_index: u32, |
| 71 | navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct { | 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 | lsi: Symbol.LocalIndex, | 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 | uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct { | 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 | lsi: Symbol.LocalIndex, | 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 | lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { | 108 | lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { |
| 82 | map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct { | 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 | lsi: Symbol.LocalIndex, | 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 | pending_index: u32, | 116 | pending_index: u32, |
| 88 | }), | 117 | }), |
| 89 | pending_uavs: std.ArrayList(Node.UavMapIndex), | 118 | pending_uavs: std.ArrayList(Node.UavMapIndex), |
| 90 | relocs: std.ArrayList(Reloc), | 119 | symbol_relocs: std.ArrayList(SymbolReloc), |
| 91 | | 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 | /// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation | 125 | /// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation |
| 93 | /// entries which target that symbol must be updated to reference the correct symbol index. | 126 | /// entries which target that symbol must be updated to reference the correct symbol index. |
| 94 | changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void), | 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 | const_prog_node: std.Progress.Node, | 133 | const_prog_node: std.Progress.Node, |
| 97 | synth_prog_node: std.Progress.Node, | 134 | synth_prog_node: std.Progress.Node, |
| ... | @@ -106,21 +143,21 @@ const Node = union(enum) { | ... | @@ -106,21 +143,21 @@ const Node = union(enum) { |
| 106 | shdr, | 143 | shdr, |
| 107 | /// Cannot contain relocations. | 144 | /// Cannot contain relocations. |
| 108 | segment: u32, | 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 | /// Otherwise, cannot contain relocations. | 150 | /// Otherwise, cannot contain relocations. |
| 114 | section: Section.Index, | 151 | section: Section.Index, |
| 115 | /// May contain relocations through the `first_reloc` field in `elf.input_sections`. | 152 | /// May contain relocations. |
| 116 | input_section: InputSection.Index, | 153 | input_section: InputSection.Index, |
| 117 | /// May contain relocations through the `first_reloc` field in `elf.navs`. | 154 | /// May contain relocations. |
| 118 | nav: NavMapIndex, | 155 | nav: NavMapIndex, |
| 119 | /// May contain relocations through the `first_reloc` field in `elf.uavs`. | 156 | /// May contain relocations. |
| 120 | uav: UavMapIndex, | 157 | uav: UavMapIndex, |
| 121 | /// May contain relocations through the `first_reloc` field in `elf.lazy.map`. | 158 | /// May contain relocations. |
| 122 | lazy_code: LazyMapRef.Index(.code), | 159 | lazy_code: LazyMapRef.Index(.code), |
| 123 | /// May contain relocations through the `first_reloc` field in `elf.lazy.map`. | 160 | /// May contain relocations. |
| 124 | lazy_const_data: LazyMapRef.Index(.const_data), | 161 | lazy_const_data: LazyMapRef.Index(.const_data), |
| 125 | | 162 | |
| 126 | pub const InputIndex = enum(u32) { | 163 | pub const InputIndex = enum(u32) { |
| ... | @@ -162,8 +199,11 @@ const Node = union(enum) { | ... | @@ -162,8 +199,11 @@ const Node = union(enum) { |
| 162 | return elf.navs.values()[@intFromEnum(nmi)].lsi; | 199 | return elf.navs.values()[@intFromEnum(nmi)].lsi; |
| 163 | } | 200 | } |
| 164 | | 201 | |
| 165 | fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index { | 202 | fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 166 | return elf.navs.values()[@intFromEnum(nmi)].first_reloc; | 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,8 +218,13 @@ const Node = union(enum) { |
| 178 | return elf.uavs.values()[@intFromEnum(umi)].lsi; | 218 | return elf.uavs.values()[@intFromEnum(umi)].lsi; |
| 179 | } | 219 | } |
| 180 | | 220 | |
| 181 | fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index { | 221 | fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 182 | return elf.uavs.values()[@intFromEnum(umi)].first_reloc; | 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,8 +248,11 @@ const Node = union(enum) { |
| 203 | return lmi.ref().symbol(elf); | 248 | return lmi.ref().symbol(elf); |
| 204 | } | 249 | } |
| 205 | | 250 | |
| 206 | fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index { | 251 | fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index { |
| 207 | return lmi.ref().firstReloc(elf); | 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,10 +264,6 @@ const Node = union(enum) { |
| 216 | pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex { | 264 | pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex { |
| 217 | return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi; | 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 | pub const Known = struct { | 269 | pub const Known = struct { |
| ... | @@ -255,8 +299,10 @@ const InputSection = struct { | ... | @@ -255,8 +299,10 @@ const InputSection = struct { |
| 255 | vaddr: u64, | 299 | vaddr: u64, |
| 256 | /// The node corresponding to this input section. | 300 | /// The node corresponding to this input section. |
| 257 | node: MappedFile.Node.Index, | 301 | node: MappedFile.Node.Index, |
| 258 | /// The start index of the contiguous sequence of relocations in this input section. | 302 | /// The start index of the contiguous sequence of symbol relocations in this input section. |
| 259 | first_reloc: Reloc.Index, | 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 | const Index = enum(u32) { | 307 | const Index = enum(u32) { |
| 262 | _, | 308 | _, |
| ... | @@ -290,21 +336,53 @@ const Section = struct { | ... | @@ -290,21 +336,53 @@ const Section = struct { |
| 290 | /// | 336 | /// |
| 291 | /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`. | 337 | /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`. |
| 292 | lsi: Symbol.LocalIndex, | 338 | lsi: Symbol.LocalIndex, |
| 293 | rela_shndx: Section.Index, | 339 | rela: union { |
| 294 | rela_free: RelIndex, | 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 | none, | 369 | none, |
| 298 | _, | 370 | _, |
| 299 | | 371 | |
| 300 | pub fn wrap(i: ?u32) RelIndex { | 372 | const Optional = enum(u32) { |
| 301 | return @enumFromInt((i orelse return .none) + 1); | 373 | none = std.math.maxInt(u32), |
| 302 | } | 374 | _, |
| 303 | pub fn unwrap(ri: RelIndex) ?u32 { | 375 | |
| 304 | return switch (ri) { | 376 | fn unwrap(opt: RelaIndex.Optional) ?RelaIndex { |
| 305 | .none => null, | 377 | return switch (opt) { |
| 306 | _ => @intFromEnum(ri) - 1, | 378 | .none => null, |
| 307 | }; | 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,11 +435,10 @@ const Section = struct { |
| 357 | return &elf.shdrs.items[@intFromEnum(s)]; | 435 | return &elf.shdrs.items[@intFromEnum(s)]; |
| 358 | } | 436 | } |
| 359 | | 437 | |
| 360 | fn name(s: Index, elf: *Elf) [:0]const u8 { | 438 | fn name(s: Index, elf: *Elf) String(.shstrtab) { |
| 361 | const str: String(.shstrtab) = switch (elf.shdrPtr(s)) { | 439 | return switch (elf.shdrPtr(s)) { |
| 362 | inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)), | 440 | inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)), |
| 363 | }; | 441 | }; |
| 364 | return str.slice(elf); | | |
| 365 | } | 442 | } |
| 366 | | 443 | |
| 367 | fn vaddr(s: Index, elf: *Elf) u64 { | 444 | fn vaddr(s: Index, elf: *Elf) u64 { |
| ... | @@ -377,9 +454,690 @@ const Section = struct { | ... | @@ -377,9 +454,690 @@ const Section = struct { |
| 377 | inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)), | 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 | fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void { | 1141 | fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void { |
| 384 | const gpa = elf.base.comp.gpa; | 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,59 +1184,70 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe |
| 426 | try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size); | 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); | 1187 | try elf.ensureUnusedPltCapacity(len); |
| 430 | const need_plt_capacity = elf.got.plt.count() + 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)) { | 1195 | try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len); |
| 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 | } | | |
| 442 | | 1196 | |
| 443 | // Ensure the `.got.plt` section's node is big enough | 1197 | try elf.plt.ensureUnusedCapacity(gpa, len); |
| 444 | const got_plt_need_size: usize = switch (elf.identClass()) { | 1198 | const need_plt_capacity = elf.plt.count() + len; |
| 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 | } | | |
| 453 | | 1199 | |
| 454 | // Ensure the `.plt.sec` section's node is big enough | 1200 | switch (elf.ehdrField(.machine)) { |
| 455 | const plt_sec_need_size: usize = 16 * need_plt_capacity; | 1201 | else => |machine| @panic(@tagName(machine)), |
| 456 | _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf); | 1202 | .X86_64 => { |
| 457 | if (plt_sec_cur_size < plt_sec_need_size) { | 1203 | // Ensure the `.plt` section's node is big enough |
| 458 | const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor; | 1204 | const plt_need_size: usize = 16 * (1 + need_plt_capacity); |
| 459 | try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size); | 1205 | _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 460 | } | 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 | 1211 | // Ensure the `.got.plt` section's node is big enough |
| 463 | const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx; | 1212 | const got_plt_need_size: usize = switch (elf.identClass()) { |
| 464 | const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) { | 1213 | .NONE, _ => unreachable, |
| 465 | inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity), | 1214 | inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity), |
| 466 | }; | 1215 | }; |
| 467 | _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf); | 1216 | _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 468 | if (rela_plt_cur_size < rela_plt_need_size) { | 1217 | if (got_plt_cur_size < got_plt_need_size) { |
| 469 | const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor; | 1218 | const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor; |
| 470 | try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size); | 1219 | try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 471 | } else { | 1220 | } |
| 472 | // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can | 1221 | |
| 473 | // be updated if we do indeed add a PLT entry. | 1222 | // Ensure the `.plt.sec` section's node is big enough |
| 474 | try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf); | 1223 | const plt_sec_need_size: usize = 16 * need_plt_capacity; |
| 475 | } | 1224 | _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 476 | }, | 1225 | if (plt_sec_cur_size < plt_sec_need_size) { |
| 477 | } | 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 | const AddLocalSymbolOptions = struct { | 1252 | const AddLocalSymbolOptions = struct { |
| 484 | node: MappedFile.Node.Index, | 1253 | node: MappedFile.Node.Index, |
| ... | @@ -695,6 +1464,11 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -695,6 +1464,11 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 695 | .weak => .WEAK, | 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 | const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len); | 1472 | const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len); |
| 699 | elf.symtab.appendAssumeCapacity(.{ | 1473 | elf.symtab.appendAssumeCapacity(.{ |
| 700 | .node = opts.node, | 1474 | .node = opts.node, |
| ... | @@ -713,7 +1487,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -713,7 +1487,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 713 | .name = @intFromEnum(opts.name.strtab), | 1487 | .name = @intFromEnum(opts.name.strtab), |
| 714 | .value = @intCast(opts.value), | 1488 | .value = @intCast(opts.value), |
| 715 | .size = @intCast(opts.size), | 1489 | .size = @intCast(opts.size), |
| 716 | .info = .{ .type = opts.type, .bind = bind }, | 1490 | .info = .{ .type = @"type", .bind = bind }, |
| 717 | .other = .{ .visibility = opts.visibility }, | 1491 | .other = .{ .visibility = opts.visibility }, |
| 718 | .shndx = opts.shndx.toSection().?, | 1492 | .shndx = opts.shndx.toSection().?, |
| 719 | }; | 1493 | }; |
| ... | @@ -749,7 +1523,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -749,7 +1523,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 749 | .name = @intFromEnum(opts.name.dynstr), | 1523 | .name = @intFromEnum(opts.name.dynstr), |
| 750 | .value = @intCast(opts.value), | 1524 | .value = @intCast(opts.value), |
| 751 | .size = @intCast(opts.size), | 1525 | .size = @intCast(opts.size), |
| 752 | .info = .{ .type = opts.type, .bind = bind }, | 1526 | .info = .{ .type = @"type", .bind = bind }, |
| 753 | .other = .{ .visibility = opts.visibility }, | 1527 | .other = .{ .visibility = opts.visibility }, |
| 754 | .shndx = opts.shndx.toSection().?, | 1528 | .shndx = opts.shndx.toSection().?, |
| 755 | }; | 1529 | }; |
| ... | @@ -778,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -778,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 778 | if (new_global_ptr.dynsym_index != 0 and | 1552 | if (new_global_ptr.dynsym_index != 0 and |
| 779 | opts.visibility == .DEFAULT and | 1553 | opts.visibility == .DEFAULT and |
| 780 | opts.shndx == .UNDEF and | 1554 | opts.shndx == .UNDEF and |
| 781 | opts.type == .FUNC) | 1555 | (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC)) |
| 782 | { | 1556 | { |
| 783 | // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO. | 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 | 1558 | // We therefore might need a PLT entry, so let's add one now. |
| 785 | // the PLT entry if we later discover a link inpu which resolves this reference. | | |
| 786 | elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index); | 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 | return .global(opts.name.strtab); | 1564 | return .global(opts.name.strtab); |
| ... | @@ -800,6 +1575,7 @@ fn setGlobalSymbolValue( | ... | @@ -800,6 +1575,7 @@ fn setGlobalSymbolValue( |
| 800 | shndx: Section.Index, | 1575 | shndx: Section.Index, |
| 801 | }, | 1576 | }, |
| 802 | ) void { | 1577 | ) void { |
| | 1578 | assert(new.shndx != .UNDEF); |
| 803 | const old_node = global_ptr.symtab_index.ptr(elf).node; | 1579 | const old_node = global_ptr.symtab_index.ptr(elf).node; |
| 804 | if (old_node != .none) { | 1580 | if (old_node != .none) { |
| 805 | if (global_ptr.next_in_node != .empty) { | 1581 | if (global_ptr.next_in_node != .empty) { |
| ... | @@ -874,7 +1650,40 @@ fn setGlobalSymbolValue( | ... | @@ -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 | /// When the same global symbol appears in two inputs---even if one symbol is defined and the other | 1688 | /// When the same global symbol appears in two inputs---even if one symbol is defined and the other |
| 880 | /// undefined---their visibility values are combined to determine the resulting visibility, which | 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,14 +1818,45 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 1009 | if (elf.targetEndian() != native_endian) { | 1818 | if (elf.targetEndian() != native_endian) { |
| 1010 | std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym); | 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 | fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void { | 1826 | fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void { |
| 1017 | const target_endian = elf.targetEndian(); | 1827 | const target_endian = elf.targetEndian(); |
| 1018 | const plt_index: u32 = @intCast(elf.got.plt.count()); | 1828 | |
| 1019 | elf.got.plt.putAssumeCapacityNoClobber(.global(global_name), {}); | 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 | switch (elf.ehdrField(.machine)) { | 1860 | switch (elf.ehdrField(.machine)) { |
| 1021 | else => |machine| @panic(@tagName(machine)), | 1861 | else => |machine| @panic(@tagName(machine)), |
| 1022 | .X86_64 => { | 1862 | .X86_64 => { |
| ... | @@ -1044,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void | ... | @@ -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 | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; | 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 | inline else => |shdr, class| { | 1889 | inline else => |shdr, class| { |
| 1051 | const ent_size = @sizeOf(class.ElfN().Addr); | 1890 | const ent_size = @sizeOf(class.ElfN().Addr); |
| 1052 | const old_size = ent_size * (3 + plt_index); | 1891 | const old_size = ent_size * (3 + plt_index); |
| | 1892 | assert(elf.targetLoad(&shdr.size) == old_size); |
| 1053 | elf.targetStore(&shdr.size, old_size + ent_size); | 1893 | elf.targetStore(&shdr.size, old_size + ent_size); |
| 1054 | std.mem.writeInt( | 1894 | std.mem.writeInt( |
| 1055 | class.ElfN().Addr, | 1895 | class.ElfN().Addr, |
| ... | @@ -1057,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void | ... | @@ -1057,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1057 | @intCast(plt_addr), | 1897 | @intCast(plt_addr), |
| 1058 | target_endian, | 1898 | target_endian, |
| 1059 | ); | 1899 | ); |
| 1060 | break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size; | | |
| 1061 | }, | 1900 | }, |
| 1062 | }; | 1901 | } |
| 1063 | | 1902 | |
| 1064 | const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni; | 1903 | const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni; |
| 1065 | switch (elf.shdrPtr(elf.shndx.plt_sec)) { | 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,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,13 +1928,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1113 | const Symbol = struct { | 1928 | const Symbol = struct { |
| 1114 | /// The node which this symbol's value is defined relative to. Possible values are: | 1929 | /// The node which this symbol's value is defined relative to. Possible values are: |
| 1115 | /// * `.none` for a SHN_ABS or SHN_UNDEF symbol | 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 | /// * An input section (the symbol's value is some vaddr in that input section) | 1932 | /// * An input section (the symbol's value is some vaddr in that input section) |
| 1118 | /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node) | 1933 | /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node) |
| 1119 | node: MappedFile.Node.Index, | 1934 | node: MappedFile.Node.Index, |
| 1120 | | 1935 | |
| 1121 | /// The head of a linked list of relocations targeting this symbol. | 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 | const Global = struct { | 1939 | const Global = struct { |
| 1125 | /// The current index of the symtab entry for this global symbol. | 1940 | /// The current index of the symtab entry for this global symbol. |
| ... | @@ -1136,16 +1951,6 @@ const Symbol = struct { | ... | @@ -1136,16 +1951,6 @@ const Symbol = struct { |
| 1136 | /// | 1951 | /// |
| 1137 | /// If `node` is `.none`, this is `.empty`. | 1952 | /// If `node` is `.none`, this is `.empty`. |
| 1138 | prev_in_node: String(.strtab), | 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 | /// An index directly into the symtab. These values are not stable (global symbols are sometimes | 1956 | /// An index directly into the symtab. These values are not stable (global symbols are sometimes |
| ... | @@ -1158,23 +1963,20 @@ const Symbol = struct { | ... | @@ -1158,23 +1963,20 @@ const Symbol = struct { |
| 1158 | null = 0, | 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 | fn ptr(si: Symbol.Index, elf: *Elf) *Symbol { | 1966 | fn ptr(si: Symbol.Index, elf: *Elf) *Symbol { |
| 1176 | return &elf.symtab.items[@intFromEnum(si)]; | 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 | /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the | 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,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 | /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at | 2082 | /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at |
| 1243 | /// some point due to a call to `flushMoved`. | 2083 | /// some point due to a call to `flushMoved`. |
| 1244 | fn hasMoved(s: Symbol.Id, elf: *Elf) bool { | 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,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId { |
| 1305 | .type = sym_type, | 2145 | .type = sym_type, |
| 1306 | .shndx = shndx, | 2146 | .shndx = shndx, |
| 1307 | }), | 2147 | }), |
| 1308 | .first_reloc = .none, | 2148 | .first_symbol_reloc = .none, |
| | 2149 | .first_got_reloc = .none, |
| 1309 | }; | 2150 | }; |
| 1310 | elf.nodes.appendAssumeCapacity(switch (lazy.kind) { | 2151 | elf.nodes.appendAssumeCapacity(switch (lazy.kind) { |
| 1311 | .code => .{ .lazy_code = @enumFromInt(gop.index) }, | 2152 | .code => .{ .lazy_code = @enumFromInt(gop.index) }, |
| ... | @@ -1354,7 +2195,7 @@ pub fn addReloc( | ... | @@ -1354,7 +2195,7 @@ pub fn addReloc( |
| 1354 | offset: u64, | 2195 | offset: u64, |
| 1355 | target: link.File.SymbolId, | 2196 | target: link.File.SymbolId, |
| 1356 | addend: i64, | 2197 | addend: i64, |
| 1357 | @"type": Reloc.Type, | 2198 | @"type": MachineRelocType, |
| 1358 | ) !void { | 2199 | ) !void { |
| 1359 | const node: MappedFile.Node.Index = Node.fromAtom(atom); | 2200 | const node: MappedFile.Node.Index = Node.fromAtom(atom); |
| 1360 | try elf.ensureUnusedRelocCapacity(node, 1); | 2201 | try elf.ensureUnusedRelocCapacity(node, 1); |
| ... | @@ -1368,7 +2209,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId | ... | @@ -1368,7 +2209,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId |
| 1368 | return elf.externSymbol(.{ | 2209 | return elf.externSymbol(.{ |
| 1369 | .name = @"extern".name.toSlice(ip), | 2210 | .name = @"extern".name.toSlice(ip), |
| 1370 | .lib_name = @"extern".lib_name.toSlice(ip), | 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 | .linkage = @"extern".linkage, | 2213 | .linkage = @"extern".linkage, |
| 1373 | .visibility = @"extern".visibility, | 2214 | .visibility = @"extern".visibility, |
| 1374 | }); | 2215 | }); |
| ... | @@ -1507,310 +2348,44 @@ const StringTable = struct { | ... | @@ -1507,310 +2348,44 @@ const StringTable = struct { |
| 1507 | key, | 2348 | key, |
| 1508 | StringTable.Adapter{ .slice = slice_const }, | 2349 | StringTable.Adapter{ .slice = slice_const }, |
| 1509 | .{ .slice = slice_const }, | 2350 | .{ .slice = slice_const }, |
| 1510 | ); | 2351 | ); |
| 1511 | if (gop.found_existing) return gop.key_ptr.*; | 2352 | if (gop.found_existing) return gop.key_ptr.*; |
| 1512 | try ni.resized(gpa, &elf.mf); | 2353 | const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) { |
| 1513 | const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) { | 2354 | inline else => |shdr| { |
| 1514 | inline else => |shdr| { | 2355 | const old_size: u32 = @intCast(elf.targetLoad(&shdr.size)); |
| 1515 | const old_size: u32 = @intCast(elf.targetLoad(&shdr.size)); | 2356 | const new_size: u32 = @intCast(old_size + key.len + 1); |
| 1516 | const new_size: u32 = @intCast(old_size + key.len + 1); | 2357 | elf.targetStore(&shdr.size, new_size); |
| 1517 | elf.targetStore(&shdr.size, new_size); | 2358 | break :size .{ old_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; | | |
| 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 { | 2375 | const GotIndex = enum(u32) { |
| 1781 | assert(elf.ehdrField(.type) == .REL); | 2376 | none = std.math.maxInt(u32), |
| 1782 | const sh = elf.getNodeShndx(reloc.node).get(elf); | 2377 | _, |
| 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 | } | | |
| 1796 | | 2378 | |
| 1797 | fn updateNodeOffset(reloc: *const Reloc, elf: *Elf, node_offset: u64) void { | 2379 | pub fn wrap(i: ?u32) GotIndex { |
| 1798 | assert(elf.ehdrField(.type) == .REL); | 2380 | const gi: GotIndex = @enumFromInt(i orelse return .none); |
| 1799 | const total_offset = node_offset + reloc.offset; | 2381 | assert(gi != .none); |
| 1800 | const sh = elf.getNodeShndx(reloc.node).get(elf); | 2382 | return gi; |
| 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 | } | | |
| 1810 | } | 2383 | } |
| 1811 | | 2384 | pub fn unwrap(gi: GotIndex) ?u32 { |
| 1812 | comptime { | 2385 | return switch (gi) { |
| 1813 | if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40); | 2386 | _ => @intFromEnum(gi), |
| | 2387 | .none => null, |
| | 2388 | }; |
| 1814 | } | 2389 | } |
| 1815 | }; | 2390 | }; |
| 1816 | | 2391 | |
| ... | @@ -1918,6 +2493,11 @@ fn create( | ... | @@ -1918,6 +2493,11 @@ fn create( |
| 1918 | .dynstr = .UNDEF, | 2493 | .dynstr = .UNDEF, |
| 1919 | .dynamic = .UNDEF, | 2494 | .dynamic = .UNDEF, |
| 1920 | .tdata = .UNDEF, | 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 | .symtab = .empty, | 2502 | .symtab = .empty, |
| 1923 | .globals = .{ | 2503 | .globals = .{ |
| ... | @@ -1927,16 +2507,14 @@ fn create( | ... | @@ -1927,16 +2507,14 @@ fn create( |
| 1927 | .weak_undef = .empty, | 2507 | .weak_undef = .empty, |
| 1928 | }, | 2508 | }, |
| 1929 | .node_global_symbols = .empty, | 2509 | .node_global_symbols = .empty, |
| | 2510 | .dso_globals = .empty, |
| 1930 | .shstrtab = .{ .map = .empty }, | 2511 | .shstrtab = .{ .map = .empty }, |
| 1931 | .strtab = .{ .map = .empty }, | 2512 | .strtab = .{ .map = .empty }, |
| 1932 | .dynstr = .{ .map = .empty }, | 2513 | .dynstr = .{ .map = .empty }, |
| 1933 | .got = .{ | 2514 | .got = .empty, |
| 1934 | .len = 0, | 2515 | .plt = .empty, |
| 1935 | .tlsld = .none, | 2516 | .plt_first_symbol_reloc = .none, |
| 1936 | .plt = .empty, | 2517 | .dynamic_first_symbol_reloc = .none, |
| 1937 | }, | | |
| 1938 | .first_plt_reloc = .none, | | |
| 1939 | .first_dynamic_reloc = .none, | | |
| 1940 | .needed = .empty, | 2518 | .needed = .empty, |
| 1941 | .inputs = .empty, | 2519 | .inputs = .empty, |
| 1942 | .input_sections = .empty, | 2520 | .input_sections = .empty, |
| ... | @@ -1948,11 +2526,15 @@ fn create( | ... | @@ -1948,11 +2526,15 @@ fn create( |
| 1948 | .pending_index = 0, | 2526 | .pending_index = 0, |
| 1949 | }), | 2527 | }), |
| 1950 | .pending_uavs = .empty, | 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 | .changed_symtab_index = .empty, | 2533 | .changed_symtab_index = .empty, |
| 1953 | .const_prog_node = .none, | 2534 | .const_prog_node = .none, |
| 1954 | .synth_prog_node = .none, | 2535 | .synth_prog_node = .none, |
| 1955 | .input_prog_node = .none, | 2536 | .input_prog_node = .none, |
| | 2537 | .textrel_count = 0, |
| 1956 | }; | 2538 | }; |
| 1957 | errdefer elf.deinit(); | 2539 | errdefer elf.deinit(); |
| 1958 | | 2540 | |
| ... | @@ -1972,10 +2554,12 @@ pub fn deinit(elf: *Elf) void { | ... | @@ -1972,10 +2554,12 @@ pub fn deinit(elf: *Elf) void { |
| 1972 | elf.globals.strong_undef.deinit(gpa); | 2554 | elf.globals.strong_undef.deinit(gpa); |
| 1973 | elf.globals.weak_undef.deinit(gpa); | 2555 | elf.globals.weak_undef.deinit(gpa); |
| 1974 | elf.node_global_symbols.deinit(gpa); | 2556 | elf.node_global_symbols.deinit(gpa); |
| | 2557 | elf.dso_globals.deinit(gpa); |
| 1975 | elf.shstrtab.map.deinit(gpa); | 2558 | elf.shstrtab.map.deinit(gpa); |
| 1976 | elf.strtab.map.deinit(gpa); | 2559 | elf.strtab.map.deinit(gpa); |
| 1977 | elf.dynstr.map.deinit(gpa); | 2560 | elf.dynstr.map.deinit(gpa); |
| 1978 | elf.got.plt.deinit(gpa); | 2561 | elf.got.deinit(gpa); |
| | 2562 | elf.plt.deinit(gpa); |
| 1979 | elf.needed.deinit(gpa); | 2563 | elf.needed.deinit(gpa); |
| 1980 | for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m); | 2564 | for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m); |
| 1981 | elf.inputs.deinit(gpa); | 2565 | elf.inputs.deinit(gpa); |
| ... | @@ -1984,7 +2568,10 @@ pub fn deinit(elf: *Elf) void { | ... | @@ -1984,7 +2568,10 @@ pub fn deinit(elf: *Elf) void { |
| 1984 | elf.uavs.deinit(gpa); | 2568 | elf.uavs.deinit(gpa); |
| 1985 | for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); | 2569 | for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); |
| 1986 | elf.pending_uavs.deinit(gpa); | 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 | elf.changed_symtab_index.deinit(gpa); | 2575 | elf.changed_symtab_index.deinit(gpa); |
| 1989 | elf.* = undefined; | 2576 | elf.* = undefined; |
| 1990 | } | 2577 | } |
| ... | @@ -2293,7 +2880,7 @@ fn initHeaders( | ... | @@ -2293,7 +2880,7 @@ fn initHeaders( |
| 2293 | .entsize = 0, | 2880 | .entsize = 0, |
| 2294 | }; | 2881 | }; |
| 2295 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef); | 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 | elf.symtab.addOneAssumeCapacity().* = .{ | 2885 | elf.symtab.addOneAssumeCapacity().* = .{ |
| 2299 | .node = .none, | 2886 | .node = .none, |
| ... | @@ -2368,8 +2955,15 @@ fn initHeaders( | ... | @@ -2368,8 +2955,15 @@ fn initHeaders( |
| 2368 | if (@"type" != .REL) { | 2955 | if (@"type" != .REL) { |
| 2369 | elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{ | 2956 | elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{ |
| 2370 | .name = ".got", | 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 | .flags = .{ .WRITE = true, .ALLOC = true }, | 2964 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 2372 | .addralign = addr_align, | 2965 | .addralign = addr_align, |
| | 2966 | .entsize = @intCast(addr_align.toByteUnits()), |
| 2373 | }); | 2967 | }); |
| 2374 | elf.shndx.got_plt = try elf.addSection( | 2968 | elf.shndx.got_plt = try elf.addSection( |
| 2375 | if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data, | 2969 | if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data, |
| ... | @@ -2383,6 +2977,7 @@ fn initHeaders( | ... | @@ -2383,6 +2977,7 @@ fn initHeaders( |
| 2383 | .X86_64 => 3 * 8, | 2977 | .X86_64 => 3 * 8, |
| 2384 | }, | 2978 | }, |
| 2385 | .addralign = addr_align, | 2979 | .addralign = addr_align, |
| | 2980 | .entsize = @intCast(addr_align.toByteUnits()), |
| 2386 | }, | 2981 | }, |
| 2387 | ); | 2982 | ); |
| 2388 | const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec = | 2983 | const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec = |
| ... | @@ -2478,7 +3073,7 @@ fn initHeaders( | ... | @@ -2478,7 +3073,7 @@ fn initHeaders( |
| 2478 | .NONE, _ => unreachable, | 3073 | .NONE, _ => unreachable, |
| 2479 | inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela), | 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 | .name = ".rela.dyn", | 3077 | .name = ".rela.dyn", |
| 2483 | .type = .RELA, | 3078 | .type = .RELA, |
| 2484 | .flags = .{ .ALLOC = true }, | 3079 | .flags = .{ .ALLOC = true }, |
| ... | @@ -2487,13 +3082,12 @@ fn initHeaders( | ... | @@ -2487,13 +3082,12 @@ fn initHeaders( |
| 2487 | .entsize = rela_size, | 3082 | .entsize = rela_size, |
| 2488 | .node_align = elf.mf.flags.block_size, | 3083 | .node_align = elf.mf.flags.block_size, |
| 2489 | }); | 3084 | }); |
| 2490 | const got_plt_shndx = elf.shndx.got_plt; | 3085 | elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{ |
| 2491 | got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{ | | |
| 2492 | .name = ".rela.plt", | 3086 | .name = ".rela.plt", |
| 2493 | .type = .RELA, | 3087 | .type = .RELA, |
| 2494 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, | 3088 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, |
| 2495 | .link = elf.shndx.dynsym.toSection().?, | 3089 | .link = elf.shndx.dynsym.toSection().?, |
| 2496 | .info = got_plt_shndx.toSection().?, | 3090 | .info = elf.shndx.got_plt.toSection().?, |
| 2497 | .addralign = addr_align, | 3091 | .addralign = addr_align, |
| 2498 | .entsize = rela_size, | 3092 | .entsize = rela_size, |
| 2499 | .node_align = elf.mf.flags.block_size, | 3093 | .node_align = elf.mf.flags.block_size, |
| ... | @@ -2516,7 +3110,7 @@ fn initHeaders( | ... | @@ -2516,7 +3110,7 @@ fn initHeaders( |
| 2516 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip) | 3110 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip) |
| 2517 | 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax) | 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 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); | 3114 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); |
| 2521 | elf.addRelocAssumeCapacity( | 3115 | elf.addRelocAssumeCapacity( |
| 2522 | plt_ni, | 3116 | plt_ni, |
| ... | @@ -2544,6 +3138,83 @@ fn initHeaders( | ... | @@ -2544,6 +3138,83 @@ fn initHeaders( |
| 2544 | elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx }); | 3138 | elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx }); |
| 2545 | elf.phdrs.items[tls_phndx] = elf.ni.tls; | 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 | } else { | 3218 | } else { |
| 2548 | assert(maybe_interp == null); | 3219 | assert(maybe_interp == null); |
| 2549 | assert(!have_dynamic_section); | 3220 | assert(!have_dynamic_section); |
| ... | @@ -2554,6 +3225,12 @@ fn initHeaders( | ... | @@ -2554,6 +3225,12 @@ fn initHeaders( |
| 2554 | .addralign = elf.mf.flags.block_size, | 3225 | .addralign = elf.mf.flags.block_size, |
| 2555 | }); | 3226 | }); |
| 2556 | assert(elf.nodes.len == expected_nodes_len); | 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 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { | 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,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node { |
| 2586 | return elf.nodes.get(@intFromEnum(ni)); | 3263 | return elf.nodes.get(@intFromEnum(ni)); |
| 2587 | } | 3264 | } |
| 2588 | /// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data. | 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 | return switch (elf.getNode(ni)) { | 3267 | return switch (elf.getNode(ni)) { |
| 2591 | .file => unreachable, | 3268 | .file => unreachable, |
| 2592 | .ehdr => unreachable, | 3269 | .ehdr => unreachable, |
| ... | @@ -2624,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { | ... | @@ -2624,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 2624 | /// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support | 3301 | /// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support |
| 2625 | /// the special-case sections '.plt' and '.dynamic'. | 3302 | /// the special-case sections '.plt' and '.dynamic'. |
| 2626 | fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { | 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 | .file => unreachable, // cannot contain relocs | 3305 | .file => unreachable, // cannot contain relocs |
| 2629 | .ehdr => unreachable, // cannot contain relocs | 3306 | .ehdr => unreachable, // cannot contain relocs |
| 2630 | .shdr => unreachable, // cannot contain relocs | 3307 | .shdr => unreachable, // cannot contain relocs |
| 2631 | .segment => unreachable, // cannot contain relocs | 3308 | .segment => unreachable, // cannot contain relocs |
| 2632 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) | 3309 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) |
| 2633 | .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc, | 3310 | .input_section => |isi| .{ |
| 2634 | .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc, | 3311 | &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc, |
| 2635 | .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc, | 3312 | &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc, |
| 2636 | inline .lazy_code, .lazy_const_data => |lmi| &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_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) { | 3327 | |
| 2639 | for (elf.relocs.items[@intFromEnum(first_reloc_ptr.*)..]) |*reloc| { | 3328 | if (symbol_relocs.* != .none) { |
| | 3329 | for ( |
| | 3330 | elf.symbol_relocs.items[@intFromEnum(symbol_relocs.*)..], |
| | 3331 | @intFromEnum(symbol_relocs.*).., |
| | 3332 | ) |*reloc, index| { |
| 2640 | if (reloc.node != ni) break; | 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 | 3350 | /// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this |
| 2648 | /// needed. In relocatables, this means updating the offsets of those relocations. In ELF modules, | 3351 | /// means updating the relocations' offsets. In ELF modules, this means applying the relocations. |
| 2649 | /// this means applying the relocations. | | |
| 2650 | fn flushMovedNodeRelocs( | 3352 | fn flushMovedNodeRelocs( |
| 2651 | elf: *Elf, | 3353 | elf: *Elf, |
| 2652 | node: MappedFile.Node.Index, | 3354 | node: MappedFile.Node.Index, |
| 2653 | node_vaddr: u64, | 3355 | node_vaddr: u64, |
| 2654 | first_reloc: Reloc.Index, | 3356 | first_symbol_reloc: SymbolReloc.Index, |
| | 3357 | first_got_reloc: GotReloc.Index, |
| 2655 | ) void { | 3358 | ) void { |
| 2656 | if (first_reloc == .none) return; | 3359 | if (first_symbol_reloc != .none) { |
| 2657 | switch (elf.ehdrField(.type)) { | 3360 | for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| { |
| 2658 | .NONE, .CORE, _ => unreachable, | 3361 | if (reloc.node != node) break; |
| 2659 | .REL => { | 3362 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 2660 | // In a relocatable, we're not actually applying any relocations ourselves, but we need | 3363 | // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're |
| 2661 | // to update the offsets of the relocation entries since the node they're in has moved. | 3364 | // in has moved, so their offset within the section might also have moved. |
| 2662 | for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| { | 3365 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 2663 | if (reloc.node != node) break; | 3366 | } else { |
| 2664 | reloc.updateNodeOffset(elf, node_vaddr); | 3367 | // We've applied this relocation ourselves! Just re-apply it now. |
| 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; | | |
| 2671 | reloc.apply(elf); | 3368 | reloc.apply(elf); |
| 2672 | } | 3369 | } |
| 2673 | // TODO: once we're emitting runtime relocation entries, we need to update their offsets | 3370 | } |
| 2674 | // too, like the logic for relocatables above. | 3371 | } |
| 2675 | }, | 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,28 +3505,137 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr { |
| 2803 | } | 3505 | } |
| 2804 | } | 3506 | } |
| 2805 | | 3507 | |
| 2806 | fn navType( | 3508 | fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT { |
| 2807 | ip: *const InternPool, | 3509 | const any_non_single_threaded = elf.base.comp.config.any_non_single_threaded; |
| 2808 | nav_resolved: @typeInfo(@FieldType(InternPool.Nav, "resolved")).optional.child, | | |
| 2809 | any_non_single_threaded: bool, | | |
| 2810 | ) std.elf.STT { | | |
| 2811 | return if (any_non_single_threaded and nav_resolved.@"threadlocal") | 3510 | return if (any_non_single_threaded and nav_resolved.@"threadlocal") |
| 2812 | .TLS | 3511 | .TLS |
| 2813 | else if (ip.isFunctionType(nav_resolved.type)) | 3512 | else if (elf.base.comp.zcu.?.intern_pool.isFunctionType(nav_resolved.type)) |
| 2814 | .FUNC | 3513 | .FUNC |
| 2815 | else | 3514 | else |
| 2816 | .OBJECT; | 3515 | .OBJECT; |
| 2817 | } | 3516 | } |
| 2818 | fn namedSection(elf: *const Elf, name: []const u8) ?Section.Index { | 3517 | fn mapInputSection(elf: *Elf, opts: struct { |
| 2819 | if (std.mem.eql(u8, name, ".rodata") or | 3518 | name: []const u8, |
| 2820 | std.mem.startsWith(u8, name, ".rodata.")) return .rodata; | 3519 | flags: std.elf.SHF, |
| 2821 | if (std.mem.eql(u8, name, ".text") or | 3520 | addralign: std.elf.Xword, |
| 2822 | std.mem.startsWith(u8, name, ".text.")) return .text; | 3521 | entsize: std.elf.Xword, |
| 2823 | if (std.mem.eql(u8, name, ".data") or | 3522 | }) !Section.Index { |
| 2824 | std.mem.startsWith(u8, name, ".data.")) return .data; | 3523 | const gpa = elf.base.comp.gpa; |
| 2825 | if (std.mem.eql(u8, name, ".tdata") or | 3524 | if (opts.flags.INFO_LINK or |
| 2826 | std.mem.startsWith(u8, name, ".tdata.")) return elf.shndx.tdata; | 3525 | opts.flags.LINK_ORDER or |
| 2827 | return null; | 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 | fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex { | 3640 | fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex { |
| 2830 | const gpa = zcu.gpa; | 3641 | const gpa = zcu.gpa; |
| ... | @@ -2838,17 +3649,41 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM | ... | @@ -2838,17 +3649,41 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM |
| 2838 | const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index); | 3649 | const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index); |
| 2839 | const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index); | 3650 | const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index); |
| 2840 | if (!nav_gop.found_existing) { | 3651 | if (!nav_gop.found_existing) { |
| 2841 | const sym_type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded); | | |
| 2842 | const shndx: Section.Index = section: { | 3652 | const shndx: Section.Index = section: { |
| 2843 | if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| { | 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 | const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) { | 3688 | const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) { |
| 2854 | .@"fn" => a: { | 3689 | .@"fn" => a: { |
| ... | @@ -2880,10 +3715,11 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM | ... | @@ -2880,10 +3715,11 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM |
| 2880 | .name = try elf.string(.strtab, nav.fqn.toSlice(ip)), | 3715 | .name = try elf.string(.strtab, nav.fqn.toSlice(ip)), |
| 2881 | .value = 0, | 3716 | .value = 0, |
| 2882 | .size = 0, | 3717 | .size = 0, |
| 2883 | .type = sym_type, | 3718 | .type = elf.navType(nav.resolved.?), |
| 2884 | .shndx = shndx, | 3719 | .shndx = shndx, |
| 2885 | }), | 3720 | }), |
| 2886 | .first_reloc = .none, | 3721 | .first_symbol_reloc = .none, |
| | 3722 | .first_got_reloc = .none, |
| 2887 | }; | 3723 | }; |
| 2888 | elf.nodes.appendAssumeCapacity(.{ .nav = nmi }); | 3724 | elf.nodes.appendAssumeCapacity(.{ .nav = nmi }); |
| 2889 | } | 3725 | } |
| ... | @@ -2932,7 +3768,7 @@ fn uavMapIndex( | ... | @@ -2932,7 +3768,7 @@ fn uavMapIndex( |
| 2932 | .type = .OBJECT, | 3768 | .type = .OBJECT, |
| 2933 | .shndx = shndx, | 3769 | .shndx = shndx, |
| 2934 | }), | 3770 | }), |
| 2935 | .first_reloc = .none, | 3771 | .first_symbol_reloc = .none, |
| 2936 | }; | 3772 | }; |
| 2937 | elf.nodes.appendAssumeCapacity(.{ .uav = umi }); | 3773 | elf.nodes.appendAssumeCapacity(.{ .uav = umi }); |
| 2938 | elf.const_prog_node.increaseEstimatedTotalItems(1); | 3774 | elf.const_prog_node.increaseEstimatedTotalItems(1); |
| ... | @@ -2940,7 +3776,7 @@ fn uavMapIndex( | ... | @@ -2940,7 +3776,7 @@ fn uavMapIndex( |
| 2940 | } else { | 3776 | } else { |
| 2941 | const node = uav_gop.value_ptr.lsi.index().ptr(elf).node; | 3777 | const node = uav_gop.value_ptr.lsi.index().ptr(elf).node; |
| 2942 | if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) { | 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 | return umi; | 3782 | return umi; |
| ... | @@ -2977,7 +3813,14 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError || | ... | @@ -2977,7 +3813,14 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError || |
| 2977 | else => |e| return e, | 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 | fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | 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,6 +3849,13 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void |
| 3006 | continue; | 3849 | continue; |
| 3007 | } | 3850 | } |
| 3008 | load_object: { | 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 | const member = header.name() orelse member: { | 3859 | const member = header.name() orelse member: { |
| 3010 | const strtab_offset = header.nameOffset() catch |err| switch (err) { | 3860 | const strtab_offset = header.nameOffset() catch |err| switch (err) { |
| 3011 | error.Overflow => break :member error.Overflow, | 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,7 +3871,6 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void |
| 3021 | } catch |err| switch (err) { | 3871 | } catch |err| switch (err) { |
| 3022 | error.Overflow => return diags.failParse(path, "bad member name offset", .{}), | 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 | try elf.loadObject(path, member, fr, .{ .offset = offset, .size = size }); | 3874 | try elf.loadObject(path, member, fr, .{ .offset = offset, .size = size }); |
| 3026 | } | 3875 | } |
| 3027 | try fr.seekTo(std.mem.alignForward(u64, offset + size, 2)); | 3876 | try fr.seekTo(std.mem.alignForward(u64, offset + size, 2)); |
| ... | @@ -3109,47 +3958,146 @@ fn loadObject( | ... | @@ -3109,47 +3958,146 @@ fn loadObject( |
| 3109 | defer gpa.free(shstrtab); | 3958 | defer gpa.free(shstrtab); |
| 3110 | try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); | 3959 | try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 3111 | try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); | 3960 | try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 3112 | for (sections[1..]) |*section| switch (section.shdr.type) { | 3961 | for (sections[1..]) |*section| { |
| 3113 | else => {}, | 3962 | if (section.shdr.name >= shstrtab.len) continue; |
| 3114 | .PROGBITS, .NOBITS => { | 3963 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 3115 | if (section.shdr.name >= shstrtab.len) continue; | 3964 | const opts: struct { |
| 3116 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); | 3965 | shndx: Section.Index, |
| 3117 | const shndx: Section.Index = elf.namedSection(name) orelse shndx: { | 3966 | has_file_bits: bool, |
| 3118 | // TODO: actually generate a .bss section. For now, just throw it into `.data`. | 3967 | node_fixed: bool, |
| 3119 | if (std.mem.eql(u8, name, ".bss") or | 3968 | } = switch (section.shdr.type) { |
| 3120 | std.mem.startsWith(u8, name, ".bss.")) break :shndx .data; | 3969 | else => continue, |
| 3121 | if (std.mem.eql(u8, name, ".tbss") or | 3970 | .PROGBITS, .NOBITS => opts: { |
| 3122 | std.mem.startsWith(u8, name, ".tbss.")) break :shndx elf.shndx.tdata; | 3971 | const shndx = elf.mapInputSection(.{ |
| 3123 | break :shndx .UNDEF; | 3972 | .name = name, |
| 3124 | }; | 3973 | .flags = section.shdr.flags.shf, |
| 3125 | if (shndx == .UNDEF) continue; | 3974 | .addralign = section.shdr.addralign, |
| 3126 | const ni = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{ | 3975 | .entsize = section.shdr.entsize, |
| 3127 | .size = section.shdr.size, | 3976 | }) catch |err| switch (err) { |
| 3128 | .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( | 3977 | error.StripSection => continue, |
| 3129 | usize, | 3978 | error.TlsSectionUnavailable => return diags.failParse( |
| 3130 | @intCast(@max(section.shdr.addralign, 1)), | 3979 | path, |
| 3131 | )), | 3980 | "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'", |
| 3132 | .moved = true, // see assert at end of `flushInputSection` | 3981 | .{name}, |
| 3133 | }); | 3982 | ), |
| 3134 | elf.nodes.appendAssumeCapacity(.{ | 3983 | error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) { |
| 3135 | .input_section = @enumFromInt(elf.input_sections.items.len), | 3984 | // It probably doesn't matter, just skip this section. |
| 3136 | }); | 3985 | continue; |
| 3137 | section.isi = @enumFromInt(elf.input_sections.items.len); | 3986 | } else return diags.failParse( |
| 3138 | elf.input_sections.addOneAssumeCapacity().* = .{ | 3987 | path, |
| 3139 | .input = input_index, | 3988 | "unsupported flags for section '{s}'", |
| 3140 | .file_location = .{ | 3989 | .{name}, |
| 3141 | .offset = fl.offset + section.shdr.offset, | 3990 | ), |
| 3142 | .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size, | 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 | 4067 | .has_file_bits = true, |
| 3145 | // zero-based. This will eventually be updated by `flushMoved`. | 4068 | // This node must be fixed to prevent padding from being added between different |
| 3146 | .vaddr = 0, | 4069 | // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections. |
| 3147 | .node = ni, | 4070 | .node_fixed = true, |
| 3148 | .first_reloc = .none, | 4071 | }, |
| 3149 | }; | 4072 | }; |
| 3150 | elf.synth_prog_node.increaseEstimatedTotalItems(1); | 4073 | const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{ |
| 3151 | }, | 4074 | .size = section.shdr.size, |
| 3152 | }; | 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 | var symmap: std.ArrayList(Symbol.Id) = .empty; | 4101 | var symmap: std.ArrayList(Symbol.Id) = .empty; |
| 3154 | defer symmap.deinit(gpa); | 4102 | defer symmap.deinit(gpa); |
| 3155 | for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) { | 4103 | for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) { |
| ... | @@ -3198,20 +4146,24 @@ fn loadObject( | ... | @@ -3198,20 +4146,24 @@ fn loadObject( |
| 3198 | }; | 4146 | }; |
| 3199 | | 4147 | |
| 3200 | if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) { | 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 | path, | 4150 | path, |
| 3203 | "symbol '{s}' has unsupported binding (0x{x})", | 4151 | "symbol '{s}' has unsupported binding (0x{x})", |
| 3204 | .{ name, bind }, | 4152 | .{ name, bind }, |
| 3205 | ), | 4153 | ), |
| 3206 | .LOCAL => continue, | 4154 | .LOCAL => continue, |
| 3207 | .GLOBAL, .WEAK => |bind| { | 4155 | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { |
| 3208 | si.* = elf.addGlobalSymbolAssumeCapacity(.{ | 4156 | si.* = elf.addGlobalSymbolAssumeCapacity(.{ |
| 3209 | .node = .none, | 4157 | .node = .none, |
| 3210 | .name = try .string(elf, name), | 4158 | .name = try .string(elf, name), |
| 3211 | .value = input_sym.value, | 4159 | .value = input_sym.value, |
| 3212 | .size = input_sym.size, | 4160 | .size = input_sym.size, |
| 3213 | .type = sym_type, | 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 | .visibility = input_sym.other.visibility, | 4167 | .visibility = input_sym.other.visibility, |
| 3216 | .shndx = .UNDEF, | 4168 | .shndx = .UNDEF, |
| 3217 | }) catch |err| switch (err) { | 4169 | }) catch |err| switch (err) { |
| ... | @@ -3224,7 +4176,7 @@ fn loadObject( | ... | @@ -3224,7 +4176,7 @@ fn loadObject( |
| 3224 | const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf); | 4176 | const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf); |
| 3225 | | 4177 | |
| 3226 | switch (input_sym.info.bind) { | 4178 | switch (input_sym.info.bind) { |
| 3227 | _ => |bind| return diags.failParse( | 4179 | else => |bind| return diags.failParse( |
| 3228 | path, | 4180 | path, |
| 3229 | "symbol '{s}' has unsupported binding (0x{x})", | 4181 | "symbol '{s}' has unsupported binding (0x{x})", |
| 3230 | .{ name, bind }, | 4182 | .{ name, bind }, |
| ... | @@ -3240,14 +4192,18 @@ fn loadObject( | ... | @@ -3240,14 +4192,18 @@ fn loadObject( |
| 3240 | }); | 4192 | }); |
| 3241 | si.* = .local(lsi); | 4193 | si.* = .local(lsi); |
| 3242 | }, | 4194 | }, |
| 3243 | .GLOBAL, .WEAK => |bind| { | 4195 | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { |
| 3244 | si.* = elf.addGlobalSymbolAssumeCapacity(.{ | 4196 | si.* = elf.addGlobalSymbolAssumeCapacity(.{ |
| 3245 | .node = input_section_node, | 4197 | .node = input_section_node, |
| 3246 | .name = try .string(elf, name), | 4198 | .name = try .string(elf, name), |
| 3247 | .value = input_sym.value, | 4199 | .value = input_sym.value, |
| 3248 | .size = input_sym.size, | 4200 | .size = input_sym.size, |
| 3249 | .type = sym_type, | 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 | .visibility = input_sym.other.visibility, | 4207 | .visibility = input_sym.other.visibility, |
| 3252 | .shndx = elf.getNodeShndx(input_section_node), | 4208 | .shndx = elf.getNodeShndx(input_section_node), |
| 3253 | }) catch |err| switch (err) { | 4209 | }) catch |err| switch (err) { |
| ... | @@ -3298,11 +4254,17 @@ fn loadObject( | ... | @@ -3298,11 +4254,17 @@ fn loadObject( |
| 3298 | .{rel.info.sym}, | 4254 | .{rel.info.sym}, |
| 3299 | ); | 4255 | ); |
| 3300 | const target = symmap.items[rel.info.sym - 1]; | 4256 | const target = symmap.items[rel.info.sym - 1]; |
| 3301 | if (target == Symbol.Id.null) return diags.failParse( | 4257 | if (target == Symbol.Id.null) { |
| 3302 | path, | 4258 | // If this is not an SHF_ALLOC section, then let's let this |
| 3303 | "unsupported symbol at index {d} required for relocation", | 4259 | // slide for now, because it probably doesn't affect the final |
| 3304 | .{rel.info.sym}, | 4260 | // binary's functionality for this section to be a bit broken. |
| 3305 | ); | 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 | elf.addRelocAssumeCapacity( | 4268 | elf.addRelocAssumeCapacity( |
| 3307 | loc_node, | 4269 | loc_node, |
| 3308 | rel.offset - loc_sec.shdr.addr, | 4270 | rel.offset - loc_sec.shdr.addr, |
| ... | @@ -3320,6 +4282,7 @@ fn loadObject( | ... | @@ -3320,6 +4282,7 @@ fn loadObject( |
| 3320 | } | 4282 | } |
| 3321 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | 4283 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 3322 | const comp = elf.base.comp; | 4284 | const comp = elf.base.comp; |
| | 4285 | const gpa = comp.gpa; |
| 3323 | const diags = &comp.link_diags; | 4286 | const diags = &comp.link_diags; |
| 3324 | const r = &fr.interface; | 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,68 +4297,157 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 3334 | if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{}); | 4297 | if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{}); |
| 3335 | if (ehdr.machine != elf.ehdrField(.machine)) | 4298 | if (ehdr.machine != elf.ehdrField(.machine)) |
| 3336 | return diags.failParse(path, "bad machine", .{}); | 4299 | return diags.failParse(path, "bad machine", .{}); |
| 3337 | if (ehdr.phoff == 0 or ehdr.phnum <= 1) | 4300 | if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff); |
| 3338 | return diags.failParse(path, "no program headers", .{}); | 4301 | const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: { |
| 3339 | try fr.seekTo(ehdr.phoff); | 4302 | var dynamic_sh: ?ElfN.Shdr = null; |
| 3340 | const dynamic_ph = for (0..ehdr.phnum) |_| { | 4303 | var dynsym_sh: ?ElfN.Shdr = null; |
| 3341 | const ph = try r.peekStruct(ElfN.Phdr, target_endian); | 4304 | for (0..ehdr.shnum) |_| { |
| 3342 | try r.discardAll(ehdr.phentsize); | 4305 | const sh = try r.peekStruct(ElfN.Shdr, target_endian); |
| 3343 | switch (ph.type) { | 4306 | try r.discardAll(ehdr.shentsize); |
| 3344 | else => {}, | 4307 | switch (sh.type) { |
| 3345 | .DYNAMIC => break ph, | 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 | const dynnum = std.math.divExact( | 4329 | const dynnum = std.math.divExact( |
| 3349 | u32, | 4330 | u32, |
| 3350 | @intCast(dynamic_ph.filesz), | 4331 | @intCast(dynamic_sh.size), |
| 3351 | @sizeOf(ElfN.Addr) * 2, | 4332 | @sizeOf(ElfN.Addr) * 2, |
| 3352 | ) catch return diags.failParse( | 4333 | ) catch return diags.failParse( |
| 3353 | path, | 4334 | path, |
| 3354 | "dynamic segment filesz (0x{x}) is not a multiple of entsize (0x{x})", | 4335 | "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})", |
| 3355 | .{ dynamic_ph.filesz, @sizeOf(ElfN.Addr) * 2 }, | 4336 | .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 }, |
| 3356 | ); | 4337 | ); |
| 3357 | var strtab: ?ElfN.Addr = null; | 4338 | |
| 3358 | var strsz: ?ElfN.Addr = null; | 4339 | if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) { |
| 3359 | var soname: ?ElfN.Addr = null; | 4340 | return diags.failParse(path, "bad dynsym entsize", .{}); |
| 3360 | try fr.seekTo(dynamic_ph.offset); | 4341 | } |
| 3361 | for (0..dynnum) |_| { | 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 | const tag = try r.takeInt(ElfN.Addr, target_endian); | 4360 | const tag = try r.takeInt(ElfN.Addr, target_endian); |
| 3363 | const val = try r.takeInt(ElfN.Addr, target_endian); | 4361 | const val = try r.takeInt(ElfN.Addr, target_endian); |
| 3364 | switch (tag) { | 4362 | if (tag == std.elf.DT_SONAME) { |
| 3365 | else => {}, | 4363 | // val is a dynstr index |
| 3366 | std.elf.DT_STRTAB => strtab = val, | 4364 | if (val >= dynstr.len) { |
| 3367 | std.elf.DT_STRSZ => strsz = val, | 4365 | return diags.failParse(path, "bad soname string", .{}); |
| 3368 | std.elf.DT_SONAME => soname = val, | 4366 | } |
| | 4367 | break std.mem.sliceTo(dynstr[@intCast(val)..], 0); |
| 3369 | } | 4368 | } |
| 3370 | } | 4369 | } else std.fs.path.basename(path.sub_path); |
| 3371 | if (strtab == null or soname == null) | 4370 | try elf.needed.put(gpa, try elf.string(.dynstr, soname), {}); |
| 3372 | return elf.loadDsoExact(std.fs.path.basename(path.sub_path)); | 4371 | |
| 3373 | if (strsz) |size| if (soname.? >= size) | 4372 | // Scan the symbol table and populate `elf.dso_globals`. |
| 3374 | return diags.failParse(path, "bad soname string", .{}); | 4373 | const first_global = @min(dynsym_sh.info, symnum); |
| 3375 | try fr.seekTo(ehdr.phoff); | 4374 | try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global); |
| 3376 | const ph = for (0..ehdr.phnum) |_| { | 4375 | try elf.ensureUnusedPltCapacity(symnum - first_global); |
| 3377 | const ph = try r.peekStruct(ElfN.Phdr, target_endian); | 4376 | try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize); |
| 3378 | try r.discardAll(ehdr.phentsize); | 4377 | for (first_global..symnum) |_| { |
| 3379 | switch (ph.type) { | 4378 | const sym = try r.peekStruct(ElfN.Sym, target_endian); |
| 3380 | else => {}, | 4379 | try r.discardAll(@intCast(dynsym_sh.entsize)); |
| 3381 | .LOAD => if (strtab.? >= ph.vaddr and | 4380 | |
| 3382 | strtab.? + (strsz orelse 0) <= ph.vaddr + ph.filesz) break ph, | 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", .{}); | 4385 | // STV_HIDDEN/STV_INTERNAL symbols should be marked as STB_LOCAL and hence skipped |
| 3385 | try fr.seekTo(strtab.? + soname.? - ph.vaddr + ph.offset); | 4386 | // above, but we might as well double-check. |
| 3386 | return elf.loadDsoExact(r.peekSentinel(0) catch |err| switch (err) { | 4387 | switch (sym.other.visibility) { |
| 3387 | error.StreamTooLong => return diags.failParse(path, "soname too lang", .{}), | 4388 | .HIDDEN, .INTERNAL => continue, |
| 3388 | else => |e| return e, | 4389 | .DEFAULT, .PROTECTED => {}, |
| 3389 | }); | 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 | /// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input. | 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,22 +4484,100 @@ fn checkInputIdent( |
| 3432 | .{ident.version}, | 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. | 4487 | // 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`. | 4488 | // It sounds reasonable to allow the value we chose *and* allow `.NONE`. |
| 3437 | const expect_abiversion: u8 = abiver: { | 4489 | const expect_abiversion: u8 = abiver: { |
| 3438 | if (ident.osabi == .NONE) break :abiver 0; | 4490 | if (ident.osabi == .NONE) break :abiver 0; |
| 3439 | if (ident.osabi == target.osabi) break :abiver target.abiversion; | 4491 | if (ident.osabi == target.osabi) break :abiver target.abiversion; |
| 3440 | return diags.failParse( | 4492 | return diags.failParse( |
| 3441 | path, | 4493 | path, |
| 3442 | "bad ELF OS/ABI ({?s})", | 4494 | "bad ELF OS/ABI ({?s})", |
| 3443 | .{std.enums.tagName(std.elf.OSABI, ident.osabi)}, | 4495 | .{std.enums.tagName(std.elf.OSABI, ident.osabi)}, |
| 3444 | ); | 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( | 4579 | const end_sym_name = elf.string(.strtab, "__" ++ name ++ "_end") catch unreachable; // string definitely already exists |
| 3447 | path, | 4580 | Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr); |
| 3448 | "bad ELF ABI version ({d})", | | |
| 3449 | .{ident.abiversion}, | | |
| 3450 | ); | | |
| 3451 | } | 4581 | } |
| 3452 | | 4582 | |
| 3453 | pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { | 4583 | pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { |
| ... | @@ -3486,9 +4616,22 @@ fn prelinkInner(elf: *Elf) !void { | ... | @@ -3486,9 +4616,22 @@ fn prelinkInner(elf: *Elf) !void { |
| 3486 | const ElfN = ct_class.ElfN(); | 4616 | const ElfN = ct_class.ElfN(); |
| 3487 | const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0; | 4617 | const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0; |
| 3488 | const flags_1: ElfN.Addr = if (elf.options.z_now) std.elf.DF_1_NOW else 0; | 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 | const needed_len = elf.needed.count(); | 4628 | const needed_len = elf.needed.count(); |
| 3490 | const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) + | 4629 | const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) + |
| | 4630 | @intFromBool(rpath != .empty) + |
| 3491 | @intFromBool(flags != 0) + @intFromBool(flags_1 != 0) + | 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 | @intFromBool(comp.config.output_mode == .Exe) + 12; | 4635 | @intFromBool(comp.config.output_mode == .Exe) + 12; |
| 3493 | const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len); | 4636 | const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len); |
| 3494 | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; | 4637 | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; |
| ... | @@ -3508,6 +4651,10 @@ fn prelinkInner(elf: *Elf) !void { | ... | @@ -3508,6 +4651,10 @@ fn prelinkInner(elf: *Elf) !void { |
| 3508 | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) }; | 4651 | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) }; |
| 3509 | dynamic_index += 1; | 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 | if (flags != 0) { | 4658 | if (flags != 0) { |
| 3512 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags }; | 4659 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags }; |
| 3513 | dynamic_index += 1; | 4660 | dynamic_index += 1; |
| ... | @@ -3520,23 +4667,72 @@ fn prelinkInner(elf: *Elf) !void { | ... | @@ -3520,23 +4667,72 @@ fn prelinkInner(elf: *Elf) !void { |
| 3520 | dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 }; | 4667 | dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 }; |
| 3521 | dynamic_index += 1; | 4668 | dynamic_index += 1; |
| 3522 | } | 4669 | } |
| 3523 | const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx; | 4670 | if (elf.shndx.init_array != .UNDEF) { |
| 3524 | const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx; | 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 | dynamic_entries[dynamic_index..][0..12].* = .{ | 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 | .{ std.elf.DT_RELASZ, elf.targetLoad( | 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 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, | 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 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( | 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 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, | 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 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, | 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 | .{ std.elf.DT_STRSZ, elf.targetLoad( | 4736 | .{ std.elf.DT_STRSZ, elf.targetLoad( |
| 3541 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, | 4737 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, |
| 3542 | ) }, | 4738 | ) }, |
| ... | @@ -3547,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void { | ... | @@ -3547,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void { |
| 3547 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| | 4743 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 3548 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); | 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 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 5); | 4747 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 5); |
| 3552 | elf.addRelocAssumeCapacity( | 4748 | elf.addRelocAssumeCapacity( |
| 3553 | dynamic_ni, | 4749 | dynamic_ni, |
| 3554 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1), | 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 | 0, | 4752 | 0, |
| 3557 | .absAddr(elf), | 4753 | .absAddr(elf), |
| 3558 | ); | 4754 | ); |
| 3559 | elf.addRelocAssumeCapacity( | 4755 | elf.addRelocAssumeCapacity( |
| 3560 | dynamic_ni, | 4756 | dynamic_ni, |
| 3561 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1), | 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 | 0, | 4759 | 0, |
| 3564 | .absAddr(elf), | 4760 | .absAddr(elf), |
| 3565 | ); | 4761 | ); |
| ... | @@ -3663,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { | ... | @@ -3663,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 3663 | .type = .SECTION, | 4859 | .type = .SECTION, |
| 3664 | .shndx = shndx, | 4860 | .shndx = shndx, |
| 3665 | }) else .null; | 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 | elf.nodes.appendAssumeCapacity(.{ .section = shndx }); | 4867 | elf.nodes.appendAssumeCapacity(.{ .section = shndx }); |
| 3668 | const offset = ni.fileLocation(&elf.mf, false).offset; | 4868 | const offset = ni.fileLocation(&elf.mf, false).offset; |
| 3669 | switch (elf.shdrPtr(shndx)) { | 4869 | switch (elf.shdrPtr(shndx)) { |
| ... | @@ -3689,20 +4889,23 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { | ... | @@ -3689,20 +4889,23 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 3689 | fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void { | 4889 | fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void { |
| 3690 | if (len == 0) return; | 4890 | if (len == 0) return; |
| 3691 | const gpa = elf.base.comp.gpa; | 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 | const class = elf.identClass(); | 4894 | const class = elf.identClass(); |
| 3694 | const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) { | 4895 | switch (elf.ehdrField(.type)) { |
| 3695 | .NONE, .CORE, _ => unreachable, | 4896 | .NONE, .CORE, _ => unreachable, |
| 3696 | .REL => { | 4897 | .REL => { |
| 3697 | const shndx = elf.getNodeShndx(node); | 4898 | const shndx = elf.getNodeShndx(node); |
| 3698 | if (shndx.get(elf).rela_shndx == .UNDEF) { | 4899 | if (shndx.get(elf).rela.shndx == .UNDEF) { |
| 3699 | var bfa_buf: [32]u8 = undefined; | 4900 | var bfa_buf: [32]u8 = undefined; |
| 3700 | var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa); | 4901 | var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa); |
| 3701 | const allocator = bfa.allocator(); | 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 | defer allocator.free(rela_name); | 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 | const rela_shndx = try elf.addSection(.none, .{ | 4909 | const rela_shndx = try elf.addSection(.none, .{ |
| 3707 | .name = rela_name, | 4910 | .name = rela_name, |
| 3708 | .type = .RELA, | 4911 | .type = .RELA, |
| ... | @@ -3719,33 +4922,29 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) | ... | @@ -3719,33 +4922,29 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) |
| 3719 | }, | 4922 | }, |
| 3720 | .node_align = elf.mf.flags.block_size, | 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) { | 4930 | .EXEC, .DYN => { |
| 3727 | _ => return, | 4931 | try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 3728 | .none => if (elf.shndx.dynamic != .UNDEF) { | 4932 | const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD |
| 3729 | try elf.mf.updates.ensureUnusedCapacity(gpa, 1); | 4933 | try elf.got.ensureUnusedCapacity(gpa, new_got_entries); |
| 3730 | const got_ni = elf.shndx.got.get(elf).ni; | 4934 | const got_ni = elf.shndx.got.get(elf).ni; |
| 3731 | _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf); | 4935 | _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf); |
| 3732 | const got_size = switch (class) { | 4936 | const need_got_size = switch (class) { |
| 3733 | .NONE, _ => unreachable, | 4937 | .NONE, _ => unreachable, |
| 3734 | inline else => |ct_class| (elf.got.len + 2) * @sizeOf(ct_class.ElfN().Addr), | 4938 | inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr), |
| 3735 | }; | 4939 | }; |
| 3736 | if (got_size > got_node_size) | 4940 | if (need_got_size > got_node_size) |
| 3737 | try got_ni.resize(&elf.mf, gpa, got_size +| got_size / MappedFile.growth_factor); | 4941 | try got_ni.resize(&elf.mf, gpa, need_got_size +| need_got_size / MappedFile.growth_factor); |
| 3738 | break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 }; | 4942 | |
| 3739 | } else return, | 4943 | if (elf.shndx.dynamic != .UNDEF) { |
| | 4944 | try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries); |
| | 4945 | } |
| 3740 | }, | 4946 | }, |
| 3741 | }; | 4947 | } |
| 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); | | |
| 3749 | } | 4948 | } |
| 3750 | fn addRelocAssumeCapacity( | 4949 | fn addRelocAssumeCapacity( |
| 3751 | elf: *Elf, | 4950 | elf: *Elf, |
| ... | @@ -3753,123 +4952,443 @@ fn addRelocAssumeCapacity( | ... | @@ -3753,123 +4952,443 @@ fn addRelocAssumeCapacity( |
| 3753 | offset: u64, | 4952 | offset: u64, |
| 3754 | target: Symbol.Id, | 4953 | target: Symbol.Id, |
| 3755 | addend: i64, | 4954 | addend: i64, |
| 3756 | @"type": Reloc.Type, | 4955 | @"type": MachineRelocType, |
| 3757 | ) void { | 4956 | ) void { |
| 3758 | assert(node != .none); | 4957 | assert(node != .none); |
| 3759 | const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len); | 4958 | switch (elf.ehdrField(.type)) { |
| 3760 | const next: Reloc.Index = next: { | 4959 | .NONE, .CORE, _ => unreachable, |
| 3761 | const target_ptr = target.index(elf).ptr(elf); | 4960 | .REL => { |
| 3762 | const next = target_ptr.first_target_reloc; | 4961 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| 3763 | target_ptr.first_target_reloc = ri; | 4962 | const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ |
| 3764 | break :next next; | 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 | if (next != .none) { | 5131 | if (next != .none) { |
| 3767 | next.get(elf).prev = ri; | 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 | .type = @"type", | 5139 | .type = @"type", |
| 3771 | .prev = .none, | | |
| 3772 | .next = next, | 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 | .node = node, | 5203 | .node = node, |
| | 5204 | .offset = offset, |
| 3774 | .target = target, | 5205 | .target = target, |
| 3775 | .index = index: switch (elf.ehdrField(.type)) { | 5206 | .addend = addend, |
| 3776 | .NONE, .CORE, _ => unreachable, | 5207 | .type = @"type", |
| 3777 | .REL => { | 5208 | }); |
| 3778 | const sh = elf.getNodeShndx(node).get(elf); | 5209 | } |
| 3779 | switch (elf.shdrPtr(sh.rela_shndx)) { | 5210 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 3780 | inline else => |shdr, class| { | 5211 | const entry_value: union(enum) { |
| 3781 | const Rela = class.ElfN().Rela; | 5212 | unsigned: u64, |
| 3782 | const ent_size = elf.targetLoad(&shdr.entsize); | 5213 | signed: i64, |
| 3783 | const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); | 5214 | reloc: struct { |
| 3784 | const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: { | 5215 | type: MachineRelocType, |
| 3785 | const rela: *Rela = @ptrCast(@alignCast( | 5216 | dynsym_index: u32, |
| 3786 | rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], | 5217 | }, |
| 3787 | )); | 5218 | } = switch (elf.got.keys()[got_index]) { |
| 3788 | sh.rela_free = @enumFromInt(rela.offset); | 5219 | .reserved => .{ .unsigned = 0 }, |
| 3789 | break :alloc_index index; | 5220 | .tpoff => |sym_id| val: { |
| 3790 | } else alloc_index: { | 5221 | // We will break from this block if we require a relocation. |
| 3791 | const old_size = elf.targetLoad(&shdr.size); | 5222 | known: { |
| 3792 | const new_size = old_size + ent_size; | 5223 | if (elf.base.comp.config.output_mode != .Exe) { |
| 3793 | elf.targetStore(&shdr.size, @intCast(new_size)); | 5224 | // Only the executable's per-module TLS block is at a known offset from the |
| 3794 | break :alloc_index @intCast(@divExact(old_size, ent_size)); | 5225 | // general TLS pointer. |
| 3795 | }; | 5226 | break :known; |
| 3796 | const rela: *Rela = @ptrCast(@alignCast( | 5227 | } |
| 3797 | rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], | 5228 | switch (sym_id.unwrap()) { |
| 3798 | )); | 5229 | .local => {}, |
| 3799 | // The `offset` field here needs to equal the offset into the section, which | 5230 | .global => |name| if (elf.globals.strong_undef.contains(name) or |
| 3800 | // is *not* the same as our `offset` which is the offset into `node`. We | 5231 | elf.globals.weak_undef.contains(name)) |
| 3801 | // could calculate it now, but there's no point since `flushMovedNodeRelocs` | 5232 | { |
| 3802 | // will eventually do that for us anyway. So for now, just set offset to 0. | 5233 | // This is an external TLS symbol, so we don't know its offset. |
| 3803 | rela.* = .{ | 5234 | break :known; |
| 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); | | |
| 3813 | }, | 5235 | }, |
| 3814 | } | 5236 | } |
| 3815 | }, | 5237 | // It's a symbol which we define, the symbol is not interposable because we're the |
| 3816 | .EXEC, .DYN => { | 5238 | // executable, and we know our per-module TLS block's offset because we're the |
| 3817 | switch (elf.ehdrField(.machine)) { | 5239 | // executable. We therefore know this value! |
| 3818 | else => |machine| @panic(@tagName(machine)), | 5240 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 3819 | .AARCH64, .PPC64, .RISCV => {}, | 5241 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 3820 | .X86_64 => switch (@"type".X86_64) { | 5242 | inline else => |phdr| tls_size: { |
| 3821 | else => {}, | 5243 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 3822 | .TLSLD => switch (elf.got.tlsld) { | 5244 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 3823 | _ => {}, | 5245 | }, |
| 3824 | .none => if (elf.shndx.dynamic != .UNDEF) { | 5246 | }; |
| 3825 | const tlsld_index = elf.got.len; | 5247 | const sym_value = sym_id.value(elf); |
| 3826 | elf.got.tlsld = .wrap(tlsld_index); | 5248 | break :val .{ .signed = @bitCast(sym_value -% tls_size) }; |
| 3827 | elf.got.len = tlsld_index + 2; | 5249 | } |
| 3828 | const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) { | 5250 | break :val .{ |
| 3829 | inline else => |shdr, class| { | 5251 | .reloc = .{ |
| 3830 | const addr_size = @sizeOf(class.ElfN().Addr); | 5252 | .type = switch (elf.ehdrField(.machine)) { |
| 3831 | const old_size = addr_size * tlsld_index; | 5253 | else => |machine| @panic(@tagName(machine)), |
| 3832 | const new_size = old_size + addr_size * 2; | 5254 | .X86_64 => .{ .X86_64 = .TPOFF64 }, |
| 3833 | @memset( | 5255 | }, |
| 3834 | elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size], | 5256 | .dynsym_index = switch (sym_id.unwrap()) { |
| 3835 | 0, | 5257 | .global => |name| elf.globalByName(name).?.dynsym_index, |
| 3836 | ); | 5258 | // TODO: I have no idea if compilers are even allowed to emit this, but if they |
| 3837 | break :got_addr elf.targetLoad(&shdr.addr) + old_size; | 5259 | // are then I guess we need to add this local symbol to `.dynsym`? |
| 3838 | }, | 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; | 5317 | switch (visibility) { |
| 3841 | const rela_dyn_ni = rela_dyn_shndx.get(elf).ni; | 5318 | .DEFAULT => {}, |
| 3842 | switch (elf.shdrPtr(rela_dyn_shndx)) { | 5319 | .INTERNAL, .HIDDEN, .PROTECTED => { |
| 3843 | inline else => |shdr, class| { | 5320 | break :dsi 0; // non-interposable definition |
| 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); | | |
| 3860 | }, | 5321 | }, |
| 3861 | } | 5322 | } |
| 3862 | rela_dyn_ni.resizedAssumeCapacity(&elf.mf); | 5323 | } |
| 3863 | }, | 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 | } | 5329 | }, |
| 3867 | break :index .none; | | |
| 3868 | }, | 5330 | }, |
| 3869 | }, | 5331 | }, |
| 3870 | .offset = offset, | 5332 | .tlsld0 => switch (elf.shndx.dynamic) { |
| 3871 | .addend = addend, | 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 | pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | 5394 | pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| ... | @@ -3999,16 +5518,21 @@ pub fn flush( | ... | @@ -3999,16 +5518,21 @@ pub fn flush( |
| 3999 | _ = arena; | 5518 | _ = arena; |
| 4000 | _ = prog_node; | 5519 | _ = prog_node; |
| 4001 | | 5520 | |
| 4002 | if (elf.ehdrField(.type) != .REL and | 5521 | if (comp.config.output_mode == .Exe) { |
| 4003 | elf.shndx.dynamic == .UNDEF and | 5522 | var any_undef = false; |
| 4004 | elf.globals.strong_undef.count() > 0) | | |
| 4005 | { | | |
| 4006 | for (elf.globals.strong_undef.keys()) |name| { | 5523 | for (elf.globals.strong_undef.keys()) |name| { |
| | 5524 | if (elf.dso_globals.contains(name)) continue; |
| | 5525 | any_undef = true; |
| 4007 | comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)}); | 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 | while (try elf.idle(tid)) {} | 5536 | while (try elf.idle(tid)) {} |
| 4013 | | 5537 | |
| 4014 | const entry_addr: u64 = entry: { | 5538 | const entry_addr: u64 = entry: { |
| ... | @@ -4039,6 +5563,47 @@ pub fn flush( | ... | @@ -4039,6 +5563,47 @@ pub fn flush( |
| 4039 | else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}), | 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 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { | 5608 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4044 | const comp = elf.base.comp; | 5609 | const comp = elf.base.comp; |
| ... | @@ -4093,7 +5658,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { | ... | @@ -4093,7 +5658,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4093 | return comp.link_diags.fail( | 5658 | return comp.link_diags.fail( |
| 4094 | "linker failed to read input section '{s}' from \"{f}{f}\": {t}", | 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 | ii.path(elf).fmtEscapeString(), | 5662 | ii.path(elf).fmtEscapeString(), |
| 4098 | fmtMemberString(ii.member(elf)), | 5663 | fmtMemberString(ii.member(elf)), |
| 4099 | e, | 5664 | e, |
| ... | @@ -4104,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { | ... | @@ -4104,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4104 | break :task; | 5669 | break :task; |
| 4105 | } | 5670 | } |
| 4106 | if (elf.changed_symtab_index.pop()) |kv| { | 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 | if (elf.ehdrField(.type) == .REL) { | 5675 | if (elf.ehdrField(.type) == .REL) { |
| 4108 | const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0); | 5676 | const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0); |
| 4109 | defer sub_prog_node.end(); | 5677 | defer sub_prog_node.end(); |
| ... | @@ -4111,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { | ... | @@ -4111,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4111 | var ri = sym.first_target_reloc; | 5679 | var ri = sym.first_target_reloc; |
| 4112 | while (ri != .none) { | 5680 | while (ri != .none) { |
| 4113 | const reloc = ri.get(elf); | 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 | ri = reloc.next; | 5687 | ri = reloc.next; |
| 4116 | } | 5688 | } |
| 4117 | break :task; | 5689 | break :task; |
| ... | @@ -4146,13 +5718,13 @@ fn idleProgNode( | ... | @@ -4146,13 +5718,13 @@ fn idleProgNode( |
| 4146 | var name: [std.Progress.Node.max_name_len]u8 = undefined; | 5718 | var name: [std.Progress.Node.max_name_len]u8 = undefined; |
| 4147 | return prog_node.start(name: switch (node) { | 5719 | return prog_node.start(name: switch (node) { |
| 4148 | else => |tag| @tagName(tag), | 5720 | else => |tag| @tagName(tag), |
| 4149 | .section => |shndx| shndx.name(elf), | 5721 | .section => |shndx| shndx.name(elf).slice(elf), |
| 4150 | .input_section => |isi| { | 5722 | .input_section => |isi| { |
| 4151 | const ii = isi.input(elf); | 5723 | const ii = isi.input(elf); |
| 4152 | break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{ | 5724 | break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{ |
| 4153 | ii.path(elf).fmtEscapeString(), | 5725 | ii.path(elf).fmtEscapeString(), |
| 4154 | fmtMemberString(ii.member(elf)), | 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 | }) catch &name; | 5728 | }) catch &name; |
| 4157 | }, | 5729 | }, |
| 4158 | .nav => |nmi| { | 5730 | .nav => |nmi| { |
| ... | @@ -4320,110 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -4320,110 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4320 | .section => |shndx| { | 5892 | .section => |shndx| { |
| 4321 | try elf.flushFileOffset(ni); | 5893 | try elf.flushFileOffset(ni); |
| 4322 | const addr = elf.computeNodeVAddr(ni); | 5894 | const addr = elf.computeNodeVAddr(ni); |
| 4323 | switch (elf.shdrPtr(shndx)) { | 5895 | const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) { |
| 4324 | inline else => |shdr, class| { | 5896 | inline else => |shdr| .{ |
| 4325 | const flags = elf.targetLoad(&shdr.flags).shf; | 5897 | elf.targetLoad(&shdr.addr), |
| 4326 | if (flags.ALLOC) { | 5898 | elf.targetLoad(&shdr.flags).shf, |
| 4327 | if (elf.shndx.dynamic != .UNDEF) { | 5899 | }, |
| 4328 | if (shndx == elf.shndx.got) { | 5900 | }; |
| 4329 | const old_addr = elf.targetLoad(&shdr.addr); | 5901 | |
| 4330 | const rela_dyn_shndx = shndx.get(elf).rela_shndx; | 5902 | if (flags.ALLOC) { |
| 4331 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( | 5903 | switch (elf.shdrPtr(shndx)) { |
| 4332 | rela_dyn_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast( | 5904 | inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)), |
| 4333 | elf.targetLoad(&@field( | 5905 | } |
| 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 | } | | |
| 4420 | | 5906 | |
| 4421 | if (shndx == elf.shndx.plt) { | 5907 | // Update global symbols targeting this section |
| 4422 | elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc); | 5908 | if (elf.node_global_symbols.get(ni)) |first_name| { |
| 4423 | } else if (shndx == elf.shndx.dynamic) { | 5909 | assert(first_name != .empty); |
| 4424 | elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc); | 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 | .input_section => |isi| { | 5947 | .input_section => |isi| { |
| ... | @@ -4448,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -4448,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4448 | .DEFAULT => elf.targetLoad(&sym.value), | 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 | // Update global symbols | 5975 | // Update global symbols |
| ... | @@ -4460,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -4460,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4460 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { | 5981 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { |
| 4461 | inline else => |sym| elf.targetLoad(&sym.value), | 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 | name = global.next_in_node; | 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 | inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { | 5999 | inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { |
| 4471 | const new_addr = elf.computeNodeVAddr(ni); | 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 | if (elf.node_global_symbols.get(ni)) |first_name| { | 6002 | if (elf.node_global_symbols.get(ni)) |first_name| { |
| 4474 | assert(first_name != .empty); | 6003 | assert(first_name != .empty); |
| 4475 | var name = first_name; | 6004 | var name = first_name; |
| 4476 | while (name != .empty) { | 6005 | while (name != .empty) { |
| 4477 | const global = elf.globalByName(name).?; | 6006 | Symbol.Id.global(name).flushMoved(elf, new_addr); |
| 4478 | global.flushMoved(elf, new_addr); | 6007 | name = elf.globalByName(name).?.next_in_node; |
| 4479 | name = global.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 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); | 6018 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| ... | @@ -4507,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -4507,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4507 | }, | 6040 | }, |
| 4508 | .TLS => { | 6041 | .TLS => { |
| 4509 | elf.targetStore(&ph.memsz, @intCast(size)); | 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 | return ni.childrenMoved(elf.base.comp.gpa, &elf.mf); | 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,53 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4541 | }, | 6095 | }, |
| 4542 | }, | 6096 | }, |
| 4543 | .section => |shndx| switch (elf.shdrPtr(shndx)) { | 6097 | .section => |shndx| switch (elf.shdrPtr(shndx)) { |
| 4544 | inline else => |shdr, class| { | 6098 | inline else => |shdr| { |
| 4545 | switch (elf.targetLoad(&shdr.type)) { | 6099 | switch (elf.targetLoad(&shdr.type)) { |
| 4546 | else => unreachable, | 6100 | else => unreachable, |
| | 6101 | |
| 4547 | .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), | 6102 | .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), |
| 4548 | .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), | 6103 | .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), |
| 4549 | .SYMTAB, .DYNAMIC, .REL, .DYNSYM => return, | 6104 | |
| 4550 | .STRTAB => { | 6105 | .INIT_ARRAY, |
| 4551 | if (elf.shndx.dynamic != .UNDEF) { | 6106 | .FINI_ARRAY, |
| 4552 | if (shndx == elf.shndx.dynstr) { | 6107 | .PREINIT_ARRAY, |
| 4553 | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( | 6108 | .STRTAB, |
| 4554 | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), | 6109 | .SYMTAB, |
| 4555 | )); | 6110 | .DYNAMIC, |
| 4556 | for (dynamic_entries) |*dynamic_entry| | 6111 | .REL, |
| 4557 | switch (elf.targetLoad(&dynamic_entry[0])) { | 6112 | .RELA, |
| 4558 | else => {}, | 6113 | .DYNSYM, |
| 4559 | std.elf.DT_STRSZ => dynamic_entry[1] = shdr.size, | 6114 | => return, |
| 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 | }, | | |
| 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 | elf.targetStore(&shdr.size, @intCast(size)); | 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,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4595 | .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {}, | 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 | pub fn updateExports( | 6207 | pub fn updateExports( |
| 4600 | elf: *Elf, | 6208 | elf: *Elf, |
| ... | @@ -4633,7 +6241,7 @@ fn updateExportsInner( | ... | @@ -4633,7 +6241,7 @@ fn updateExportsInner( |
| 4633 | const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) { | 6241 | const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) { |
| 4634 | .nav => |nav| .{ | 6242 | .nav => |nav| .{ |
| 4635 | (try elf.navMapIndex(zcu, nav)).symbol(elf), | 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 | .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT }, | 6246 | .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT }, |
| 4639 | }; | 6247 | }; |
| ... | @@ -4735,13 +6343,13 @@ pub fn printNode( | ... | @@ -4735,13 +6343,13 @@ pub fn printNode( |
| 4735 | try w.writeByte(')'); | 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 | .input_section => |isi| { | 6347 | .input_section => |isi| { |
| 4740 | const ii = isi.input(elf); | 6348 | const ii = isi.input(elf); |
| 4741 | try w.print("({f}{f}, {s})", .{ | 6349 | try w.print("({f}{f}, {s})", .{ |
| 4742 | ii.path(elf).fmtEscapeString(), | 6350 | ii.path(elf).fmtEscapeString(), |
| 4743 | fmtMemberString(ii.member(elf)), | 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 | .nav => |nmi| { | 6355 | .nav => |nmi| { |