| ... | ... | @@ -34,6 +34,8 @@ shndx: struct { |
| 34 | 34 | dynstr: Section.Index, |
| 35 | 35 | dynamic: Section.Index, |
| 36 | 36 | tdata: Section.Index, |
| 37 | rela_dyn: Section.Index, |
| 38 | rela_plt: Section.Index, |
| 37 | 39 | // These sections are created only as needed, and are initially `.UNDEF`. |
| 38 | 40 | init_array: Section.Index, |
| 39 | 41 | fini_array: Section.Index, |
| ... | ... | @@ -65,13 +67,23 @@ dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT), |
| 65 | 67 | shstrtab: StringTable, |
| 66 | 68 | strtab: StringTable, |
| 67 | 69 | dynstr: StringTable, |
| 68 | | got: struct { |
| 69 | | len: u32, |
| 70 | | tlsld: GotIndex, |
| 71 | | plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void), |
| 72 | | }, |
| 73 | | first_plt_reloc: Reloc.Index, |
| 74 | | first_dynamic_reloc: Reloc.Index, |
| 70 | |
| 71 | /// Indices map 1--1 to indices into the actual `.got` section. |
| 72 | /// |
| 73 | /// Value is the output relocation in `.rela.dyn` for the GOT entry. |
| 74 | got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), |
| 75 | /// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into |
| 76 | /// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime |
| 77 | /// relocation is no longer necessary, then neither is the corresponding PLT entry!). |
| 78 | /// |
| 79 | /// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is |
| 80 | /// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs. |
| 81 | plt: std.array_hash_map.Auto(Symbol.Id, void), |
| 82 | /// The `.plt` section contains zero or more symbol relocations starting at this index. |
| 83 | plt_first_symbol_reloc: SymbolReloc.Index, |
| 84 | /// The `.dynamic` section contains zero or more symbol relocations starting at this index. |
| 85 | dynamic_first_symbol_reloc: SymbolReloc.Index, |
| 86 | |
| 75 | 87 | needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void), |
| 76 | 88 | inputs: std.ArrayList(struct { |
| 77 | 89 | path: std.Build.Cache.Path, |
| ... | ... | @@ -81,31 +93,42 @@ inputs: std.ArrayList(struct { |
| 81 | 93 | input_sections: std.ArrayList(InputSection), |
| 82 | 94 | input_section_pending_index: u32, |
| 83 | 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 | 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 | 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 | 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 | 108 | lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { |
| 94 | 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 | 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 | 116 | pending_index: u32, |
| 100 | 117 | }), |
| 101 | 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 | 123 | /// Index matches the index into `shdrs`. |
| 104 | 124 | section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), |
| 105 | | |
| 106 | 125 | /// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation |
| 107 | 126 | /// entries which target that symbol must be updated to reference the correct symbol index. |
| 108 | 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 | 133 | const_prog_node: std.Progress.Node, |
| 111 | 134 | synth_prog_node: std.Progress.Node, |
| ... | ... | @@ -120,21 +143,21 @@ const Node = union(enum) { |
| 120 | 143 | shdr, |
| 121 | 144 | /// Cannot contain relocations. |
| 122 | 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 | 150 | /// Otherwise, cannot contain relocations. |
| 128 | 151 | section: Section.Index, |
| 129 | | /// May contain relocations through the `first_reloc` field in `elf.input_sections`. |
| 152 | /// May contain relocations. |
| 130 | 153 | input_section: InputSection.Index, |
| 131 | | /// May contain relocations through the `first_reloc` field in `elf.navs`. |
| 154 | /// May contain relocations. |
| 132 | 155 | nav: NavMapIndex, |
| 133 | | /// May contain relocations through the `first_reloc` field in `elf.uavs`. |
| 156 | /// May contain relocations. |
| 134 | 157 | uav: UavMapIndex, |
| 135 | | /// May contain relocations through the `first_reloc` field in `elf.lazy.map`. |
| 158 | /// May contain relocations. |
| 136 | 159 | lazy_code: LazyMapRef.Index(.code), |
| 137 | | /// May contain relocations through the `first_reloc` field in `elf.lazy.map`. |
| 160 | /// May contain relocations. |
| 138 | 161 | lazy_const_data: LazyMapRef.Index(.const_data), |
| 139 | 162 | |
| 140 | 163 | pub const InputIndex = enum(u32) { |
| ... | ... | @@ -176,8 +199,11 @@ const Node = union(enum) { |
| 176 | 199 | return elf.navs.values()[@intFromEnum(nmi)].lsi; |
| 177 | 200 | } |
| 178 | 201 | |
| 179 | | fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index { |
| 180 | | return elf.navs.values()[@intFromEnum(nmi)].first_reloc; |
| 202 | fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 203 | return elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc; |
| 204 | } |
| 205 | fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index { |
| 206 | return elf.navs.values()[@intFromEnum(nmi)].first_got_reloc; |
| 181 | 207 | } |
| 182 | 208 | }; |
| 183 | 209 | |
| ... | ... | @@ -192,8 +218,13 @@ const Node = union(enum) { |
| 192 | 218 | return elf.uavs.values()[@intFromEnum(umi)].lsi; |
| 193 | 219 | } |
| 194 | 220 | |
| 195 | | fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index { |
| 196 | | return elf.uavs.values()[@intFromEnum(umi)].first_reloc; |
| 221 | fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 222 | return elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc; |
| 223 | } |
| 224 | fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index { |
| 225 | _ = umi; |
| 226 | _ = elf; |
| 227 | return .none; |
| 197 | 228 | } |
| 198 | 229 | }; |
| 199 | 230 | |
| ... | ... | @@ -217,8 +248,11 @@ const Node = union(enum) { |
| 217 | 248 | return lmi.ref().symbol(elf); |
| 218 | 249 | } |
| 219 | 250 | |
| 220 | | fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index { |
| 221 | | return lmi.ref().firstReloc(elf); |
| 251 | fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index { |
| 252 | return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_symbol_reloc; |
| 253 | } |
| 254 | fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index { |
| 255 | return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_got_reloc; |
| 222 | 256 | } |
| 223 | 257 | }; |
| 224 | 258 | } |
| ... | ... | @@ -230,10 +264,6 @@ const Node = union(enum) { |
| 230 | 264 | pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex { |
| 231 | 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 | 269 | pub const Known = struct { |
| ... | ... | @@ -269,8 +299,10 @@ const InputSection = struct { |
| 269 | 299 | vaddr: u64, |
| 270 | 300 | /// The node corresponding to this input section. |
| 271 | 301 | node: MappedFile.Node.Index, |
| 272 | | /// The start index of the contiguous sequence of relocations in this input section. |
| 273 | | first_reloc: Reloc.Index, |
| 302 | /// The start index of the contiguous sequence of symbol relocations in this input section. |
| 303 | first_symbol_reloc: SymbolReloc.Index, |
| 304 | /// The start index of the contiguous sequence of GOT relocations in this input section. |
| 305 | first_got_reloc: GotReloc.Index, |
| 274 | 306 | |
| 275 | 307 | const Index = enum(u32) { |
| 276 | 308 | _, |
| ... | ... | @@ -304,21 +336,53 @@ const Section = struct { |
| 304 | 336 | /// |
| 305 | 337 | /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`. |
| 306 | 338 | lsi: Symbol.LocalIndex, |
| 307 | | rela_shndx: Section.Index, |
| 308 | | rela_free: RelIndex, |
| 339 | rela: union { |
| 340 | /// This field is active if and only if this section is *not* a `SHT_RELA` section. |
| 341 | /// |
| 342 | /// This field's value refers to this section's corresponding relocation section, if it |
| 343 | /// currently has one. If this section does not currently have a relocation section, the |
| 344 | /// value is `.UNDEF`. |
| 345 | /// |
| 346 | /// This field is only ever non-`.UNDEF` when emitting a relocatable (`ET_REL`). While there |
| 347 | /// are also output relocations in DSOs, they are all placed in the `.rela.dyn` |
| 348 | /// (`elf.shdnx.rela_dyn`) and `.rela.plt` (`elf.shndx.rela_plt`) sections, rather than |
| 349 | /// having separate relocation sections for each section. |
| 350 | shndx: Section.Index, |
| 351 | |
| 352 | /// This field is active if and only if this section *is* a `SHT_RELA` section. |
| 353 | /// |
| 354 | /// This is the head of a single-linked list of free `ElfN.Rela` entries in this section. |
| 355 | /// Entries in this list have `info.type` set to `R_*_NONE`, have `info.sym` set to 0, and |
| 356 | /// have `offset` set to `@enumFromInt(next)` where `next` is `RelaIndex.Optional`. Also, |
| 357 | /// `addend` is set to the length of the list starting from this point; so the last node in |
| 358 | /// the list has `addend = 1`, the one before it has `addend = 2`, etc. This is so that the |
| 359 | /// head node always contains the current length of the list. |
| 360 | /// |
| 361 | /// It would be okay to store these values (in the `offset` and `addend` fields) in the |
| 362 | /// compiler's host endianness, because they will never be read by other tooling. However, |
| 363 | /// we nonetheless use target endianness, because using host endianness would introduce an |
| 364 | /// unnecessary dependency of the output binary on the compiler's host architecture. |
| 365 | free_head: RelaIndex.Optional, |
| 366 | }, |
| 309 | 367 | |
| 310 | | pub const RelIndex = enum(u32) { |
| 368 | const RelaIndex = enum(u32) { |
| 311 | 369 | none, |
| 312 | 370 | _, |
| 313 | 371 | |
| 314 | | pub fn wrap(i: ?u32) RelIndex { |
| 315 | | return @enumFromInt((i orelse return .none) + 1); |
| 316 | | } |
| 317 | | pub fn unwrap(ri: RelIndex) ?u32 { |
| 318 | | return switch (ri) { |
| 319 | | .none => null, |
| 320 | | _ => @intFromEnum(ri) - 1, |
| 321 | | }; |
| 372 | const Optional = enum(u32) { |
| 373 | none = std.math.maxInt(u32), |
| 374 | _, |
| 375 | |
| 376 | fn unwrap(opt: RelaIndex.Optional) ?RelaIndex { |
| 377 | return switch (opt) { |
| 378 | .none => null, |
| 379 | _ => @enumFromInt(@intFromEnum(opt)), |
| 380 | }; |
| 381 | } |
| 382 | }; |
| 383 | |
| 384 | fn toOptional(i: RelaIndex) RelaIndex.Optional { |
| 385 | return @enumFromInt(@intFromEnum(i)); |
| 322 | 386 | } |
| 323 | 387 | }; |
| 324 | 388 | |
| ... | ... | @@ -390,9 +454,690 @@ const Section = struct { |
| 390 | 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 | 1141 | fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void { |
| 397 | 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 | 1192 | fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { |
| 448 | 1193 | const gpa = elf.base.comp.gpa; |
| 449 | 1194 | |
| 450 | | try elf.got.plt.ensureUnusedCapacity(gpa, len); |
| 451 | | const need_plt_capacity = elf.got.plt.count() + len; |
| 1195 | try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len); |
| 1196 | |
| 1197 | try elf.plt.ensureUnusedCapacity(gpa, len); |
| 1198 | const need_plt_capacity = elf.plt.count() + len; |
| 452 | 1199 | |
| 453 | 1200 | switch (elf.ehdrField(.machine)) { |
| 454 | 1201 | else => |machine| @panic(@tagName(machine)), |
| ... | ... | @@ -479,21 +1226,25 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { |
| 479 | 1226 | const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor; |
| 480 | 1227 | try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 481 | 1228 | } |
| 482 | | |
| 483 | | // Ensure the `.rela.plt` section's node is big enough |
| 484 | | const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx; |
| 485 | | const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) { |
| 486 | | inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity), |
| 487 | | }; |
| 488 | | _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf); |
| 489 | | if (rela_plt_cur_size < rela_plt_need_size) { |
| 490 | | const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor; |
| 491 | | try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size); |
| 492 | | } else { |
| 493 | | // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can |
| 494 | | // be updated if we do indeed add a PLT entry. |
| 495 | | try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf); |
| 496 | | } |
| 1229 | }, |
| 1230 | } |
| 1231 | } |
| 1232 | /// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at |
| 1233 | /// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`. |
| 1234 | fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool { |
| 1235 | assert(elf.shndx.plt != .UNDEF); |
| 1236 | assert(plt_index <= elf.plt.count()); |
| 1237 | // We track which PLT entries are alive based on the relocation entries, since there is a 1-1 |
| 1238 | // mapping between PLT entries and `.rela.plt` entries and the relocation entries already have |
| 1239 | // a free-list mechanism. |
| 1240 | switch (elf.shdrPtr(elf.shndx.rela_plt)) { |
| 1241 | inline else => |rela_shdr, class| { |
| 1242 | const size = elf.targetLoad(&rela_shdr.size); |
| 1243 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| 1244 | elf.shndx.rela_plt.get(elf).ni.slice(&elf.mf)[0..@intCast(size)], |
| 1245 | )); |
| 1246 | const rel_type = elf.targetLoad(&relas[plt_index].info).type; |
| 1247 | return rel_type == MachineRelocType.none(elf).unwrap(elf); |
| 497 | 1248 | }, |
| 498 | 1249 | } |
| 499 | 1250 | } |
| ... | ... | @@ -801,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 801 | 1552 | if (new_global_ptr.dynsym_index != 0 and |
| 802 | 1553 | opts.visibility == .DEFAULT and |
| 803 | 1554 | opts.shndx == .UNDEF and |
| 804 | | @"type" == .FUNC) |
| 1555 | (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC)) |
| 805 | 1556 | { |
| 806 | 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 |
| 808 | | // the PLT entry if we later discover a link inpu which resolves this reference. |
| 1558 | // We therefore might need a PLT entry, so let's add one now. |
| 809 | 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 | 1564 | return .global(opts.name.strtab); |
| ... | ... | @@ -823,6 +1575,7 @@ fn setGlobalSymbolValue( |
| 823 | 1575 | shndx: Section.Index, |
| 824 | 1576 | }, |
| 825 | 1577 | ) void { |
| 1578 | assert(new.shndx != .UNDEF); |
| 826 | 1579 | const old_node = global_ptr.symtab_index.ptr(elf).node; |
| 827 | 1580 | if (old_node != .none) { |
| 828 | 1581 | if (global_ptr.next_in_node != .empty) { |
| ... | ... | @@ -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 | 1688 | /// When the same global symbol appears in two inputs---even if one symbol is defined and the other |
| 903 | 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 | 1818 | if (elf.targetEndian() != native_endian) { |
| 1033 | 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 | 1826 | fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void { |
| 1040 | 1827 | const target_endian = elf.targetEndian(); |
| 1041 | | const plt_index: u32 = @intCast(elf.got.plt.count()); |
| 1042 | | elf.got.plt.putAssumeCapacityNoClobber(.global(global_name), {}); |
| 1828 | |
| 1829 | // We use the existing free-list tracking of the `.rela.plt` section to also behave as a |
| 1830 | // free-list for the PLT itself---see `pltEntryIsDead` for details. |
| 1831 | const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{ |
| 1832 | .type = .jumpSlot(elf), |
| 1833 | .offset = 0, // populated later |
| 1834 | .raw_sym_index = dynsym_index, |
| 1835 | .addend = 0, |
| 1836 | })); |
| 1837 | |
| 1838 | // Now that we know the index, we can set the relocation's offset. |
| 1839 | const got_plt_addr = switch (elf.shdrPtr(elf.shndx.got_plt)) { |
| 1840 | inline else => |shdr, class| got_plt_addr: { |
| 1841 | const ent_size = @sizeOf(class.ElfN().Addr); |
| 1842 | assert(elf.targetLoad(&shdr.entsize) == ent_size); |
| 1843 | const offset = ent_size * @as(u64, 3 + plt_index); |
| 1844 | assert(offset <= elf.targetLoad(&shdr.size)); |
| 1845 | break :got_plt_addr elf.targetLoad(&shdr.addr) + offset; |
| 1846 | }, |
| 1847 | }; |
| 1848 | elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_addr); |
| 1849 | |
| 1850 | if (plt_index < elf.plt.count()) { |
| 1851 | // We reused a free entry, so we're already done! |
| 1852 | elf.plt.setKey(plt_index, .global(global_name)); |
| 1853 | return; |
| 1854 | } |
| 1855 | |
| 1856 | // We added a new entry, so we now need to extend the PLT sections. |
| 1857 | assert(plt_index == elf.plt.count()); |
| 1858 | elf.plt.putAssumeCapacityNoClobber(.global(global_name), {}); |
| 1859 | |
| 1043 | 1860 | switch (elf.ehdrField(.machine)) { |
| 1044 | 1861 | else => |machine| @panic(@tagName(machine)), |
| 1045 | 1862 | .X86_64 => { |
| ... | ... | @@ -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 | 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 | 1889 | inline else => |shdr, class| { |
| 1074 | 1890 | const ent_size = @sizeOf(class.ElfN().Addr); |
| 1075 | 1891 | const old_size = ent_size * (3 + plt_index); |
| 1892 | assert(elf.targetLoad(&shdr.size) == old_size); |
| 1076 | 1893 | elf.targetStore(&shdr.size, old_size + ent_size); |
| 1077 | 1894 | std.mem.writeInt( |
| 1078 | 1895 | class.ElfN().Addr, |
| ... | ... | @@ -1080,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 1080 | 1897 | @intCast(plt_addr), |
| 1081 | 1898 | target_endian, |
| 1082 | 1899 | ); |
| 1083 | | break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size; |
| 1084 | 1900 | }, |
| 1085 | | }; |
| 1901 | } |
| 1086 | 1902 | |
| 1087 | 1903 | const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni; |
| 1088 | 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 | 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 | 1934 | node: MappedFile.Node.Index, |
| 1143 | 1935 | |
| 1144 | 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 | 1939 | const Global = struct { |
| 1148 | 1940 | /// The current index of the symtab entry for this global symbol. |
| ... | ... | @@ -1159,16 +1951,6 @@ const Symbol = struct { |
| 1159 | 1951 | /// |
| 1160 | 1952 | /// If `node` is `.none`, this is `.empty`. |
| 1161 | 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 | 1956 | /// An index directly into the symtab. These values are not stable (global symbols are sometimes |
| ... | ... | @@ -1181,23 +1963,20 @@ const Symbol = struct { |
| 1181 | 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 | 1966 | fn ptr(si: Symbol.Index, elf: *Elf) *Symbol { |
| 1199 | 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 | 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 | 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 | 2082 | /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at |
| 1266 | 2083 | /// some point due to a call to `flushMoved`. |
| 1267 | 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 | 2145 | .type = sym_type, |
| 1329 | 2146 | .shndx = shndx, |
| 1330 | 2147 | }), |
| 1331 | | .first_reloc = .none, |
| 2148 | .first_symbol_reloc = .none, |
| 2149 | .first_got_reloc = .none, |
| 1332 | 2150 | }; |
| 1333 | 2151 | elf.nodes.appendAssumeCapacity(switch (lazy.kind) { |
| 1334 | 2152 | .code => .{ .lazy_code = @enumFromInt(gop.index) }, |
| ... | ... | @@ -1377,7 +2195,7 @@ pub fn addReloc( |
| 1377 | 2195 | offset: u64, |
| 1378 | 2196 | target: link.File.SymbolId, |
| 1379 | 2197 | addend: i64, |
| 1380 | | @"type": Reloc.Type, |
| 2198 | @"type": MachineRelocType, |
| 1381 | 2199 | ) !void { |
| 1382 | 2200 | const node: MappedFile.Node.Index = Node.fromAtom(atom); |
| 1383 | 2201 | try elf.ensureUnusedRelocCapacity(node, 1); |
| ... | ... | @@ -1532,7 +2350,6 @@ const StringTable = struct { |
| 1532 | 2350 | .{ .slice = slice_const }, |
| 1533 | 2351 | ); |
| 1534 | 2352 | if (gop.found_existing) return gop.key_ptr.*; |
| 1535 | | try ni.resized(gpa, &elf.mf); |
| 1536 | 2353 | const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) { |
| 1537 | 2354 | inline else => |shdr| { |
| 1538 | 2355 | const old_size: u32 = @intCast(elf.targetLoad(&shdr.size)); |
| ... | ... | @@ -1541,6 +2358,9 @@ const StringTable = struct { |
| 1541 | 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 | 2364 | _, const node_size = ni.location(&elf.mf).resolve(&elf.mf); |
| 1545 | 2365 | if (new_size > node_size) |
| 1546 | 2366 | try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor); |
| ... | ... | @@ -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 | 2392 | pub fn open( |
| 1841 | 2393 | arena: std.mem.Allocator, |
| 1842 | 2394 | comp: *Compilation, |
| ... | ... | @@ -1941,6 +2493,8 @@ fn create( |
| 1941 | 2493 | .dynstr = .UNDEF, |
| 1942 | 2494 | .dynamic = .UNDEF, |
| 1943 | 2495 | .tdata = .UNDEF, |
| 2496 | .rela_dyn = .UNDEF, |
| 2497 | .rela_plt = .UNDEF, |
| 1944 | 2498 | .init_array = .UNDEF, |
| 1945 | 2499 | .fini_array = .UNDEF, |
| 1946 | 2500 | .preinit_array = .UNDEF, |
| ... | ... | @@ -1957,13 +2511,10 @@ fn create( |
| 1957 | 2511 | .shstrtab = .{ .map = .empty }, |
| 1958 | 2512 | .strtab = .{ .map = .empty }, |
| 1959 | 2513 | .dynstr = .{ .map = .empty }, |
| 1960 | | .got = .{ |
| 1961 | | .len = 0, |
| 1962 | | .tlsld = .none, |
| 1963 | | .plt = .empty, |
| 1964 | | }, |
| 1965 | | .first_plt_reloc = .none, |
| 1966 | | .first_dynamic_reloc = .none, |
| 2514 | .got = .empty, |
| 2515 | .plt = .empty, |
| 2516 | .plt_first_symbol_reloc = .none, |
| 2517 | .dynamic_first_symbol_reloc = .none, |
| 1967 | 2518 | .needed = .empty, |
| 1968 | 2519 | .inputs = .empty, |
| 1969 | 2520 | .input_sections = .empty, |
| ... | ... | @@ -1975,12 +2526,15 @@ fn create( |
| 1975 | 2526 | .pending_index = 0, |
| 1976 | 2527 | }), |
| 1977 | 2528 | .pending_uavs = .empty, |
| 1978 | | .relocs = .empty, |
| 2529 | .symbol_relocs = .empty, |
| 2530 | .got_relocs = .empty, |
| 2531 | .tls_size_symbol_relocs = .empty, |
| 1979 | 2532 | .section_by_name = .empty, |
| 1980 | 2533 | .changed_symtab_index = .empty, |
| 1981 | 2534 | .const_prog_node = .none, |
| 1982 | 2535 | .synth_prog_node = .none, |
| 1983 | 2536 | .input_prog_node = .none, |
| 2537 | .textrel_count = 0, |
| 1984 | 2538 | }; |
| 1985 | 2539 | errdefer elf.deinit(); |
| 1986 | 2540 | |
| ... | ... | @@ -2004,7 +2558,8 @@ pub fn deinit(elf: *Elf) void { |
| 2004 | 2558 | elf.shstrtab.map.deinit(gpa); |
| 2005 | 2559 | elf.strtab.map.deinit(gpa); |
| 2006 | 2560 | elf.dynstr.map.deinit(gpa); |
| 2007 | | elf.got.plt.deinit(gpa); |
| 2561 | elf.got.deinit(gpa); |
| 2562 | elf.plt.deinit(gpa); |
| 2008 | 2563 | elf.needed.deinit(gpa); |
| 2009 | 2564 | for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m); |
| 2010 | 2565 | elf.inputs.deinit(gpa); |
| ... | ... | @@ -2013,7 +2568,9 @@ pub fn deinit(elf: *Elf) void { |
| 2013 | 2568 | elf.uavs.deinit(gpa); |
| 2014 | 2569 | for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); |
| 2015 | 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 | 2574 | elf.section_by_name.deinit(gpa); |
| 2018 | 2575 | elf.changed_symtab_index.deinit(gpa); |
| 2019 | 2576 | elf.* = undefined; |
| ... | ... | @@ -2323,7 +2880,7 @@ fn initHeaders( |
| 2323 | 2880 | .entsize = 0, |
| 2324 | 2881 | }; |
| 2325 | 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 | 2885 | elf.symtab.addOneAssumeCapacity().* = .{ |
| 2329 | 2886 | .node = .none, |
| ... | ... | @@ -2398,8 +2955,15 @@ fn initHeaders( |
| 2398 | 2955 | if (@"type" != .REL) { |
| 2399 | 2956 | elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{ |
| 2400 | 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 | 2964 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 2402 | 2965 | .addralign = addr_align, |
| 2966 | .entsize = @intCast(addr_align.toByteUnits()), |
| 2403 | 2967 | }); |
| 2404 | 2968 | elf.shndx.got_plt = try elf.addSection( |
| 2405 | 2969 | if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data, |
| ... | ... | @@ -2413,6 +2977,7 @@ fn initHeaders( |
| 2413 | 2977 | .X86_64 => 3 * 8, |
| 2414 | 2978 | }, |
| 2415 | 2979 | .addralign = addr_align, |
| 2980 | .entsize = @intCast(addr_align.toByteUnits()), |
| 2416 | 2981 | }, |
| 2417 | 2982 | ); |
| 2418 | 2983 | const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec = |
| ... | ... | @@ -2508,7 +3073,7 @@ fn initHeaders( |
| 2508 | 3073 | .NONE, _ => unreachable, |
| 2509 | 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 | 3077 | .name = ".rela.dyn", |
| 2513 | 3078 | .type = .RELA, |
| 2514 | 3079 | .flags = .{ .ALLOC = true }, |
| ... | ... | @@ -2517,13 +3082,12 @@ fn initHeaders( |
| 2517 | 3082 | .entsize = rela_size, |
| 2518 | 3083 | .node_align = elf.mf.flags.block_size, |
| 2519 | 3084 | }); |
| 2520 | | const got_plt_shndx = elf.shndx.got_plt; |
| 2521 | | got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{ |
| 3085 | elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{ |
| 2522 | 3086 | .name = ".rela.plt", |
| 2523 | 3087 | .type = .RELA, |
| 2524 | 3088 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, |
| 2525 | 3089 | .link = elf.shndx.dynsym.toSection().?, |
| 2526 | | .info = got_plt_shndx.toSection().?, |
| 3090 | .info = elf.shndx.got_plt.toSection().?, |
| 2527 | 3091 | .addralign = addr_align, |
| 2528 | 3092 | .entsize = rela_size, |
| 2529 | 3093 | .node_align = elf.mf.flags.block_size, |
| ... | ... | @@ -2546,7 +3110,7 @@ fn initHeaders( |
| 2546 | 3110 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip) |
| 2547 | 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 | 3114 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); |
| 2551 | 3115 | elf.addRelocAssumeCapacity( |
| 2552 | 3116 | plt_ni, |
| ... | ... | @@ -2575,6 +3139,26 @@ fn initHeaders( |
| 2575 | 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 | 3162 | // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/ |
| 2579 | 3163 | // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection` |
| 2580 | 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 | 3263 | return elf.nodes.get(@intFromEnum(ni)); |
| 2680 | 3264 | } |
| 2681 | 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 | 3267 | return switch (elf.getNode(ni)) { |
| 2684 | 3268 | .file => unreachable, |
| 2685 | 3269 | .ehdr => unreachable, |
| ... | ... | @@ -2717,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 2717 | 3301 | /// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support |
| 2718 | 3302 | /// the special-case sections '.plt' and '.dynamic'. |
| 2719 | 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 | 3305 | .file => unreachable, // cannot contain relocs |
| 2722 | 3306 | .ehdr => unreachable, // cannot contain relocs |
| 2723 | 3307 | .shdr => unreachable, // cannot contain relocs |
| 2724 | 3308 | .segment => unreachable, // cannot contain relocs |
| 2725 | 3309 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) |
| 2726 | | .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc, |
| 2727 | | .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc, |
| 2728 | | .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc, |
| 2729 | | inline .lazy_code, .lazy_const_data => |lmi| &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_reloc, |
| 3310 | .input_section => |isi| .{ |
| 3311 | &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc, |
| 3312 | &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc, |
| 3313 | }, |
| 3314 | .nav => |nmi| .{ |
| 3315 | &elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc, |
| 3316 | &elf.navs.values()[@intFromEnum(nmi)].first_got_reloc, |
| 3317 | }, |
| 3318 | .uav => |umi| .{ |
| 3319 | &elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc, |
| 3320 | null, |
| 3321 | }, |
| 3322 | inline .lazy_code, .lazy_const_data => |lmi| .{ |
| 3323 | &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc, |
| 3324 | &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc, |
| 3325 | }, |
| 2730 | 3326 | }; |
| 2731 | | if (first_reloc_ptr.* != .none) { |
| 2732 | | for (elf.relocs.items[@intFromEnum(first_reloc_ptr.*)..]) |*reloc| { |
| 3327 | |
| 3328 | if (symbol_relocs.* != .none) { |
| 3329 | for ( |
| 3330 | elf.symbol_relocs.items[@intFromEnum(symbol_relocs.*)..], |
| 3331 | @intFromEnum(symbol_relocs.*).., |
| 3332 | ) |*reloc, index| { |
| 2733 | 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 |
| 2741 | | /// needed. In relocatables, this means updating the offsets of those relocations. In ELF modules, |
| 2742 | | /// this means applying the relocations. |
| 3350 | /// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this |
| 3351 | /// means updating the relocations' offsets. In ELF modules, this means applying the relocations. |
| 2743 | 3352 | fn flushMovedNodeRelocs( |
| 2744 | 3353 | elf: *Elf, |
| 2745 | 3354 | node: MappedFile.Node.Index, |
| 2746 | 3355 | node_vaddr: u64, |
| 2747 | | first_reloc: Reloc.Index, |
| 3356 | first_symbol_reloc: SymbolReloc.Index, |
| 3357 | first_got_reloc: GotReloc.Index, |
| 2748 | 3358 | ) void { |
| 2749 | | if (first_reloc == .none) return; |
| 2750 | | switch (elf.ehdrField(.type)) { |
| 2751 | | .NONE, .CORE, _ => unreachable, |
| 2752 | | .REL => { |
| 2753 | | // In a relocatable, we're not actually applying any relocations ourselves, but we need |
| 2754 | | // to update the offsets of the relocation entries since the node they're in has moved. |
| 2755 | | for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| { |
| 2756 | | if (reloc.node != node) break; |
| 2757 | | reloc.updateNodeOffset(elf, node_vaddr); |
| 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; |
| 3359 | if (first_symbol_reloc != .none) { |
| 3360 | for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| { |
| 3361 | if (reloc.node != node) break; |
| 3362 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 3363 | // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're |
| 3364 | // in has moved, so their offset within the section might also have moved. |
| 3365 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 3366 | } else { |
| 3367 | // We've applied this relocation ourselves! Just re-apply it now. |
| 2764 | 3368 | reloc.apply(elf); |
| 2765 | 3369 | } |
| 2766 | | // TODO: once we're emitting runtime relocation entries, we need to update their offsets |
| 2767 | | // too, like the logic for relocatables above. |
| 2768 | | }, |
| 3370 | } |
| 3371 | } |
| 3372 | |
| 3373 | if (first_got_reloc != .none) { |
| 3374 | for (elf.got_relocs.items[@intFromEnum(first_got_reloc)..]) |*reloc| { |
| 3375 | if (reloc.node != node) break; |
| 3376 | reloc.apply(elf); |
| 3377 | } |
| 2769 | 3378 | } |
| 2770 | 3379 | } |
| 2771 | 3380 | |
| ... | ... | @@ -3109,7 +3718,8 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM |
| 3109 | 3718 | .type = elf.navType(nav.resolved.?), |
| 3110 | 3719 | .shndx = shndx, |
| 3111 | 3720 | }), |
| 3112 | | .first_reloc = .none, |
| 3721 | .first_symbol_reloc = .none, |
| 3722 | .first_got_reloc = .none, |
| 3113 | 3723 | }; |
| 3114 | 3724 | elf.nodes.appendAssumeCapacity(.{ .nav = nmi }); |
| 3115 | 3725 | } |
| ... | ... | @@ -3158,7 +3768,7 @@ fn uavMapIndex( |
| 3158 | 3768 | .type = .OBJECT, |
| 3159 | 3769 | .shndx = shndx, |
| 3160 | 3770 | }), |
| 3161 | | .first_reloc = .none, |
| 3771 | .first_symbol_reloc = .none, |
| 3162 | 3772 | }; |
| 3163 | 3773 | elf.nodes.appendAssumeCapacity(.{ .uav = umi }); |
| 3164 | 3774 | elf.const_prog_node.increaseEstimatedTotalItems(1); |
| ... | ... | @@ -3447,10 +4057,11 @@ fn loadObject( |
| 3447 | 4057 | switch (elf.shdrPtr(shndx.*)) { |
| 3448 | 4058 | inline else => |shdr| { |
| 3449 | 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 | 4065 | break :shndx shndx.*; |
| 3455 | 4066 | }, |
| 3456 | 4067 | .has_file_bits = true, |
| ... | ... | @@ -3482,7 +4093,8 @@ fn loadObject( |
| 3482 | 4093 | // zero-based. This will eventually be updated by `flushMoved`. |
| 3483 | 4094 | .vaddr = 0, |
| 3484 | 4095 | .node = ni, |
| 3485 | | .first_reloc = .none, |
| 4096 | .first_symbol_reloc = .none, |
| 4097 | .first_got_reloc = .none, |
| 3486 | 4098 | }; |
| 3487 | 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 | 4389 | .DEFAULT, .PROTECTED => {}, |
| 3778 | 4390 | } |
| 3779 | 4391 | |
| 4392 | if (sym.shndx == std.elf.SHN_UNDEF) continue; |
| 4393 | |
| 3780 | 4394 | if (sym.name >= dynstr.len) { |
| 3781 | 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 | 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 |
| 3791 | | // its type now. |
| 3792 | | update_sym_type: { |
| 3793 | | const global_ptr = elf.globals.strong_undef.getPtr(name) orelse |
| 3794 | | elf.globals.weak_undef.getPtr(name) orelse |
| 3795 | | break :update_sym_type; |
| 4404 | // If there's already an undefined symbol by this name of type STT_NOTYPE, populate |
| 4405 | // its type now. |
| 4406 | const global_ptr = elf.globals.strong_undef.getPtr(name) orelse |
| 4407 | elf.globals.weak_undef.getPtr(name) orelse |
| 4408 | continue; |
| 4409 | |
| 4410 | if (global_ptr.dynsym_index == 0) continue; |
| 4411 | |
| 4412 | const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class)); |
| 4413 | switch (elf.targetLoad(&sym_ptr.other).visibility) { |
| 4414 | .HIDDEN, .INTERNAL, .PROTECTED => continue, |
| 4415 | .DEFAULT => {}, |
| 4416 | } |
| 4417 | |
| 4418 | if (elf.targetLoad(&sym_ptr.shndx) != std.elf.SHN_UNDEF) continue; |
| 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)); |
| 3800 | | switch (elf.targetLoad(&sym_ptr.other).visibility) { |
| 3801 | | .HIDDEN, .INTERNAL, .PROTECTED => break :update_sym_type, |
| 3802 | | .DEFAULT => {}, |
| 3803 | | } |
| 4427 | elf.targetStore(&sym_ptr.info, .{ |
| 4428 | .bind = cur_info.bind, |
| 4429 | .type = new_type, |
| 4430 | }); |
| 3804 | 4431 | |
| 3805 | | const cur_info = elf.targetLoad(&sym_ptr.info); |
| 3806 | | if (cur_info.type == .NOTYPE) { |
| 3807 | | elf.targetStore(&sym_ptr.info, .{ |
| 3808 | | .bind = cur_info.bind, |
| 3809 | | .type = sym.info.type, |
| 3810 | | }); |
| 3811 | | |
| 3812 | | const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class)); |
| 3813 | | elf.targetStore(&dynsym_ptr.info, .{ |
| 3814 | | .bind = elf.targetLoad(&dynsym_ptr.info).bind, |
| 3815 | | .type = sym.info.type, |
| 3816 | | }); |
| 3817 | | |
| 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 | | } |
| 4432 | const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class)); |
| 4433 | elf.targetStore(&dynsym_ptr.info, .{ |
| 4434 | .bind = elf.targetLoad(&dynsym_ptr.info).bind, |
| 4435 | .type = new_type, |
| 4436 | }); |
| 4437 | |
| 4438 | // If we just turned this into an STT_FUNC symbol, then we have determined |
| 4439 | // that it needs a PLT entry. |
| 4440 | if (new_type == .FUNC) { |
| 4441 | elf.addPltEntry(name, global_ptr.dynsym_index); |
| 4442 | // ...and therefore, we need to re-apply that symbol's relocations, as |
| 4443 | // some might be targeting its PLT entry. |
| 4444 | global_ptr.symtab_index.applyTargetRelocs(elf); |
| 3823 | 4445 | } |
| 3824 | 4446 | } |
| 3825 | 4447 | } |
| ... | ... | @@ -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 | 4583 | pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { |
| 3939 | 4584 | _ = prog_node; |
| ... | ... | @@ -4073,17 +4718,15 @@ fn prelinkInner(elf: *Elf) !void { |
| 4073 | 4718 | ); |
| 4074 | 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 | 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 | 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 | 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 | 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 | 4731 | .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) }, |
| 4089 | 4732 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, |
| ... | ... | @@ -4100,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void { |
| 4100 | 4743 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 4101 | 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 | 4747 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 5); |
| 4105 | 4748 | elf.addRelocAssumeCapacity( |
| 4106 | 4749 | dynamic_ni, |
| 4107 | 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 | 4752 | 0, |
| 4110 | 4753 | .absAddr(elf), |
| 4111 | 4754 | ); |
| 4112 | 4755 | elf.addRelocAssumeCapacity( |
| 4113 | 4756 | dynamic_ni, |
| 4114 | 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 | 4759 | 0, |
| 4117 | 4760 | .absAddr(elf), |
| 4118 | 4761 | ); |
| ... | ... | @@ -4216,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 4216 | 4859 | .type = .SECTION, |
| 4217 | 4860 | .shndx = shndx, |
| 4218 | 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 | 4867 | elf.nodes.appendAssumeCapacity(.{ .section = shndx }); |
| 4221 | 4868 | const offset = ni.fileLocation(&elf.mf, false).offset; |
| 4222 | 4869 | switch (elf.shdrPtr(shndx)) { |
| ... | ... | @@ -4242,13 +4889,14 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 4242 | 4889 | fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void { |
| 4243 | 4890 | if (len == 0) return; |
| 4244 | 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 | 4894 | const class = elf.identClass(); |
| 4247 | | const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) { |
| 4895 | switch (elf.ehdrField(.type)) { |
| 4248 | 4896 | .NONE, .CORE, _ => unreachable, |
| 4249 | 4897 | .REL => { |
| 4250 | 4898 | const shndx = elf.getNodeShndx(node); |
| 4251 | | if (shndx.get(elf).rela_shndx == .UNDEF) { |
| 4899 | if (shndx.get(elf).rela.shndx == .UNDEF) { |
| 4252 | 4900 | var bfa_buf: [32]u8 = undefined; |
| 4253 | 4901 | var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa); |
| 4254 | 4902 | const allocator = bfa.allocator(); |
| ... | ... | @@ -4275,33 +4923,28 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) |
| 4275 | 4923 | .node_align = elf.mf.flags.block_size, |
| 4276 | 4924 | }); |
| 4277 | 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) { |
| 4283 | | _ => return, |
| 4284 | | .none => if (elf.shndx.dynamic != .UNDEF) { |
| 4285 | | try elf.mf.updates.ensureUnusedCapacity(gpa, 1); |
| 4286 | | const got_ni = elf.shndx.got.get(elf).ni; |
| 4287 | | _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf); |
| 4288 | | const got_size = switch (class) { |
| 4289 | | .NONE, _ => unreachable, |
| 4290 | | inline else => |ct_class| (elf.got.len + 2) * @sizeOf(ct_class.ElfN().Addr), |
| 4291 | | }; |
| 4292 | | if (got_size > got_node_size) |
| 4293 | | try got_ni.resize(&elf.mf, gpa, got_size +| got_size / MappedFile.growth_factor); |
| 4294 | | break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 }; |
| 4295 | | } else return, |
| 4930 | .EXEC, .DYN => { |
| 4931 | try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 4932 | const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD |
| 4933 | try elf.got.ensureUnusedCapacity(gpa, new_got_entries); |
| 4934 | const got_ni = elf.shndx.got.get(elf).ni; |
| 4935 | _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf); |
| 4936 | const need_got_size = switch (class) { |
| 4937 | .NONE, _ => unreachable, |
| 4938 | inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr), |
| 4939 | }; |
| 4940 | if (need_got_size > got_node_size) |
| 4941 | try got_ni.resize(&elf.mf, gpa, need_got_size +| need_got_size / MappedFile.growth_factor); |
| 4942 | |
| 4943 | if (elf.shndx.dynamic != .UNDEF) { |
| 4944 | try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries); |
| 4945 | } |
| 4296 | 4946 | }, |
| 4297 | | }; |
| 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); |
| 4947 | } |
| 4305 | 4948 | } |
| 4306 | 4949 | fn addRelocAssumeCapacity( |
| 4307 | 4950 | elf: *Elf, |
| ... | ... | @@ -4309,123 +4952,443 @@ fn addRelocAssumeCapacity( |
| 4309 | 4952 | offset: u64, |
| 4310 | 4953 | target: Symbol.Id, |
| 4311 | 4954 | addend: i64, |
| 4312 | | @"type": Reloc.Type, |
| 4955 | @"type": MachineRelocType, |
| 4313 | 4956 | ) void { |
| 4314 | 4957 | assert(node != .none); |
| 4315 | | const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len); |
| 4316 | | const next: Reloc.Index = next: { |
| 4317 | | const target_ptr = target.index(elf).ptr(elf); |
| 4318 | | const next = target_ptr.first_target_reloc; |
| 4319 | | target_ptr.first_target_reloc = ri; |
| 4320 | | break :next next; |
| 4958 | switch (elf.ehdrField(.type)) { |
| 4959 | .NONE, .CORE, _ => unreachable, |
| 4960 | .REL => { |
| 4961 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| 4962 | const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ |
| 4963 | .type = @"type", |
| 4964 | // This field needs to equal the offset into the section, which is *not* necessarily |
| 4965 | // the same thing as our `offset`, which is the offset into `node`. We could compute |
| 4966 | // the section offset now, but there's no point, because `flushMovedNodeRelocs` will |
| 4967 | // eventually do it for us anyway, so just init to 0. |
| 4968 | .offset = 0, |
| 4969 | .raw_sym_index = @intFromEnum(target.index(elf)), |
| 4970 | .addend = addend, |
| 4971 | }); |
| 4972 | const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len); |
| 4973 | const next: SymbolReloc.Index = next: { |
| 4974 | const target_ptr = target.index(elf).ptr(elf); |
| 4975 | const next = target_ptr.first_target_reloc; |
| 4976 | target_ptr.first_target_reloc = ri; |
| 4977 | break :next next; |
| 4978 | }; |
| 4979 | if (next != .none) { |
| 4980 | next.get(elf).prev = ri; |
| 4981 | } |
| 4982 | elf.symbol_relocs.appendAssumeCapacity(.{ |
| 4983 | .node = node, |
| 4984 | .offset = offset, |
| 4985 | .type = .write_rela, |
| 4986 | .target = target, |
| 4987 | .addend = addend, |
| 4988 | .next = next, |
| 4989 | .prev = .none, |
| 4990 | .rela_index = rela_index.toOptional(), |
| 4991 | }); |
| 4992 | }, |
| 4993 | |
| 4994 | .DYN, .EXEC => switch (elf.ehdrField(.machine)) { |
| 4995 | else => |machine| @panic(@tagName(machine)), |
| 4996 | .X86_64 => switch (@"type".X86_64) { |
| 4997 | _, |
| 4998 | .NONE, |
| 4999 | .COPY, |
| 5000 | .GLOB_DAT, |
| 5001 | .JUMP_SLOT, |
| 5002 | .RELATIVE64, |
| 5003 | .RELATIVE, |
| 5004 | .IRELATIVE, |
| 5005 | .@"16", |
| 5006 | .PC16, |
| 5007 | .@"8", |
| 5008 | .PC8, |
| 5009 | .DTPMOD64, |
| 5010 | .GOTPLT64, |
| 5011 | => @panic("TODO: error for illegal or unsupported input relocation"), |
| 5012 | |
| 5013 | // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt |
| 5014 | .GOTPC32_TLSDESC => @panic("TODO: R_X86_64_GOTPC32_TLSDESC"), |
| 5015 | .TLSDESC_CALL => @panic("TODO: R_X86_64_TLSDESC_CALL"), |
| 5016 | .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"), |
| 5017 | |
| 5018 | // Relocations targeting a symbol |
| 5019 | .@"64" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), |
| 5020 | .@"32" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), |
| 5021 | .@"32S" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s), |
| 5022 | .PC64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64), |
| 5023 | .PC32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), |
| 5024 | .PLT32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32), |
| 5025 | .SIZE64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64), |
| 5026 | .SIZE32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32), |
| 5027 | .DTPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64), |
| 5028 | .DTPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32), |
| 5029 | .TPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64), |
| 5030 | .TPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32), |
| 5031 | .GOTPC64 => { |
| 5032 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 5033 | return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64); |
| 5034 | }, |
| 5035 | .GOTPC32 => { |
| 5036 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 5037 | return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32); |
| 5038 | }, |
| 5039 | |
| 5040 | // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the |
| 5041 | // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm |
| 5042 | // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which |
| 5043 | // need to be re-applied whenever the GOT moves. |
| 5044 | .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base |
| 5045 | .PLTOFF64 => @panic("TODO: R_X86_64_PLTOFF64"), // offset of PLT entry from GOT base (yes, I know, the name is stupid) |
| 5046 | |
| 5047 | // Relocations targeting a GOT entry |
| 5048 | .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset64), |
| 5049 | .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset32), |
| 5050 | .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64), |
| 5051 | .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 5052 | // TODO: the next two are relaxable to non-GOT relocations, but I haven't figured |
| 5053 | // out how to represent relaxations yet. If we want to remove a `GotReloc` and add a |
| 5054 | // `SymbolReloc` at some point, we can't do that in `GotReloc.apply`, because that |
| 5055 | // function must be idempotent to ensure reproducible binaries. I think we would |
| 5056 | // need to do that as soon as the operation is known to be relaxable (e.g. because |
| 5057 | // we found a defininition for a non-preemptible symbol). |
| 5058 | .GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 5059 | .REX_GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 5060 | |
| 5061 | .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .rel32), |
| 5062 | .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32), |
| 5063 | .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32), |
| 5064 | }, |
| 5065 | }, |
| 5066 | } |
| 5067 | } |
| 5068 | fn addSymbolRelocAssumeCapacity( |
| 5069 | elf: *Elf, |
| 5070 | node: MappedFile.Node.Index, |
| 5071 | offset: u64, |
| 5072 | target: Symbol.Id, |
| 5073 | addend: i64, |
| 5074 | @"type": SymbolReloc.Type, |
| 5075 | ) void { |
| 5076 | assert(elf.ehdrField(.type) != .REL); |
| 5077 | |
| 5078 | const rela_index: Section.RelaIndex.Optional = r: { |
| 5079 | if (elf.shndx.dynamic == .UNDEF) break :r .none; |
| 5080 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 5081 | else => |machine| @panic(@tagName(machine)), |
| 5082 | .X86_64 => .{ .X86_64 = switch (@"type") { |
| 5083 | .write_rela => unreachable, |
| 5084 | .abs64 => .@"64", |
| 5085 | .abs32 => .@"32", |
| 5086 | .abs32s => .@"32S", |
| 5087 | .rel64 => .PC64, |
| 5088 | .rel32 => .PC32, |
| 5089 | .pltrel64 => break :r .none, |
| 5090 | .pltrel32 => break :r .none, |
| 5091 | .dtpoff64 => .DTPOFF64, |
| 5092 | .dtpoff32 => .DTPOFF32, |
| 5093 | .tpoff64 => .TPOFF64, |
| 5094 | .tpoff32 => .TPOFF32, |
| 5095 | .size64 => .SIZE64, |
| 5096 | .size32 => .SIZE32, |
| 5097 | } }, |
| 5098 | }; |
| 5099 | const dynsym_index: u32 = switch (target.unwrap()) { |
| 5100 | .local => break :r .none, |
| 5101 | // TODO: even if the symbol is locally defined, preemption/interposition is a |
| 5102 | // possibility, which this condition does not currently consider! |
| 5103 | .global => |name| if (elf.globals.strong_def.contains(name) or |
| 5104 | elf.globals.weak_def.contains(name)) |
| 5105 | { |
| 5106 | break :r .none; |
| 5107 | } else elf.globalByName(name).?.dynsym_index, |
| 5108 | }; |
| 5109 | |
| 5110 | if (elf.nodeRequiresTextrel(node)) { |
| 5111 | elf.textrel_count += 1; |
| 5112 | } |
| 5113 | |
| 5114 | // It currently looks like we need a runtime relocation for this. |
| 5115 | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 5116 | .type = rela_type, |
| 5117 | // This field needs to equal the offset into the section, which is *not* necessarily |
| 5118 | // the same thing as our `offset`, which is the offset into `node`. We could compute |
| 5119 | // the section offset now, but there's no point, because `flushMovedNodeRelocs` will |
| 5120 | // eventually do it for us anyway, so just init to 0. |
| 5121 | .offset = 0, |
| 5122 | .raw_sym_index = dynsym_index, |
| 5123 | .addend = addend, |
| 5124 | }).toOptional(); |
| 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 | 5131 | if (next != .none) { |
| 4323 | 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 | 5139 | .type = @"type", |
| 4327 | | .prev = .none, |
| 4328 | 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 | 5203 | .node = node, |
| 5204 | .offset = offset, |
| 4330 | 5205 | .target = target, |
| 4331 | | .index = index: switch (elf.ehdrField(.type)) { |
| 4332 | | .NONE, .CORE, _ => unreachable, |
| 4333 | | .REL => { |
| 4334 | | const sh = elf.getNodeShndx(node).get(elf); |
| 4335 | | switch (elf.shdrPtr(sh.rela_shndx)) { |
| 4336 | | inline else => |shdr, class| { |
| 4337 | | const Rela = class.ElfN().Rela; |
| 4338 | | const ent_size = elf.targetLoad(&shdr.entsize); |
| 4339 | | const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf); |
| 4340 | | const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: { |
| 4341 | | const rela: *Rela = @ptrCast(@alignCast( |
| 4342 | | rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], |
| 4343 | | )); |
| 4344 | | sh.rela_free = @enumFromInt(rela.offset); |
| 4345 | | break :alloc_index index; |
| 4346 | | } else alloc_index: { |
| 4347 | | const old_size = elf.targetLoad(&shdr.size); |
| 4348 | | const new_size = old_size + ent_size; |
| 4349 | | elf.targetStore(&shdr.size, @intCast(new_size)); |
| 4350 | | break :alloc_index @intCast(@divExact(old_size, ent_size)); |
| 4351 | | }; |
| 4352 | | const rela: *Rela = @ptrCast(@alignCast( |
| 4353 | | rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], |
| 4354 | | )); |
| 4355 | | // The `offset` field here needs to equal the offset into the section, which |
| 4356 | | // is *not* the same as our `offset` which is the offset into `node`. We |
| 4357 | | // could calculate it now, but there's no point since `flushMovedNodeRelocs` |
| 4358 | | // will eventually do that for us anyway. So for now, just set offset to 0. |
| 4359 | | rela.* = .{ |
| 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); |
| 5206 | .addend = addend, |
| 5207 | .type = @"type", |
| 5208 | }); |
| 5209 | } |
| 5210 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 5211 | const entry_value: union(enum) { |
| 5212 | unsigned: u64, |
| 5213 | signed: i64, |
| 5214 | reloc: struct { |
| 5215 | type: MachineRelocType, |
| 5216 | dynsym_index: u32, |
| 5217 | }, |
| 5218 | } = switch (elf.got.keys()[got_index]) { |
| 5219 | .reserved => .{ .unsigned = 0 }, |
| 5220 | .tpoff => |sym_id| val: { |
| 5221 | // We will break from this block if we require a relocation. |
| 5222 | known: { |
| 5223 | if (elf.base.comp.config.output_mode != .Exe) { |
| 5224 | // Only the executable's per-module TLS block is at a known offset from the |
| 5225 | // general TLS pointer. |
| 5226 | break :known; |
| 5227 | } |
| 5228 | switch (sym_id.unwrap()) { |
| 5229 | .local => {}, |
| 5230 | .global => |name| if (elf.globals.strong_undef.contains(name) or |
| 5231 | elf.globals.weak_undef.contains(name)) |
| 5232 | { |
| 5233 | // This is an external TLS symbol, so we don't know its offset. |
| 5234 | break :known; |
| 4369 | 5235 | }, |
| 4370 | 5236 | } |
| 4371 | | }, |
| 4372 | | .EXEC, .DYN => { |
| 4373 | | switch (elf.ehdrField(.machine)) { |
| 4374 | | else => |machine| @panic(@tagName(machine)), |
| 4375 | | .AARCH64, .PPC64, .RISCV => {}, |
| 4376 | | .X86_64 => switch (@"type".X86_64) { |
| 4377 | | else => {}, |
| 4378 | | .TLSLD => switch (elf.got.tlsld) { |
| 4379 | | _ => {}, |
| 4380 | | .none => if (elf.shndx.dynamic != .UNDEF) { |
| 4381 | | const tlsld_index = elf.got.len; |
| 4382 | | elf.got.tlsld = .wrap(tlsld_index); |
| 4383 | | elf.got.len = tlsld_index + 2; |
| 4384 | | const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) { |
| 4385 | | inline else => |shdr, class| { |
| 4386 | | const addr_size = @sizeOf(class.ElfN().Addr); |
| 4387 | | const old_size = addr_size * tlsld_index; |
| 4388 | | const new_size = old_size + addr_size * 2; |
| 4389 | | @memset( |
| 4390 | | elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size], |
| 4391 | | 0, |
| 4392 | | ); |
| 4393 | | break :got_addr elf.targetLoad(&shdr.addr) + old_size; |
| 4394 | | }, |
| 5237 | // It's a symbol which we define, the symbol is not interposable because we're the |
| 5238 | // executable, and we know our per-module TLS block's offset because we're the |
| 5239 | // executable. We therefore know this value! |
| 5240 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 5241 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 5242 | inline else => |phdr| tls_size: { |
| 5243 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 5244 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 5245 | }, |
| 5246 | }; |
| 5247 | const sym_value = sym_id.value(elf); |
| 5248 | break :val .{ .signed = @bitCast(sym_value -% tls_size) }; |
| 5249 | } |
| 5250 | break :val .{ |
| 5251 | .reloc = .{ |
| 5252 | .type = switch (elf.ehdrField(.machine)) { |
| 5253 | else => |machine| @panic(@tagName(machine)), |
| 5254 | .X86_64 => .{ .X86_64 = .TPOFF64 }, |
| 5255 | }, |
| 5256 | .dynsym_index = switch (sym_id.unwrap()) { |
| 5257 | .global => |name| elf.globalByName(name).?.dynsym_index, |
| 5258 | // TODO: I have no idea if compilers are even allowed to emit this, but if they |
| 5259 | // are then I guess we need to add this local symbol to `.dynsym`? |
| 5260 | .local => @panic("TODO(Elf2): GOT tpoff entry referencing local symbol"), |
| 5261 | }, |
| 5262 | }, |
| 5263 | }; |
| 5264 | }, |
| 5265 | .symbol, .tlsgd1 => |sym_id, tag| val: { |
| 5266 | const name = switch (sym_id.unwrap()) { |
| 5267 | .local => break :val .{ .unsigned = sym_id.value(elf) }, |
| 5268 | .global => |name| name, |
| 5269 | }; |
| 5270 | // If the symbol is *defined* in this module, we might be able to avoid the relocation. |
| 5271 | if (elf.globals.strong_def.getPtr(name) orelse |
| 5272 | elf.globals.weak_def.getPtr(name)) |global| |
| 5273 | { |
| 5274 | // We have a definition, but it might be interposable (aka preemptible). There |
| 5275 | // are two cases where it is not and so we can (and, in fact, must) elide the |
| 5276 | // runtime relocation: |
| 5277 | // * We are the executable. Symbols from executables cannot be interposed. |
| 5278 | // * The symbol's visibility disallows interposition. |
| 5279 | if (elf.base.comp.config.output_mode == .Exe) { |
| 5280 | // No relocation needed. |
| 5281 | break :val .{ .unsigned = sym_id.value(elf) }; |
| 5282 | } |
| 5283 | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { |
| 5284 | inline else => |sym| elf.targetLoad(&sym.other).visibility, |
| 5285 | }; |
| 5286 | switch (visibility) { |
| 5287 | .DEFAULT => {}, |
| 5288 | .INTERNAL, .HIDDEN, .PROTECTED => { |
| 5289 | // No relocation needed. |
| 5290 | break :val .{ .unsigned = sym_id.value(elf) }; |
| 5291 | }, |
| 5292 | } |
| 5293 | } |
| 5294 | break :val .{ .reloc = .{ |
| 5295 | .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf), |
| 5296 | .dynsym_index = elf.globalByName(name).?.dynsym_index, |
| 5297 | } }; |
| 5298 | }, |
| 5299 | .tlsgd0 => |sym| switch (elf.shndx.dynamic) { |
| 5300 | .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable |
| 5301 | else => .{ |
| 5302 | .reloc = .{ |
| 5303 | .type = .{ .X86_64 = .DTPMOD64 }, |
| 5304 | .dynsym_index = switch (sym.unwrap()) { |
| 5305 | .local => 0, |
| 5306 | .global => |name| dsi: { |
| 5307 | // Like in the `.tlsgd1` case, we need to check for a non-interposable definition. |
| 5308 | if (elf.globals.strong_def.getPtr(name) orelse |
| 5309 | elf.globals.weak_def.getPtr(name)) |global| |
| 5310 | { |
| 5311 | if (elf.base.comp.config.output_mode == .Exe) { |
| 5312 | break :dsi 0; // non-interposable definition |
| 5313 | } |
| 5314 | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { |
| 5315 | inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility, |
| 4395 | 5316 | }; |
| 4396 | | const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx; |
| 4397 | | const rela_dyn_ni = rela_dyn_shndx.get(elf).ni; |
| 4398 | | switch (elf.shdrPtr(rela_dyn_shndx)) { |
| 4399 | | inline else => |shdr, class| { |
| 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); |
| 5317 | switch (visibility) { |
| 5318 | .DEFAULT => {}, |
| 5319 | .INTERNAL, .HIDDEN, .PROTECTED => { |
| 5320 | break :dsi 0; // non-interposable definition |
| 4416 | 5321 | }, |
| 4417 | 5322 | } |
| 4418 | | rela_dyn_ni.resizedAssumeCapacity(&elf.mf); |
| 4419 | | }, |
| 5323 | } |
| 5324 | // `sym` is either undefined or an interposable definition, so use its |
| 5325 | // actual dynsym index. |
| 5326 | break :dsi elf.globalByName(name).?.dynsym_index; |
| 4420 | 5327 | }, |
| 4421 | 5328 | }, |
| 4422 | | } |
| 4423 | | break :index .none; |
| 5329 | }, |
| 4424 | 5330 | }, |
| 4425 | 5331 | }, |
| 4426 | | .offset = offset, |
| 4427 | | .addend = addend, |
| 5332 | .tlsld0 => switch (elf.shndx.dynamic) { |
| 5333 | .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable |
| 5334 | else => .{ .reloc = .{ |
| 5335 | .type = .{ .X86_64 = .DTPMOD64 }, |
| 5336 | .dynsym_index = 0, |
| 5337 | } }, |
| 5338 | }, |
| 5339 | .tlsld1 => .{ .unsigned = 0 }, |
| 5340 | }; |
| 5341 | |
| 5342 | // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes. |
| 5343 | const got_entry_addr: u64 = switch (elf.shdrPtr(elf.shndx.got)) { |
| 5344 | inline else => |got_shdr, class| got_entry_addr: { |
| 5345 | const addr_size = @sizeOf(class.ElfN().Addr); |
| 5346 | const offset = got_index * addr_size; |
| 5347 | const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast( |
| 5348 | elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size], |
| 5349 | )); |
| 5350 | entry_ptr.* = switch (entry_value) { |
| 5351 | .unsigned => |x| @intCast(x), |
| 5352 | .signed => |x| switch (class) { |
| 5353 | .NONE, _ => comptime unreachable, |
| 5354 | .@"32" => @bitCast(@as(i32, @intCast(x))), |
| 5355 | .@"64" => @bitCast(x), |
| 5356 | }, |
| 5357 | .reloc => 0, |
| 5358 | }; |
| 5359 | break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset; |
| 5360 | }, |
| 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 | 5394 | pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| ... | ... | @@ -4565,6 +5528,11 @@ pub fn flush( |
| 4565 | 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 | 5536 | while (try elf.idle(tid)) {} |
| 4569 | 5537 | |
| 4570 | 5538 | const entry_addr: u64 = entry: { |
| ... | ... | @@ -4595,6 +5563,47 @@ pub fn flush( |
| 4595 | 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 | 5608 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4600 | 5609 | const comp = elf.base.comp; |
| ... | ... | @@ -4660,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4660 | 5669 | break :task; |
| 4661 | 5670 | } |
| 4662 | 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 | 5675 | if (elf.ehdrField(.type) == .REL) { |
| 4664 | 5676 | const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0); |
| 4665 | 5677 | defer sub_prog_node.end(); |
| ... | ... | @@ -4667,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 4667 | 5679 | var ri = sym.first_target_reloc; |
| 4668 | 5680 | while (ri != .none) { |
| 4669 | 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 | 5687 | ri = reloc.next; |
| 4672 | 5688 | } |
| 4673 | 5689 | break :task; |
| ... | ... | @@ -4876,126 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 4876 | 5892 | .section => |shndx| { |
| 4877 | 5893 | try elf.flushFileOffset(ni); |
| 4878 | 5894 | const addr = elf.computeNodeVAddr(ni); |
| 4879 | | switch (elf.shdrPtr(shndx)) { |
| 4880 | | inline else => |shdr, class| { |
| 4881 | | const flags = elf.targetLoad(&shdr.flags).shf; |
| 4882 | | if (flags.ALLOC) { |
| 4883 | | if (elf.shndx.dynamic != .UNDEF) { |
| 4884 | | if (shndx == elf.shndx.got) { |
| 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 | | } |
| 5895 | const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) { |
| 5896 | inline else => |shdr| .{ |
| 5897 | elf.targetLoad(&shdr.addr), |
| 5898 | elf.targetLoad(&shdr.flags).shf, |
| 5899 | }, |
| 5900 | }; |
| 4973 | 5901 | |
| 4974 | | // Update global symbols targeting this section |
| 4975 | | if (elf.node_global_symbols.get(ni)) |first_name| { |
| 4976 | | assert(first_name != .empty); |
| 4977 | | const old_addr = elf.targetLoad(&shdr.addr); |
| 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 | | } |
| 5902 | if (flags.ALLOC) { |
| 5903 | switch (elf.shdrPtr(shndx)) { |
| 5904 | inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)), |
| 5905 | } |
| 4988 | 5906 | |
| 4989 | | elf.targetStore(&shdr.addr, @intCast(addr)); |
| 4990 | | shndx.get(elf).lsi.index().flushMoved(elf, addr); |
| 5907 | // Update global symbols targeting this section |
| 5908 | if (elf.node_global_symbols.get(ni)) |first_name| { |
| 5909 | assert(first_name != .empty); |
| 5910 | var name = first_name; |
| 5911 | while (name != .empty) { |
| 5912 | const global = elf.globalByName(name).?; |
| 5913 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { |
| 5914 | inline else => |sym| elf.targetLoad(&sym.value), |
| 5915 | }; |
| 5916 | Symbol.Id.global(name).flushMoved( |
| 5917 | elf, |
| 5918 | old_sym_addr - old_addr + addr, |
| 5919 | ); |
| 5920 | name = global.next_in_node; |
| 4991 | 5921 | } |
| 5922 | } |
| 4992 | 5923 | |
| 4993 | | if (shndx == elf.shndx.plt) { |
| 4994 | | elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc); |
| 4995 | | } else if (shndx == elf.shndx.dynamic) { |
| 4996 | | elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc); |
| 4997 | | } |
| 4998 | | }, |
| 5924 | Symbol.Id.local(shndx.get(elf).lsi).flushMoved(elf, addr); |
| 5925 | } |
| 5926 | |
| 5927 | if (shndx == elf.shndx.got) { |
| 5928 | const rela_dyn_shndx = elf.shndx.rela_dyn; |
| 5929 | for (elf.got.values()) |opt_rela_index| { |
| 5930 | const rela_index = opt_rela_index.unwrap() orelse continue; |
| 5931 | rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr); |
| 5932 | } |
| 5933 | for (elf.got_relocs.items) |*reloc| { |
| 5934 | reloc.apply(elf); |
| 5935 | } |
| 5936 | } else if (shndx == elf.shndx.plt) { |
| 5937 | elf.flushMovedNodeRelocs(ni, addr, elf.plt_first_symbol_reloc, .none); |
| 5938 | elf.flushMovedPltSection(.plt, old_addr, addr); |
| 5939 | } else if (shndx == elf.shndx.got_plt) { |
| 5940 | elf.flushMovedPltSection(.got_plt, old_addr, addr); |
| 5941 | } else if (shndx == elf.shndx.plt_sec) { |
| 5942 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); |
| 5943 | } else if (shndx == elf.shndx.dynamic) { |
| 5944 | elf.flushMovedNodeRelocs(ni, addr, elf.dynamic_first_symbol_reloc, .none); |
| 4999 | 5945 | } |
| 5000 | 5946 | }, |
| 5001 | 5947 | .input_section => |isi| { |
| ... | ... | @@ -5020,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5020 | 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 | 5975 | // Update global symbols |
| ... | ... | @@ -5032,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5032 | 5981 | const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) { |
| 5033 | 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 | 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 | 5999 | inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { |
| 5043 | 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 | 6002 | if (elf.node_global_symbols.get(ni)) |first_name| { |
| 5046 | 6003 | assert(first_name != .empty); |
| 5047 | 6004 | var name = first_name; |
| 5048 | 6005 | while (name != .empty) { |
| 5049 | | const global = elf.globalByName(name).?; |
| 5050 | | global.flushMoved(elf, new_addr); |
| 5051 | | name = global.next_in_node; |
| 6006 | Symbol.Id.global(name).flushMoved(elf, new_addr); |
| 6007 | name = elf.globalByName(name).?.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 | 6018 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| ... | ... | @@ -5079,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| 5079 | 6040 | }, |
| 5080 | 6041 | .TLS => { |
| 5081 | 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 | 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 | 6095 | }, |
| 5114 | 6096 | }, |
| 5115 | 6097 | .section => |shndx| switch (elf.shdrPtr(shndx)) { |
| 5116 | | inline else => |shdr, class| { |
| 6098 | inline else => |shdr| { |
| 5117 | 6099 | switch (elf.targetLoad(&shdr.type)) { |
| 5118 | 6100 | else => unreachable, |
| 6101 | |
| 5119 | 6102 | .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), |
| 5120 | 6103 | .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), |
| 5121 | | .SYMTAB, .DYNAMIC, .REL, .DYNSYM => return, |
| 5122 | | .INIT_ARRAY => { |
| 5123 | | assert(shndx == elf.shndx.init_array); |
| 5124 | | if (elf.shndx.dynamic != .UNDEF) { |
| 5125 | | const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 5126 | | elf.shndx.dynamic.get(elf).ni.slice(&elf.mf), |
| 5127 | | )); |
| 5128 | | for (dynamic_entries) |*dynamic_entry| |
| 5129 | | switch (elf.targetLoad(&dynamic_entry[0])) { |
| 5130 | | else => {}, |
| 5131 | | std.elf.DT_INIT_ARRAYSZ => dynamic_entry[1] = shdr.size, |
| 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 | | }, |
| 6104 | |
| 6105 | .INIT_ARRAY, |
| 6106 | .FINI_ARRAY, |
| 6107 | .PREINIT_ARRAY, |
| 6108 | .STRTAB, |
| 6109 | .SYMTAB, |
| 6110 | .DYNAMIC, |
| 6111 | .REL, |
| 6112 | .RELA, |
| 6113 | .DYNSYM, |
| 6114 | => return, |
| 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 | 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 | 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 | 6207 | pub fn updateExports( |
| 5229 | 6208 | elf: *Elf, |