| ... | @@ -34,6 +34,8 @@ shndx: struct { | ... | @@ -34,6 +34,8 @@ 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, |
| 37 | // These sections are created only as needed, and are initially `.UNDEF`. | 39 | // These sections are created only as needed, and are initially `.UNDEF`. |
| 38 | init_array: Section.Index, | 40 | init_array: Section.Index, |
| 39 | fini_array: Section.Index, | 41 | fini_array: Section.Index, |
| ... | @@ -65,13 +67,23 @@ dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT), | ... | @@ -65,13 +67,23 @@ dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT), |
| 65 | shstrtab: StringTable, | 67 | shstrtab: StringTable, |
| 66 | strtab: StringTable, | 68 | strtab: StringTable, |
| 67 | dynstr: StringTable, | 69 | dynstr: StringTable, |
| 68 | got: struct { | 70 | |
| 69 | len: u32, | 71 | /// Indices map 1--1 to indices into the actual `.got` section. |
| 70 | tlsld: GotIndex, | 72 | /// |
| 71 | plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void), | 73 | /// Value is the output relocation in `.rela.dyn` for the GOT entry. |
| 72 | }, | 74 | got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), |
| 73 | first_plt_reloc: Reloc.Index, | 75 | /// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into |
| 74 | 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 | |
| 75 | needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void), | 87 | needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void), |
| 76 | inputs: std.ArrayList(struct { | 88 | inputs: std.ArrayList(struct { |
| 77 | path: std.Build.Cache.Path, | 89 | path: std.Build.Cache.Path, |
| ... | @@ -81,31 +93,42 @@ inputs: std.ArrayList(struct { | ... | @@ -81,31 +93,42 @@ inputs: std.ArrayList(struct { |
| 81 | input_sections: std.ArrayList(InputSection), | 93 | input_sections: std.ArrayList(InputSection), |
| 82 | input_section_pending_index: u32, | 94 | input_section_pending_index: u32, |
| 83 | navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct { | 95 | navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct { |
| 84 | /// The start index of the contiguous sequence of relocations in this NAV. | | |
| 85 | first_reloc: Reloc.Index, | | |
| 86 | 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, |
| 87 | }), | 101 | }), |
| 88 | uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct { | 102 | uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct { |
| 89 | /// The start index of the contiguous sequence of relocations in this UAV. | | |
| 90 | first_reloc: Reloc.Index, | | |
| 91 | 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. |
| 92 | }), | 107 | }), |
| 93 | lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { | 108 | lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { |
| 94 | map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct { | 109 | map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct { |
| 95 | /// The start index of the contiguous sequence of relocations in this lazy code/data. | | |
| 96 | first_reloc: Reloc.Index, | | |
| 97 | 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, |
| 98 | }), | 115 | }), |
| 99 | pending_index: u32, | 116 | pending_index: u32, |
| 100 | }), | 117 | }), |
| 101 | pending_uavs: std.ArrayList(Node.UavMapIndex), | 118 | pending_uavs: std.ArrayList(Node.UavMapIndex), |
| 102 | relocs: std.ArrayList(Reloc), | 119 | symbol_relocs: std.ArrayList(SymbolReloc), |
| | 120 | got_relocs: std.ArrayList(GotReloc), |
| | 121 | /// Set of relocations which must be re-applied if the size of the TLS segment changes. |
| | 122 | tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), |
| 103 | /// Index matches the index into `shdrs`. | 123 | /// Index matches the index into `shdrs`. |
| 104 | section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), | 124 | section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), |
| 105 | | | |
| 106 | /// 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 |
| 107 | /// 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. |
| 108 | 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, |
| 109 | | 132 | |
| 110 | const_prog_node: std.Progress.Node, | 133 | const_prog_node: std.Progress.Node, |
| 111 | synth_prog_node: std.Progress.Node, | 134 | synth_prog_node: std.Progress.Node, |
| ... | @@ -120,21 +143,21 @@ const Node = union(enum) { | ... | @@ -120,21 +143,21 @@ const Node = union(enum) { |
| 120 | shdr, | 143 | shdr, |
| 121 | /// Cannot contain relocations. | 144 | /// Cannot contain relocations. |
| 122 | segment: u32, | 145 | segment: u32, |
| 123 | /// The section '.plt' may contain relocations via `elf.first_plt_reloc`. | 146 | /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. |
| 124 | /// | 147 | /// |
| 125 | /// The section '.dynamic' may contain relocations via `elf.first_dynamic_reloc`. | 148 | /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`. |
| 126 | /// | 149 | /// |
| 127 | /// Otherwise, cannot contain relocations. | 150 | /// Otherwise, cannot contain relocations. |
| 128 | section: Section.Index, | 151 | section: Section.Index, |
| 129 | /// May contain relocations through the `first_reloc` field in `elf.input_sections`. | 152 | /// May contain relocations. |
| 130 | input_section: InputSection.Index, | 153 | input_section: InputSection.Index, |
| 131 | /// May contain relocations through the `first_reloc` field in `elf.navs`. | 154 | /// May contain relocations. |
| 132 | nav: NavMapIndex, | 155 | nav: NavMapIndex, |
| 133 | /// May contain relocations through the `first_reloc` field in `elf.uavs`. | 156 | /// May contain relocations. |
| 134 | uav: UavMapIndex, | 157 | uav: UavMapIndex, |
| 135 | /// May contain relocations through the `first_reloc` field in `elf.lazy.map`. | 158 | /// May contain relocations. |
| 136 | lazy_code: LazyMapRef.Index(.code), | 159 | lazy_code: LazyMapRef.Index(.code), |
| 137 | /// May contain relocations through the `first_reloc` field in `elf.lazy.map`. | 160 | /// May contain relocations. |
| 138 | lazy_const_data: LazyMapRef.Index(.const_data), | 161 | lazy_const_data: LazyMapRef.Index(.const_data), |
| 139 | | 162 | |
| 140 | pub const InputIndex = enum(u32) { | 163 | pub const InputIndex = enum(u32) { |
| ... | @@ -176,8 +199,11 @@ const Node = union(enum) { | ... | @@ -176,8 +199,11 @@ const Node = union(enum) { |
| 176 | return elf.navs.values()[@intFromEnum(nmi)].lsi; | 199 | return elf.navs.values()[@intFromEnum(nmi)].lsi; |
| 177 | } | 200 | } |
| 178 | | 201 | |
| 179 | fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index { | 202 | fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 180 | 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; |
| 181 | } | 207 | } |
| 182 | }; | 208 | }; |
| 183 | | 209 | |
| ... | @@ -192,8 +218,13 @@ const Node = union(enum) { | ... | @@ -192,8 +218,13 @@ const Node = union(enum) { |
| 192 | return elf.uavs.values()[@intFromEnum(umi)].lsi; | 218 | return elf.uavs.values()[@intFromEnum(umi)].lsi; |
| 193 | } | 219 | } |
| 194 | | 220 | |
| 195 | fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index { | 221 | fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 196 | 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; |
| 197 | } | 228 | } |
| 198 | }; | 229 | }; |
| 199 | | 230 | |
| ... | @@ -217,8 +248,11 @@ const Node = union(enum) { | ... | @@ -217,8 +248,11 @@ const Node = union(enum) { |
| 217 | return lmi.ref().symbol(elf); | 248 | return lmi.ref().symbol(elf); |
| 218 | } | 249 | } |
| 219 | | 250 | |
| 220 | fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index { | 251 | fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index { |
| 221 | 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; |
| 222 | } | 256 | } |
| 223 | }; | 257 | }; |
| 224 | } | 258 | } |
| ... | @@ -230,10 +264,6 @@ const Node = union(enum) { | ... | @@ -230,10 +264,6 @@ const Node = union(enum) { |
| 230 | pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex { | 264 | pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex { |
| 231 | return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi; | 265 | return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi; |
| 232 | } | 266 | } |
| 233 | | | |
| 234 | fn firstReloc(lmr: LazyMapRef, elf: *const Elf) Reloc.Index { | | |
| 235 | return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].first_reloc; | | |
| 236 | } | | |
| 237 | }; | 267 | }; |
| 238 | | 268 | |
| 239 | pub const Known = struct { | 269 | pub const Known = struct { |
| ... | @@ -269,8 +299,10 @@ const InputSection = struct { | ... | @@ -269,8 +299,10 @@ const InputSection = struct { |
| 269 | vaddr: u64, | 299 | vaddr: u64, |
| 270 | /// The node corresponding to this input section. | 300 | /// The node corresponding to this input section. |
| 271 | node: MappedFile.Node.Index, | 301 | node: MappedFile.Node.Index, |
| 272 | /// 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. |
| 273 | 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, |
| 274 | | 306 | |
| 275 | const Index = enum(u32) { | 307 | const Index = enum(u32) { |
| 276 | _, | 308 | _, |
| ... | @@ -304,21 +336,53 @@ const Section = struct { | ... | @@ -304,21 +336,53 @@ const Section = struct { |
| 304 | /// | 336 | /// |
| 305 | /// 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`. |
| 306 | lsi: Symbol.LocalIndex, | 338 | lsi: Symbol.LocalIndex, |
| 307 | rela_shndx: Section.Index, | 339 | rela: union { |
| 308 | 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 | }, |
| 309 | | 367 | |
| 310 | pub const RelIndex = enum(u32) { | 368 | const RelaIndex = enum(u32) { |
| 311 | none, | 369 | none, |
| 312 | _, | 370 | _, |
| 313 | | 371 | |
| 314 | pub fn wrap(i: ?u32) RelIndex { | 372 | const Optional = enum(u32) { |
| 315 | return @enumFromInt((i orelse return .none) + 1); | 373 | none = std.math.maxInt(u32), |
| 316 | } | 374 | _, |
| 317 | pub fn unwrap(ri: RelIndex) ?u32 { | 375 | |
| 318 | return switch (ri) { | 376 | fn unwrap(opt: RelaIndex.Optional) ?RelaIndex { |
| 319 | .none => null, | 377 | return switch (opt) { |
| 320 | _ => @intFromEnum(ri) - 1, | 378 | .none => null, |
| 321 | }; | 379 | _ => @enumFromInt(@intFromEnum(opt)), |
| | 380 | }; |
| | 381 | } |
| | 382 | }; |
| | 383 | |
| | 384 | fn toOptional(i: RelaIndex) RelaIndex.Optional { |
| | 385 | return @enumFromInt(@intFromEnum(i)); |
| 322 | } | 386 | } |
| 323 | }; | 387 | }; |
| 324 | | 388 | |
| ... | @@ -390,9 +454,690 @@ const Section = struct { | ... | @@ -390,9 +454,690 @@ const Section = struct { |
| 390 | inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)), | 454 | inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)), |
| 391 | } | 455 | } |
| 392 | } | 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 | } |
| 393 | }; | 661 | }; |
| 394 | }; | 662 | }; |
| 395 | | 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 | |
| 396 | 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 { |
| 397 | const gpa = elf.base.comp.gpa; | 1142 | const gpa = elf.base.comp.gpa; |
| 398 | | 1143 | |
| ... | @@ -447,8 +1192,10 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe | ... | @@ -447,8 +1192,10 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe |
| 447 | fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { | 1192 | fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { |
| 448 | const gpa = elf.base.comp.gpa; | 1193 | const gpa = elf.base.comp.gpa; |
| 449 | | 1194 | |
| 450 | try elf.got.plt.ensureUnusedCapacity(gpa, len); | 1195 | try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len); |
| 451 | const need_plt_capacity = elf.got.plt.count() + len; | 1196 | |
| | 1197 | try elf.plt.ensureUnusedCapacity(gpa, len); |
| | 1198 | const need_plt_capacity = elf.plt.count() + len; |
| 452 | | 1199 | |
| 453 | switch (elf.ehdrField(.machine)) { | 1200 | switch (elf.ehdrField(.machine)) { |
| 454 | else => |machine| @panic(@tagName(machine)), | 1201 | else => |machine| @panic(@tagName(machine)), |
| ... | @@ -479,21 +1226,25 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { | ... | @@ -479,21 +1226,25 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { |
| 479 | const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor; | 1226 | const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor; |
| 480 | try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size); | 1227 | try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 481 | } | 1228 | } |
| 482 | | 1229 | }, |
| 483 | // Ensure the `.rela.plt` section's node is big enough | 1230 | } |
| 484 | const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx; | 1231 | } |
| 485 | const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) { | 1232 | /// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at |
| 486 | inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity), | 1233 | /// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`. |
| 487 | }; | 1234 | fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool { |
| 488 | _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf); | 1235 | assert(elf.shndx.plt != .UNDEF); |
| 489 | if (rela_plt_cur_size < rela_plt_need_size) { | 1236 | assert(plt_index <= elf.plt.count()); |
| 490 | const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor; | 1237 | // We track which PLT entries are alive based on the relocation entries, since there is a 1-1 |
| 491 | try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size); | 1238 | // mapping between PLT entries and `.rela.plt` entries and the relocation entries already have |
| 492 | } else { | 1239 | // a free-list mechanism. |
| 493 | // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can | 1240 | switch (elf.shdrPtr(elf.shndx.rela_plt)) { |
| 494 | // be updated if we do indeed add a PLT entry. | 1241 | inline else => |rela_shdr, class| { |
| 495 | try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf); | 1242 | const size = elf.targetLoad(&rela_shdr.size); |
| 496 | } | 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); |
| 497 | }, | 1248 | }, |
| 498 | } | 1249 | } |
| 499 | } | 1250 | } |
| ... | @@ -801,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -801,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 801 | if (new_global_ptr.dynsym_index != 0 and | 1552 | if (new_global_ptr.dynsym_index != 0 and |
| 802 | opts.visibility == .DEFAULT and | 1553 | opts.visibility == .DEFAULT and |
| 803 | opts.shndx == .UNDEF and | 1554 | opts.shndx == .UNDEF and |
| 804 | @"type" == .FUNC) | 1555 | (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC)) |
| 805 | { | 1556 | { |
| 806 | // 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. |
| 807 | // 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. |
| 808 | // the PLT entry if we later discover a link inpu which resolves this reference. | | |
| 809 | 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`. |
| 810 | } | 1562 | } |
| 811 | | 1563 | |
| 812 | return .global(opts.name.strtab); | 1564 | return .global(opts.name.strtab); |
| ... | @@ -823,6 +1575,7 @@ fn setGlobalSymbolValue( | ... | @@ -823,6 +1575,7 @@ fn setGlobalSymbolValue( |
| 823 | shndx: Section.Index, | 1575 | shndx: Section.Index, |
| 824 | }, | 1576 | }, |
| 825 | ) void { | 1577 | ) void { |
| | 1578 | assert(new.shndx != .UNDEF); |
| 826 | const old_node = global_ptr.symtab_index.ptr(elf).node; | 1579 | const old_node = global_ptr.symtab_index.ptr(elf).node; |
| 827 | if (old_node != .none) { | 1580 | if (old_node != .none) { |
| 828 | if (global_ptr.next_in_node != .empty) { | 1581 | if (global_ptr.next_in_node != .empty) { |
| ... | @@ -897,7 +1650,40 @@ fn setGlobalSymbolValue( | ... | @@ -897,7 +1650,40 @@ fn setGlobalSymbolValue( |
| 897 | }, | 1650 | }, |
| 898 | }; | 1651 | }; |
| 899 | | 1652 | |
| 900 | 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); |
| 901 | } | 1687 | } |
| 902 | /// 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 |
| 903 | /// 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 |
| ... | @@ -1032,14 +1818,45 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { | ... | @@ -1032,14 +1818,45 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 1032 | if (elf.targetEndian() != native_endian) { | 1818 | if (elf.targetEndian() != native_endian) { |
| 1033 | std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym); | 1819 | std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym); |
| 1034 | } | 1820 | } |
| | 1821 | global_ptr.dynsym_index = 0; |
| 1035 | } | 1822 | } |
| 1036 | }, | 1823 | }, |
| 1037 | } | 1824 | } |
| 1038 | } | 1825 | } |
| 1039 | 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 { |
| 1040 | const target_endian = elf.targetEndian(); | 1827 | const target_endian = elf.targetEndian(); |
| 1041 | const plt_index: u32 = @intCast(elf.got.plt.count()); | 1828 | |
| 1042 | 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 | |
| 1043 | switch (elf.ehdrField(.machine)) { | 1860 | switch (elf.ehdrField(.machine)) { |
| 1044 | else => |machine| @panic(@tagName(machine)), | 1861 | else => |machine| @panic(@tagName(machine)), |
| 1045 | .X86_64 => { | 1862 | .X86_64 => { |
| ... | @@ -1067,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void | ... | @@ -1067,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1067 | }, | 1884 | }, |
| 1068 | }; | 1885 | }; |
| 1069 | | 1886 | |
| 1070 | const got_plt_shndx = elf.shndx.got_plt; | | |
| 1071 | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; | 1887 | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; |
| 1072 | const got_plt_addr = got_plt_addr: switch (elf.shdrPtr(got_plt_shndx)) { | 1888 | switch (elf.shdrPtr(elf.shndx.got_plt)) { |
| 1073 | inline else => |shdr, class| { | 1889 | inline else => |shdr, class| { |
| 1074 | const ent_size = @sizeOf(class.ElfN().Addr); | 1890 | const ent_size = @sizeOf(class.ElfN().Addr); |
| 1075 | 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); |
| 1076 | elf.targetStore(&shdr.size, old_size + ent_size); | 1893 | elf.targetStore(&shdr.size, old_size + ent_size); |
| 1077 | std.mem.writeInt( | 1894 | std.mem.writeInt( |
| 1078 | class.ElfN().Addr, | 1895 | class.ElfN().Addr, |
| ... | @@ -1080,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void | ... | @@ -1080,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1080 | @intCast(plt_addr), | 1897 | @intCast(plt_addr), |
| 1081 | target_endian, | 1898 | target_endian, |
| 1082 | ); | 1899 | ); |
| 1083 | break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size; | | |
| 1084 | }, | 1900 | }, |
| 1085 | }; | 1901 | } |
| 1086 | | 1902 | |
| 1087 | const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni; | 1903 | const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni; |
| 1088 | switch (elf.shdrPtr(elf.shndx.plt_sec)) { | 1904 | switch (elf.shdrPtr(elf.shndx.plt_sec)) { |
| ... | @@ -1105,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void | ... | @@ -1105,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1105 | ); | 1921 | ); |
| 1106 | }, | 1922 | }, |
| 1107 | } | 1923 | } |
| 1108 | | | |
| 1109 | const rela_plt_shndx = got_plt_shndx.get(elf).rela_shndx; | | |
| 1110 | const rela_plt_ni = rela_plt_shndx.get(elf).ni; | | |
| 1111 | switch (elf.shdrPtr(rela_plt_shndx)) { | | |
| 1112 | inline else => |shdr, class| { | | |
| 1113 | const Rela = class.ElfN().Rela; | | |
| 1114 | const rela_size = elf.targetLoad(&shdr.entsize); | | |
| 1115 | const old_size = rela_size * plt_index; | | |
| 1116 | const new_size = old_size + rela_size; | | |
| 1117 | elf.targetStore(&shdr.size, new_size); | | |
| 1118 | const rela: *Rela = @ptrCast(@alignCast( | | |
| 1119 | rela_plt_ni.slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)], | | |
| 1120 | )); | | |
| 1121 | rela.* = .{ | | |
| 1122 | .offset = @intCast(got_plt_addr), | | |
| 1123 | .info = .{ | | |
| 1124 | .type = @intFromEnum(std.elf.R_X86_64.JUMP_SLOT), | | |
| 1125 | .sym = @intCast(dynsym_index), | | |
| 1126 | }, | | |
| 1127 | .addend = 0, | | |
| 1128 | }; | | |
| 1129 | if (target_endian != native_endian) std.mem.byteSwapAllFields(Rela, rela); | | |
| 1130 | }, | | |
| 1131 | } | | |
| 1132 | }, | 1924 | }, |
| 1133 | } | 1925 | } |
| 1134 | } | 1926 | } |
| ... | @@ -1142,7 +1934,7 @@ const Symbol = struct { | ... | @@ -1142,7 +1934,7 @@ const Symbol = struct { |
| 1142 | node: MappedFile.Node.Index, | 1934 | node: MappedFile.Node.Index, |
| 1143 | | 1935 | |
| 1144 | /// The head of a linked list of relocations targeting this symbol. | 1936 | /// The head of a linked list of relocations targeting this symbol. |
| 1145 | first_target_reloc: Reloc.Index, | 1937 | first_target_reloc: SymbolReloc.Index, |
| 1146 | | 1938 | |
| 1147 | const Global = struct { | 1939 | const Global = struct { |
| 1148 | /// The current index of the symtab entry for this global symbol. | 1940 | /// The current index of the symtab entry for this global symbol. |
| ... | @@ -1159,16 +1951,6 @@ const Symbol = struct { | ... | @@ -1159,16 +1951,6 @@ const Symbol = struct { |
| 1159 | /// | 1951 | /// |
| 1160 | /// If `node` is `.none`, this is `.empty`. | 1952 | /// If `node` is `.none`, this is `.empty`. |
| 1161 | prev_in_node: String(.strtab), | 1953 | prev_in_node: String(.strtab), |
| 1162 | | | |
| 1163 | /// Like `Symbol.Index.flushMoved`, but also updates the dynamic symbol table if necessary. | | |
| 1164 | fn flushMoved(g: *const Global, elf: *Elf, value: u64) void { | | |
| 1165 | g.symtab_index.flushMoved(elf, value); | | |
| 1166 | if (g.dynsym_index != 0) { | | |
| 1167 | switch (elf.dynsymPtr(g.dynsym_index)) { | | |
| 1168 | inline else => |sym| elf.targetStore(&sym.value, @intCast(value)), | | |
| 1169 | } | | |
| 1170 | } | | |
| 1171 | } | | |
| 1172 | }; | 1954 | }; |
| 1173 | | 1955 | |
| 1174 | /// 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 |
| ... | @@ -1181,23 +1963,20 @@ const Symbol = struct { | ... | @@ -1181,23 +1963,20 @@ const Symbol = struct { |
| 1181 | null = 0, | 1963 | null = 0, |
| 1182 | _, | 1964 | _, |
| 1183 | | 1965 | |
| 1184 | fn flushMoved(si: Symbol.Index, elf: *Elf, value: u64) void { | | |
| 1185 | switch (elf.symPtr(si)) { | | |
| 1186 | inline else => |sym| elf.targetStore(&sym.value, @intCast(value)), | | |
| 1187 | } | | |
| 1188 | if (elf.ehdrField(.type) != .REL) { | | |
| 1189 | var ri = si.ptr(elf).first_target_reloc; | | |
| 1190 | while (ri != .none) { | | |
| 1191 | const reloc = ri.get(elf); | | |
| 1192 | assert(reloc.target.index(elf) == si); | | |
| 1193 | reloc.apply(elf); | | |
| 1194 | ri = reloc.next; | | |
| 1195 | } | | |
| 1196 | } | | |
| 1197 | } | | |
| 1198 | fn ptr(si: Symbol.Index, elf: *Elf) *Symbol { | 1966 | fn ptr(si: Symbol.Index, elf: *Elf) *Symbol { |
| 1199 | return &elf.symtab.items[@intFromEnum(si)]; | 1967 | return &elf.symtab.items[@intFromEnum(si)]; |
| 1200 | } | 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 | } |
| 1201 | }; | 1980 | }; |
| 1202 | | 1981 | |
| 1203 | /// 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 |
| ... | @@ -1262,6 +2041,44 @@ const Symbol = struct { | ... | @@ -1262,6 +2041,44 @@ const Symbol = struct { |
| 1262 | }; | 2041 | }; |
| 1263 | } | 2042 | } |
| 1264 | | 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 | |
| 1265 | /// 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 |
| 1266 | /// some point due to a call to `flushMoved`. | 2083 | /// some point due to a call to `flushMoved`. |
| 1267 | fn hasMoved(s: Symbol.Id, elf: *Elf) bool { | 2084 | fn hasMoved(s: Symbol.Id, elf: *Elf) bool { |
| ... | @@ -1328,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId { | ... | @@ -1328,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId { |
| 1328 | .type = sym_type, | 2145 | .type = sym_type, |
| 1329 | .shndx = shndx, | 2146 | .shndx = shndx, |
| 1330 | }), | 2147 | }), |
| 1331 | .first_reloc = .none, | 2148 | .first_symbol_reloc = .none, |
| | 2149 | .first_got_reloc = .none, |
| 1332 | }; | 2150 | }; |
| 1333 | elf.nodes.appendAssumeCapacity(switch (lazy.kind) { | 2151 | elf.nodes.appendAssumeCapacity(switch (lazy.kind) { |
| 1334 | .code => .{ .lazy_code = @enumFromInt(gop.index) }, | 2152 | .code => .{ .lazy_code = @enumFromInt(gop.index) }, |
| ... | @@ -1377,7 +2195,7 @@ pub fn addReloc( | ... | @@ -1377,7 +2195,7 @@ pub fn addReloc( |
| 1377 | offset: u64, | 2195 | offset: u64, |
| 1378 | target: link.File.SymbolId, | 2196 | target: link.File.SymbolId, |
| 1379 | addend: i64, | 2197 | addend: i64, |
| 1380 | @"type": Reloc.Type, | 2198 | @"type": MachineRelocType, |
| 1381 | ) !void { | 2199 | ) !void { |
| 1382 | const node: MappedFile.Node.Index = Node.fromAtom(atom); | 2200 | const node: MappedFile.Node.Index = Node.fromAtom(atom); |
| 1383 | try elf.ensureUnusedRelocCapacity(node, 1); | 2201 | try elf.ensureUnusedRelocCapacity(node, 1); |
| ... | @@ -1532,7 +2350,6 @@ const StringTable = struct { | ... | @@ -1532,7 +2350,6 @@ const StringTable = struct { |
| 1532 | .{ .slice = slice_const }, | 2350 | .{ .slice = slice_const }, |
| 1533 | ); | 2351 | ); |
| 1534 | if (gop.found_existing) return gop.key_ptr.*; | 2352 | if (gop.found_existing) return gop.key_ptr.*; |
| 1535 | try ni.resized(gpa, &elf.mf); | | |
| 1536 | const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) { | 2353 | const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) { |
| 1537 | inline else => |shdr| { | 2354 | inline else => |shdr| { |
| 1538 | const old_size: u32 = @intCast(elf.targetLoad(&shdr.size)); | 2355 | const old_size: u32 = @intCast(elf.targetLoad(&shdr.size)); |
| ... | @@ -1541,6 +2358,9 @@ const StringTable = struct { | ... | @@ -1541,6 +2358,9 @@ const StringTable = struct { |
| 1541 | break :size .{ old_size, new_size }; | 2358 | break :size .{ old_size, new_size }; |
| 1542 | }, | 2359 | }, |
| 1543 | }; | 2360 | }; |
| | 2361 | if (shndx == elf.shndx.dynstr) { |
| | 2362 | elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size); |
| | 2363 | } |
| 1544 | _, const node_size = ni.location(&elf.mf).resolve(&elf.mf); | 2364 | _, const node_size = ni.location(&elf.mf).resolve(&elf.mf); |
| 1545 | if (new_size > node_size) | 2365 | if (new_size > node_size) |
| 1546 | try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor); | 2366 | try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor); |
| ... | @@ -1569,274 +2389,6 @@ const GotIndex = enum(u32) { | ... | @@ -1569,274 +2389,6 @@ const GotIndex = enum(u32) { |
| 1569 | } | 2389 | } |
| 1570 | }; | 2390 | }; |
| 1571 | | 2391 | |
| 1572 | const Reloc = extern struct { | | |
| 1573 | type: Reloc.Type, | | |
| 1574 | prev: Reloc.Index, | | |
| 1575 | next: Reloc.Index, | | |
| 1576 | node: MappedFile.Node.Index, | | |
| 1577 | target: Symbol.Id, | | |
| 1578 | index: Section.RelIndex, | | |
| 1579 | offset: u64, | | |
| 1580 | addend: i64, | | |
| 1581 | | | |
| 1582 | pub const Type = extern union { | | |
| 1583 | X86_64: std.elf.R_X86_64, | | |
| 1584 | AARCH64: std.elf.R_AARCH64, | | |
| 1585 | RISCV: std.elf.R_RISCV, | | |
| 1586 | PPC64: std.elf.R_PPC64, | | |
| 1587 | | | |
| 1588 | pub fn none(elf: *Elf) Reloc.Type { | | |
| 1589 | return switch (elf.ehdrField(.machine)) { | | |
| 1590 | else => unreachable, | | |
| 1591 | .AARCH64 => .{ .AARCH64 = .NONE }, | | |
| 1592 | .PPC64 => .{ .PPC64 = .NONE }, | | |
| 1593 | .RISCV => .{ .RISCV = .NONE }, | | |
| 1594 | .X86_64 => .{ .X86_64 = .NONE }, | | |
| 1595 | }; | | |
| 1596 | } | | |
| 1597 | pub fn absAddr(elf: *Elf) Reloc.Type { | | |
| 1598 | return switch (elf.ehdrField(.machine)) { | | |
| 1599 | else => unreachable, | | |
| 1600 | .AARCH64 => .{ .AARCH64 = .ABS64 }, | | |
| 1601 | .PPC64 => .{ .PPC64 = .ADDR64 }, | | |
| 1602 | .RISCV => .{ .RISCV = .@"64" }, | | |
| 1603 | .X86_64 => .{ .X86_64 = .@"64" }, | | |
| 1604 | }; | | |
| 1605 | } | | |
| 1606 | pub fn sizeAddr(elf: *Elf) Reloc.Type { | | |
| 1607 | return switch (elf.ehdrField(.machine)) { | | |
| 1608 | else => unreachable, | | |
| 1609 | .X86_64 => .{ .X86_64 = .SIZE64 }, | | |
| 1610 | }; | | |
| 1611 | } | | |
| 1612 | | | |
| 1613 | pub fn wrap(int: u32, elf: *Elf) Reloc.Type { | | |
| 1614 | return switch (elf.ehdrField(.machine)) { | | |
| 1615 | else => unreachable, | | |
| 1616 | inline .AARCH64, | | |
| 1617 | .PPC64, | | |
| 1618 | .RISCV, | | |
| 1619 | .X86_64, | | |
| 1620 | => |machine| @unionInit(Reloc.Type, @tagName(machine), @enumFromInt(int)), | | |
| 1621 | }; | | |
| 1622 | } | | |
| 1623 | pub fn unwrap(rt: Reloc.Type, elf: *Elf) u32 { | | |
| 1624 | return switch (elf.ehdrField(.machine)) { | | |
| 1625 | else => unreachable, | | |
| 1626 | inline .AARCH64, | | |
| 1627 | .PPC64, | | |
| 1628 | .RISCV, | | |
| 1629 | .X86_64, | | |
| 1630 | => |machine| @intFromEnum(@field(rt, @tagName(machine))), | | |
| 1631 | }; | | |
| 1632 | } | | |
| 1633 | }; | | |
| 1634 | | | |
| 1635 | pub const Index = enum(u32) { | | |
| 1636 | none = std.math.maxInt(u32), | | |
| 1637 | _, | | |
| 1638 | | | |
| 1639 | pub fn get(si: Reloc.Index, elf: *Elf) *Reloc { | | |
| 1640 | return &elf.relocs.items[@intFromEnum(si)]; | | |
| 1641 | } | | |
| 1642 | }; | | |
| 1643 | | | |
| 1644 | pub fn apply(reloc: *const Reloc, elf: *Elf) void { | | |
| 1645 | assert(elf.ehdrField(.type) != .REL); | | |
| 1646 | assert(reloc.node != .none); | | |
| 1647 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { | | |
| 1648 | // There's no point applying the relocation now, because it will be re-applied by | | |
| 1649 | // `flushMoved` at some point anyway. | | |
| 1650 | return; | | |
| 1651 | } | | |
| 1652 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { | | |
| 1653 | .file => unreachable, | | |
| 1654 | .ehdr => unreachable, | | |
| 1655 | .shdr => unreachable, | | |
| 1656 | .segment => unreachable, | | |
| 1657 | .section => |shndx| shndx.vaddr(elf), | | |
| 1658 | .input_section => |isi| isi.ptrConst(elf).vaddr, | | |
| 1659 | inline .nav, | | |
| 1660 | .uav, | | |
| 1661 | .lazy_code, | | |
| 1662 | .lazy_const_data, | | |
| 1663 | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), | | |
| 1664 | }; | | |
| 1665 | const dest_vaddr = node_vaddr + reloc.offset; | | |
| 1666 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; | | |
| 1667 | const target_endian = elf.targetEndian(); | | |
| 1668 | switch (elf.symPtr(reloc.target.index(elf))) { | | |
| 1669 | inline else => |target_sym, class| { | | |
| 1670 | const target_value = elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend)); | | |
| 1671 | switch (elf.ehdrField(.machine)) { | | |
| 1672 | else => |machine| @panic(@tagName(machine)), | | |
| 1673 | .X86_64 => switch (reloc.type.X86_64) { | | |
| 1674 | else => |kind| @panic(@tagName(kind)), | | |
| 1675 | .@"64" => std.mem.writeInt( | | |
| 1676 | u64, | | |
| 1677 | dest_slice[0..8], | | |
| 1678 | target_value, | | |
| 1679 | target_endian, | | |
| 1680 | ), | | |
| 1681 | .PC32 => std.mem.writeInt( | | |
| 1682 | i32, | | |
| 1683 | dest_slice[0..4], | | |
| 1684 | @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))), | | |
| 1685 | target_endian, | | |
| 1686 | ), | | |
| 1687 | .PLT32 => std.mem.writeInt( | | |
| 1688 | i32, | | |
| 1689 | dest_slice[0..4], | | |
| 1690 | @intCast(@as(i64, @bitCast(if (elf.got.plt.getIndex(reloc.target)) |plt_index| | | |
| 1691 | elf.targetLoad(&@field( | | |
| 1692 | elf.shdrPtr(elf.shndx.plt_sec), | | |
| 1693 | @tagName(class), | | |
| 1694 | ).addr) +% 16 * plt_index +% | | |
| 1695 | @as(u64, @bitCast(reloc.addend)) -% dest_vaddr | | |
| 1696 | else | | |
| 1697 | target_value -% dest_vaddr))), | | |
| 1698 | target_endian, | | |
| 1699 | ), | | |
| 1700 | .@"32" => std.mem.writeInt( | | |
| 1701 | u32, | | |
| 1702 | dest_slice[0..4], | | |
| 1703 | @intCast(target_value), | | |
| 1704 | target_endian, | | |
| 1705 | ), | | |
| 1706 | .@"32S" => std.mem.writeInt( | | |
| 1707 | i32, | | |
| 1708 | dest_slice[0..4], | | |
| 1709 | @intCast(@as(i64, @bitCast(target_value))), | | |
| 1710 | target_endian, | | |
| 1711 | ), | | |
| 1712 | .TLSLD => std.mem.writeInt( | | |
| 1713 | i32, | | |
| 1714 | dest_slice[0..4], | | |
| 1715 | @intCast(@as(i64, @bitCast( | | |
| 1716 | elf.shndx.got.vaddr(elf) +% | | |
| 1717 | @as(u64, @bitCast(reloc.addend)) +% | | |
| 1718 | @as(u64, 8) * elf.got.tlsld.unwrap().? -% | | |
| 1719 | dest_vaddr, | | |
| 1720 | ))), | | |
| 1721 | target_endian, | | |
| 1722 | ), | | |
| 1723 | .DTPOFF32 => std.mem.writeInt( | | |
| 1724 | i32, | | |
| 1725 | dest_slice[0..4], | | |
| 1726 | @intCast(@as(i64, @bitCast(target_value))), | | |
| 1727 | target_endian, | | |
| 1728 | ), | | |
| 1729 | .TPOFF32 => { | | |
| 1730 | const phdr = @field(elf.phdrSlice(), @tagName(class)); | | |
| 1731 | const ph = &phdr[elf.getNode(elf.ni.tls).segment]; | | |
| 1732 | assert(elf.targetLoad(&ph.type) == .TLS); | | |
| 1733 | std.mem.writeInt( | | |
| 1734 | i32, | | |
| 1735 | dest_slice[0..4], | | |
| 1736 | @intCast(@as(i64, @bitCast(target_value -% elf.targetLoad(&ph.memsz)))), | | |
| 1737 | target_endian, | | |
| 1738 | ); | | |
| 1739 | }, | | |
| 1740 | .SIZE32 => std.mem.writeInt( | | |
| 1741 | u32, | | |
| 1742 | dest_slice[0..4], | | |
| 1743 | @intCast( | | |
| 1744 | elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)), | | |
| 1745 | ), | | |
| 1746 | target_endian, | | |
| 1747 | ), | | |
| 1748 | .SIZE64 => std.mem.writeInt( | | |
| 1749 | u64, | | |
| 1750 | dest_slice[0..8], | | |
| 1751 | elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)), | | |
| 1752 | target_endian, | | |
| 1753 | ), | | |
| 1754 | }, | | |
| 1755 | } | | |
| 1756 | }, | | |
| 1757 | } | | |
| 1758 | } | | |
| 1759 | | | |
| 1760 | pub fn delete(reloc: *Reloc, elf: *Elf) void { | | |
| 1761 | switch (reloc.prev) { | | |
| 1762 | .none => { | | |
| 1763 | const target_ptr = reloc.target.index(elf).ptr(elf); | | |
| 1764 | assert(target_ptr.first_target_reloc.get(elf) == reloc); | | |
| 1765 | target_ptr.first_target_reloc = reloc.next; | | |
| 1766 | }, | | |
| 1767 | else => |prev| prev.get(elf).next = reloc.next, | | |
| 1768 | } | | |
| 1769 | switch (reloc.next) { | | |
| 1770 | .none => {}, | | |
| 1771 | else => |next| next.get(elf).prev = reloc.prev, | | |
| 1772 | } | | |
| 1773 | switch (elf.ehdrField(.type)) { | | |
| 1774 | .NONE, .CORE, _ => unreachable, | | |
| 1775 | .REL => { | | |
| 1776 | const sh = elf.getNodeShndx(reloc.node).get(elf); | | |
| 1777 | switch (elf.shdrPtr(sh.rela_shndx)) { | | |
| 1778 | inline else => |shdr, class| { | | |
| 1779 | const Rela = class.ElfN().Rela; | | |
| 1780 | const ent_size = elf.targetLoad(&shdr.entsize); | | |
| 1781 | const start = ent_size * reloc.index.unwrap().?; | | |
| 1782 | const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); | | |
| 1783 | const rela: *Rela = @ptrCast(@alignCast( | | |
| 1784 | rela_slice[@intCast(start)..][0..@intCast(ent_size)], | | |
| 1785 | )); | | |
| 1786 | rela.* = .{ | | |
| 1787 | .offset = @intFromEnum(sh.rela_free), | | |
| 1788 | .info = .{ | | |
| 1789 | .type = @intCast(Reloc.Type.none(elf).unwrap(elf)), | | |
| 1790 | .sym = 0, | | |
| 1791 | }, | | |
| 1792 | .addend = 0, | | |
| 1793 | }; | | |
| 1794 | }, | | |
| 1795 | } | | |
| 1796 | sh.rela_free = reloc.index; | | |
| 1797 | }, | | |
| 1798 | .EXEC, .DYN => assert(reloc.index == .none), | | |
| 1799 | } | | |
| 1800 | reloc.* = undefined; | | |
| 1801 | } | | |
| 1802 | | | |
| 1803 | fn updateTargetIndex(reloc: *const Reloc, elf: *Elf) void { | | |
| 1804 | assert(elf.ehdrField(.type) == .REL); | | |
| 1805 | const sh = elf.getNodeShndx(reloc.node).get(elf); | | |
| 1806 | switch (elf.shdrPtr(sh.rela_shndx)) { | | |
| 1807 | inline else => |shdr, class| { | | |
| 1808 | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); | | |
| 1809 | const size = elf.targetLoad(&shdr.size); | | |
| 1810 | const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); | | |
| 1811 | const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)])); | | |
| 1812 | elf.targetStore(&rela_slice[reloc.index.unwrap().?].info, .{ | | |
| 1813 | .type = @intCast(reloc.type.unwrap(elf)), | | |
| 1814 | .sym = @intCast(@intFromEnum(reloc.target.index(elf))), | | |
| 1815 | }); | | |
| 1816 | }, | | |
| 1817 | } | | |
| 1818 | } | | |
| 1819 | | | |
| 1820 | fn updateNodeOffset(reloc: *const Reloc, elf: *Elf, node_offset: u64) void { | | |
| 1821 | assert(elf.ehdrField(.type) == .REL); | | |
| 1822 | const total_offset = node_offset + reloc.offset; | | |
| 1823 | const sh = elf.getNodeShndx(reloc.node).get(elf); | | |
| 1824 | switch (elf.shdrPtr(sh.rela_shndx)) { | | |
| 1825 | inline else => |shdr, class| { | | |
| 1826 | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); | | |
| 1827 | const size = elf.targetLoad(&shdr.size); | | |
| 1828 | const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); | | |
| 1829 | const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)])); | | |
| 1830 | elf.targetStore(&rela_slice[reloc.index.unwrap().?].offset, @intCast(total_offset)); | | |
| 1831 | }, | | |
| 1832 | } | | |
| 1833 | } | | |
| 1834 | | | |
| 1835 | comptime { | | |
| 1836 | if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40); | | |
| 1837 | } | | |
| 1838 | }; | | |
| 1839 | | | |
| 1840 | pub fn open( | 2392 | pub fn open( |
| 1841 | arena: std.mem.Allocator, | 2393 | arena: std.mem.Allocator, |
| 1842 | comp: *Compilation, | 2394 | comp: *Compilation, |
| ... | @@ -1941,6 +2493,8 @@ fn create( | ... | @@ -1941,6 +2493,8 @@ fn create( |
| 1941 | .dynstr = .UNDEF, | 2493 | .dynstr = .UNDEF, |
| 1942 | .dynamic = .UNDEF, | 2494 | .dynamic = .UNDEF, |
| 1943 | .tdata = .UNDEF, | 2495 | .tdata = .UNDEF, |
| | 2496 | .rela_dyn = .UNDEF, |
| | 2497 | .rela_plt = .UNDEF, |
| 1944 | .init_array = .UNDEF, | 2498 | .init_array = .UNDEF, |
| 1945 | .fini_array = .UNDEF, | 2499 | .fini_array = .UNDEF, |
| 1946 | .preinit_array = .UNDEF, | 2500 | .preinit_array = .UNDEF, |
| ... | @@ -1957,13 +2511,10 @@ fn create( | ... | @@ -1957,13 +2511,10 @@ fn create( |
| 1957 | .shstrtab = .{ .map = .empty }, | 2511 | .shstrtab = .{ .map = .empty }, |
| 1958 | .strtab = .{ .map = .empty }, | 2512 | .strtab = .{ .map = .empty }, |
| 1959 | .dynstr = .{ .map = .empty }, | 2513 | .dynstr = .{ .map = .empty }, |
| 1960 | .got = .{ | 2514 | .got = .empty, |
| 1961 | .len = 0, | 2515 | .plt = .empty, |
| 1962 | .tlsld = .none, | 2516 | .plt_first_symbol_reloc = .none, |
| 1963 | .plt = .empty, | 2517 | .dynamic_first_symbol_reloc = .none, |
| 1964 | }, | | |
| 1965 | .first_plt_reloc = .none, | | |
| 1966 | .first_dynamic_reloc = .none, | | |
| 1967 | .needed = .empty, | 2518 | .needed = .empty, |
| 1968 | .inputs = .empty, | 2519 | .inputs = .empty, |
| 1969 | .input_sections = .empty, | 2520 | .input_sections = .empty, |
| ... | @@ -1975,12 +2526,15 @@ fn create( | ... | @@ -1975,12 +2526,15 @@ fn create( |
| 1975 | .pending_index = 0, | 2526 | .pending_index = 0, |
| 1976 | }), | 2527 | }), |
| 1977 | .pending_uavs = .empty, | 2528 | .pending_uavs = .empty, |
| 1978 | .relocs = .empty, | 2529 | .symbol_relocs = .empty, |
| | 2530 | .got_relocs = .empty, |
| | 2531 | .tls_size_symbol_relocs = .empty, |
| 1979 | .section_by_name = .empty, | 2532 | .section_by_name = .empty, |
| 1980 | .changed_symtab_index = .empty, | 2533 | .changed_symtab_index = .empty, |
| 1981 | .const_prog_node = .none, | 2534 | .const_prog_node = .none, |
| 1982 | .synth_prog_node = .none, | 2535 | .synth_prog_node = .none, |
| 1983 | .input_prog_node = .none, | 2536 | .input_prog_node = .none, |
| | 2537 | .textrel_count = 0, |
| 1984 | }; | 2538 | }; |
| 1985 | errdefer elf.deinit(); | 2539 | errdefer elf.deinit(); |
| 1986 | | 2540 | |
| ... | @@ -2004,7 +2558,8 @@ pub fn deinit(elf: *Elf) void { | ... | @@ -2004,7 +2558,8 @@ pub fn deinit(elf: *Elf) void { |
| 2004 | elf.shstrtab.map.deinit(gpa); | 2558 | elf.shstrtab.map.deinit(gpa); |
| 2005 | elf.strtab.map.deinit(gpa); | 2559 | elf.strtab.map.deinit(gpa); |
| 2006 | elf.dynstr.map.deinit(gpa); | 2560 | elf.dynstr.map.deinit(gpa); |
| 2007 | elf.got.plt.deinit(gpa); | 2561 | elf.got.deinit(gpa); |
| | 2562 | elf.plt.deinit(gpa); |
| 2008 | elf.needed.deinit(gpa); | 2563 | elf.needed.deinit(gpa); |
| 2009 | 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); |
| 2010 | elf.inputs.deinit(gpa); | 2565 | elf.inputs.deinit(gpa); |
| ... | @@ -2013,7 +2568,9 @@ pub fn deinit(elf: *Elf) void { | ... | @@ -2013,7 +2568,9 @@ pub fn deinit(elf: *Elf) void { |
| 2013 | elf.uavs.deinit(gpa); | 2568 | elf.uavs.deinit(gpa); |
| 2014 | for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); | 2569 | for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); |
| 2015 | elf.pending_uavs.deinit(gpa); | 2570 | elf.pending_uavs.deinit(gpa); |
| 2016 | elf.relocs.deinit(gpa); | 2571 | elf.symbol_relocs.deinit(gpa); |
| | 2572 | elf.got_relocs.deinit(gpa); |
| | 2573 | elf.tls_size_symbol_relocs.deinit(gpa); |
| 2017 | elf.section_by_name.deinit(gpa); | 2574 | elf.section_by_name.deinit(gpa); |
| 2018 | elf.changed_symtab_index.deinit(gpa); | 2575 | elf.changed_symtab_index.deinit(gpa); |
| 2019 | elf.* = undefined; | 2576 | elf.* = undefined; |
| ... | @@ -2323,7 +2880,7 @@ fn initHeaders( | ... | @@ -2323,7 +2880,7 @@ fn initHeaders( |
| 2323 | .entsize = 0, | 2880 | .entsize = 0, |
| 2324 | }; | 2881 | }; |
| 2325 | 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); |
| 2326 | elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela_shndx = .UNDEF, .rela_free = .none }); | 2883 | elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela = .{ .shndx = .UNDEF } }); |
| 2327 | | 2884 | |
| 2328 | elf.symtab.addOneAssumeCapacity().* = .{ | 2885 | elf.symtab.addOneAssumeCapacity().* = .{ |
| 2329 | .node = .none, | 2886 | .node = .none, |
| ... | @@ -2398,8 +2955,15 @@ fn initHeaders( | ... | @@ -2398,8 +2955,15 @@ fn initHeaders( |
| 2398 | if (@"type" != .REL) { | 2955 | if (@"type" != .REL) { |
| 2399 | elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{ | 2956 | elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{ |
| 2400 | .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 | }, |
| 2401 | .flags = .{ .WRITE = true, .ALLOC = true }, | 2964 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 2402 | .addralign = addr_align, | 2965 | .addralign = addr_align, |
| | 2966 | .entsize = @intCast(addr_align.toByteUnits()), |
| 2403 | }); | 2967 | }); |
| 2404 | elf.shndx.got_plt = try elf.addSection( | 2968 | elf.shndx.got_plt = try elf.addSection( |
| 2405 | 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, |
| ... | @@ -2413,6 +2977,7 @@ fn initHeaders( | ... | @@ -2413,6 +2977,7 @@ fn initHeaders( |
| 2413 | .X86_64 => 3 * 8, | 2977 | .X86_64 => 3 * 8, |
| 2414 | }, | 2978 | }, |
| 2415 | .addralign = addr_align, | 2979 | .addralign = addr_align, |
| | 2980 | .entsize = @intCast(addr_align.toByteUnits()), |
| 2416 | }, | 2981 | }, |
| 2417 | ); | 2982 | ); |
| 2418 | 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 = |
| ... | @@ -2508,7 +3073,7 @@ fn initHeaders( | ... | @@ -2508,7 +3073,7 @@ fn initHeaders( |
| 2508 | .NONE, _ => unreachable, | 3073 | .NONE, _ => unreachable, |
| 2509 | inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela), | 3074 | inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela), |
| 2510 | }; | 3075 | }; |
| 2511 | elf.shndx.got.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{ | 3076 | elf.shndx.rela_dyn = try elf.addSection(elf.ni.rodata, .{ |
| 2512 | .name = ".rela.dyn", | 3077 | .name = ".rela.dyn", |
| 2513 | .type = .RELA, | 3078 | .type = .RELA, |
| 2514 | .flags = .{ .ALLOC = true }, | 3079 | .flags = .{ .ALLOC = true }, |
| ... | @@ -2517,13 +3082,12 @@ fn initHeaders( | ... | @@ -2517,13 +3082,12 @@ fn initHeaders( |
| 2517 | .entsize = rela_size, | 3082 | .entsize = rela_size, |
| 2518 | .node_align = elf.mf.flags.block_size, | 3083 | .node_align = elf.mf.flags.block_size, |
| 2519 | }); | 3084 | }); |
| 2520 | const got_plt_shndx = elf.shndx.got_plt; | 3085 | elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{ |
| 2521 | got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{ | | |
| 2522 | .name = ".rela.plt", | 3086 | .name = ".rela.plt", |
| 2523 | .type = .RELA, | 3087 | .type = .RELA, |
| 2524 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, | 3088 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, |
| 2525 | .link = elf.shndx.dynsym.toSection().?, | 3089 | .link = elf.shndx.dynsym.toSection().?, |
| 2526 | .info = got_plt_shndx.toSection().?, | 3090 | .info = elf.shndx.got_plt.toSection().?, |
| 2527 | .addralign = addr_align, | 3091 | .addralign = addr_align, |
| 2528 | .entsize = rela_size, | 3092 | .entsize = rela_size, |
| 2529 | .node_align = elf.mf.flags.block_size, | 3093 | .node_align = elf.mf.flags.block_size, |
| ... | @@ -2546,7 +3110,7 @@ fn initHeaders( | ... | @@ -2546,7 +3110,7 @@ fn initHeaders( |
| 2546 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip) | 3110 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip) |
| 2547 | 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax) | 3111 | 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax) |
| 2548 | }); | 3112 | }); |
| 2549 | elf.first_plt_reloc = @enumFromInt(elf.relocs.items.len); | 3113 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 2550 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); | 3114 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); |
| 2551 | elf.addRelocAssumeCapacity( | 3115 | elf.addRelocAssumeCapacity( |
| 2552 | plt_ni, | 3116 | plt_ni, |
| ... | @@ -2575,6 +3139,26 @@ fn initHeaders( | ... | @@ -2575,6 +3139,26 @@ fn initHeaders( |
| 2575 | elf.phdrs.items[tls_phndx] = elf.ni.tls; | 3139 | elf.phdrs.items[tls_phndx] = elf.ni.tls; |
| 2576 | } | 3140 | } |
| 2577 | | 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 | |
| 2578 | // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/ | 3162 | // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/ |
| 2579 | // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection` | 3163 | // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection` |
| 2580 | // when needed (it seems to be legal to leave those undefined if the section doesn't exist). | 3164 | // when needed (it seems to be legal to leave those undefined if the section doesn't exist). |
| ... | @@ -2679,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node { | ... | @@ -2679,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node { |
| 2679 | return elf.nodes.get(@intFromEnum(ni)); | 3263 | return elf.nodes.get(@intFromEnum(ni)); |
| 2680 | } | 3264 | } |
| 2681 | /// 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. |
| 2682 | fn getNodeShndx(elf: *Elf, ni: MappedFile.Node.Index) Section.Index { | 3266 | fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { |
| 2683 | return switch (elf.getNode(ni)) { | 3267 | return switch (elf.getNode(ni)) { |
| 2684 | .file => unreachable, | 3268 | .file => unreachable, |
| 2685 | .ehdr => unreachable, | 3269 | .ehdr => unreachable, |
| ... | @@ -2717,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { | ... | @@ -2717,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 2717 | /// 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 |
| 2718 | /// the special-case sections '.plt' and '.dynamic'. | 3302 | /// the special-case sections '.plt' and '.dynamic'. |
| 2719 | fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { | 3303 | fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 2720 | 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)) { |
| 2721 | .file => unreachable, // cannot contain relocs | 3305 | .file => unreachable, // cannot contain relocs |
| 2722 | .ehdr => unreachable, // cannot contain relocs | 3306 | .ehdr => unreachable, // cannot contain relocs |
| 2723 | .shdr => unreachable, // cannot contain relocs | 3307 | .shdr => unreachable, // cannot contain relocs |
| 2724 | .segment => unreachable, // cannot contain relocs | 3308 | .segment => unreachable, // cannot contain relocs |
| 2725 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) | 3309 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) |
| 2726 | .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc, | 3310 | .input_section => |isi| .{ |
| 2727 | .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc, | 3311 | &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc, |
| 2728 | .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc, | 3312 | &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc, |
| 2729 | 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 | }, |
| 2730 | }; | 3326 | }; |
| 2731 | if (first_reloc_ptr.* != .none) { | 3327 | |
| 2732 | 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| { |
| 2733 | if (reloc.node != ni) break; | 3333 | if (reloc.node != ni) break; |
| 2734 | reloc.delete(elf); | 3334 | reloc.delete(elf, @enumFromInt(index)); |
| 2735 | } | 3335 | } |
| 2736 | } | 3336 | } |
| 2737 | first_reloc_ptr.* = @enumFromInt(elf.relocs.items.len); | 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 | } |
| | 3345 | } |
| | 3346 | ptr.* = @enumFromInt(elf.got_relocs.items.len); |
| | 3347 | } |
| 2738 | } | 3348 | } |
| 2739 | | 3349 | |
| 2740 | /// 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 |
| 2741 | /// 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. |
| 2742 | /// this means applying the relocations. | | |
| 2743 | fn flushMovedNodeRelocs( | 3352 | fn flushMovedNodeRelocs( |
| 2744 | elf: *Elf, | 3353 | elf: *Elf, |
| 2745 | node: MappedFile.Node.Index, | 3354 | node: MappedFile.Node.Index, |
| 2746 | node_vaddr: u64, | 3355 | node_vaddr: u64, |
| 2747 | first_reloc: Reloc.Index, | 3356 | first_symbol_reloc: SymbolReloc.Index, |
| | 3357 | first_got_reloc: GotReloc.Index, |
| 2748 | ) void { | 3358 | ) void { |
| 2749 | if (first_reloc == .none) return; | 3359 | if (first_symbol_reloc != .none) { |
| 2750 | switch (elf.ehdrField(.type)) { | 3360 | for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| { |
| 2751 | .NONE, .CORE, _ => unreachable, | 3361 | if (reloc.node != node) break; |
| 2752 | .REL => { | 3362 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 2753 | // 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 |
| 2754 | // 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. |
| 2755 | for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| { | 3365 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 2756 | if (reloc.node != node) break; | 3366 | } else { |
| 2757 | reloc.updateNodeOffset(elf, node_vaddr); | 3367 | // We've applied this relocation ourselves! Just re-apply it now. |
| 2758 | } | | |
| 2759 | }, | | |
| 2760 | .EXEC, .DYN => { | | |
| 2761 | // For an ELF module, we just need to apply relocations. | | |
| 2762 | for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| { | | |
| 2763 | if (reloc.node != node) break; | | |
| 2764 | reloc.apply(elf); | 3368 | reloc.apply(elf); |
| 2765 | } | 3369 | } |
| 2766 | // TODO: once we're emitting runtime relocation entries, we need to update their offsets | 3370 | } |
| 2767 | // too, like the logic for relocatables above. | 3371 | } |
| 2768 | }, | 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 | } |
| 2769 | } | 3378 | } |
| 2770 | } | 3379 | } |
| 2771 | | 3380 | |
| ... | @@ -3109,7 +3718,8 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM | ... | @@ -3109,7 +3718,8 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM |
| 3109 | .type = elf.navType(nav.resolved.?), | 3718 | .type = elf.navType(nav.resolved.?), |
| 3110 | .shndx = shndx, | 3719 | .shndx = shndx, |
| 3111 | }), | 3720 | }), |
| 3112 | .first_reloc = .none, | 3721 | .first_symbol_reloc = .none, |
| | 3722 | .first_got_reloc = .none, |
| 3113 | }; | 3723 | }; |
| 3114 | elf.nodes.appendAssumeCapacity(.{ .nav = nmi }); | 3724 | elf.nodes.appendAssumeCapacity(.{ .nav = nmi }); |
| 3115 | } | 3725 | } |
| ... | @@ -3158,7 +3768,7 @@ fn uavMapIndex( | ... | @@ -3158,7 +3768,7 @@ fn uavMapIndex( |
| 3158 | .type = .OBJECT, | 3768 | .type = .OBJECT, |
| 3159 | .shndx = shndx, | 3769 | .shndx = shndx, |
| 3160 | }), | 3770 | }), |
| 3161 | .first_reloc = .none, | 3771 | .first_symbol_reloc = .none, |
| 3162 | }; | 3772 | }; |
| 3163 | elf.nodes.appendAssumeCapacity(.{ .uav = umi }); | 3773 | elf.nodes.appendAssumeCapacity(.{ .uav = umi }); |
| 3164 | elf.const_prog_node.increaseEstimatedTotalItems(1); | 3774 | elf.const_prog_node.increaseEstimatedTotalItems(1); |
| ... | @@ -3447,10 +4057,11 @@ fn loadObject( | ... | @@ -3447,10 +4057,11 @@ fn loadObject( |
| 3447 | switch (elf.shdrPtr(shndx.*)) { | 4057 | switch (elf.shdrPtr(shndx.*)) { |
| 3448 | inline else => |shdr| { | 4058 | inline else => |shdr| { |
| 3449 | const old_size = elf.targetLoad(&shdr.size); | 4059 | const old_size = elf.targetLoad(&shdr.size); |
| 3450 | elf.targetStore(&shdr.size, @intCast(old_size + section.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); |
| 3451 | }, | 4063 | }, |
| 3452 | } | 4064 | } |
| 3453 | try shndx.get(elf).ni.resized(gpa, &elf.mf); | | |
| 3454 | break :shndx shndx.*; | 4065 | break :shndx shndx.*; |
| 3455 | }, | 4066 | }, |
| 3456 | .has_file_bits = true, | 4067 | .has_file_bits = true, |
| ... | @@ -3482,7 +4093,8 @@ fn loadObject( | ... | @@ -3482,7 +4093,8 @@ fn loadObject( |
| 3482 | // zero-based. This will eventually be updated by `flushMoved`. | 4093 | // zero-based. This will eventually be updated by `flushMoved`. |
| 3483 | .vaddr = 0, | 4094 | .vaddr = 0, |
| 3484 | .node = ni, | 4095 | .node = ni, |
| 3485 | .first_reloc = .none, | 4096 | .first_symbol_reloc = .none, |
| | 4097 | .first_got_reloc = .none, |
| 3486 | }; | 4098 | }; |
| 3487 | elf.synth_prog_node.increaseEstimatedTotalItems(1); | 4099 | elf.synth_prog_node.increaseEstimatedTotalItems(1); |
| 3488 | } | 4100 | } |
| ... | @@ -3777,6 +4389,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | ... | @@ -3777,6 +4389,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 3777 | .DEFAULT, .PROTECTED => {}, | 4389 | .DEFAULT, .PROTECTED => {}, |
| 3778 | } | 4390 | } |
| 3779 | | 4391 | |
| | 4392 | if (sym.shndx == std.elf.SHN_UNDEF) continue; |
| | 4393 | |
| 3780 | if (sym.name >= dynstr.len) { | 4394 | if (sym.name >= dynstr.len) { |
| 3781 | return diags.failParse(path, "bad symbol name string", .{}); | 4395 | return diags.failParse(path, "bad symbol name string", .{}); |
| 3782 | } | 4396 | } |
| ... | @@ -3787,39 +4401,47 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { | ... | @@ -3787,39 +4401,47 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| 3787 | gop.value_ptr.* = sym.info.type; | 4401 | gop.value_ptr.* = sym.info.type; |
| 3788 | } | 4402 | } |
| 3789 | | 4403 | |
| 3790 | // If there's already an undefined symbol by this name of type STT_NOTYPE, populate | 4404 | // If there's already an undefined symbol by this name of type STT_NOTYPE, populate |
| 3791 | // its type now. | 4405 | // its type now. |
| 3792 | update_sym_type: { | 4406 | const global_ptr = elf.globals.strong_undef.getPtr(name) orelse |
| 3793 | const global_ptr = elf.globals.strong_undef.getPtr(name) orelse | 4407 | elf.globals.weak_undef.getPtr(name) orelse |
| 3794 | elf.globals.weak_undef.getPtr(name) orelse | 4408 | continue; |
| 3795 | break :update_sym_type; | 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; |
| 3796 | | 4419 | |
| 3797 | if (global_ptr.dynsym_index == 0) break :update_sym_type; | 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 | }; |
| 3798 | | 4426 | |
| 3799 | const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class)); | 4427 | elf.targetStore(&sym_ptr.info, .{ |
| 3800 | switch (elf.targetLoad(&sym_ptr.other).visibility) { | 4428 | .bind = cur_info.bind, |
| 3801 | .HIDDEN, .INTERNAL, .PROTECTED => break :update_sym_type, | 4429 | .type = new_type, |
| 3802 | .DEFAULT => {}, | 4430 | }); |
| 3803 | } | | |
| 3804 | | 4431 | |
| 3805 | const cur_info = elf.targetLoad(&sym_ptr.info); | 4432 | const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class)); |
| 3806 | if (cur_info.type == .NOTYPE) { | 4433 | elf.targetStore(&dynsym_ptr.info, .{ |
| 3807 | elf.targetStore(&sym_ptr.info, .{ | 4434 | .bind = elf.targetLoad(&dynsym_ptr.info).bind, |
| 3808 | .bind = cur_info.bind, | 4435 | .type = new_type, |
| 3809 | .type = sym.info.type, | 4436 | }); |
| 3810 | }); | 4437 | |
| 3811 | | 4438 | // If we just turned this into an STT_FUNC symbol, then we have determined |
| 3812 | const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class)); | 4439 | // that it needs a PLT entry. |
| 3813 | elf.targetStore(&dynsym_ptr.info, .{ | 4440 | if (new_type == .FUNC) { |
| 3814 | .bind = elf.targetLoad(&dynsym_ptr.info).bind, | 4441 | elf.addPltEntry(name, global_ptr.dynsym_index); |
| 3815 | .type = sym.info.type, | 4442 | // ...and therefore, we need to re-apply that symbol's relocations, as |
| 3816 | }); | 4443 | // some might be targeting its PLT entry. |
| 3817 | | 4444 | global_ptr.symtab_index.applyTargetRelocs(elf); |
| 3818 | if (sym.info.type == .FUNC) { | | |
| 3819 | // We've just determined that this symbol actually needs a PLT entry. | | |
| 3820 | elf.addPltEntry(name, global_ptr.dynsym_index); | | |
| 3821 | // TODO: we therefore need to re-apply PLT32 relocs for that symbol! | | |
| 3822 | } | | |
| 3823 | } | 4445 | } |
| 3824 | } | 4446 | } |
| 3825 | } | 4447 | } |
| ... | @@ -3934,6 +4556,29 @@ fn createInitFiniArraySection( | ... | @@ -3934,6 +4556,29 @@ fn createInitFiniArraySection( |
| 3934 | ), | 4556 | ), |
| 3935 | }; | 4557 | }; |
| 3936 | } | 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), |
| | 4578 | }; |
| | 4579 | const end_sym_name = elf.string(.strtab, "__" ++ name ++ "_end") catch unreachable; // string definitely already exists |
| | 4580 | Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr); |
| | 4581 | } |
| 3937 | | 4582 | |
| 3938 | pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { | 4583 | pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { |
| 3939 | _ = prog_node; | 4584 | _ = prog_node; |
| ... | @@ -4073,17 +4718,15 @@ fn prelinkInner(elf: *Elf) !void { | ... | @@ -4073,17 +4718,15 @@ fn prelinkInner(elf: *Elf) !void { |
| 4073 | ); | 4718 | ); |
| 4074 | dynamic_index += 2; | 4719 | dynamic_index += 2; |
| 4075 | } | 4720 | } |
| 4076 | const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx; | | |
| 4077 | const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx; | | |
| 4078 | dynamic_entries[dynamic_index..][0..12].* = .{ | 4721 | dynamic_entries[dynamic_index..][0..12].* = .{ |
| 4079 | .{ std.elf.DT_RELA, @intCast(rela_dyn_shndx.vaddr(elf)) }, | 4722 | .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) }, |
| 4080 | .{ std.elf.DT_RELASZ, elf.targetLoad( | 4723 | .{ std.elf.DT_RELASZ, elf.targetLoad( |
| 4081 | &@field(elf.shdrPtr(rela_dyn_shndx), @tagName(ct_class)).size, | 4724 | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, |
| 4082 | ) }, | 4725 | ) }, |
| 4083 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, | 4726 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, |
| 4084 | .{ std.elf.DT_JMPREL, @intCast(rela_plt_shndx.vaddr(elf)) }, | 4727 | .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) }, |
| 4085 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( | 4728 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( |
| 4086 | &@field(elf.shdrPtr(rela_plt_shndx), @tagName(ct_class)).size, | 4729 | &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size, |
| 4087 | ) }, | 4730 | ) }, |
| 4088 | .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) }, | 4731 | .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) }, |
| 4089 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, | 4732 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, |
| ... | @@ -4100,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void { | ... | @@ -4100,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void { |
| 4100 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| | 4743 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 4101 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); | 4744 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); |
| 4102 | | 4745 | |
| 4103 | elf.first_dynamic_reloc = @enumFromInt(elf.relocs.items.len); | 4746 | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 4104 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 5); | 4747 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 5); |
| 4105 | elf.addRelocAssumeCapacity( | 4748 | elf.addRelocAssumeCapacity( |
| 4106 | dynamic_ni, | 4749 | dynamic_ni, |
| 4107 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1), | 4750 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1), |
| 4108 | .local(rela_dyn_shndx.get(elf).lsi), | 4751 | .local(elf.shndx.rela_dyn.get(elf).lsi), |
| 4109 | 0, | 4752 | 0, |
| 4110 | .absAddr(elf), | 4753 | .absAddr(elf), |
| 4111 | ); | 4754 | ); |
| 4112 | elf.addRelocAssumeCapacity( | 4755 | elf.addRelocAssumeCapacity( |
| 4113 | dynamic_ni, | 4756 | dynamic_ni, |
| 4114 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1), | 4757 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1), |
| 4115 | .local(rela_plt_shndx.get(elf).lsi), | 4758 | .local(elf.shndx.rela_plt.get(elf).lsi), |
| 4116 | 0, | 4759 | 0, |
| 4117 | .absAddr(elf), | 4760 | .absAddr(elf), |
| 4118 | ); | 4761 | ); |
| ... | @@ -4216,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { | ... | @@ -4216,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 4216 | .type = .SECTION, | 4859 | .type = .SECTION, |
| 4217 | .shndx = shndx, | 4860 | .shndx = shndx, |
| 4218 | }) else .null; | 4861 | }) else .null; |
| 4219 | 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 | } }); |
| 4220 | elf.nodes.appendAssumeCapacity(.{ .section = shndx }); | 4867 | elf.nodes.appendAssumeCapacity(.{ .section = shndx }); |
| 4221 | const offset = ni.fileLocation(&elf.mf, false).offset; | 4868 | const offset = ni.fileLocation(&elf.mf, false).offset; |
| 4222 | switch (elf.shdrPtr(shndx)) { | 4869 | switch (elf.shdrPtr(shndx)) { |
| ... | @@ -4242,13 +4889,14 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { | ... | @@ -4242,13 +4889,14 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 4242 | fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void { | 4889 | fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void { |
| 4243 | if (len == 0) return; | 4890 | if (len == 0) return; |
| 4244 | const gpa = elf.base.comp.gpa; | 4891 | const gpa = elf.base.comp.gpa; |
| 4245 | try elf.relocs.ensureUnusedCapacity(gpa, len); | 4892 | try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); |
| | 4893 | try elf.got_relocs.ensureUnusedCapacity(gpa, len); |
| 4246 | const class = elf.identClass(); | 4894 | const class = elf.identClass(); |
| 4247 | const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) { | 4895 | switch (elf.ehdrField(.type)) { |
| 4248 | .NONE, .CORE, _ => unreachable, | 4896 | .NONE, .CORE, _ => unreachable, |
| 4249 | .REL => { | 4897 | .REL => { |
| 4250 | const shndx = elf.getNodeShndx(node); | 4898 | const shndx = elf.getNodeShndx(node); |
| 4251 | if (shndx.get(elf).rela_shndx == .UNDEF) { | 4899 | if (shndx.get(elf).rela.shndx == .UNDEF) { |
| 4252 | var bfa_buf: [32]u8 = undefined; | 4900 | var bfa_buf: [32]u8 = undefined; |
| 4253 | var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa); | 4901 | var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa); |
| 4254 | const allocator = bfa.allocator(); | 4902 | const allocator = bfa.allocator(); |
| ... | @@ -4275,33 +4923,28 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) | ... | @@ -4275,33 +4923,28 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) |
| 4275 | .node_align = elf.mf.flags.block_size, | 4923 | .node_align = elf.mf.flags.block_size, |
| 4276 | }); | 4924 | }); |
| 4277 | elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {}); | 4925 | elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {}); |
| 4278 | shndx.get(elf).rela_shndx = rela_shndx; | 4926 | shndx.get(elf).rela.shndx = rela_shndx; |
| 4279 | } | 4927 | } |
| 4280 | break :rela .{ shndx.get(elf).rela_shndx, len }; | 4928 | try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len); |
| 4281 | }, | 4929 | }, |
| 4282 | .EXEC, .DYN => switch (elf.got.tlsld) { | 4930 | .EXEC, .DYN => { |
| 4283 | _ => return, | 4931 | try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 4284 | .none => if (elf.shndx.dynamic != .UNDEF) { | 4932 | const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD |
| 4285 | try elf.mf.updates.ensureUnusedCapacity(gpa, 1); | 4933 | try elf.got.ensureUnusedCapacity(gpa, new_got_entries); |
| 4286 | const got_ni = elf.shndx.got.get(elf).ni; | 4934 | const got_ni = elf.shndx.got.get(elf).ni; |
| 4287 | _, 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); |
| 4288 | const got_size = switch (class) { | 4936 | const need_got_size = switch (class) { |
| 4289 | .NONE, _ => unreachable, | 4937 | .NONE, _ => unreachable, |
| 4290 | 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), |
| 4291 | }; | 4939 | }; |
| 4292 | if (got_size > got_node_size) | 4940 | if (need_got_size > got_node_size) |
| 4293 | 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); |
| 4294 | break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 }; | 4942 | |
| 4295 | } else return, | 4943 | if (elf.shndx.dynamic != .UNDEF) { |
| | 4944 | try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries); |
| | 4945 | } |
| 4296 | }, | 4946 | }, |
| 4297 | }; | 4947 | } |
| 4298 | const rela_ni = rela_shndx.get(elf).ni; | | |
| 4299 | _, const rela_node_size = rela_ni.location(&elf.mf).resolve(&elf.mf); | | |
| 4300 | const rela_size = switch (elf.shdrPtr(rela_shndx)) { | | |
| 4301 | inline else => |shdr| elf.targetLoad(&shdr.size) + elf.targetLoad(&shdr.entsize) * rela_len, | | |
| 4302 | }; | | |
| 4303 | if (rela_size > rela_node_size) | | |
| 4304 | try rela_ni.resize(&elf.mf, gpa, rela_size +| rela_size / MappedFile.growth_factor); | | |
| 4305 | } | 4948 | } |
| 4306 | fn addRelocAssumeCapacity( | 4949 | fn addRelocAssumeCapacity( |
| 4307 | elf: *Elf, | 4950 | elf: *Elf, |
| ... | @@ -4309,123 +4952,443 @@ fn addRelocAssumeCapacity( | ... | @@ -4309,123 +4952,443 @@ fn addRelocAssumeCapacity( |
| 4309 | offset: u64, | 4952 | offset: u64, |
| 4310 | target: Symbol.Id, | 4953 | target: Symbol.Id, |
| 4311 | addend: i64, | 4954 | addend: i64, |
| 4312 | @"type": Reloc.Type, | 4955 | @"type": MachineRelocType, |
| 4313 | ) void { | 4956 | ) void { |
| 4314 | assert(node != .none); | 4957 | assert(node != .none); |
| 4315 | const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len); | 4958 | switch (elf.ehdrField(.type)) { |
| 4316 | const next: Reloc.Index = next: { | 4959 | .NONE, .CORE, _ => unreachable, |
| 4317 | const target_ptr = target.index(elf).ptr(elf); | 4960 | .REL => { |
| 4318 | const next = target_ptr.first_target_reloc; | 4961 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| 4319 | target_ptr.first_target_reloc = ri; | 4962 | const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ |
| 4320 | 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(); |
| 4321 | }; | 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; |
| 4322 | if (next != .none) { | 5131 | if (next != .none) { |
| 4323 | next.get(elf).prev = ri; | 5132 | next.get(elf).prev = ri; |
| 4324 | } | 5133 | } |
| 4325 | elf.relocs.addOneAssumeCapacity().* = .{ | 5134 | elf.symbol_relocs.appendAssumeCapacity(.{ |
| | 5135 | .node = node, |
| | 5136 | .offset = offset, |
| | 5137 | .target = target, |
| | 5138 | .addend = addend, |
| 4326 | .type = @"type", | 5139 | .type = @"type", |
| 4327 | .prev = .none, | | |
| 4328 | .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(.{ |
| 4329 | .node = node, | 5203 | .node = node, |
| | 5204 | .offset = offset, |
| 4330 | .target = target, | 5205 | .target = target, |
| 4331 | .index = index: switch (elf.ehdrField(.type)) { | 5206 | .addend = addend, |
| 4332 | .NONE, .CORE, _ => unreachable, | 5207 | .type = @"type", |
| 4333 | .REL => { | 5208 | }); |
| 4334 | const sh = elf.getNodeShndx(node).get(elf); | 5209 | } |
| 4335 | switch (elf.shdrPtr(sh.rela_shndx)) { | 5210 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 4336 | inline else => |shdr, class| { | 5211 | const entry_value: union(enum) { |
| 4337 | const Rela = class.ElfN().Rela; | 5212 | unsigned: u64, |
| 4338 | const ent_size = elf.targetLoad(&shdr.entsize); | 5213 | signed: i64, |
| 4339 | const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); | 5214 | reloc: struct { |
| 4340 | const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: { | 5215 | type: MachineRelocType, |
| 4341 | const rela: *Rela = @ptrCast(@alignCast( | 5216 | dynsym_index: u32, |
| 4342 | rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], | 5217 | }, |
| 4343 | )); | 5218 | } = switch (elf.got.keys()[got_index]) { |
| 4344 | sh.rela_free = @enumFromInt(rela.offset); | 5219 | .reserved => .{ .unsigned = 0 }, |
| 4345 | break :alloc_index index; | 5220 | .tpoff => |sym_id| val: { |
| 4346 | } else alloc_index: { | 5221 | // We will break from this block if we require a relocation. |
| 4347 | const old_size = elf.targetLoad(&shdr.size); | 5222 | known: { |
| 4348 | const new_size = old_size + ent_size; | 5223 | if (elf.base.comp.config.output_mode != .Exe) { |
| 4349 | elf.targetStore(&shdr.size, @intCast(new_size)); | 5224 | // Only the executable's per-module TLS block is at a known offset from the |
| 4350 | break :alloc_index @intCast(@divExact(old_size, ent_size)); | 5225 | // general TLS pointer. |
| 4351 | }; | 5226 | break :known; |
| 4352 | const rela: *Rela = @ptrCast(@alignCast( | 5227 | } |
| 4353 | rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], | 5228 | switch (sym_id.unwrap()) { |
| 4354 | )); | 5229 | .local => {}, |
| 4355 | // The `offset` field here needs to equal the offset into the section, which | 5230 | .global => |name| if (elf.globals.strong_undef.contains(name) or |
| 4356 | // is *not* the same as our `offset` which is the offset into `node`. We | 5231 | elf.globals.weak_undef.contains(name)) |
| 4357 | // could calculate it now, but there's no point since `flushMovedNodeRelocs` | 5232 | { |
| 4358 | // 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. |
| 4359 | rela.* = .{ | 5234 | break :known; |
| 4360 | .offset = 0, | | |
| 4361 | .info = .{ | | |
| 4362 | .type = @intCast(@"type".unwrap(elf)), | | |
| 4363 | .sym = @intCast(@intFromEnum(target.index(elf))), | | |
| 4364 | }, | | |
| 4365 | .addend = @intCast(addend), | | |
| 4366 | }; | | |
| 4367 | if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(Rela, rela); | | |
| 4368 | break :index .wrap(index); | | |
| 4369 | }, | 5235 | }, |
| 4370 | } | 5236 | } |
| 4371 | }, | 5237 | // It's a symbol which we define, the symbol is not interposable because we're the |
| 4372 | .EXEC, .DYN => { | 5238 | // executable, and we know our per-module TLS block's offset because we're the |
| 4373 | switch (elf.ehdrField(.machine)) { | 5239 | // executable. We therefore know this value! |
| 4374 | else => |machine| @panic(@tagName(machine)), | 5240 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 4375 | .AARCH64, .PPC64, .RISCV => {}, | 5241 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 4376 | .X86_64 => switch (@"type".X86_64) { | 5242 | inline else => |phdr| tls_size: { |
| 4377 | else => {}, | 5243 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 4378 | .TLSLD => switch (elf.got.tlsld) { | 5244 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 4379 | _ => {}, | 5245 | }, |
| 4380 | .none => if (elf.shndx.dynamic != .UNDEF) { | 5246 | }; |
| 4381 | const tlsld_index = elf.got.len; | 5247 | const sym_value = sym_id.value(elf); |
| 4382 | elf.got.tlsld = .wrap(tlsld_index); | 5248 | break :val .{ .signed = @bitCast(sym_value -% tls_size) }; |
| 4383 | elf.got.len = tlsld_index + 2; | 5249 | } |
| 4384 | const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) { | 5250 | break :val .{ |
| 4385 | inline else => |shdr, class| { | 5251 | .reloc = .{ |
| 4386 | const addr_size = @sizeOf(class.ElfN().Addr); | 5252 | .type = switch (elf.ehdrField(.machine)) { |
| 4387 | const old_size = addr_size * tlsld_index; | 5253 | else => |machine| @panic(@tagName(machine)), |
| 4388 | const new_size = old_size + addr_size * 2; | 5254 | .X86_64 => .{ .X86_64 = .TPOFF64 }, |
| 4389 | @memset( | 5255 | }, |
| 4390 | elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size], | 5256 | .dynsym_index = switch (sym_id.unwrap()) { |
| 4391 | 0, | 5257 | .global => |name| elf.globalByName(name).?.dynsym_index, |
| 4392 | ); | 5258 | // TODO: I have no idea if compilers are even allowed to emit this, but if they |
| 4393 | break :got_addr elf.targetLoad(&shdr.addr) + old_size; | 5259 | // are then I guess we need to add this local symbol to `.dynsym`? |
| 4394 | }, | 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, |
| 4395 | }; | 5316 | }; |
| 4396 | const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx; | 5317 | switch (visibility) { |
| 4397 | const rela_dyn_ni = rela_dyn_shndx.get(elf).ni; | 5318 | .DEFAULT => {}, |
| 4398 | switch (elf.shdrPtr(rela_dyn_shndx)) { | 5319 | .INTERNAL, .HIDDEN, .PROTECTED => { |
| 4399 | inline else => |shdr, class| { | 5320 | break :dsi 0; // non-interposable definition |
| 4400 | const Rela = class.ElfN().Rela; | | |
| 4401 | const old_size = elf.targetLoad(&shdr.size); | | |
| 4402 | const new_size = old_size + elf.targetLoad(&shdr.entsize); | | |
| 4403 | elf.targetStore(&shdr.size, new_size); | | |
| 4404 | const rela: *Rela = @ptrCast(@alignCast(rela_dyn_ni | | |
| 4405 | .slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)])); | | |
| 4406 | rela.* = .{ | | |
| 4407 | .offset = @intCast(got_addr), | | |
| 4408 | .info = .{ | | |
| 4409 | .type = @intFromEnum(std.elf.R_X86_64.DTPMOD64), | | |
| 4410 | .sym = 0, | | |
| 4411 | }, | | |
| 4412 | .addend = 0, | | |
| 4413 | }; | | |
| 4414 | if (elf.targetEndian() != native_endian) | | |
| 4415 | std.mem.byteSwapAllFields(Rela, rela); | | |
| 4416 | }, | 5321 | }, |
| 4417 | } | 5322 | } |
| 4418 | rela_dyn_ni.resizedAssumeCapacity(&elf.mf); | 5323 | } |
| 4419 | }, | 5324 | // `sym` is either undefined or an interposable definition, so use its |
| | 5325 | // actual dynsym index. |
| | 5326 | break :dsi elf.globalByName(name).?.dynsym_index; |
| 4420 | }, | 5327 | }, |
| 4421 | }, | 5328 | }, |
| 4422 | } | 5329 | }, |
| 4423 | break :index .none; | | |
| 4424 | }, | 5330 | }, |
| 4425 | }, | 5331 | }, |
| 4426 | .offset = offset, | 5332 | .tlsld0 => switch (elf.shndx.dynamic) { |
| 4427 | .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 | }, |
| 4428 | }; | 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, |
| | 5390 | }; |
| | 5391 | return shf.ALLOC and !shf.WRITE; |
| 4429 | } | 5392 | } |
| 4430 | | 5393 | |
| 4431 | 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 { |
| ... | @@ -4565,6 +5528,11 @@ pub fn flush( | ... | @@ -4565,6 +5528,11 @@ pub fn flush( |
| 4565 | if (any_undef) return error.LinkFailure; | 5528 | if (any_undef) return error.LinkFailure; |
| 4566 | } | 5529 | } |
| 4567 | | 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 | |
| 4568 | while (try elf.idle(tid)) {} | 5536 | while (try elf.idle(tid)) {} |
| 4569 | | 5537 | |
| 4570 | const entry_addr: u64 = entry: { | 5538 | const entry_addr: u64 = entry: { |
| ... | @@ -4595,6 +5563,47 @@ pub fn flush( | ... | @@ -4595,6 +5563,47 @@ pub fn flush( |
| 4595 | 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}), |
| 4596 | }; | 5564 | }; |
| 4597 | } | 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 | } |
| 4598 | | 5607 | |
| 4599 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { | 5608 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4600 | const comp = elf.base.comp; | 5609 | const comp = elf.base.comp; |
| ... | @@ -4660,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { | ... | @@ -4660,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4660 | break :task; | 5669 | break :task; |
| 4661 | } | 5670 | } |
| 4662 | 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. |
| 4663 | if (elf.ehdrField(.type) == .REL) { | 5675 | if (elf.ehdrField(.type) == .REL) { |
| 4664 | 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); |
| 4665 | defer sub_prog_node.end(); | 5677 | defer sub_prog_node.end(); |
| ... | @@ -4667,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { | ... | @@ -4667,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4667 | var ri = sym.first_target_reloc; | 5679 | var ri = sym.first_target_reloc; |
| 4668 | while (ri != .none) { | 5680 | while (ri != .none) { |
| 4669 | const reloc = ri.get(elf); | 5681 | const reloc = ri.get(elf); |
| 4670 | reloc.updateTargetIndex(elf); | 5682 | reloc.relaSection(elf).relaUpdateSym( |
| | 5683 | elf, |
| | 5684 | reloc.rela_index.unwrap().?, |
| | 5685 | @intFromEnum(reloc.target.index(elf)), |
| | 5686 | ); |
| 4671 | ri = reloc.next; | 5687 | ri = reloc.next; |
| 4672 | } | 5688 | } |
| 4673 | break :task; | 5689 | break :task; |
| ... | @@ -4876,126 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -4876,126 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4876 | .section => |shndx| { | 5892 | .section => |shndx| { |
| 4877 | try elf.flushFileOffset(ni); | 5893 | try elf.flushFileOffset(ni); |
| 4878 | const addr = elf.computeNodeVAddr(ni); | 5894 | const addr = elf.computeNodeVAddr(ni); |
| 4879 | switch (elf.shdrPtr(shndx)) { | 5895 | const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) { |
| 4880 | inline else => |shdr, class| { | 5896 | inline else => |shdr| .{ |
| 4881 | const flags = elf.targetLoad(&shdr.flags).shf; | 5897 | elf.targetLoad(&shdr.addr), |
| 4882 | if (flags.ALLOC) { | 5898 | elf.targetLoad(&shdr.flags).shf, |
| 4883 | if (elf.shndx.dynamic != .UNDEF) { | 5899 | }, |
| 4884 | if (shndx == elf.shndx.got) { | 5900 | }; |
| 4885 | const old_addr = elf.targetLoad(&shdr.addr); | | |
| 4886 | const rela_dyn_shndx = shndx.get(elf).rela_shndx; | | |
| 4887 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( | | |
| 4888 | rela_dyn_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast( | | |
| 4889 | elf.targetLoad(&@field( | | |
| 4890 | elf.shdrPtr(rela_dyn_shndx), | | |
| 4891 | @tagName(class), | | |
| 4892 | ).size), | | |
| 4893 | )], | | |
| 4894 | )); | | |
| 4895 | switch (elf.ehdrField(.machine)) { | | |
| 4896 | else => |machine| @panic(@tagName(machine)), | | |
| 4897 | .AARCH64, .PPC64, .RISCV => {}, | | |
| 4898 | .X86_64 => for (relas) |*rela| switch (@as( | | |
| 4899 | std.elf.R_X86_64, | | |
| 4900 | @enumFromInt(elf.targetLoad(&rela.info).type), | | |
| 4901 | )) { | | |
| 4902 | else => |@"type"| @panic(@tagName(@"type")), | | |
| 4903 | .RELATIVE => {}, | | |
| 4904 | .GLOB_DAT, .DTPMOD64, .DTPOFF64 => elf.targetStore( | | |
| 4905 | &rela.offset, | | |
| 4906 | @intCast(elf.targetLoad(&rela.offset) - old_addr + addr), | | |
| 4907 | ), | | |
| 4908 | }, | | |
| 4909 | } | | |
| 4910 | } else if (shndx == elf.shndx.got_plt) { | | |
| 4911 | const target_endian = elf.targetEndian(); | | |
| 4912 | const old_addr = elf.targetLoad(&shdr.addr); | | |
| 4913 | const rela_plt_shndx = shndx.get(elf).rela_shndx; | | |
| 4914 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( | | |
| 4915 | rela_plt_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast( | | |
| 4916 | elf.targetLoad(&@field( | | |
| 4917 | elf.shdrPtr(rela_plt_shndx), | | |
| 4918 | @tagName(class), | | |
| 4919 | ).size), | | |
| 4920 | )], | | |
| 4921 | )); | | |
| 4922 | const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf); | | |
| 4923 | switch (elf.ehdrField(.machine)) { | | |
| 4924 | else => |machine| @panic(@tagName(machine)), | | |
| 4925 | .AARCH64, .PPC64, .RISCV => {}, | | |
| 4926 | .X86_64 => { | | |
| 4927 | for (relas) |*rela| switch (@as( | | |
| 4928 | std.elf.R_X86_64, | | |
| 4929 | @enumFromInt(elf.targetLoad(&rela.info).type), | | |
| 4930 | )) { | | |
| 4931 | else => |@"type"| @panic(@tagName(@"type")), | | |
| 4932 | .JUMP_SLOT => elf.targetStore( | | |
| 4933 | &rela.offset, | | |
| 4934 | @intCast(elf.targetLoad(&rela.offset) - old_addr + addr), | | |
| 4935 | ), | | |
| 4936 | }; | | |
| 4937 | for (0..elf.got.plt.count()) |plt_index| { | | |
| 4938 | const slice = plt_sec_slice[16 * plt_index + 6 ..][0..4]; | | |
| 4939 | std.mem.writeInt( | | |
| 4940 | i32, | | |
| 4941 | slice, | | |
| 4942 | @intCast(@as(i64, @bitCast(@as(u64, @bitCast(@as( | | |
| 4943 | i64, | | |
| 4944 | std.mem.readInt(i32, slice, target_endian), | | |
| 4945 | ))) -% old_addr +% addr))), | | |
| 4946 | target_endian, | | |
| 4947 | ); | | |
| 4948 | } | | |
| 4949 | }, | | |
| 4950 | } | | |
| 4951 | } else if (shndx == elf.shndx.plt_sec) { | | |
| 4952 | const target_endian = elf.targetEndian(); | | |
| 4953 | const old_addr = elf.targetLoad(&shdr.addr); | | |
| 4954 | const plt_sec_slice = ni.slice(&elf.mf); | | |
| 4955 | switch (elf.ehdrField(.machine)) { | | |
| 4956 | else => |machine| @panic(@tagName(machine)), | | |
| 4957 | .AARCH64, .PPC64, .RISCV => {}, | | |
| 4958 | .X86_64 => for (0..elf.got.plt.count()) |plt_index| { | | |
| 4959 | const slice = plt_sec_slice[16 * plt_index + 6 ..][0..4]; | | |
| 4960 | std.mem.writeInt( | | |
| 4961 | i32, | | |
| 4962 | slice, | | |
| 4963 | @intCast(@as(i64, @bitCast(@as(u64, @bitCast(@as( | | |
| 4964 | i64, | | |
| 4965 | std.mem.readInt(i32, slice, target_endian), | | |
| 4966 | ))) -% addr +% old_addr))), | | |
| 4967 | target_endian, | | |
| 4968 | ); | | |
| 4969 | }, | | |
| 4970 | } | | |
| 4971 | } | | |
| 4972 | } | | |
| 4973 | | 5901 | |
| 4974 | // Update global symbols targeting this section | 5902 | if (flags.ALLOC) { |
| 4975 | if (elf.node_global_symbols.get(ni)) |first_name| { | 5903 | switch (elf.shdrPtr(shndx)) { |
| 4976 | assert(first_name != .empty); | 5904 | inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)), |
| 4977 | const old_addr = elf.targetLoad(&shdr.addr); | 5905 | } |
| 4978 | var name = first_name; | | |
| 4979 | while (name != .empty) { | | |
| 4980 | const global = elf.globalByName(name).?; | | |
| 4981 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { | | |
| 4982 | inline else => |sym| elf.targetLoad(&sym.value), | | |
| 4983 | }; | | |
| 4984 | global.flushMoved(elf, old_sym_addr - old_addr + addr); | | |
| 4985 | name = global.next_in_node; | | |
| 4986 | } | | |
| 4987 | } | | |
| 4988 | | 5906 | |
| 4989 | elf.targetStore(&shdr.addr, @intCast(addr)); | 5907 | // Update global symbols targeting this section |
| 4990 | shndx.get(elf).lsi.index().flushMoved(elf, addr); | 5908 | if (elf.node_global_symbols.get(ni)) |first_name| { |
| | 5909 | assert(first_name != .empty); |
| | 5910 | var name = first_name; |
| | 5911 | while (name != .empty) { |
| | 5912 | const global = elf.globalByName(name).?; |
| | 5913 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { |
| | 5914 | inline else => |sym| elf.targetLoad(&sym.value), |
| | 5915 | }; |
| | 5916 | Symbol.Id.global(name).flushMoved( |
| | 5917 | elf, |
| | 5918 | old_sym_addr - old_addr + addr, |
| | 5919 | ); |
| | 5920 | name = global.next_in_node; |
| 4991 | } | 5921 | } |
| | 5922 | } |
| 4992 | | 5923 | |
| 4993 | if (shndx == elf.shndx.plt) { | 5924 | Symbol.Id.local(shndx.get(elf).lsi).flushMoved(elf, addr); |
| 4994 | elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc); | 5925 | } |
| 4995 | } else if (shndx == elf.shndx.dynamic) { | 5926 | |
| 4996 | elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc); | 5927 | if (shndx == elf.shndx.got) { |
| 4997 | } | 5928 | const rela_dyn_shndx = elf.shndx.rela_dyn; |
| 4998 | }, | 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); |
| 4999 | } | 5945 | } |
| 5000 | }, | 5946 | }, |
| 5001 | .input_section => |isi| { | 5947 | .input_section => |isi| { |
| ... | @@ -5020,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -5020,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5020 | .DEFAULT => elf.targetLoad(&sym.value), | 5966 | .DEFAULT => elf.targetLoad(&sym.value), |
| 5021 | }, | 5967 | }, |
| 5022 | }; | 5968 | }; |
| 5023 | 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 | ); |
| 5024 | } | 5973 | } |
| 5025 | | 5974 | |
| 5026 | // Update global symbols | 5975 | // Update global symbols |
| ... | @@ -5032,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -5032,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5032 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { | 5981 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { |
| 5033 | inline else => |sym| elf.targetLoad(&sym.value), | 5982 | inline else => |sym| elf.targetLoad(&sym.value), |
| 5034 | }; | 5983 | }; |
| 5035 | 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 | ); |
| 5036 | name = global.next_in_node; | 5988 | name = global.next_in_node; |
| 5037 | } | 5989 | } |
| 5038 | } | 5990 | } |
| 5039 | | 5991 | |
| 5040 | 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 | ); |
| 5041 | }, | 5998 | }, |
| 5042 | inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { | 5999 | inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { |
| 5043 | const new_addr = elf.computeNodeVAddr(ni); | 6000 | const new_addr = elf.computeNodeVAddr(ni); |
| 5044 | mi.symbol(elf).index().flushMoved(elf, new_addr); | 6001 | Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr); |
| 5045 | if (elf.node_global_symbols.get(ni)) |first_name| { | 6002 | if (elf.node_global_symbols.get(ni)) |first_name| { |
| 5046 | assert(first_name != .empty); | 6003 | assert(first_name != .empty); |
| 5047 | var name = first_name; | 6004 | var name = first_name; |
| 5048 | while (name != .empty) { | 6005 | while (name != .empty) { |
| 5049 | const global = elf.globalByName(name).?; | 6006 | Symbol.Id.global(name).flushMoved(elf, new_addr); |
| 5050 | global.flushMoved(elf, new_addr); | 6007 | name = elf.globalByName(name).?.next_in_node; |
| 5051 | name = global.next_in_node; | | |
| 5052 | } | 6008 | } |
| 5053 | } | 6009 | } |
| 5054 | 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 | ); |
| 5055 | }, | 6016 | }, |
| 5056 | } | 6017 | } |
| 5057 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); | 6018 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| ... | @@ -5079,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -5079,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5079 | }, | 6040 | }, |
| 5080 | .TLS => { | 6041 | .TLS => { |
| 5081 | 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 | } |
| 5082 | return ni.childrenMoved(elf.base.comp.gpa, &elf.mf); | 6064 | return ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 5083 | }, | 6065 | }, |
| 5084 | } | 6066 | } |
| ... | @@ -5113,110 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -5113,110 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5113 | }, | 6095 | }, |
| 5114 | }, | 6096 | }, |
| 5115 | .section => |shndx| switch (elf.shdrPtr(shndx)) { | 6097 | .section => |shndx| switch (elf.shdrPtr(shndx)) { |
| 5116 | inline else => |shdr, class| { | 6098 | inline else => |shdr| { |
| 5117 | switch (elf.targetLoad(&shdr.type)) { | 6099 | switch (elf.targetLoad(&shdr.type)) { |
| 5118 | else => unreachable, | 6100 | else => unreachable, |
| | 6101 | |
| 5119 | .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), | 6102 | .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), |
| 5120 | .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), | 6103 | .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), |
| 5121 | .SYMTAB, .DYNAMIC, .REL, .DYNSYM => return, | 6104 | |
| 5122 | .INIT_ARRAY => { | 6105 | .INIT_ARRAY, |
| 5123 | assert(shndx == elf.shndx.init_array); | 6106 | .FINI_ARRAY, |
| 5124 | if (elf.shndx.dynamic != .UNDEF) { | 6107 | .PREINIT_ARRAY, |
| 5125 | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( | 6108 | .STRTAB, |
| 5126 | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), | 6109 | .SYMTAB, |
| 5127 | )); | 6110 | .DYNAMIC, |
| 5128 | for (dynamic_entries) |*dynamic_entry| | 6111 | .REL, |
| 5129 | switch (elf.targetLoad(&dynamic_entry[0])) { | 6112 | .RELA, |
| 5130 | else => {}, | 6113 | .DYNSYM, |
| 5131 | std.elf.DT_INIT_ARRAYSZ => dynamic_entry[1] = shdr.size, | 6114 | => return, |
| 5132 | }; | | |
| 5133 | } | | |
| 5134 | const end_sym_index = elf.globalByName(elf.string(.strtab, "__init_array_end") catch unreachable).?.symtab_index; | | |
| 5135 | const end_sym_ptr = @field(elf.symPtr(end_sym_index), @tagName(class)); | | |
| 5136 | const end_vaddr = shndx.vaddr(elf) + elf.targetLoad(&shdr.size); | | |
| 5137 | elf.targetStore(&end_sym_ptr.value, @intCast(end_vaddr)); | | |
| 5138 | end_sym_index.flushMoved(elf, end_vaddr); | | |
| 5139 | return; | | |
| 5140 | }, | | |
| 5141 | .FINI_ARRAY => { | | |
| 5142 | assert(shndx == elf.shndx.fini_array); | | |
| 5143 | if (elf.shndx.dynamic != .UNDEF) { | | |
| 5144 | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( | | |
| 5145 | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), | | |
| 5146 | )); | | |
| 5147 | for (dynamic_entries) |*dynamic_entry| | | |
| 5148 | switch (elf.targetLoad(&dynamic_entry[0])) { | | |
| 5149 | else => {}, | | |
| 5150 | std.elf.DT_FINI_ARRAYSZ => dynamic_entry[1] = shdr.size, | | |
| 5151 | }; | | |
| 5152 | } | | |
| 5153 | const end_sym_index = elf.globalByName(elf.string(.strtab, "__fini_array_end") catch unreachable).?.symtab_index; | | |
| 5154 | const end_sym_ptr = @field(elf.symPtr(end_sym_index), @tagName(class)); | | |
| 5155 | const end_vaddr = shndx.vaddr(elf) + elf.targetLoad(&shdr.size); | | |
| 5156 | elf.targetStore(&end_sym_ptr.value, @intCast(end_vaddr)); | | |
| 5157 | end_sym_index.flushMoved(elf, end_vaddr); | | |
| 5158 | return; | | |
| 5159 | }, | | |
| 5160 | .PREINIT_ARRAY => { | | |
| 5161 | assert(shndx == elf.shndx.preinit_array); | | |
| 5162 | if (elf.shndx.dynamic != .UNDEF) { | | |
| 5163 | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( | | |
| 5164 | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), | | |
| 5165 | )); | | |
| 5166 | for (dynamic_entries) |*dynamic_entry| | | |
| 5167 | switch (elf.targetLoad(&dynamic_entry[0])) { | | |
| 5168 | else => {}, | | |
| 5169 | std.elf.DT_PREINIT_ARRAYSZ => dynamic_entry[1] = shdr.size, | | |
| 5170 | }; | | |
| 5171 | } | | |
| 5172 | const end_sym_index = elf.globalByName(elf.string(.strtab, "__preinit_array_end") catch unreachable).?.symtab_index; | | |
| 5173 | const end_sym_ptr = @field(elf.symPtr(end_sym_index), @tagName(class)); | | |
| 5174 | const end_vaddr = shndx.vaddr(elf) + elf.targetLoad(&shdr.size); | | |
| 5175 | elf.targetStore(&end_sym_ptr.value, @intCast(end_vaddr)); | | |
| 5176 | end_sym_index.flushMoved(elf, end_vaddr); | | |
| 5177 | return; | | |
| 5178 | }, | | |
| 5179 | .STRTAB => { | | |
| 5180 | if (elf.shndx.dynamic != .UNDEF) { | | |
| 5181 | if (shndx == elf.shndx.dynstr) { | | |
| 5182 | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( | | |
| 5183 | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), | | |
| 5184 | )); | | |
| 5185 | for (dynamic_entries) |*dynamic_entry| | | |
| 5186 | switch (elf.targetLoad(&dynamic_entry[0])) { | | |
| 5187 | else => {}, | | |
| 5188 | std.elf.DT_STRSZ => dynamic_entry[1] = shdr.size, | | |
| 5189 | }; | | |
| 5190 | } | | |
| 5191 | } | | |
| 5192 | return; | | |
| 5193 | }, | | |
| 5194 | .RELA => { | | |
| 5195 | if (elf.shndx.dynamic != .UNDEF) { | | |
| 5196 | if (shndx == elf.shndx.got.get(elf).rela_shndx) { | | |
| 5197 | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( | | |
| 5198 | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), | | |
| 5199 | )); | | |
| 5200 | for (dynamic_entries) |*dynamic_entry| | | |
| 5201 | switch (elf.targetLoad(&dynamic_entry[0])) { | | |
| 5202 | else => {}, | | |
| 5203 | std.elf.DT_RELASZ => dynamic_entry[1] = shdr.size, | | |
| 5204 | }; | | |
| 5205 | } else if (shndx == elf.shndx.got_plt.get(elf).rela_shndx) { | | |
| 5206 | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( | | |
| 5207 | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), | | |
| 5208 | )); | | |
| 5209 | for (dynamic_entries) |*dynamic_entry| | | |
| 5210 | switch (elf.targetLoad(&dynamic_entry[0])) { | | |
| 5211 | else => {}, | | |
| 5212 | std.elf.DT_PLTRELSZ => dynamic_entry[1] = shdr.size, | | |
| 5213 | }; | | |
| 5214 | } | | |
| 5215 | } | | |
| 5216 | return; | | |
| 5217 | }, | | |
| 5218 | } | 6115 | } |
| 5219 | 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 | { |
| 5220 | elf.targetStore(&shdr.size, @intCast(size)); | 6120 | elf.targetStore(&shdr.size, @intCast(size)); |
| 5221 | } | 6121 | } |
| 5222 | }, | 6122 | }, |
| ... | @@ -5224,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { | ... | @@ -5224,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5224 | .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {}, | 6124 | .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {}, |
| 5225 | } | 6125 | } |
| 5226 | } | 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 | } |
| 5227 | | 6206 | |
| 5228 | pub fn updateExports( | 6207 | pub fn updateExports( |
| 5229 | elf: *Elf, | 6208 | elf: *Elf, |