| ... | ... | @@ -151,6 +151,11 @@ bss_inputs: std.ArrayList(Node.InputIndex), |
| 151 | 151 | /// symbols when the address of `.tbss` changes. |
| 152 | 152 | tbss_inputs: std.ArrayList(Node.InputIndex), |
| 153 | 153 | |
| 154 | /// Indicates that the address of the `.bss` section needs to be updated. |
| 155 | /// |
| 156 | /// Equivalent to a `PROGBITS` section's `MappedFile.Node` being marked as "moved". |
| 157 | bss_moved: bool, |
| 158 | |
| 154 | 159 | shstrtab: StringTable, |
| 155 | 160 | strtab: StringTable, |
| 156 | 161 | dynstr: StringTable, |
| ... | ... | @@ -4815,6 +4820,7 @@ fn create( |
| 4815 | 4820 | .tbss_globals = .empty, |
| 4816 | 4821 | .bss_inputs = .empty, |
| 4817 | 4822 | .tbss_inputs = .empty, |
| 4823 | .bss_moved = false, |
| 4818 | 4824 | .shstrtab = .{ .map = .empty }, |
| 4819 | 4825 | .strtab = .{ .map = .empty }, |
| 4820 | 4826 | .dynstr = .{ .map = .empty }, |
| ... | ... | @@ -6426,20 +6432,12 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6426 | 6432 | }; |
| 6427 | 6433 | } |
| 6428 | 6434 | |
| 6429 | | fn tbssBaseOffset(elf: *Elf) u64 { |
| 6430 | | const tbss_addr = elf.shndx.tbss.vaddr(elf); |
| 6431 | | |
| 6432 | | // Hasn't been laid out yet. |
| 6433 | | if (tbss_addr == 0 or elf.ehdrType() == .REL) return 0; |
| 6434 | | |
| 6435 | | const tls_ni = elf.ni.tls.unwrap().?; |
| 6436 | | const tls_phndx = elf.getNode(tls_ni).segment; |
| 6437 | | |
| 6438 | | const tls_vaddr = switch (elf.phdrSlice()) { |
| 6439 | | inline else => |phdr| elf.targetLoad(&phdr[tls_phndx].vaddr), |
| 6435 | fn tbssOffset(elf: *Elf) u64 { |
| 6436 | // The `.tbss` section is placed exactly at the end of the TLS segment. |
| 6437 | const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment; |
| 6438 | return switch (elf.phdrSlice()) { |
| 6439 | inline else => |phdr| elf.targetLoad(&phdr[tls_phndx].filesz), |
| 6440 | 6440 | }; |
| 6441 | | |
| 6442 | | return tbss_addr - tls_vaddr; |
| 6443 | 6441 | } |
| 6444 | 6442 | |
| 6445 | 6443 | fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| ... | ... | @@ -7921,7 +7919,7 @@ fn loadObject( |
| 7921 | 7919 | if (elf.ni.tls != .none and |
| 7922 | 7920 | nobits.shndx == elf.shndx.tbss) |
| 7923 | 7921 | { |
| 7924 | | break :base elf.tbssBaseOffset(); |
| 7922 | break :base elf.tbssOffset(); |
| 7925 | 7923 | } |
| 7926 | 7924 | assert(nobits.shndx == elf.shndx.bss); |
| 7927 | 7925 | break :base nobits.shndx.vaddr(elf); |
| ... | ... | @@ -10871,12 +10869,46 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 10871 | 10869 | if (clean_moved or clean_resized or clean_next_moved) try elf.flushPadding(ni); |
| 10872 | 10870 | break :task; |
| 10873 | 10871 | } |
| 10872 | if (elf.bss_moved) { |
| 10873 | elf.bss_moved = false; |
| 10874 | |
| 10875 | const data_phndx = elf.getNode(elf.ni.data).segment; |
| 10876 | const data_offset: u64, const data_vaddr: u64, const data_filesz: u64 = switch (elf.phdrSlice()) { |
| 10877 | inline else => |phdr| .{ |
| 10878 | elf.targetLoad(&phdr[data_phndx].offset), |
| 10879 | elf.targetLoad(&phdr[data_phndx].vaddr), |
| 10880 | elf.targetLoad(&phdr[data_phndx].filesz), |
| 10881 | }, |
| 10882 | }; |
| 10883 | |
| 10884 | const old_bss_addr = elf.shndx.bss.vaddr(elf); |
| 10885 | const new_bss_addr = data_vaddr + data_filesz; |
| 10886 | |
| 10887 | const bss_section_sym: Symbol.Id = .local(elf.shndx.bss.get(elf).lsi); |
| 10888 | bss_section_sym.flushMoved(elf, new_bss_addr); |
| 10889 | switch (elf.shdrPtr(elf.shndx.bss)) { |
| 10890 | inline else => |shdr| { |
| 10891 | elf.targetStore(&shdr.offset, @intCast(data_offset + data_filesz)); |
| 10892 | elf.targetStore(&shdr.addr, @intCast(new_bss_addr)); |
| 10893 | }, |
| 10894 | } |
| 10895 | elf.flushMovedNobitsSymbols( |
| 10896 | elf.shndx.bss, |
| 10897 | old_bss_addr, |
| 10898 | new_bss_addr, |
| 10899 | elf.bss_inputs.items, |
| 10900 | elf.bss_globals.keys(), |
| 10901 | ); |
| 10902 | |
| 10903 | break :task; |
| 10904 | } |
| 10874 | 10905 | } |
| 10875 | 10906 | if (elf.inputs.items.len - elf.input_pending_index > 0) return true; |
| 10876 | 10907 | if (elf.input_sections.items.len - elf.input_section_pending_index > 0) return true; |
| 10877 | 10908 | if (elf.one_shot_fixups.items.len > 0) return true; |
| 10878 | 10909 | if (elf.changed_symtab_index.count() > 0) return true; |
| 10879 | 10910 | if (elf.mf.updates.items.len > 0) return true; |
| 10911 | if (elf.bss_moved) return true; |
| 10880 | 10912 | return false; |
| 10881 | 10913 | } |
| 10882 | 10914 | |
| ... | ... | @@ -11231,8 +11263,23 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 11231 | 11263 | inline else => |phdr, class| { |
| 11232 | 11264 | const ph = &phdr[phndx]; |
| 11233 | 11265 | elf.targetStore(&ph.offset, @intCast(elf_offset)); |
| 11234 | | if (elf.targetLoad(&ph.type) == .PHDR) { |
| 11235 | | @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset; |
| 11266 | switch (elf.targetLoad(&ph.type)) { |
| 11267 | .PHDR => { |
| 11268 | const ehdr = @field(elf.ehdrPtr(), @tagName(class)); |
| 11269 | elf.targetStore(&ehdr.phoff, @intCast(elf_offset)); |
| 11270 | }, |
| 11271 | .TLS => { |
| 11272 | // The `.tbss` section's offset should be at the end of the TLS segment. |
| 11273 | const tls_filesz = elf.targetLoad(&ph.filesz); |
| 11274 | const tbss_shdr = @field(elf.shdrPtr(elf.shndx.tbss), @tagName(class)); |
| 11275 | elf.targetStore(&tbss_shdr.offset, @intCast(elf_offset + tls_filesz)); |
| 11276 | }, |
| 11277 | else => if (ni == elf.ni.data) { |
| 11278 | // The `.bss` section's offset should be at the end of the data segment. |
| 11279 | const data_filesz = elf.targetLoad(&ph.filesz); |
| 11280 | const bss_shdr = @field(elf.shdrPtr(elf.shndx.bss), @tagName(class)); |
| 11281 | elf.targetStore(&bss_shdr.offset, @intCast(elf_offset + data_filesz)); |
| 11282 | }, |
| 11236 | 11283 | } |
| 11237 | 11284 | }, |
| 11238 | 11285 | } |
| ... | ... | @@ -11283,23 +11330,16 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11283 | 11330 | }, |
| 11284 | 11331 | |
| 11285 | 11332 | .TLS => { |
| 11286 | | const tbss_base = elf.tbssBaseOffset(); |
| 11287 | 11333 | const new_vaddr = elf.computeNodeVAddr(ni); |
| 11288 | | const tls_offset = elf.targetLoad(&ph.offset); |
| 11289 | | const tls_filesz = elf.targetLoad(&ph.filesz); |
| 11290 | | |
| 11291 | 11334 | elf.targetStore(&ph.vaddr, @intCast(new_vaddr)); |
| 11292 | 11335 | elf.targetStore(&ph.paddr, @intCast(new_vaddr)); |
| 11293 | 11336 | |
| 11294 | | const tbss_shndx = elf.shndx.tbss; |
| 11295 | | const tbss_addr = new_vaddr + tbss_base; |
| 11296 | | switch (elf.shdrPtr(tbss_shndx)) { |
| 11297 | | inline else => |shdr| { |
| 11298 | | elf.targetStore(&shdr.addr, @intCast(tbss_addr)); |
| 11299 | | elf.targetStore(&shdr.offset, @intCast(tls_offset + tls_filesz)); |
| 11300 | | }, |
| 11337 | // Update the address of the `.tbss` section at the end of this segment: |
| 11338 | const tbss_addr = new_vaddr + elf.tbssOffset(); |
| 11339 | switch (elf.shdrPtr(elf.shndx.tbss)) { |
| 11340 | inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(tbss_addr)), |
| 11301 | 11341 | } |
| 11302 | | Symbol.Id.local(tbss_shndx.get(elf).lsi).flushMoved( |
| 11342 | Symbol.Id.local(elf.shndx.tbss.get(elf).lsi).flushMoved( |
| 11303 | 11343 | elf, |
| 11304 | 11344 | tbss_addr, |
| 11305 | 11345 | ); |
| ... | ... | @@ -11634,13 +11674,15 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11634 | 11674 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11635 | 11675 | } |
| 11636 | 11676 | |
| 11637 | | /// Update the position of a `NOBITS` section. |
| 11638 | | /// BSS needs an absolute symbol base, and TBSS needs TLS-segment-relative base. |
| 11639 | | fn updateNobitsAddress( |
| 11677 | /// Update the value of all symbols in a `NOBITS` section. All symbol values are updated to |
| 11678 | /// `old_value - old_symbol_base + new_symbol_base`. |
| 11679 | /// |
| 11680 | /// For `.bss`, the symbol base is the section's virtual address. |
| 11681 | /// |
| 11682 | /// For `.tbss`, the symbol base is the offset into the TLS segment. |
| 11683 | fn flushMovedNobitsSymbols( |
| 11640 | 11684 | elf: *Elf, |
| 11641 | 11685 | shndx: Section.Index, |
| 11642 | | new_section_addr: u64, |
| 11643 | | new_file_offset: u64, |
| 11644 | 11686 | old_symbol_base: u64, |
| 11645 | 11687 | new_symbol_base: u64, |
| 11646 | 11688 | inputs: []const Node.InputIndex, |
| ... | ... | @@ -11648,13 +11690,6 @@ fn updateNobitsAddress( |
| 11648 | 11690 | ) void { |
| 11649 | 11691 | assert(shndx != .UNDEF); |
| 11650 | 11692 | |
| 11651 | | switch (elf.shdrPtr(shndx)) { |
| 11652 | | inline else => |shdr| { |
| 11653 | | elf.targetStore(&shdr.addr, @intCast(new_section_addr)); |
| 11654 | | elf.targetStore(&shdr.offset, @intCast(new_file_offset)); |
| 11655 | | }, |
| 11656 | | } |
| 11657 | | |
| 11658 | 11693 | // Update local symbols. |
| 11659 | 11694 | for (inputs) |ii| { |
| 11660 | 11695 | var lsi, const end_lsi = ii.localSymbolRange(elf); |
| ... | ... | @@ -11662,14 +11697,14 @@ fn updateNobitsAddress( |
| 11662 | 11697 | while (lsi != end_lsi) : (lsi = @fromBackingInt( |
| 11663 | 11698 | @backingInt(lsi) + 1, |
| 11664 | 11699 | )) { |
| 11665 | | const symbol_shndx: Section.Index, const visibility = switch (elf.symPtr(lsi.index())) { |
| 11700 | const sym_shndx: Section.Index, const visibility = switch (elf.symPtr(lsi.index())) { |
| 11666 | 11701 | inline else => |sym| .{ |
| 11667 | 11702 | .fromSection(elf.targetLoad(&sym.shndx)), |
| 11668 | 11703 | elf.targetLoad(&sym.other).visibility, |
| 11669 | 11704 | }, |
| 11670 | 11705 | }; |
| 11671 | 11706 | |
| 11672 | | if (symbol_shndx != shndx) continue; |
| 11707 | if (sym_shndx != shndx) continue; |
| 11673 | 11708 | |
| 11674 | 11709 | switch (visibility) { |
| 11675 | 11710 | // These are globals demoted to STB_LOCAL and are updated below. |
| ... | ... | @@ -11700,40 +11735,26 @@ fn updateNobitsAddress( |
| 11700 | 11735 | symbol.value(elf) - old_symbol_base + new_symbol_base, |
| 11701 | 11736 | ); |
| 11702 | 11737 | } |
| 11703 | | |
| 11704 | | Symbol.Id.local(shndx.get(elf).lsi).flushMoved( |
| 11705 | | elf, |
| 11706 | | new_section_addr, |
| 11707 | | ); |
| 11708 | 11738 | } |
| 11709 | 11739 | |
| 11710 | | fn loadSegmentChildrenMoved( |
| 11740 | /// Updates `vaddr` and `paddr` of the segment header, and marks everything in the segment as moved. |
| 11741 | fn updateSegmentLoadAddress( |
| 11711 | 11742 | elf: *Elf, |
| 11712 | 11743 | segment_ni: MappedFile.Node.Index, |
| 11713 | | old_vaddr: u64, |
| 11714 | 11744 | new_vaddr: u64, |
| 11715 | | offset: u64, |
| 11716 | | filesz: u64, |
| 11717 | 11745 | ) std.mem.Allocator.Error!void { |
| 11718 | | if (segment_ni == elf.ni.data) { |
| 11719 | | const bss_shndx = elf.shndx.bss; |
| 11720 | | const old_bss_addr = bss_shndx.vaddr(elf); |
| 11721 | | const new_bss_addr = new_vaddr + filesz; |
| 11722 | | |
| 11723 | | elf.updateNobitsAddress( |
| 11724 | | bss_shndx, |
| 11725 | | new_bss_addr, |
| 11726 | | offset + filesz, |
| 11727 | | old_bss_addr, |
| 11728 | | new_bss_addr, |
| 11729 | | elf.bss_inputs.items, |
| 11730 | | elf.bss_globals.keys(), |
| 11731 | | ); |
| 11732 | | } |
| 11746 | const phndx = elf.getNode(segment_ni).segment; |
| 11733 | 11747 | |
| 11734 | | if (old_vaddr != new_vaddr) { |
| 11735 | | try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11748 | switch (elf.phdrSlice()) { |
| 11749 | inline else => |phdr| { |
| 11750 | assert(new_vaddr != elf.targetLoad(&phdr[phndx].vaddr)); |
| 11751 | elf.targetStore(&phdr[phndx].vaddr, @intCast(new_vaddr)); |
| 11752 | elf.targetStore(&phdr[phndx].paddr, @intCast(new_vaddr)); |
| 11753 | }, |
| 11736 | 11754 | } |
| 11755 | |
| 11756 | try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11757 | if (segment_ni == elf.ni.data) elf.bss_moved = true; |
| 11737 | 11758 | } |
| 11738 | 11759 | |
| 11739 | 11760 | /// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz` |
| ... | ... | @@ -11852,19 +11873,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11852 | 11873 | const next_offset = elf.targetLoad(&next_ph.offset); |
| 11853 | 11874 | const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits(); |
| 11854 | 11875 | |
| 11855 | | // This logic for updating the data segment's vaddr is identical to how we |
| 11856 | | // will update the vaddr of `phndx` when we break from the loop. |
| 11857 | | elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr)); |
| 11858 | | elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr)); |
| 11859 | | |
| 11860 | | const next_filesz = elf.targetLoad(&next_ph.filesz); |
| 11861 | | try elf.loadSegmentChildrenMoved( |
| 11862 | | next_ni, |
| 11863 | | next_vaddr, |
| 11864 | | next_new_vaddr, |
| 11865 | | next_offset, |
| 11866 | | next_filesz, |
| 11867 | | ); |
| 11876 | try elf.updateSegmentLoadAddress(next_ni, next_new_vaddr); |
| 11868 | 11877 | |
| 11869 | 11878 | break; |
| 11870 | 11879 | }, |
| ... | ... | @@ -11887,17 +11896,8 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11887 | 11896 | } |
| 11888 | 11897 | |
| 11889 | 11898 | if (vaddr != orig_vaddr) { |
| 11890 | | elf.targetStore(&phdr[phndx].vaddr, @intCast(vaddr)); |
| 11891 | | elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr)); |
| 11899 | try elf.updateSegmentLoadAddress(segment_ni, vaddr); |
| 11892 | 11900 | } |
| 11893 | | |
| 11894 | | try elf.loadSegmentChildrenMoved( |
| 11895 | | segment_ni, |
| 11896 | | orig_vaddr, |
| 11897 | | vaddr, |
| 11898 | | offset, |
| 11899 | | filesz, |
| 11900 | | ); |
| 11901 | 11901 | }, |
| 11902 | 11902 | } |
| 11903 | 11903 | } |
| ... | ... | @@ -11926,40 +11926,46 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 11926 | 11926 | .ehdr => unreachable, |
| 11927 | 11927 | .shdr => {}, |
| 11928 | 11928 | .segment => |phndx| switch (elf.phdrSlice()) { |
| 11929 | | inline else => |phdr| { |
| 11929 | inline else => |phdr, class| { |
| 11930 | 11930 | assert(elf.phdrs.items[phndx].unwrap().? == ni); |
| 11931 | 11931 | const ph = &phdr[phndx]; |
| 11932 | | elf.targetStore(&ph.filesz, @intCast(size)); |
| 11933 | 11932 | switch (elf.targetLoad(&ph.type)) { |
| 11934 | 11933 | else => unreachable, |
| 11935 | 11934 | .NULL, .LOAD => { |
| 11935 | elf.targetStore(&ph.filesz, @intCast(size)); |
| 11936 | 11936 | try elf.allocateSegmentLoadAddress(phndx); |
| 11937 | if (ni == elf.ni.data) elf.bss_moved = true; |
| 11937 | 11938 | }, |
| 11938 | 11939 | .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => { |
| 11940 | elf.targetStore(&ph.filesz, @intCast(size)); |
| 11939 | 11941 | elf.targetStore(&ph.memsz, @intCast(size)); |
| 11940 | 11942 | }, |
| 11941 | 11943 | .TLS => { |
| 11942 | | const tls_filesz: u64 = size; |
| 11944 | // We must fetch this value before modifying our `filesz`. |
| 11945 | const old_tbss_offset = elf.tbssOffset(); |
| 11943 | 11946 | |
| 11944 | | const old_tbss_base = elf.tbssBaseOffset(); |
| 11945 | | const new_tbss_base = tls_filesz; |
| 11946 | | const tbss_size = elf.shndx.tbss.size(elf); |
| 11947 | | const tls_vaddr = elf.targetLoad(&ph.vaddr); |
| 11948 | | const tls_offset = elf.targetLoad(&ph.offset); |
| 11947 | elf.targetStore(&ph.filesz, @intCast(size)); |
| 11949 | 11948 | |
| 11950 | | const tls_memsz = std.mem.alignForward( |
| 11951 | | u64, |
| 11952 | | tls_filesz + tbss_size, |
| 11953 | | elf.targetLoad(&ph.@"align"), |
| 11954 | | ); |
| 11955 | | elf.targetStore(&ph.memsz, @intCast(tls_memsz)); |
| 11949 | // Our memsz must include the `.tbss` section. |
| 11950 | const tls_align: Alignment = .fromByteUnits(elf.targetLoad(&ph.@"align")); |
| 11951 | elf.targetStore(&ph.memsz, @intCast( |
| 11952 | tls_align.forward(size + elf.shndx.tbss.size(elf)), |
| 11953 | )); |
| 11956 | 11954 | |
| 11957 | | elf.updateNobitsAddress( |
| 11955 | // `.tbss` section goes at the end of this segment. Update its offset: |
| 11956 | const tls_offset = elf.targetLoad(&ph.offset); |
| 11957 | const tbss_shdr = @field(elf.shdrPtr(elf.shndx.tbss), @tagName(class)); |
| 11958 | elf.targetStore(&tbss_shdr.offset, @intCast(tls_offset + size)); |
| 11959 | // and its address: |
| 11960 | const tls_vaddr = elf.targetLoad(&ph.vaddr); |
| 11961 | const tbss_section_sym: Symbol.Id = .local(elf.shndx.tbss.get(elf).lsi); |
| 11962 | tbss_section_sym.flushMoved(elf, tls_vaddr + size); |
| 11963 | elf.targetStore(&tbss_shdr.addr, @intCast(tls_vaddr + size)); |
| 11964 | // and update the values of symbols in that section: |
| 11965 | elf.flushMovedNobitsSymbols( |
| 11958 | 11966 | elf.shndx.tbss, |
| 11959 | | tls_vaddr + new_tbss_base, |
| 11960 | | tls_offset + new_tbss_base, |
| 11961 | | old_tbss_base, |
| 11962 | | new_tbss_base, |
| 11967 | old_tbss_offset, |
| 11968 | size, |
| 11963 | 11969 | elf.tbss_inputs.items, |
| 11964 | 11970 | elf.tbss_globals.keys(), |
| 11965 | 11971 | ); |