authorgravatar for abbix@riseup.netlg <abbix@riseup.net> 2026-09-01 17:18:16-04:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-16 12:41:30+01:00
log7a6eb167bcde3ed8fdc81f88bd683b30b6290e45
tree4e5ececf03ca85a23941bf73ad2c9d1fb253c82e
parente03ff669f56421a13760356c9fbb979c74c65622
signature Commit is signed but in an unrecognized format.

Elf2: clean things up

As per review: - Don't allow non-bss NOBITS sections (which simplifies the code quite a bit). - Remove duplicated and superfluous code. - General style improvements.

1 files changed, 102 insertions(+), 101 deletions(-)

src/link/Elf2.zig+102-101
......@@ -6381,11 +6381,11 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
63816381 };
63826382}
63836383
6384fn tbssSegmentOffset(elf: *Elf) u64 {
6384fn tbssBaseOffset(elf: *Elf) u64 {
63856385 const tbss_addr = elf.shndx.tbss.vaddr(elf);
63866386
63876387 // Hasn't been laid out yet.
6388 if (tbss_addr == 0) return 0;
6388 if (tbss_addr == 0 or elf.ehdrType() == .REL) return 0;
63896389
63906390 const tls_ni = elf.ni.tls.unwrap().?;
63916391 const tls_phndx = elf.getNode(tls_ni).segment;
......@@ -7016,6 +7016,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
70167016 StripSection,
70177017 SectionFlagsConflict,
70187018 SectionTypeConflict,
7019 UnsupportedNobitsSection,
70197020})!Section.Index {
70207021 const gpa = elf.base.comp.gpa;
70217022 if (opts.flags.INFO_LINK or
......@@ -7073,7 +7074,8 @@ fn mapInputSection(elf: *Elf, opts: struct {
70737074 .name = name,
70747075 .type = switch (opts.type) {
70757076 .PROGBITS => .NULL, // size is 0
7076 .NOBITS => .NOBITS,
7077 .X86_64_UNWIND => .X86_64_UNWIND,
7078 .NOBITS => return error.UnsupportedNobitsSection,
70777079 else => unreachable,
70787080 },
70797081 .flags = flags: {
......@@ -7099,7 +7101,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
70997101 const existing_type = elf.targetLoad(&shdr.type);
71007102
71017103 switch (opts.type) {
7102 .PROGBITS => switch (existing_type) {
7104 .PROGBITS, .X86_64_UNWIND => switch (existing_type) {
71037105 .NULL, .PROGBITS, .X86_64_UNWIND => {},
71047106 else => return error.SectionTypeConflict,
71057107 },
......@@ -7157,6 +7159,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
71577159 error.UnsupportedSectionFlags,
71587160 error.SectionTypeConflict,
71597161 error.SectionFlagsConflict,
7162 error.UnsupportedNobitsSection,
71607163 => {}, // fall back to default behavior below
71617164
71627165 }
......@@ -7548,16 +7551,15 @@ fn loadObject(
75487551
75497552 const sections = try gpa.alloc(struct {
75507553 shdr: ElfN.Shdr,
7551 placement: ?union(enum) {
7554 placement: union(enum) {
75527555 progbits: InputSection.Index,
75537556 bss: struct {
7554 shndx: Section.Index,
75557557 offset: u64,
75567558 },
75577559 tbss: struct {
7558 shndx: Section.Index,
75597560 offset: u64,
75607561 },
7562 none,
75617563 },
75627564 }, ehdr.shnum);
75637565
......@@ -7566,7 +7568,7 @@ fn loadObject(
75667568 for (sections) |*section| {
75677569 section.* = .{
75687570 .shdr = try r.peekStruct(ElfN.Shdr, target_endian),
7569 .placement = null,
7571 .placement = .none,
75707572 };
75717573 try r.discardAll(ehdr.shentsize);
75727574 switch (section.shdr.type) {
......@@ -7594,10 +7596,6 @@ fn loadObject(
75947596 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
75957597 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;
75967598 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;
76017599 const opts: struct {
76027600 shndx: Section.Index,
76037601 node_fixed: bool,
......@@ -7608,7 +7606,7 @@ fn loadObject(
76087606 .name = name,
76097607 .flags = section.shdr.flags.shf,
76107608 .entsize = section.shdr.entsize,
7611 .type = if (is_bss or is_tbss) .NOBITS else .PROGBITS,
7609 .type = section.shdr.type,
76127610 }) catch |err| switch (err) {
76137611 else => |e| return e,
76147612 error.StripSection => continue,
......@@ -7641,6 +7639,11 @@ fn loadObject(
76417639 "flags of section '{s}' conflict with other inputs",
76427640 .{name},
76437641 ),
7642 error.UnsupportedNobitsSection => return diags.failParse(
7643 path,
7644 "unsupported section type SHT_NOBITS for non-bss section '{s}'",
7645 .{name},
7646 ),
76447647 };
76457648 if (section.shdr.flags.shf.COMPRESSED) {
76467649 // SHF_COMPRESSED is only allowed on non-alloc sections.
......@@ -7664,8 +7667,7 @@ fn loadObject(
76647667 shndx != .rodata and
76657668 shndx != .data and
76667669 shndx != .data_rel_ro and
7667 shndx != elf.shndx.tdata and
7668 shndx != elf.shndx.tbss,
7670 shndx != elf.shndx.tdata,
76697671 };
76707672 },
76717673 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
......@@ -7711,35 +7713,25 @@ fn loadObject(
77117713
77127714 try opts.shndx.ensureAligned(elf, need_align);
77137715
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 },
77267725 };
77277726
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 } };
77337729 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 } };
77427732 input_has_tbss = true;
7733 } else {
7734 unreachable;
77437735 }
77447736
77457737 continue;
......@@ -7861,8 +7853,9 @@ fn loadObject(
78617853 },
78627854 };
78637855
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;
78667859
78677860 const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx, const symbol_value = switch (placement) {
78687861 .progbits => |isi| .{
......@@ -7871,20 +7864,16 @@ fn loadObject(
78717864 input_sym.value,
78727865 },
78737866 .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,
78777870 },
78787871 .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,
78877875 },
7876 .none => unreachable,
78887877 };
78897878
78907879 switch (input_sym.info.bind) {
......@@ -7949,7 +7938,8 @@ fn loadObject(
79497938 return diags.failParse(path, "unsupported rel entsize", .{});
79507939
79517940 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,
79537943 .progbits => |isi| isi.node(elf),
79547944 .bss, .tbss => return diags.failParse(
79557945 path,
......@@ -9007,6 +8997,8 @@ fn flushDynamic(elf: *Elf) void {
90078997
90088998fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
90098999 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.
90109002 type: std.elf.SHT = .NULL,
90119003 flags: std.elf.SHF = .{},
90129004 size: std.elf.Xword = 0,
......@@ -11229,7 +11221,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1122911221 },
1123011222
1123111223 .TLS => {
11232 const tbss_base = elf.tbssSegmentOffset();
11224 const tbss_base = elf.tbssBaseOffset();
1123311225 const new_vaddr = elf.computeNodeVAddr(ni);
1123411226 const tls_offset = elf.targetLoad(&ph.offset);
1123511227 const tls_filesz = elf.targetLoad(&ph.filesz);
......@@ -11237,16 +11229,17 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1123711229 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
1123811230 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
1123911231
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,
1125011243 );
1125111244 },
1125211245 }
......@@ -11651,6 +11644,34 @@ fn updateNobitsAddress(
1165111644 );
1165211645}
1165311646
11647fn 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
1165411675/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
1165511676/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and
1165611677/// `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
1169311714 var vaddr: u64 = orig_vaddr;
1169411715
1169511716 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);
1169611719
1169711720 // First, we will shift the virtual address as needed in order to maintain the required
1169811721 // 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
1177011793 elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr));
1177111794 elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr));
1177211795
11773 const bss_shndx = elf.shndx.bss;
1177411796 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,
1178511803 );
1178611804
11787 try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
11788
1178911805 break;
1179011806 },
1179111807 }
......@@ -11809,29 +11825,15 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1180911825 if (vaddr != orig_vaddr) {
1181011826 elf.targetStore(&phdr[phndx].vaddr, @intCast(vaddr));
1181111827 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));
11812 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
1181311828 }
1181411829
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 );
1183511837 },
1183611838 }
1183711839}
......@@ -11867,7 +11869,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1186711869 switch (elf.targetLoad(&ph.type)) {
1186811870 else => unreachable,
1186911871 .NULL, .LOAD => {
11870 elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL);
1187111872 try elf.allocateSegmentLoadAddress(phndx);
1187211873 },
1187311874 .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
1187611877 .TLS => {
1187711878 const tls_filesz: u64 = size;
1187811879
11879 const old_tbss_base = elf.tbssSegmentOffset();
11880 const old_tbss_base = elf.tbssBaseOffset();
1188011881 const new_tbss_base = tls_filesz;
1188111882 const tbss_size = elf.shndx.tbss.size(elf);
1188211883 const tls_vaddr = elf.targetLoad(&ph.vaddr);