| ... | ... | @@ -6381,11 +6381,11 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6381 | 6381 | }; |
| 6382 | 6382 | } |
| 6383 | 6383 | |
| 6384 | | fn tbssSegmentOffset(elf: *Elf) u64 { |
| 6384 | fn tbssBaseOffset(elf: *Elf) u64 { |
| 6385 | 6385 | const tbss_addr = elf.shndx.tbss.vaddr(elf); |
| 6386 | 6386 | |
| 6387 | 6387 | // Hasn't been laid out yet. |
| 6388 | | if (tbss_addr == 0) return 0; |
| 6388 | if (tbss_addr == 0 or elf.ehdrType() == .REL) return 0; |
| 6389 | 6389 | |
| 6390 | 6390 | const tls_ni = elf.ni.tls.unwrap().?; |
| 6391 | 6391 | const tls_phndx = elf.getNode(tls_ni).segment; |
| ... | ... | @@ -7016,6 +7016,7 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 7016 | 7016 | StripSection, |
| 7017 | 7017 | SectionFlagsConflict, |
| 7018 | 7018 | SectionTypeConflict, |
| 7019 | UnsupportedNobitsSection, |
| 7019 | 7020 | })!Section.Index { |
| 7020 | 7021 | const gpa = elf.base.comp.gpa; |
| 7021 | 7022 | if (opts.flags.INFO_LINK or |
| ... | ... | @@ -7073,7 +7074,8 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 7073 | 7074 | .name = name, |
| 7074 | 7075 | .type = switch (opts.type) { |
| 7075 | 7076 | .PROGBITS => .NULL, // size is 0 |
| 7076 | | .NOBITS => .NOBITS, |
| 7077 | .X86_64_UNWIND => .X86_64_UNWIND, |
| 7078 | .NOBITS => return error.UnsupportedNobitsSection, |
| 7077 | 7079 | else => unreachable, |
| 7078 | 7080 | }, |
| 7079 | 7081 | .flags = flags: { |
| ... | ... | @@ -7099,7 +7101,7 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 7099 | 7101 | const existing_type = elf.targetLoad(&shdr.type); |
| 7100 | 7102 | |
| 7101 | 7103 | switch (opts.type) { |
| 7102 | | .PROGBITS => switch (existing_type) { |
| 7104 | .PROGBITS, .X86_64_UNWIND => switch (existing_type) { |
| 7103 | 7105 | .NULL, .PROGBITS, .X86_64_UNWIND => {}, |
| 7104 | 7106 | else => return error.SectionTypeConflict, |
| 7105 | 7107 | }, |
| ... | ... | @@ -7157,6 +7159,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node |
| 7157 | 7159 | error.UnsupportedSectionFlags, |
| 7158 | 7160 | error.SectionTypeConflict, |
| 7159 | 7161 | error.SectionFlagsConflict, |
| 7162 | error.UnsupportedNobitsSection, |
| 7160 | 7163 | => {}, // fall back to default behavior below |
| 7161 | 7164 | |
| 7162 | 7165 | } |
| ... | ... | @@ -7548,16 +7551,15 @@ fn loadObject( |
| 7548 | 7551 | |
| 7549 | 7552 | const sections = try gpa.alloc(struct { |
| 7550 | 7553 | shdr: ElfN.Shdr, |
| 7551 | | placement: ?union(enum) { |
| 7554 | placement: union(enum) { |
| 7552 | 7555 | progbits: InputSection.Index, |
| 7553 | 7556 | bss: struct { |
| 7554 | | shndx: Section.Index, |
| 7555 | 7557 | offset: u64, |
| 7556 | 7558 | }, |
| 7557 | 7559 | tbss: struct { |
| 7558 | | shndx: Section.Index, |
| 7559 | 7560 | offset: u64, |
| 7560 | 7561 | }, |
| 7562 | none, |
| 7561 | 7563 | }, |
| 7562 | 7564 | }, ehdr.shnum); |
| 7563 | 7565 | |
| ... | ... | @@ -7566,7 +7568,7 @@ fn loadObject( |
| 7566 | 7568 | for (sections) |*section| { |
| 7567 | 7569 | section.* = .{ |
| 7568 | 7570 | .shdr = try r.peekStruct(ElfN.Shdr, target_endian), |
| 7569 | | .placement = null, |
| 7571 | .placement = .none, |
| 7570 | 7572 | }; |
| 7571 | 7573 | try r.discardAll(ehdr.shentsize); |
| 7572 | 7574 | switch (section.shdr.type) { |
| ... | ... | @@ -7594,10 +7596,6 @@ fn loadObject( |
| 7594 | 7596 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 7595 | 7597 | if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue; |
| 7596 | 7598 | const sect_type = section.shdr.type; |
| 7597 | | const is_bss = (std.mem.startsWith(u8, name, ".bss.") or |
| 7598 | | std.mem.eql(u8, name, ".bss")) and sect_type == .NOBITS; |
| 7599 | | const is_tbss = (std.mem.startsWith(u8, name, ".tbss.") or |
| 7600 | | std.mem.eql(u8, name, ".tbss")) and sect_type == .NOBITS; |
| 7601 | 7599 | const opts: struct { |
| 7602 | 7600 | shndx: Section.Index, |
| 7603 | 7601 | node_fixed: bool, |
| ... | ... | @@ -7608,7 +7606,7 @@ fn loadObject( |
| 7608 | 7606 | .name = name, |
| 7609 | 7607 | .flags = section.shdr.flags.shf, |
| 7610 | 7608 | .entsize = section.shdr.entsize, |
| 7611 | | .type = if (is_bss or is_tbss) .NOBITS else .PROGBITS, |
| 7609 | .type = section.shdr.type, |
| 7612 | 7610 | }) catch |err| switch (err) { |
| 7613 | 7611 | else => |e| return e, |
| 7614 | 7612 | error.StripSection => continue, |
| ... | ... | @@ -7641,6 +7639,11 @@ fn loadObject( |
| 7641 | 7639 | "flags of section '{s}' conflict with other inputs", |
| 7642 | 7640 | .{name}, |
| 7643 | 7641 | ), |
| 7642 | error.UnsupportedNobitsSection => return diags.failParse( |
| 7643 | path, |
| 7644 | "unsupported section type SHT_NOBITS for non-bss section '{s}'", |
| 7645 | .{name}, |
| 7646 | ), |
| 7644 | 7647 | }; |
| 7645 | 7648 | if (section.shdr.flags.shf.COMPRESSED) { |
| 7646 | 7649 | // SHF_COMPRESSED is only allowed on non-alloc sections. |
| ... | ... | @@ -7664,8 +7667,7 @@ fn loadObject( |
| 7664 | 7667 | shndx != .rodata and |
| 7665 | 7668 | shndx != .data and |
| 7666 | 7669 | shndx != .data_rel_ro and |
| 7667 | | shndx != elf.shndx.tdata and |
| 7668 | | shndx != elf.shndx.tbss, |
| 7670 | shndx != elf.shndx.tdata, |
| 7669 | 7671 | }; |
| 7670 | 7672 | }, |
| 7671 | 7673 | inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{ |
| ... | ... | @@ -7711,35 +7713,25 @@ fn loadObject( |
| 7711 | 7713 | |
| 7712 | 7714 | try opts.shndx.ensureAligned(elf, need_align); |
| 7713 | 7715 | |
| 7714 | | if (is_bss or is_tbss) { |
| 7715 | | const offset: u64 = offset: { |
| 7716 | | switch (elf.shdrPtr(opts.shndx)) { |
| 7717 | | inline else => |shdr| { |
| 7718 | | // Bump our BSS output section size. |
| 7719 | | const old_size: u64 = elf.targetLoad(&shdr.size); |
| 7720 | | const offset = need_align.forward(old_size); |
| 7721 | | const new_size = offset + section.shdr.size; |
| 7722 | | elf.targetStore(&shdr.size, @intCast(new_size)); |
| 7723 | | break :offset offset; |
| 7724 | | }, |
| 7725 | | } |
| 7716 | if (sect_type == .NOBITS) { |
| 7717 | const offset: u64 = switch (elf.shdrPtr(opts.shndx)) { |
| 7718 | inline else => |shdr| offset: { |
| 7719 | // Bump our BSS output section size |
| 7720 | const old_size: u64 = elf.targetLoad(&shdr.size); |
| 7721 | const offset = need_align.forward(old_size); |
| 7722 | elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size)); |
| 7723 | break :offset offset; |
| 7724 | }, |
| 7726 | 7725 | }; |
| 7727 | 7726 | |
| 7728 | | if (is_bss) { |
| 7729 | | section.placement = .{ .bss = .{ |
| 7730 | | .shndx = opts.shndx, |
| 7731 | | .offset = offset, |
| 7732 | | } }; |
| 7727 | if (opts.shndx == elf.shndx.bss) { |
| 7728 | section.placement = .{ .bss = .{ .offset = offset } }; |
| 7733 | 7729 | input_has_bss = true; |
| 7734 | | continue; |
| 7735 | | } |
| 7736 | | |
| 7737 | | if (is_tbss) { |
| 7738 | | section.placement = .{ .tbss = .{ |
| 7739 | | .shndx = opts.shndx, |
| 7740 | | .offset = offset, |
| 7741 | | } }; |
| 7730 | } else if (opts.shndx == elf.shndx.tbss) { |
| 7731 | section.placement = .{ .tbss = .{ .offset = offset } }; |
| 7742 | 7732 | input_has_tbss = true; |
| 7733 | } else { |
| 7734 | unreachable; |
| 7743 | 7735 | } |
| 7744 | 7736 | |
| 7745 | 7737 | continue; |
| ... | ... | @@ -7861,8 +7853,9 @@ fn loadObject( |
| 7861 | 7853 | }, |
| 7862 | 7854 | }; |
| 7863 | 7855 | |
| 7864 | | const placement = |
| 7865 | | sections[input_sym.shndx].placement orelse continue; |
| 7856 | const placement = sections[input_sym.shndx].placement; |
| 7857 | |
| 7858 | if (placement == .none) continue; |
| 7866 | 7859 | |
| 7867 | 7860 | const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx, const symbol_value = switch (placement) { |
| 7868 | 7861 | .progbits => |isi| .{ |
| ... | ... | @@ -7871,20 +7864,16 @@ fn loadObject( |
| 7871 | 7864 | input_sym.value, |
| 7872 | 7865 | }, |
| 7873 | 7866 | .bss => |bss| .{ |
| 7874 | | bss.shndx.get(elf).ni, |
| 7875 | | bss.shndx, |
| 7876 | | bss.shndx.vaddr(elf) + bss.offset + input_sym.value, |
| 7867 | .none, |
| 7868 | elf.shndx.bss, |
| 7869 | elf.shndx.bss.vaddr(elf) + bss.offset + input_sym.value, |
| 7877 | 7870 | }, |
| 7878 | 7871 | .tbss => |tbss| .{ |
| 7879 | | tbss.shndx.get(elf).ni, |
| 7880 | | tbss.shndx, |
| 7881 | | switch (elf.ehdrType()) { |
| 7882 | | .REL => tbss.offset + input_sym.value, |
| 7883 | | .EXEC, .DYN => elf.tbssSegmentOffset() + |
| 7884 | | tbss.offset + |
| 7885 | | input_sym.value, |
| 7886 | | }, |
| 7872 | .none, |
| 7873 | elf.shndx.tbss, |
| 7874 | elf.tbssBaseOffset() + tbss.offset + input_sym.value, |
| 7887 | 7875 | }, |
| 7876 | .none => unreachable, |
| 7888 | 7877 | }; |
| 7889 | 7878 | |
| 7890 | 7879 | switch (input_sym.info.bind) { |
| ... | ... | @@ -7949,7 +7938,8 @@ fn loadObject( |
| 7949 | 7938 | return diags.failParse(path, "unsupported rel entsize", .{}); |
| 7950 | 7939 | |
| 7951 | 7940 | const loc_sec = &sections[rel_sec.shdr.info]; |
| 7952 | | const loc_node = switch (loc_sec.placement orelse continue) { |
| 7941 | const loc_node = switch (loc_sec.placement) { |
| 7942 | .none => continue, |
| 7953 | 7943 | .progbits => |isi| isi.node(elf), |
| 7954 | 7944 | .bss, .tbss => return diags.failParse( |
| 7955 | 7945 | path, |
| ... | ... | @@ -9007,6 +8997,8 @@ fn flushDynamic(elf: *Elf) void { |
| 9007 | 8997 | |
| 9008 | 8998 | fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 9009 | 8999 | name: []const u8 = "", |
| 9000 | /// If this is `.NOBITS`, then the section's address is initialized to 0, and it is the caller's |
| 9001 | /// responsibility to populate it. |
| 9010 | 9002 | type: std.elf.SHT = .NULL, |
| 9011 | 9003 | flags: std.elf.SHF = .{}, |
| 9012 | 9004 | size: std.elf.Xword = 0, |
| ... | ... | @@ -11229,7 +11221,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11229 | 11221 | }, |
| 11230 | 11222 | |
| 11231 | 11223 | .TLS => { |
| 11232 | | const tbss_base = elf.tbssSegmentOffset(); |
| 11224 | const tbss_base = elf.tbssBaseOffset(); |
| 11233 | 11225 | const new_vaddr = elf.computeNodeVAddr(ni); |
| 11234 | 11226 | const tls_offset = elf.targetLoad(&ph.offset); |
| 11235 | 11227 | const tls_filesz = elf.targetLoad(&ph.filesz); |
| ... | ... | @@ -11237,16 +11229,17 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11237 | 11229 | elf.targetStore(&ph.vaddr, @intCast(new_vaddr)); |
| 11238 | 11230 | elf.targetStore(&ph.paddr, @intCast(new_vaddr)); |
| 11239 | 11231 | |
| 11240 | | // This may seem like a no-op since we're moving to and fro the same base, |
| 11241 | | // but we also need to update the section STT_SECTION symbol, which this |
| 11242 | | // function will conveniently do for us. |
| 11243 | | elf.updateNobitsAddress( |
| 11244 | | elf.shndx.tbss, |
| 11245 | | new_vaddr + tbss_base, |
| 11246 | | tls_offset + tls_filesz, |
| 11247 | | tbss_base, |
| 11248 | | tbss_base, |
| 11249 | | elf.tbss_inputs.items, |
| 11232 | const tbss_shndx = elf.shndx.tbss; |
| 11233 | const tbss_addr = new_vaddr + tbss_base; |
| 11234 | switch (elf.shdrPtr(tbss_shndx)) { |
| 11235 | inline else => |shdr| { |
| 11236 | elf.targetStore(&shdr.addr, @intCast(tbss_addr)); |
| 11237 | elf.targetStore(&shdr.offset, @intCast(tls_offset + tls_filesz)); |
| 11238 | }, |
| 11239 | } |
| 11240 | Symbol.Id.local(tbss_shndx.get(elf).lsi).flushMoved( |
| 11241 | elf, |
| 11242 | tbss_addr, |
| 11250 | 11243 | ); |
| 11251 | 11244 | }, |
| 11252 | 11245 | } |
| ... | ... | @@ -11651,6 +11644,34 @@ fn updateNobitsAddress( |
| 11651 | 11644 | ); |
| 11652 | 11645 | } |
| 11653 | 11646 | |
| 11647 | fn loadSegmentChildrenMoved( |
| 11648 | elf: *Elf, |
| 11649 | segment_ni: MappedFile.Node.Index, |
| 11650 | old_vaddr: u64, |
| 11651 | new_vaddr: u64, |
| 11652 | offset: u64, |
| 11653 | filesz: u64, |
| 11654 | ) std.mem.Allocator.Error!void { |
| 11655 | if (segment_ni == elf.ni.data and elf.shndx.bss != .UNDEF) { |
| 11656 | const bss_shndx = elf.shndx.bss; |
| 11657 | const old_bss_addr = bss_shndx.vaddr(elf); |
| 11658 | const new_bss_addr = new_vaddr + filesz; |
| 11659 | |
| 11660 | elf.updateNobitsAddress( |
| 11661 | bss_shndx, |
| 11662 | new_bss_addr, |
| 11663 | offset + filesz, |
| 11664 | old_bss_addr, |
| 11665 | new_bss_addr, |
| 11666 | elf.bss_inputs.items, |
| 11667 | ); |
| 11668 | } |
| 11669 | |
| 11670 | if (old_vaddr != new_vaddr) { |
| 11671 | try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11672 | } |
| 11673 | } |
| 11674 | |
| 11654 | 11675 | /// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz` |
| 11655 | 11676 | /// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and |
| 11656 | 11677 | /// `memsz` fields of the segment, in order to place it at a valid virtual address. |
| ... | ... | @@ -11693,6 +11714,8 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11693 | 11714 | var vaddr: u64 = orig_vaddr; |
| 11694 | 11715 | |
| 11695 | 11716 | elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits())); |
| 11717 | elf.targetStore(&phdr[orig_phndx].memsz, @intCast(memsz)); |
| 11718 | elf.targetStore(&phdr[orig_phndx].type, if (memsz > 0) .LOAD else .NULL); |
| 11696 | 11719 | |
| 11697 | 11720 | // First, we will shift the virtual address as needed in order to maintain the required |
| 11698 | 11721 | // property that vaddr is congruent to offset modulo the phdr alignment. |
| ... | ... | @@ -11770,22 +11793,15 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11770 | 11793 | elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr)); |
| 11771 | 11794 | elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr)); |
| 11772 | 11795 | |
| 11773 | | const bss_shndx = elf.shndx.bss; |
| 11774 | 11796 | const next_filesz = elf.targetLoad(&next_ph.filesz); |
| 11775 | | const old_bss_addr = bss_shndx.vaddr(elf); |
| 11776 | | const new_bss_addr = next_new_vaddr + next_filesz; |
| 11777 | | |
| 11778 | | elf.updateNobitsAddress( |
| 11779 | | bss_shndx, |
| 11780 | | new_bss_addr, |
| 11781 | | next_offset + next_filesz, |
| 11782 | | old_bss_addr, |
| 11783 | | new_bss_addr, |
| 11784 | | elf.bss_inputs.items, |
| 11797 | try elf.loadSegmentChildrenMoved( |
| 11798 | next_ni, |
| 11799 | next_vaddr, |
| 11800 | next_new_vaddr, |
| 11801 | next_offset, |
| 11802 | next_filesz, |
| 11785 | 11803 | ); |
| 11786 | 11804 | |
| 11787 | | try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11788 | | |
| 11789 | 11805 | break; |
| 11790 | 11806 | }, |
| 11791 | 11807 | } |
| ... | ... | @@ -11809,29 +11825,15 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11809 | 11825 | if (vaddr != orig_vaddr) { |
| 11810 | 11826 | elf.targetStore(&phdr[phndx].vaddr, @intCast(vaddr)); |
| 11811 | 11827 | elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr)); |
| 11812 | | try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11813 | 11828 | } |
| 11814 | 11829 | |
| 11815 | | const ph = &phdr[phndx]; |
| 11816 | | |
| 11817 | | elf.targetStore(&ph.memsz, @intCast(memsz)); |
| 11818 | | elf.targetStore(&ph.type, if (memsz > 0) .LOAD else .NULL); |
| 11819 | | |
| 11820 | | if (segment_ni == elf.ni.data and elf.shndx.bss != .UNDEF) { |
| 11821 | | const bss_shndx = elf.shndx.bss; |
| 11822 | | |
| 11823 | | const old_bss_addr = bss_shndx.vaddr(elf); |
| 11824 | | const new_bss_addr = vaddr + filesz; |
| 11825 | | |
| 11826 | | elf.updateNobitsAddress( |
| 11827 | | bss_shndx, |
| 11828 | | new_bss_addr, |
| 11829 | | offset + filesz, |
| 11830 | | old_bss_addr, |
| 11831 | | new_bss_addr, |
| 11832 | | elf.bss_inputs.items, |
| 11833 | | ); |
| 11834 | | } |
| 11830 | try elf.loadSegmentChildrenMoved( |
| 11831 | segment_ni, |
| 11832 | orig_vaddr, |
| 11833 | vaddr, |
| 11834 | offset, |
| 11835 | filesz, |
| 11836 | ); |
| 11835 | 11837 | }, |
| 11836 | 11838 | } |
| 11837 | 11839 | } |
| ... | ... | @@ -11867,7 +11869,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 11867 | 11869 | switch (elf.targetLoad(&ph.type)) { |
| 11868 | 11870 | else => unreachable, |
| 11869 | 11871 | .NULL, .LOAD => { |
| 11870 | | elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL); |
| 11871 | 11872 | try elf.allocateSegmentLoadAddress(phndx); |
| 11872 | 11873 | }, |
| 11873 | 11874 | .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => { |
| ... | ... | @@ -11876,7 +11877,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 11876 | 11877 | .TLS => { |
| 11877 | 11878 | const tls_filesz: u64 = size; |
| 11878 | 11879 | |
| 11879 | | const old_tbss_base = elf.tbssSegmentOffset(); |
| 11880 | const old_tbss_base = elf.tbssBaseOffset(); |
| 11880 | 11881 | const new_tbss_base = tls_filesz; |
| 11881 | 11882 | const tbss_size = elf.shndx.tbss.size(elf); |
| 11882 | 11883 | const tls_vaddr = elf.targetLoad(&ph.vaddr); |