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 {...@@ -66,6 +66,8 @@ shndx: struct {
66 debug_rnglists: Section.Index,66 debug_rnglists: Section.Index,
67 debug_str: Section.Index,67 debug_str: Section.Index,
68 debug_str_offsets: Section.Index,68 debug_str_offsets: Section.Index,
69 bss: Section.Index,
70 tbss: Section.Index,
69 // These sections are created only as needed, and are initially `.UNDEF`.71 // These sections are created only as needed, and are initially `.UNDEF`.
70 init_array: Section.Index,72 init_array: Section.Index,
71 fini_array: Section.Index,73 fini_array: Section.Index,
...@@ -135,6 +137,10 @@ node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Globa...@@ -135,6 +137,10 @@ node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Globa
135/// See the definition of `DsoGlobals`.137/// See the definition of `DsoGlobals`.
136dso_globals: DsoGlobals,138dso_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
138shstrtab: StringTable,144shstrtab: StringTable,
139strtab: StringTable,145strtab: StringTable,
140dynstr: StringTable,146dynstr: StringTable,
...@@ -491,7 +497,8 @@ const Archive = struct {...@@ -491,7 +497,8 @@ const Archive = struct {
491};497};
492498
493const Section = struct {499const 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.
495 ni: MappedFile.Node.Index.Optional,502 ni: MappedFile.Node.Index.Optional,
496 /// A symbol which is exactly at the start of this section.503 /// A symbol which is exactly at the start of this section.
497 ///504 ///
...@@ -655,14 +662,30 @@ const Section = struct {...@@ -655,14 +662,30 @@ const Section = struct {
655 elf.targetStore(&shdr.addralign, @intCast(min_align.toByteUnits()));662 elf.targetStore(&shdr.addralign, @intCast(min_align.toByteUnits()));
656 },663 },
657 }664 }
658 const ni = shndx.node(elf);665
659 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {666 if (shndx.get(elf).ni.unwrap()) |ni| {
660 try ni.realign(elf.base.comp.gpa, &elf.mf, min_align);667 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {
661 }668 try ni.realign(elf.base.comp.gpa, &elf.mf, min_align);
662 switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {669 }
663 .elf => {},670 switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
664 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),671 .elf => {},
665 else => unreachable,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);
666 }689 }
667 }690 }
668691
...@@ -4718,6 +4741,8 @@ fn create(...@@ -4718,6 +4741,8 @@ fn create(
4718 .init_array = .UNDEF,4741 .init_array = .UNDEF,
4719 .fini_array = .UNDEF,4742 .fini_array = .UNDEF,
4720 .preinit_array = .UNDEF,4743 .preinit_array = .UNDEF,
4744 .bss = .UNDEF,
4745 .tbss = .UNDEF,
4721 },4746 },
4722 .dynamic = .{4747 .dynamic = .{
4723 .flags = 0,4748 .flags = 0,
...@@ -4745,6 +4770,8 @@ fn create(...@@ -4745,6 +4770,8 @@ fn create(
4745 .symbols = .empty,4770 .symbols = .empty,
4746 .default_sym_vers = .empty,4771 .default_sym_vers = .empty,
4747 },4772 },
4773 .bss_inputs = .empty,
4774 .tbss_inputs = .empty,
4748 .shstrtab = .{ .map = .empty },4775 .shstrtab = .{ .map = .empty },
4749 .strtab = .{ .map = .empty },4776 .strtab = .{ .map = .empty },
4750 .dynstr = .{ .map = .empty },4777 .dynstr = .{ .map = .empty },
...@@ -4827,6 +4854,8 @@ pub fn deinit(elf: *Elf) void {...@@ -4827,6 +4854,8 @@ pub fn deinit(elf: *Elf) void {
4827 elf.dso_globals.string_bytes.deinit(gpa);4854 elf.dso_globals.string_bytes.deinit(gpa);
4828 elf.dso_globals.symbols.deinit(gpa);4855 elf.dso_globals.symbols.deinit(gpa);
4829 elf.dso_globals.default_sym_vers.deinit(gpa);4856 elf.dso_globals.default_sym_vers.deinit(gpa);
4857 elf.bss_inputs.deinit(gpa);
4858 elf.tbss_inputs.deinit(gpa);
4830 elf.shstrtab.map.deinit(gpa);4859 elf.shstrtab.map.deinit(gpa);
4831 elf.strtab.map.deinit(gpa);4860 elf.strtab.map.deinit(gpa);
4832 elf.dynstr.map.deinit(gpa);4861 elf.dynstr.map.deinit(gpa);
...@@ -4912,7 +4941,9 @@ fn initHeaders(...@@ -4912,7 +4941,9 @@ fn initHeaders(
4912 shnum += 1; // .rodata4941 shnum += 1; // .rodata
4913 shnum += 1; // .text4942 shnum += 1; // .text
4914 shnum += 1; // .data4943 shnum += 1; // .data
4944 shnum += 1; // .bss
4915 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata4945 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata
4946 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tbss
4916 shnum += 1; // .data.rel.ro4947 shnum += 1; // .data.rel.ro
4917 if (have_dynamic) {4948 if (have_dynamic) {
4918 shnum += 1; // .dynamic4949 shnum += 1; // .dynamic
...@@ -5029,9 +5060,13 @@ fn initHeaders(...@@ -5029,9 +5060,13 @@ fn initHeaders(
5029 };5060 };
5030 };5061 };
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
5032 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header5067 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header
5033 3 + // `.elf`, `.ehdr`, and `.shdr` nodes5068 3 + // `.elf`, `.ehdr`, and `.shdr` nodes
5034 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node5069 (shnum - 1 - nobits_shnum) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
5035 (phnum -| 1) + // -1 because the GNU_STACK phdr does not have a `.segment` node5070 (phnum -| 1) + // -1 because the GNU_STACK phdr does not have a `.segment` node
5036 @intFromBool(have_eh_frame and @"type" != .REL); // eh_frame_footer5071 @intFromBool(have_eh_frame and @"type" != .REL); // eh_frame_footer
50375072
...@@ -5521,6 +5556,13 @@ fn initHeaders(...@@ -5521,6 +5556,13 @@ fn initHeaders(
5521 .flags = .{ .WRITE = true, .ALLOC = true },5556 .flags = .{ .WRITE = true, .ALLOC = true },
5522 .node_align = node_block_align,5557 .node_align = node_block_align,
5523 }));5558 }));
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
5524 if (@"type" != .REL) {5566 if (@"type" != .REL) {
5525 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{5567 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
5526 .name = ".got",5568 .name = ".got",
...@@ -6119,11 +6161,19 @@ fn initHeaders(...@@ -6119,11 +6161,19 @@ fn initHeaders(
6119 .manual_size = true,6161 .manual_size = true,
6120 });6162 });
6121 }6163 }
6122 if (elf.ni.tls.unwrap()) |tls_segment_ni| elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{6164 if (elf.ni.tls.unwrap()) |tls_segment_ni| {
6123 .name = ".tdata",6165 elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{
6124 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },6166 .name = ".tdata",
6125 .node_align = node_block_align,6167 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
6126 });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 }
6127 switch (comp.config.debug_format) {6177 switch (comp.config.debug_format) {
6128 .strip => {},6178 .strip => {},
6129 .dwarf => {6179 .dwarf => {
...@@ -6330,6 +6380,23 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {...@@ -6330,6 +6380,23 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
6330 => elf.computeNodeVAddr(ni),6380 => elf.computeNodeVAddr(ni),
6331 };6381 };
6332}6382}
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
6333fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {6400fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
6334 const parent_ni = ni.parent(&elf.mf).unwrap().?;6401 const parent_ni = ni.parent(&elf.mf).unwrap().?;
6335 const parent_vaddr = parent_vaddr: switch (elf.getNode(parent_ni)) {6402 const parent_vaddr = parent_vaddr: switch (elf.getNode(parent_ni)) {
...@@ -6942,6 +7009,7 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -6942,6 +7009,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
6942 name: []const u8,7009 name: []const u8,
6943 flags: std.elf.SHF,7010 flags: std.elf.SHF,
6944 entsize: std.elf.Xword,7011 entsize: std.elf.Xword,
7012 type: std.elf.SHT = .PROGBITS,
6945}) (Error || error{7013}) (Error || error{
6946 UnsupportedSectionFlags,7014 UnsupportedSectionFlags,
6947 TlsSectionUnavailable,7015 TlsSectionUnavailable,
...@@ -6979,12 +7047,10 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -6979,12 +7047,10 @@ fn mapInputSection(elf: *Elf, opts: struct {
6979 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";7047 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
6980 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";7048 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";
6981 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";7049 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";
6982 // TODO: actually generate a bss section!7050 if (std.mem.eql(u8, opts.name, ".bss")) break :name ".bss";
6983 if (std.mem.eql(u8, opts.name, ".bss")) break :name ".data";7051 if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".bss";
6984 if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".data";7052 if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tbss";
6985 // TODO: actually generate a tbss section!7053 if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tbss";
6986 if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tdata";
6987 if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tdata";
6988 break :name opts.name;7054 break :name opts.name;
6989 },7055 },
6990 };7056 };
...@@ -7005,7 +7071,11 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -7005,7 +7071,11 @@ fn mapInputSection(elf: *Elf, opts: struct {
7005 assert(gop.index == elf.shdrs.items.len);7071 assert(gop.index == elf.shdrs.items.len);
7006 return elf.addSection(parent_node, .{7072 return elf.addSection(parent_node, .{
7007 .name = name,7073 .name = name,
7008 .type = .NULL, // because initial size is 07074 .type = switch (opts.type) {
7075 .PROGBITS => .NULL, // size is 0
7076 .NOBITS => .NOBITS,
7077 else => unreachable,
7078 },
7009 .flags = flags: {7079 .flags = flags: {
7010 // We need to decompress the section for linking.7080 // We need to decompress the section for linking.
7011 var flags = opts.flags;7081 var flags = opts.flags;
...@@ -7026,9 +7096,20 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -7026,9 +7096,20 @@ fn mapInputSection(elf: *Elf, opts: struct {
7026 return error.SectionFlagsConflict;7096 return error.SectionFlagsConflict;
7027 }7097 }
70287098
7029 switch (elf.targetLoad(&shdr.type)) {7099 const existing_type = elf.targetLoad(&shdr.type);
7030 .NULL, .PROGBITS, .X86_64_UNWIND => {},7100
7031 else => return error.SectionTypeConflict,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,
7032 }7113 }
70337114
7034 // All okay, combine the section flags7115 // All okay, combine the section flags
...@@ -7447,6 +7528,9 @@ fn loadObject(...@@ -7447,6 +7528,9 @@ fn loadObject(
7447 .type = .FILE,7528 .type = .FILE,
7448 .shndx = .ABS,7529 .shndx = .ABS,
7449 }) };7530 }) };
7531
7532 var input_has_bss = false;
7533 var input_has_tbss = false;
7450 const target_endian = elf.targetEndian();7534 const target_endian = elf.targetEndian();
7451 switch (elf.identClass()) {7535 switch (elf.identClass()) {
7452 .NONE, _ => unreachable,7536 .NONE, _ => unreachable,
...@@ -7461,13 +7545,28 @@ fn loadObject(...@@ -7461,13 +7545,28 @@ fn loadObject(
7461 return diags.failParse(path, "bad section header location", .{});7545 return diags.failParse(path, "bad section header location", .{});
7462 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))7546 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
7463 return diags.failParse(path, "unsupported shentsize", .{});7547 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
7465 defer gpa.free(sections);7564 defer gpa.free(sections);
7466 try fr.seekTo(fl.offset + ehdr.shoff);7565 try fr.seekTo(fl.offset + ehdr.shoff);
7467 for (sections) |*section| {7566 for (sections) |*section| {
7468 section.* = .{7567 section.* = .{
7469 .shdr = try r.peekStruct(ElfN.Shdr, target_endian),7568 .shdr = try r.peekStruct(ElfN.Shdr, target_endian),
7470 .isi = null,7569 .placement = null,
7471 };7570 };
7472 try r.discardAll(ehdr.shentsize);7571 try r.discardAll(ehdr.shentsize);
7473 switch (section.shdr.type) {7572 switch (section.shdr.type) {
...@@ -7494,16 +7593,22 @@ fn loadObject(...@@ -7494,16 +7593,22 @@ fn loadObject(
7494 if (section.shdr.name >= shstrtab.len) continue;7593 if (section.shdr.name >= shstrtab.len) continue;
7495 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);7594 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
7496 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;7595 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;
7497 const opts: struct {7601 const opts: struct {
7498 shndx: Section.Index,7602 shndx: Section.Index,
7499 node_fixed: bool,7603 node_fixed: bool,
7500 } = switch (section.shdr.type) {7604 } = switch (sect_type) {
7501 else => continue,7605 else => continue,
7502 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {7606 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {
7503 const shndx = elf.mapInputSection(.{7607 const shndx = elf.mapInputSection(.{
7504 .name = name,7608 .name = name,
7505 .flags = section.shdr.flags.shf,7609 .flags = section.shdr.flags.shf,
7506 .entsize = section.shdr.entsize,7610 .entsize = section.shdr.entsize,
7611 .type = if (is_bss or is_tbss) .NOBITS else .PROGBITS,
7507 }) catch |err| switch (err) {7612 }) catch |err| switch (err) {
7508 else => |e| return e,7613 else => |e| return e,
7509 error.StripSection => continue,7614 error.StripSection => continue,
...@@ -7559,7 +7664,8 @@ fn loadObject(...@@ -7559,7 +7664,8 @@ fn loadObject(
7559 shndx != .rodata and7664 shndx != .rodata and
7560 shndx != .data and7665 shndx != .data and
7561 shndx != .data_rel_ro and7666 shndx != .data_rel_ro and
7562 shndx != elf.shndx.tdata,7667 shndx != elf.shndx.tdata and
7668 shndx != elf.shndx.tbss,
7563 };7669 };
7564 },7670 },
7565 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{7671 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
...@@ -7602,7 +7708,43 @@ fn loadObject(...@@ -7602,7 +7708,43 @@ fn loadObject(
7602 const need_align: Alignment = .fromByteUnits(7708 const need_align: Alignment = .fromByteUnits(
7603 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),7709 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
7604 );7710 );
7711
7605 try opts.shndx.ensureAligned(elf, need_align);7712 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
7606 const add_node_opts: MappedFile.Node.AddOptions = .{7748 const add_node_opts: MappedFile.Node.AddOptions = .{
7607 .size = need_align.forward(section.shdr.size),7749 .size = need_align.forward(section.shdr.size),
7608 .alignment = need_align,7750 .alignment = need_align,
...@@ -7622,7 +7764,7 @@ fn loadObject(...@@ -7622,7 +7764,7 @@ fn loadObject(
7622 } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts),7764 } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts),
7623 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },7765 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },
7624 );7766 );
7625 section.isi = @fromBackingInt(@intCast(elf.input_sections.items.len));7767 section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) };
7626 elf.input_sections.addOneAssumeCapacity().* = .{7768 elf.input_sections.addOneAssumeCapacity().* = .{
7627 .input = input_index,7769 .input = input_index,
7628 .file_location = .{7770 .file_location = .{
...@@ -7719,7 +7861,31 @@ fn loadObject(...@@ -7719,7 +7861,31 @@ fn loadObject(
7719 },7861 },
7720 };7862 };
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
7724 switch (input_sym.info.bind) {7890 switch (input_sym.info.bind) {
7725 else => |bind| return diags.failParse(7891 else => |bind| return diags.failParse(
...@@ -7729,20 +7895,20 @@ fn loadObject(...@@ -7729,20 +7895,20 @@ fn loadObject(
7729 ),7895 ),
7730 .LOCAL => {7896 .LOCAL => {
7731 const lsi = try elf.addLocalSymbol(.{7897 const lsi = try elf.addLocalSymbol(.{
7732 .node = .wrap(input_section_node),7898 .node = symbol_oni,
7733 .name = name,7899 .name = name,
7734 .value = input_sym.value,7900 .value = symbol_value,
7735 .size = input_sym.size,7901 .size = input_sym.size,
7736 .type = sym_type,7902 .type = sym_type,
7737 .shndx = elf.getNodeShndx(input_section_node),7903 .shndx = output_shndx,
7738 });7904 });
7739 si.* = .local(lsi);7905 si.* = .local(lsi);
7740 },7906 },
7741 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {7907 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
7742 si.* = .global(elf.addGlobalSymbol(.{7908 si.* = .global(elf.addGlobalSymbol(.{
7743 .node = .wrap(input_section_node),7909 .node = symbol_oni,
7744 .name = name,7910 .name = name,
7745 .value = input_sym.value,7911 .value = symbol_value,
7746 .size = input_sym.size,7912 .size = input_sym.size,
7747 .type = sym_type,7913 .type = sym_type,
7748 .bind = switch (bind) {7914 .bind = switch (bind) {
...@@ -7751,7 +7917,7 @@ fn loadObject(...@@ -7751,7 +7917,7 @@ fn loadObject(
7751 else => unreachable,7917 else => unreachable,
7752 },7918 },
7753 .visibility = input_sym.other.visibility,7919 .visibility = input_sym.other.visibility,
7754 .shndx = elf.getNodeShndx(input_section_node),7920 .shndx = output_shndx,
7755 }) catch |err| switch (err) {7921 }) catch |err| switch (err) {
7756 error.MultipleDefinitions => return diags.failParse(7922 error.MultipleDefinitions => return diags.failParse(
7757 path,7923 path,
...@@ -7783,7 +7949,15 @@ fn loadObject(...@@ -7783,7 +7949,15 @@ fn loadObject(
7783 return diags.failParse(path, "unsupported rel entsize", .{});7949 return diags.failParse(path, "unsupported rel entsize", .{});
77847950
7785 const loc_sec = &sections[rel_sec.shdr.info];7951 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
7787 elf.resetNodeRelocs(loc_node);7961 elf.resetNodeRelocs(loc_node);
77887962
7789 const relnum = std.math.divExact(7963 const relnum = std.math.divExact(
...@@ -7850,6 +8024,16 @@ fn loadObject(...@@ -7850,6 +8024,16 @@ fn loadObject(
7850 };8024 };
7851 },8025 },
7852 };8026 };
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 }
7853 },8037 },
7854 }8038 }
7855}8039}
...@@ -8843,7 +9027,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -8843,7 +9027,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
8843 try elf.ensureSegmentAligned(phndx, opts.addralign);9027 try elf.ensureSegmentAligned(phndx, opts.addralign);
8844 }9028 }
8845 const gpa = elf.base.comp.gpa;9029 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 }
8847 try elf.shdrs.ensureUnusedCapacity(gpa, 1);9033 try elf.shdrs.ensureUnusedCapacity(gpa, 1);
8848 const want_symbol = opts.flags.ALLOC or switch (opts.type) {9034 const want_symbol = opts.flags.ALLOC or switch (opts.type) {
8849 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,9035 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,
...@@ -8884,7 +9070,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -8884,7 +9070,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
8884 .EXEC, .DYN => segment_ni,9070 .EXEC, .DYN => segment_ni,
8885 };9071 };
8886 assert(opts.addralign.check(opts.size));9072 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, .{
8888 .size = opts.node_align.forward(opts.size),9074 .size = opts.node_align.forward(opts.size),
8889 .alignment = opts.addralign.max(opts.node_align),9075 .alignment = opts.addralign.max(opts.node_align),
8890 .resized = opts.size > 0,9076 .resized = opts.size > 0,
...@@ -8892,18 +9078,18 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -8892,18 +9078,18 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
8892 }), switch (opts.manual_size) {9078 }), switch (opts.manual_size) {
8893 false => .{ .section = shndx },9079 false => .{ .section = shndx },
8894 true => .{ .section_manual_size = shndx },9080 true => .{ .section_manual_size = shndx },
8895 });9081 }));
8896 const addr = elf.computeNodeVAddr(ni);9082 const addr = if (oni.unwrap()) |ni| elf.computeNodeVAddr(ni) else 0;
8897 elf.shdrs.appendAssumeCapacity(.{9083 elf.shdrs.appendAssumeCapacity(.{
8898 .lsi = if (want_symbol) try elf.addLocalSymbol(.{9084 .lsi = if (want_symbol) try elf.addLocalSymbol(.{
8899 .node = ni.toOptional(),9085 .node = oni,
8900 .name = "",9086 .name = "",
8901 .value = addr,9087 .value = addr,
8902 .size = 0,9088 .size = 0,
8903 .type = .SECTION,9089 .type = .SECTION,
8904 .shndx = shndx,9090 .shndx = shndx,
8905 }) else .null,9091 }) else .null,
8906 .ni = .wrap(ni),9092 .ni = oni,
8907 .rela = switch (opts.type) {9093 .rela = switch (opts.type) {
8908 .REL => unreachable,9094 .REL => unreachable,
8909 .RELA => .{ .free_head = .none },9095 .RELA => .{ .free_head = .none },
...@@ -8917,7 +9103,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -8917,7 +9103,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
8917 .type = opts.type,9103 .type = opts.type,
8918 .flags = .{ .shf = opts.flags },9104 .flags = .{ .shf = opts.flags },
8919 .addr = @intCast(addr),9105 .addr = @intCast(addr),
8920 .offset = @intCast(elf.computeNodeElfOffset(ni)),9106 .offset = if (oni.unwrap()) |ni| @intCast(elf.computeNodeElfOffset(ni)) else 0,
8921 .size = @intCast(opts.size),9107 .size = @intCast(opts.size),
8922 .link = opts.link,9108 .link = opts.link,
8923 .info = opts.info,9109 .info = opts.info,
...@@ -11034,7 +11220,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11034,7 +11220,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11034 .DYNAMIC,11220 .DYNAMIC,
11035 .INTERP,11221 .INTERP,
11036 .PHDR,11222 .PHDR,
11037 .TLS,
11038 .GNU_EH_FRAME,11223 .GNU_EH_FRAME,
11039 .GNU_RELRO,11224 .GNU_RELRO,
11040 => {11225 => {
...@@ -11042,6 +11227,28 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11042,6 +11227,28 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11042 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));11227 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
11043 elf.targetStore(&ph.paddr, @intCast(new_vaddr));11228 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
11044 },11229 },
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 },
11045 }11252 }
11046 },11253 },
11047 }11254 }
...@@ -11372,6 +11579,78 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11372,6 +11579,78 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11372 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);11579 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
11373}11580}
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
11375/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`11654/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
11376/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and11655/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and
11377/// `memsz` fields of the segment, in order to place it at a valid virtual address.11656/// `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...@@ -11400,22 +11679,21 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
11400 switch (elf.phdrSlice()) {11679 switch (elf.phdrSlice()) {
11401 inline else => |phdr| {11680 inline else => |phdr| {
11402 const offset = elf.targetLoad(&phdr[orig_phndx].offset);11681 const offset = elf.targetLoad(&phdr[orig_phndx].offset);
11403 const size = elf.targetLoad(&phdr[orig_phndx].filesz);11682 const filesz = 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()));
1141311683
11414 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);11684 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);
11415 assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr);11685 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
11417 var vaddr: u64 = orig_vaddr;11693 var vaddr: u64 = orig_vaddr;
1141811694
11695 elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits()));
11696
11419 // First, we will shift the virtual address as needed in order to maintain the required11697 // First, we will shift the virtual address as needed in order to maintain the required
11420 // property that vaddr is congruent to offset modulo the phdr alignment.11698 // property that vaddr is congruent to offset modulo the phdr alignment.
11421 {11699 {
...@@ -11459,7 +11737,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro...@@ -11459,7 +11737,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
11459 // the segment is already known to be moving---making it easier to grow in-place is11737 // the segment is already known to be moving---making it easier to grow in-place is
11460 // the whole point of the multiplier!11738 // the whole point of the multiplier!
11461 {11739 {
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;
11463 if (vaddr + target_size <= next_page_vaddr) {11741 if (vaddr + target_size <= next_page_vaddr) {
11464 break; // hooray, we fit here!11742 break; // hooray, we fit here!
11465 }11743 }
...@@ -11481,7 +11759,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro...@@ -11481,7 +11759,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
11481 else => {},11759 else => {},
11482 };11760 };
1148311761
11484 const free_vaddr = vaddr + size * reserve_size_multiplier;11762 const free_vaddr = vaddr + memsz * reserve_size_multiplier;
1148511763
11486 const next_align = page_align.max(next_ni.alignment(&elf.mf));11764 const next_align = page_align.max(next_ni.alignment(&elf.mf));
11487 const next_offset = elf.targetLoad(&next_ph.offset);11765 const next_offset = elf.targetLoad(&next_ph.offset);
...@@ -11491,6 +11769,21 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro...@@ -11491,6 +11769,21 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
11491 // will update the vaddr of `phndx` when we break from the loop.11769 // will update the vaddr of `phndx` when we break from the loop.
11492 elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr));11770 elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr));
11493 elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr));11771 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
11494 try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);11787 try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
1149511788
11496 break;11789 break;
...@@ -11518,6 +11811,27 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro...@@ -11518,6 +11811,27 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
11518 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));11811 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));
11519 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);11812 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
11520 }11813 }
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 }
11521 },11835 },
11522 }11836 }
11523}11837}
...@@ -11560,7 +11874,25 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -11560,7 +11874,25 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
11560 elf.targetStore(&ph.memsz, @intCast(size));11874 elf.targetStore(&ph.memsz, @intCast(size));
11561 },11875 },
11562 .TLS => {11876 .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
11564 // TPOFF relocations care about the size of the TLS segment. Re-apply11896 // TPOFF relocations care about the size of the TLS segment. Re-apply
11565 // those, and also update any GOT entries from GOTTPOFF relocations.11897 // those, and also update any GOT entries from GOTTPOFF relocations.
11566 for (elf.tls_size_symbol_relocs.keys()) |reloc| {11898 for (elf.tls_size_symbol_relocs.keys()) |reloc| {