authorgravatar for abbix@riseup.netlg <abbix@riseup.net> 2026-08-29 19:41:46-04:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-16 12:41:30+01:00
loge03ff669f56421a13760356c9fbb979c74c65622
tree97c8b51fadff18fc10f41a8f2e97fa131504ea3c
parentc39b73d375353f4e32fd4eb962c06a42e6ca929e
signature Commit is signed but in an unrecognized format.

Elf2: initial bss support

Adds support for generating bss sections based on inputs, i.e bss sections in object files that get linked in will get emitted as bss in the final executable. Also supports thread-local bss (.tbss sections). Partial work towards #36559

1 files changed, 391 insertions(+), 59 deletions(-)

src/link/Elf2.zig+391-59
......@@ -66,6 +66,8 @@ shndx: struct {
6666 debug_rnglists: Section.Index,
6767 debug_str: Section.Index,
6868 debug_str_offsets: Section.Index,
69 bss: Section.Index,
70 tbss: Section.Index,
6971 // These sections are created only as needed, and are initially `.UNDEF`.
7072 init_array: Section.Index,
7173 fini_array: Section.Index,
......@@ -135,6 +137,10 @@ node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Globa
135137/// See the definition of `DsoGlobals`.
136138dso_globals: DsoGlobals,
137139
140// These are needed to find local symbols when a nodeless section moves.
141bss_inputs: std.ArrayList(Node.InputIndex),
142tbss_inputs: std.ArrayList(Node.InputIndex),
143
138144shstrtab: StringTable,
139145strtab: StringTable,
140146dynstr: StringTable,
......@@ -491,7 +497,8 @@ const Archive = struct {
491497};
492498
493499const Section = struct {
494 /// The node corresponding to this section.
500 /// File-backed node corresponding to this section.
501 /// This is `.none` for `SHT_NOBITS` sections.
495502 ni: MappedFile.Node.Index.Optional,
496503 /// A symbol which is exactly at the start of this section.
497504 ///
......@@ -655,14 +662,30 @@ const Section = struct {
655662 elf.targetStore(&shdr.addralign, @intCast(min_align.toByteUnits()));
656663 },
657664 }
658 const ni = shndx.node(elf);
659 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {
660 try ni.realign(elf.base.comp.gpa, &elf.mf, min_align);
661 }
662 switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
663 .elf => {},
664 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),
665 else => unreachable,
665
666 if (shndx.get(elf).ni.unwrap()) |ni| {
667 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {
668 try ni.realign(elf.base.comp.gpa, &elf.mf, min_align);
669 }
670 switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
671 .elf => {},
672 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),
673 else => unreachable,
674 }
675 } else {
676 // For sections without nodes (NOBITS), propagate alignment to the parent.
677 assert(switch (elf.shdrPtr(shndx)) {
678 inline else => |shdr| elf.targetLoad(&shdr.type) == .NOBITS,
679 });
680
681 if (elf.ehdrType() == .REL) return;
682
683 const segment_ni = if (shndx.flags(elf).TLS)
684 elf.ni.tls.unwrap().?
685 else
686 elf.ni.data;
687
688 try elf.ensureSegmentAligned(elf.getNode(segment_ni).segment, min_align);
666689 }
667690 }
668691
......@@ -4718,6 +4741,8 @@ fn create(
47184741 .init_array = .UNDEF,
47194742 .fini_array = .UNDEF,
47204743 .preinit_array = .UNDEF,
4744 .bss = .UNDEF,
4745 .tbss = .UNDEF,
47214746 },
47224747 .dynamic = .{
47234748 .flags = 0,
......@@ -4745,6 +4770,8 @@ fn create(
47454770 .symbols = .empty,
47464771 .default_sym_vers = .empty,
47474772 },
4773 .bss_inputs = .empty,
4774 .tbss_inputs = .empty,
47484775 .shstrtab = .{ .map = .empty },
47494776 .strtab = .{ .map = .empty },
47504777 .dynstr = .{ .map = .empty },
......@@ -4827,6 +4854,8 @@ pub fn deinit(elf: *Elf) void {
48274854 elf.dso_globals.string_bytes.deinit(gpa);
48284855 elf.dso_globals.symbols.deinit(gpa);
48294856 elf.dso_globals.default_sym_vers.deinit(gpa);
4857 elf.bss_inputs.deinit(gpa);
4858 elf.tbss_inputs.deinit(gpa);
48304859 elf.shstrtab.map.deinit(gpa);
48314860 elf.strtab.map.deinit(gpa);
48324861 elf.dynstr.map.deinit(gpa);
......@@ -4912,7 +4941,9 @@ fn initHeaders(
49124941 shnum += 1; // .rodata
49134942 shnum += 1; // .text
49144943 shnum += 1; // .data
4944 shnum += 1; // .bss
49154945 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata
4946 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tbss
49164947 shnum += 1; // .data.rel.ro
49174948 if (have_dynamic) {
49184949 shnum += 1; // .dynamic
......@@ -5029,9 +5060,13 @@ fn initHeaders(
50295060 };
50305061 };
50315062
5063 // NOBITS sections don't contribute nodes.
5064 const nobits_shnum: u32 =
5065 @as(u32, 1) + @intFromBool(comp.config.any_non_single_threaded);
5066
50325067 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header
50335068 3 + // `.elf`, `.ehdr`, and `.shdr` nodes
5034 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
5069 (shnum - 1 - nobits_shnum) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
50355070 (phnum -| 1) + // -1 because the GNU_STACK phdr does not have a `.segment` node
50365071 @intFromBool(have_eh_frame and @"type" != .REL); // eh_frame_footer
50375072
......@@ -5521,6 +5556,13 @@ fn initHeaders(
55215556 .flags = .{ .WRITE = true, .ALLOC = true },
55225557 .node_align = node_block_align,
55235558 }));
5559
5560 elf.shndx.bss = try elf.addSection(elf.ni.data, .{
5561 .name = ".bss",
5562 .type = .NOBITS,
5563 .flags = .{ .WRITE = true, .ALLOC = true },
5564 });
5565
55245566 if (@"type" != .REL) {
55255567 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
55265568 .name = ".got",
......@@ -6119,11 +6161,19 @@ fn initHeaders(
61196161 .manual_size = true,
61206162 });
61216163 }
6122 if (elf.ni.tls.unwrap()) |tls_segment_ni| elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{
6123 .name = ".tdata",
6124 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
6125 .node_align = node_block_align,
6126 });
6164 if (elf.ni.tls.unwrap()) |tls_segment_ni| {
6165 elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{
6166 .name = ".tdata",
6167 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
6168 .node_align = node_block_align,
6169 });
6170
6171 elf.shndx.tbss = try elf.addSection(tls_segment_ni, .{
6172 .name = ".tbss",
6173 .type = .NOBITS,
6174 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
6175 });
6176 }
61276177 switch (comp.config.debug_format) {
61286178 .strip => {},
61296179 .dwarf => {
......@@ -6330,6 +6380,23 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
63306380 => elf.computeNodeVAddr(ni),
63316381 };
63326382}
6383
6384fn tbssSegmentOffset(elf: *Elf) u64 {
6385 const tbss_addr = elf.shndx.tbss.vaddr(elf);
6386
6387 // Hasn't been laid out yet.
6388 if (tbss_addr == 0) return 0;
6389
6390 const tls_ni = elf.ni.tls.unwrap().?;
6391 const tls_phndx = elf.getNode(tls_ni).segment;
6392
6393 const tls_vaddr = switch (elf.phdrSlice()) {
6394 inline else => |phdr| elf.targetLoad(&phdr[tls_phndx].vaddr),
6395 };
6396
6397 return tbss_addr - tls_vaddr;
6398}
6399
63336400fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
63346401 const parent_ni = ni.parent(&elf.mf).unwrap().?;
63356402 const parent_vaddr = parent_vaddr: switch (elf.getNode(parent_ni)) {
......@@ -6942,6 +7009,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
69427009 name: []const u8,
69437010 flags: std.elf.SHF,
69447011 entsize: std.elf.Xword,
7012 type: std.elf.SHT = .PROGBITS,
69457013}) (Error || error{
69467014 UnsupportedSectionFlags,
69477015 TlsSectionUnavailable,
......@@ -6979,12 +7047,10 @@ fn mapInputSection(elf: *Elf, opts: struct {
69797047 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
69807048 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";
69817049 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";
6982 // TODO: actually generate a bss section!
6983 if (std.mem.eql(u8, opts.name, ".bss")) break :name ".data";
6984 if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".data";
6985 // TODO: actually generate a tbss section!
6986 if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tdata";
6987 if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tdata";
7050 if (std.mem.eql(u8, opts.name, ".bss")) break :name ".bss";
7051 if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".bss";
7052 if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tbss";
7053 if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tbss";
69887054 break :name opts.name;
69897055 },
69907056 };
......@@ -7005,7 +7071,11 @@ fn mapInputSection(elf: *Elf, opts: struct {
70057071 assert(gop.index == elf.shdrs.items.len);
70067072 return elf.addSection(parent_node, .{
70077073 .name = name,
7008 .type = .NULL, // because initial size is 0
7074 .type = switch (opts.type) {
7075 .PROGBITS => .NULL, // size is 0
7076 .NOBITS => .NOBITS,
7077 else => unreachable,
7078 },
70097079 .flags = flags: {
70107080 // We need to decompress the section for linking.
70117081 var flags = opts.flags;
......@@ -7026,9 +7096,20 @@ fn mapInputSection(elf: *Elf, opts: struct {
70267096 return error.SectionFlagsConflict;
70277097 }
70287098
7029 switch (elf.targetLoad(&shdr.type)) {
7030 .NULL, .PROGBITS, .X86_64_UNWIND => {},
7031 else => return error.SectionTypeConflict,
7099 const existing_type = elf.targetLoad(&shdr.type);
7100
7101 switch (opts.type) {
7102 .PROGBITS => switch (existing_type) {
7103 .NULL, .PROGBITS, .X86_64_UNWIND => {},
7104 else => return error.SectionTypeConflict,
7105 },
7106
7107 .NOBITS => switch (existing_type) {
7108 .NOBITS => {},
7109 else => return error.SectionTypeConflict,
7110 },
7111
7112 else => unreachable,
70327113 }
70337114
70347115 // All okay, combine the section flags
......@@ -7447,6 +7528,9 @@ fn loadObject(
74477528 .type = .FILE,
74487529 .shndx = .ABS,
74497530 }) };
7531
7532 var input_has_bss = false;
7533 var input_has_tbss = false;
74507534 const target_endian = elf.targetEndian();
74517535 switch (elf.identClass()) {
74527536 .NONE, _ => unreachable,
......@@ -7461,13 +7545,28 @@ fn loadObject(
74617545 return diags.failParse(path, "bad section header location", .{});
74627546 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
74637547 return diags.failParse(path, "unsupported shentsize", .{});
7464 const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, isi: ?InputSection.Index }, ehdr.shnum);
7548
7549 const sections = try gpa.alloc(struct {
7550 shdr: ElfN.Shdr,
7551 placement: ?union(enum) {
7552 progbits: InputSection.Index,
7553 bss: struct {
7554 shndx: Section.Index,
7555 offset: u64,
7556 },
7557 tbss: struct {
7558 shndx: Section.Index,
7559 offset: u64,
7560 },
7561 },
7562 }, ehdr.shnum);
7563
74657564 defer gpa.free(sections);
74667565 try fr.seekTo(fl.offset + ehdr.shoff);
74677566 for (sections) |*section| {
74687567 section.* = .{
74697568 .shdr = try r.peekStruct(ElfN.Shdr, target_endian),
7470 .isi = null,
7569 .placement = null,
74717570 };
74727571 try r.discardAll(ehdr.shentsize);
74737572 switch (section.shdr.type) {
......@@ -7494,16 +7593,22 @@ fn loadObject(
74947593 if (section.shdr.name >= shstrtab.len) continue;
74957594 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
74967595 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;
7596 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;
74977601 const opts: struct {
74987602 shndx: Section.Index,
74997603 node_fixed: bool,
7500 } = switch (section.shdr.type) {
7604 } = switch (sect_type) {
75017605 else => continue,
75027606 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {
75037607 const shndx = elf.mapInputSection(.{
75047608 .name = name,
75057609 .flags = section.shdr.flags.shf,
75067610 .entsize = section.shdr.entsize,
7611 .type = if (is_bss or is_tbss) .NOBITS else .PROGBITS,
75077612 }) catch |err| switch (err) {
75087613 else => |e| return e,
75097614 error.StripSection => continue,
......@@ -7559,7 +7664,8 @@ fn loadObject(
75597664 shndx != .rodata and
75607665 shndx != .data and
75617666 shndx != .data_rel_ro and
7562 shndx != elf.shndx.tdata,
7667 shndx != elf.shndx.tdata and
7668 shndx != elf.shndx.tbss,
75637669 };
75647670 },
75657671 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
......@@ -7602,7 +7708,43 @@ fn loadObject(
76027708 const need_align: Alignment = .fromByteUnits(
76037709 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
76047710 );
7711
76057712 try opts.shndx.ensureAligned(elf, need_align);
7713
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 }
7726 };
7727
7728 if (is_bss) {
7729 section.placement = .{ .bss = .{
7730 .shndx = opts.shndx,
7731 .offset = offset,
7732 } };
7733 input_has_bss = true;
7734 continue;
7735 }
7736
7737 if (is_tbss) {
7738 section.placement = .{ .tbss = .{
7739 .shndx = opts.shndx,
7740 .offset = offset,
7741 } };
7742 input_has_tbss = true;
7743 }
7744
7745 continue;
7746 }
7747
76067748 const add_node_opts: MappedFile.Node.AddOptions = .{
76077749 .size = need_align.forward(section.shdr.size),
76087750 .alignment = need_align,
......@@ -7622,7 +7764,7 @@ fn loadObject(
76227764 } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts),
76237765 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },
76247766 );
7625 section.isi = @fromBackingInt(@intCast(elf.input_sections.items.len));
7767 section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) };
76267768 elf.input_sections.addOneAssumeCapacity().* = .{
76277769 .input = input_index,
76287770 .file_location = .{
......@@ -7719,7 +7861,31 @@ fn loadObject(
77197861 },
77207862 };
77217863
7722 const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf);
7864 const placement =
7865 sections[input_sym.shndx].placement orelse continue;
7866
7867 const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx, const symbol_value = switch (placement) {
7868 .progbits => |isi| .{
7869 .wrap(isi.node(elf)),
7870 elf.getNodeShndx(isi.node(elf)),
7871 input_sym.value,
7872 },
7873 .bss => |bss| .{
7874 bss.shndx.get(elf).ni,
7875 bss.shndx,
7876 bss.shndx.vaddr(elf) + bss.offset + input_sym.value,
7877 },
7878 .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 },
7887 },
7888 };
77237889
77247890 switch (input_sym.info.bind) {
77257891 else => |bind| return diags.failParse(
......@@ -7729,20 +7895,20 @@ fn loadObject(
77297895 ),
77307896 .LOCAL => {
77317897 const lsi = try elf.addLocalSymbol(.{
7732 .node = .wrap(input_section_node),
7898 .node = symbol_oni,
77337899 .name = name,
7734 .value = input_sym.value,
7900 .value = symbol_value,
77357901 .size = input_sym.size,
77367902 .type = sym_type,
7737 .shndx = elf.getNodeShndx(input_section_node),
7903 .shndx = output_shndx,
77387904 });
77397905 si.* = .local(lsi);
77407906 },
77417907 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
77427908 si.* = .global(elf.addGlobalSymbol(.{
7743 .node = .wrap(input_section_node),
7909 .node = symbol_oni,
77447910 .name = name,
7745 .value = input_sym.value,
7911 .value = symbol_value,
77467912 .size = input_sym.size,
77477913 .type = sym_type,
77487914 .bind = switch (bind) {
......@@ -7751,7 +7917,7 @@ fn loadObject(
77517917 else => unreachable,
77527918 },
77537919 .visibility = input_sym.other.visibility,
7754 .shndx = elf.getNodeShndx(input_section_node),
7920 .shndx = output_shndx,
77557921 }) catch |err| switch (err) {
77567922 error.MultipleDefinitions => return diags.failParse(
77577923 path,
......@@ -7783,7 +7949,15 @@ fn loadObject(
77837949 return diags.failParse(path, "unsupported rel entsize", .{});
77847950
77857951 const loc_sec = &sections[rel_sec.shdr.info];
7786 const loc_node = (loc_sec.isi orelse continue).node(elf);
7952 const loc_node = switch (loc_sec.placement orelse continue) {
7953 .progbits => |isi| isi.node(elf),
7954 .bss, .tbss => return diags.failParse(
7955 path,
7956 "relocations applying to BSS (SHT_NOBITS) sections are unsupported",
7957 .{},
7958 ),
7959 };
7960
77877961 elf.resetNodeRelocs(loc_node);
77887962
77897963 const relnum = std.math.divExact(
......@@ -7850,6 +8024,16 @@ fn loadObject(
78508024 };
78518025 },
78528026 };
8027
8028 if (input_has_bss) {
8029 try elf.bss_inputs.append(gpa, input_index);
8030 try elf.ni.data.resized(gpa, &elf.mf);
8031 }
8032
8033 if (input_has_tbss) {
8034 try elf.tbss_inputs.append(gpa, input_index);
8035 try elf.ni.tls.unwrap().?.resized(gpa, &elf.mf);
8036 }
78538037 },
78548038 }
78558039}
......@@ -8843,7 +9027,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
88439027 try elf.ensureSegmentAligned(phndx, opts.addralign);
88449028 }
88459029 const gpa = elf.base.comp.gpa;
8846 try elf.nodes.ensureUnusedCapacity(gpa, 1);
9030 if (opts.type != .NOBITS) {
9031 try elf.nodes.ensureUnusedCapacity(gpa, 1);
9032 }
88479033 try elf.shdrs.ensureUnusedCapacity(gpa, 1);
88489034 const want_symbol = opts.flags.ALLOC or switch (opts.type) {
88499035 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,
......@@ -8884,7 +9070,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
88849070 .EXEC, .DYN => segment_ni,
88859071 };
88869072 assert(opts.addralign.check(opts.size));
8887 const ni = elf.addNodeAssumeCapacity(try parent_ni.addFloatingChild(gpa, &elf.mf, .{
9073 const oni: MappedFile.Node.Index.Optional = if (opts.type == .NOBITS) .none else .wrap(elf.addNodeAssumeCapacity(try parent_ni.addFloatingChild(gpa, &elf.mf, .{
88889074 .size = opts.node_align.forward(opts.size),
88899075 .alignment = opts.addralign.max(opts.node_align),
88909076 .resized = opts.size > 0,
......@@ -8892,18 +9078,18 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
88929078 }), switch (opts.manual_size) {
88939079 false => .{ .section = shndx },
88949080 true => .{ .section_manual_size = shndx },
8895 });
8896 const addr = elf.computeNodeVAddr(ni);
9081 }));
9082 const addr = if (oni.unwrap()) |ni| elf.computeNodeVAddr(ni) else 0;
88979083 elf.shdrs.appendAssumeCapacity(.{
88989084 .lsi = if (want_symbol) try elf.addLocalSymbol(.{
8899 .node = ni.toOptional(),
9085 .node = oni,
89009086 .name = "",
89019087 .value = addr,
89029088 .size = 0,
89039089 .type = .SECTION,
89049090 .shndx = shndx,
89059091 }) else .null,
8906 .ni = .wrap(ni),
9092 .ni = oni,
89079093 .rela = switch (opts.type) {
89089094 .REL => unreachable,
89099095 .RELA => .{ .free_head = .none },
......@@ -8917,7 +9103,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
89179103 .type = opts.type,
89189104 .flags = .{ .shf = opts.flags },
89199105 .addr = @intCast(addr),
8920 .offset = @intCast(elf.computeNodeElfOffset(ni)),
9106 .offset = if (oni.unwrap()) |ni| @intCast(elf.computeNodeElfOffset(ni)) else 0,
89219107 .size = @intCast(opts.size),
89229108 .link = opts.link,
89239109 .info = opts.info,
......@@ -11034,7 +11220,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1103411220 .DYNAMIC,
1103511221 .INTERP,
1103611222 .PHDR,
11037 .TLS,
1103811223 .GNU_EH_FRAME,
1103911224 .GNU_RELRO,
1104011225 => {
......@@ -11042,6 +11227,28 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1104211227 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
1104311228 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
1104411229 },
11230
11231 .TLS => {
11232 const tbss_base = elf.tbssSegmentOffset();
11233 const new_vaddr = elf.computeNodeVAddr(ni);
11234 const tls_offset = elf.targetLoad(&ph.offset);
11235 const tls_filesz = elf.targetLoad(&ph.filesz);
11236
11237 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
11238 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
11239
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,
11250 );
11251 },
1104511252 }
1104611253 },
1104711254 }
......@@ -11372,6 +11579,78 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1137211579 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
1137311580}
1137411581
11582/// Update the position of a `NOBITS` section.
11583/// BSS needs an absolute symbol base, and TBSS needs TLS-segment-relative base.
11584fn updateNobitsAddress(
11585 elf: *Elf,
11586 shndx: Section.Index,
11587 new_section_addr: u64,
11588 new_file_offset: u64,
11589 old_symbol_base: u64,
11590 new_symbol_base: u64,
11591 inputs: []const Node.InputIndex,
11592) void {
11593 assert(shndx != .UNDEF);
11594
11595 switch (elf.shdrPtr(shndx)) {
11596 inline else => |shdr| {
11597 elf.targetStore(&shdr.addr, @intCast(new_section_addr));
11598 elf.targetStore(&shdr.offset, @intCast(new_file_offset));
11599 },
11600 }
11601
11602 const expected_shndx = shndx.toSection().?;
11603
11604 // Update local symbols.
11605 for (inputs) |ii| {
11606 var lsi, const end_lsi = ii.localSymbolRange(elf);
11607
11608 while (lsi != end_lsi) : (lsi = @fromBackingInt(
11609 @backingInt(lsi) + 1,
11610 )) {
11611 const symbol_shndx, const visibility = switch (elf.symPtr(lsi.index())) {
11612 inline else => |sym| .{
11613 elf.targetLoad(&sym.shndx),
11614 elf.targetLoad(&sym.other).visibility,
11615 },
11616 };
11617
11618 if (symbol_shndx != expected_shndx) continue;
11619
11620 switch (visibility) {
11621 // These are globals demoted to STB_LOCAL and are updated below.
11622 .HIDDEN, .INTERNAL => continue,
11623 .PROTECTED => unreachable,
11624 .DEFAULT => {},
11625 }
11626
11627 const symbol: Symbol.Id = .local(lsi);
11628 symbol.flushMoved(
11629 elf,
11630 symbol.value(elf) - old_symbol_base + new_symbol_base,
11631 );
11632 }
11633 }
11634
11635 // Update globals.
11636 // Since nobits sections have no nodes, we can't just iterate over `node_global_symbols`,
11637 // so instead filter manually.
11638 for (elf.globals.items, 0..) |global, global_index| {
11639 if (global.symtab_index.ptr(elf).node != .none) continue;
11640 const symbol: Symbol.Id = .global(@fromBackingInt(@intCast(global_index)));
11641 const symbol_shndx = switch (elf.symPtr(global.symtab_index)) {
11642 inline else => |sym| elf.targetLoad(&sym.shndx),
11643 };
11644 if (symbol_shndx != expected_shndx) continue;
11645 symbol.flushMoved(elf, symbol.value(elf) - old_symbol_base + new_symbol_base);
11646 }
11647
11648 Symbol.Id.local(shndx.get(elf).lsi).flushMoved(
11649 elf,
11650 new_section_addr,
11651 );
11652}
11653
1137511654/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
1137611655/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and
1137711656/// `memsz` fields of the segment, in order to place it at a valid virtual address.
......@@ -11400,22 +11679,21 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1140011679 switch (elf.phdrSlice()) {
1140111680 inline else => |phdr| {
1140211681 const offset = elf.targetLoad(&phdr[orig_phndx].offset);
11403 const size = elf.targetLoad(&phdr[orig_phndx].filesz);
11404
11405 if (size == 0) {
11406 assert(elf.targetLoad(&phdr[orig_phndx].type) == .NULL);
11407 } else {
11408 assert(elf.targetLoad(&phdr[orig_phndx].type) == .LOAD);
11409 }
11410
11411 elf.targetStore(&phdr[orig_phndx].memsz, size);
11412 elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits()));
11682 const filesz = elf.targetLoad(&phdr[orig_phndx].filesz);
1141311683
1141411684 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);
1141511685 assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr);
1141611686
11687 const bss_size = if (segment_ni == elf.ni.data)
11688 elf.shndx.bss.size(elf)
11689 else
11690 0;
11691 const memsz = filesz + bss_size;
11692
1141711693 var vaddr: u64 = orig_vaddr;
1141811694
11695 elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits()));
11696
1141911697 // First, we will shift the virtual address as needed in order to maintain the required
1142011698 // property that vaddr is congruent to offset modulo the phdr alignment.
1142111699 {
......@@ -11459,7 +11737,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1145911737 // the segment is already known to be moving---making it easier to grow in-place is
1146011738 // the whole point of the multiplier!
1146111739 {
11462 const target_size = if (vaddr == orig_vaddr) size else size * reserve_size_multiplier;
11740 const target_size = if (vaddr == orig_vaddr) memsz else memsz * reserve_size_multiplier;
1146311741 if (vaddr + target_size <= next_page_vaddr) {
1146411742 break; // hooray, we fit here!
1146511743 }
......@@ -11481,7 +11759,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1148111759 else => {},
1148211760 };
1148311761
11484 const free_vaddr = vaddr + size * reserve_size_multiplier;
11762 const free_vaddr = vaddr + memsz * reserve_size_multiplier;
1148511763
1148611764 const next_align = page_align.max(next_ni.alignment(&elf.mf));
1148711765 const next_offset = elf.targetLoad(&next_ph.offset);
......@@ -11491,6 +11769,21 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1149111769 // will update the vaddr of `phndx` when we break from the loop.
1149211770 elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr));
1149311771 elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr));
11772
11773 const bss_shndx = elf.shndx.bss;
11774 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,
11785 );
11786
1149411787 try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
1149511788
1149611789 break;
......@@ -11518,6 +11811,27 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1151811811 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));
1151911812 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
1152011813 }
11814
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 }
1152111835 },
1152211836 }
1152311837}
......@@ -11560,7 +11874,25 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1156011874 elf.targetStore(&ph.memsz, @intCast(size));
1156111875 },
1156211876 .TLS => {
11563 elf.targetStore(&ph.memsz, @intCast(size));
11877 const tls_filesz: u64 = size;
11878
11879 const old_tbss_base = elf.tbssSegmentOffset();
11880 const new_tbss_base = tls_filesz;
11881 const tbss_size = elf.shndx.tbss.size(elf);
11882 const tls_vaddr = elf.targetLoad(&ph.vaddr);
11883 const tls_offset = elf.targetLoad(&ph.offset);
11884
11885 elf.targetStore(&ph.memsz, @intCast(tls_filesz + tbss_size));
11886
11887 elf.updateNobitsAddress(
11888 elf.shndx.tbss,
11889 tls_vaddr + new_tbss_base,
11890 tls_offset + new_tbss_base,
11891 old_tbss_base,
11892 new_tbss_base,
11893 elf.tbss_inputs.items,
11894 );
11895
1156411896 // TPOFF relocations care about the size of the TLS segment. Re-apply
1156511897 // those, and also update any GOT entries from GOTTPOFF relocations.
1156611898 for (elf.tls_size_symbol_relocs.keys()) |reloc| {