authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-21 08:28:12+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-26 06:48:53+01:00
log9ce9ee9ae2e2c74c782de416ed735a77e94d3c07
treeba5e508c40e3be2e7621d9d5cb68725671fae69e
parentbdc2b2846d3f8f3cd81b50cddb56a3a6f3f29485
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: basic support for custom sections

We maintain a map of all section names and, when an input section doesn't have a well-known name like `.text`, we will add a corresponding output section deduplicated with that map. The input section's flags are consulted to determine how to map the output section to a segment. For instance, input sections with SHF_ALLOC and SHF_WRITE are placed in a corresponding output section in the "data" segment, i.e. the segment which contains the `.data` section. This very loosely mimics the way that LLD and GNU ld handle these unknown sections (what they call "orphan sections"). Sections without SHF_ALLOC are placed outside of all segments. If input sections of the same name have conflicting flags an error is emitted, unless the section is not SHF_ALLOC, in which case I have opted to silently drop the input section for now on the basis that it probably isn't necessary to produce a functioning executable. An unintended (but nice!) consequence of this commit is that DWARF debug information from input objects now appears to work correctly in the output binary. I suspect that this won't *always* work correctly (I'm concerned that we might try to pad the end of the `.debug_info` section with zeroes, which I don't believe is valid?), but it's still neat that this works at all! Just to be clear, there is still no debug information emitted for Zig code compiled using this linker. That requires the linker to actually be aware of DWARF sections and to coordinate with the codegen logic to emit data to them. That is of course planned, but is not yet implemetned.

1 files changed, 249 insertions(+), 60 deletions(-)

src/link/Elf2.zig+249-60
...@@ -92,6 +92,8 @@ lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {...@@ -92,6 +92,8 @@ lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
92}),92}),
93pending_uavs: std.ArrayList(Node.UavMapIndex),93pending_uavs: std.ArrayList(Node.UavMapIndex),
94relocs: std.ArrayList(Reloc),94relocs: std.ArrayList(Reloc),
95/// Index matches the index into `shdrs`.
96section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
9597
96/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation98/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
97/// entries which target that symbol must be updated to reference the correct symbol index.99/// entries which target that symbol must be updated to reference the correct symbol index.
...@@ -361,11 +363,10 @@ const Section = struct {...@@ -361,11 +363,10 @@ const Section = struct {
361 return &elf.shdrs.items[@intFromEnum(s)];363 return &elf.shdrs.items[@intFromEnum(s)];
362 }364 }
363365
364 fn name(s: Index, elf: *Elf) [:0]const u8 {366 fn name(s: Index, elf: *Elf) String(.shstrtab) {
365 const str: String(.shstrtab) = switch (elf.shdrPtr(s)) {367 return switch (elf.shdrPtr(s)) {
366 inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)),368 inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)),
367 };369 };
368 return str.slice(elf);
369 }370 }
370371
371 fn vaddr(s: Index, elf: *Elf) u64 {372 fn vaddr(s: Index, elf: *Elf) u64 {
...@@ -1372,7 +1373,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId...@@ -1372,7 +1373,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId
1372 return elf.externSymbol(.{1373 return elf.externSymbol(.{
1373 .name = @"extern".name.toSlice(ip),1374 .name = @"extern".name.toSlice(ip),
1374 .lib_name = @"extern".lib_name.toSlice(ip),1375 .lib_name = @"extern".lib_name.toSlice(ip),
1375 .type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded),1376 .type = elf.navType(nav.resolved.?),
1376 .linkage = @"extern".linkage,1377 .linkage = @"extern".linkage,
1377 .visibility = @"extern".visibility,1378 .visibility = @"extern".visibility,
1378 });1379 });
...@@ -1956,6 +1957,7 @@ fn create(...@@ -1956,6 +1957,7 @@ fn create(
1956 }),1957 }),
1957 .pending_uavs = .empty,1958 .pending_uavs = .empty,
1958 .relocs = .empty,1959 .relocs = .empty,
1960 .section_by_name = .empty,
1959 .changed_symtab_index = .empty,1961 .changed_symtab_index = .empty,
1960 .const_prog_node = .none,1962 .const_prog_node = .none,
1961 .synth_prog_node = .none,1963 .synth_prog_node = .none,
...@@ -1992,6 +1994,7 @@ pub fn deinit(elf: *Elf) void {...@@ -1992,6 +1994,7 @@ pub fn deinit(elf: *Elf) void {
1992 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);1994 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
1993 elf.pending_uavs.deinit(gpa);1995 elf.pending_uavs.deinit(gpa);
1994 elf.relocs.deinit(gpa);1996 elf.relocs.deinit(gpa);
1997 elf.section_by_name.deinit(gpa);
1995 elf.changed_symtab_index.deinit(gpa);1998 elf.changed_symtab_index.deinit(gpa);
1996 elf.* = undefined;1999 elf.* = undefined;
1997}2000}
...@@ -2561,6 +2564,12 @@ fn initHeaders(...@@ -2561,6 +2564,12 @@ fn initHeaders(
2561 .addralign = elf.mf.flags.block_size,2564 .addralign = elf.mf.flags.block_size,
2562 });2565 });
2563 assert(elf.nodes.len == expected_nodes_len);2566 assert(elf.nodes.len == expected_nodes_len);
2567
2568 try elf.section_by_name.ensureUnusedCapacity(gpa, elf.shdrs.items.len);
2569 for (0..elf.shdrs.items.len) |shndx_raw| {
2570 const shndx: Section.Index = @enumFromInt(shndx_raw);
2571 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
2572 }
2564}2573}
25652574
2566pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {2575pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
...@@ -2810,28 +2819,129 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {...@@ -2810,28 +2819,129 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
2810 }2819 }
2811}2820}
28122821
2813fn navType(2822fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
2814 ip: *const InternPool,2823 const any_non_single_threaded = elf.base.comp.config.any_non_single_threaded;
2815 nav_resolved: @typeInfo(@FieldType(InternPool.Nav, "resolved")).optional.child,
2816 any_non_single_threaded: bool,
2817) std.elf.STT {
2818 return if (any_non_single_threaded and nav_resolved.@"threadlocal")2824 return if (any_non_single_threaded and nav_resolved.@"threadlocal")
2819 .TLS2825 .TLS
2820 else if (ip.isFunctionType(nav_resolved.type))2826 else if (elf.base.comp.zcu.?.intern_pool.isFunctionType(nav_resolved.type))
2821 .FUNC2827 .FUNC
2822 else2828 else
2823 .OBJECT;2829 .OBJECT;
2824}2830}
2825fn namedSection(elf: *const Elf, name: []const u8) ?Section.Index {2831fn mapInputSection(elf: *Elf, opts: struct {
2826 if (std.mem.eql(u8, name, ".rodata") or2832 name: []const u8,
2827 std.mem.startsWith(u8, name, ".rodata.")) return .rodata;2833 flags: std.elf.SHF,
2828 if (std.mem.eql(u8, name, ".text") or2834 addralign: std.elf.Xword,
2829 std.mem.startsWith(u8, name, ".text.")) return .text;2835 entsize: std.elf.Xword,
2830 if (std.mem.eql(u8, name, ".data") or2836}) !Section.Index {
2831 std.mem.startsWith(u8, name, ".data.")) return .data;2837 const gpa = elf.base.comp.gpa;
2832 if (std.mem.eql(u8, name, ".tdata") or2838 if (opts.flags.INFO_LINK or
2833 std.mem.startsWith(u8, name, ".tdata.")) return elf.shndx.tdata;2839 opts.flags.LINK_ORDER or
2834 return null;2840 opts.flags.OS_NONCONFORMING or
2841 (opts.flags.EXECINSTR and opts.flags.WRITE) or
2842 (opts.flags.EXECINSTR and opts.flags.TLS))
2843 {
2844 return error.UnsupportedSectionFlags;
2845 }
2846 if (opts.flags.TLS and elf.ni.tls == .none) {
2847 assert(!elf.base.comp.config.any_non_single_threaded);
2848 return error.TlsSectionUnavailable;
2849 }
2850 const name: []const u8 = switch (elf.ehdrField(.type)) {
2851 .NONE, .CORE, _ => unreachable,
2852 .REL => opts.name,
2853 .EXEC, .DYN => name: {
2854 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";
2855 if (std.mem.startsWith(u8, opts.name, ".rodata.")) break :name ".rodata";
2856 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
2857 if (std.mem.startsWith(u8, opts.name, ".data.rel.ro.")) break :name ".data.rel.ro";
2858 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";
2859 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";
2860 // TODO: actually generate a bss section!
2861 if (std.mem.eql(u8, opts.name, ".bss")) break :name ".data";
2862 if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".data";
2863 // TODO: actually generate a tbss section!
2864 if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tdata";
2865 if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tdata";
2866 break :name opts.name;
2867 },
2868 };
2869 const existing_shndx: Section.Index = existing: {
2870 const name_shstrtab = try elf.string(.shstrtab, name);
2871 const gop = try elf.section_by_name.getOrPut(gpa, name_shstrtab);
2872 if (gop.found_existing) {
2873 break :existing @enumFromInt(gop.index);
2874 }
2875 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);
2876 const parent_node: MappedFile.Node.Index = parent: {
2877 if (!opts.flags.ALLOC) break :parent elf.ni.file;
2878 if (opts.flags.EXECINSTR) break :parent elf.ni.text;
2879 if (opts.flags.TLS) break :parent elf.ni.tls;
2880 if (opts.flags.WRITE) break :parent elf.ni.data;
2881 break :parent elf.ni.rodata;
2882 };
2883 assert(gop.index == elf.shdrs.items.len);
2884 return elf.addSection(parent_node, .{
2885 .name = name,
2886 .type = .NULL, // because initial size is 0
2887 .flags = flags: {
2888 // We need to decompress the section for linking.
2889 var flags = opts.flags;
2890 flags.COMPRESSED = false;
2891 break :flags flags;
2892 },
2893 .node_align = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
2894 usize,
2895 @intCast(@max(opts.addralign, 1)),
2896 )),
2897 .entsize = std.math.lossyCast(u32, opts.entsize),
2898 });
2899 };
2900 // Validate that the input is compatible with this section...
2901 switch (elf.shdrPtr(existing_shndx)) {
2902 inline else => |shdr| {
2903 const cur_flags = elf.targetLoad(&shdr.flags).shf;
2904 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or
2905 cur_flags.WRITE != opts.flags.WRITE or
2906 cur_flags.TLS != opts.flags.TLS)
2907 {
2908 return error.SectionFlagsConflict;
2909 }
2910
2911 switch (elf.targetLoad(&shdr.type)) {
2912 .NULL, .PROGBITS => {},
2913 else => return error.SectionTypeConflict,
2914 }
2915 },
2916 }
2917 // ...then realign the section's node if necessary...
2918 if (opts.addralign > existing_shndx.get(elf).ni.alignment(&elf.mf).toByteUnits()) {
2919 const new_alignment: std.mem.Alignment = .fromByteUnits(
2920 std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)),
2921 );
2922 try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment);
2923 }
2924 // ...and update the shdr as needed.
2925 switch (elf.shdrPtr(existing_shndx)) {
2926 inline else => |shdr| {
2927 // Combine the section flags.
2928 const cur_flags = elf.targetLoad(&shdr.flags).shf;
2929 elf.targetStore(&shdr.flags, .{ .shf = .{
2930 .EXECINSTR = cur_flags.EXECINSTR,
2931 .WRITE = cur_flags.WRITE,
2932 .TLS = cur_flags.TLS,
2933 .ALLOC = cur_flags.ALLOC or opts.flags.ALLOC,
2934 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,
2935 .MERGE = cur_flags.MERGE and opts.flags.MERGE,
2936 } });
2937 // Increase addralign to the maximum of the current value and the new value---the node
2938 // alignment was already increased above.
2939 if (opts.addralign > elf.targetLoad(&shdr.addralign)) {
2940 elf.targetStore(&shdr.addralign, @intCast(opts.addralign));
2941 }
2942 },
2943 }
2944 return existing_shndx;
2835}2945}
2836fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {2946fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
2837 const gpa = zcu.gpa;2947 const gpa = zcu.gpa;
...@@ -2845,17 +2955,40 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM...@@ -2845,17 +2955,40 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM
2845 const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index);2955 const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index);
2846 const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index);2956 const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index);
2847 if (!nav_gop.found_existing) {2957 if (!nav_gop.found_existing) {
2848 const sym_type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded);
2849 const shndx: Section.Index = section: {2958 const shndx: Section.Index = section: {
2850 if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| {2959 if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| {
2851 if (elf.namedSection(@"linksection")) |shndx| break :section shndx;2960 if (elf.mapInputSection(.{
2961 .name = @"linksection",
2962 .flags = .{
2963 .ALLOC = true,
2964 .EXECINSTR = ip.isFunctionType(nav.resolved.?.type),
2965 .WRITE = !nav.resolved.?.@"const",
2966 .TLS = elf.base.comp.config.any_non_single_threaded and
2967 nav.resolved.?.@"threadlocal",
2968 },
2969 .addralign = 1,
2970 .entsize = 0,
2971 })) |shndx| {
2972 break :section shndx;
2973 } else |err| switch (err) {
2974 error.TlsSectionUnavailable,
2975 error.UnsupportedSectionFlags,
2976 error.SectionTypeConflict,
2977 error.SectionFlagsConflict,
2978 => {}, // fall back to default behavior below
2979
2980 else => |e| return e,
2981 }
2982 }
2983 if (elf.base.comp.config.any_non_single_threaded and nav.resolved.?.@"threadlocal") {
2984 break :section elf.shndx.tdata;
2985 } else if (!nav.resolved.?.@"const") {
2986 break :section .data;
2987 } else if (ip.isFunctionType(nav.resolved.?.type)) {
2988 break :section .text;
2989 } else {
2990 break :section .rodata;
2852 }2991 }
2853 break :section switch (sym_type) {
2854 else => unreachable,
2855 .FUNC => .text,
2856 .OBJECT => .data,
2857 .TLS => elf.shndx.tdata,
2858 };
2859 };2992 };
2860 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {2993 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
2861 .@"fn" => a: {2994 .@"fn" => a: {
...@@ -2887,7 +3020,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM...@@ -2887,7 +3020,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM
2887 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),3020 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
2888 .value = 0,3021 .value = 0,
2889 .size = 0,3022 .size = 0,
2890 .type = sym_type,3023 .type = elf.navType(nav.resolved.?),
2891 .shndx = shndx,3024 .shndx = shndx,
2892 }),3025 }),
2893 .first_reloc = .none,3026 .first_reloc = .none,
...@@ -3131,26 +3264,69 @@ fn loadObject(...@@ -3131,26 +3264,69 @@ fn loadObject(
3131 node_fixed: bool,3264 node_fixed: bool,
3132 } = switch (section.shdr.type) {3265 } = switch (section.shdr.type) {
3133 else => continue,3266 else => continue,
3134 .PROGBITS => .{3267 .PROGBITS, .NOBITS => opts: {
3135 .shndx = elf.namedSection(name) orelse continue,3268 const shndx = elf.mapInputSection(.{
3136 .has_file_bits = true,3269 .name = name,
3137 .node_fixed = false,3270 .flags = section.shdr.flags.shf,
3138 },3271 .addralign = section.shdr.addralign,
3139 .NOBITS => .{3272 .entsize = section.shdr.entsize,
3140 .shndx = shndx: {3273 }) catch |err| switch (err) {
3141 // TODO: actually generate a .bss section. For now, just throw it into `.data`.3274 error.TlsSectionUnavailable => return diags.failParse(
3142 if (std.mem.eql(u8, name, ".bss") or std.mem.startsWith(u8, name, ".bss.")) {3275 path,
3143 break :shndx .data;3276 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
3144 }3277 .{name},
3145 if (elf.shndx.tdata != .UNDEF and3278 ),
3146 (std.mem.eql(u8, name, ".tbss") or std.mem.startsWith(u8, name, ".tbss.")))3279 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
3147 {3280 // It probably doesn't matter, just skip this section.
3148 break :shndx elf.shndx.tdata;3281 continue;
3149 }3282 } else return diags.failParse(
3283 path,
3284 "unsupported flags for section '{s}'",
3285 .{name},
3286 ),
3287 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
3288 // It probably doesn't matter, just skip this section.
3289 continue;
3290 } else return diags.failParse(
3291 path,
3292 "type of section '{s}' conflicts with other inputs",
3293 .{name},
3294 ),
3295 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {
3296 // It probably doesn't matter, just skip this section.
3297 continue;
3298 } else return diags.failParse(
3299 path,
3300 "flags of section '{s}' conflict with other inputs",
3301 .{name},
3302 ),
3303 else => |e| return e,
3304 };
3305 if (section.shdr.flags.shf.COMPRESSED) {
3306 // SHF_COMPRESSED is only allowed on non-alloc sections.
3307 if (section.shdr.flags.shf.ALLOC) return diags.failParse(
3308 path,
3309 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",
3310 .{name},
3311 );
3312 // TODO: handle compressed input sections. We'll need to set a flag to
3313 // indicate that `flushInputSection` needs to decompress the section.
3314 // But because this section isn't SHF_ALLOC, it's probably okay to just
3315 // skip it for now.
3150 continue;3316 continue;
3151 },3317 }
3152 .has_file_bits = false,3318 break :opts .{
3153 .node_fixed = false,3319 .shndx = shndx,
3320 .has_file_bits = section.shdr.type == .PROGBITS,
3321 // For well-known sections, we know that it's fine to have e.g. random
3322 // padding, so there's no need to make the sections fixed. For custom
3323 // sections, however, we do want fixed nodes to avoid padding.
3324 .node_fixed = shndx != .text and
3325 shndx != .rodata and
3326 shndx != .data and
3327 shndx != .data_rel_ro and
3328 shndx != elf.shndx.tdata,
3329 };
3154 },3330 },
3155 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{3331 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
3156 .shndx = shndx: {3332 .shndx = shndx: {
...@@ -3372,11 +3548,17 @@ fn loadObject(...@@ -3372,11 +3548,17 @@ fn loadObject(
3372 .{rel.info.sym},3548 .{rel.info.sym},
3373 );3549 );
3374 const target = symmap.items[rel.info.sym - 1];3550 const target = symmap.items[rel.info.sym - 1];
3375 if (target == Symbol.Id.null) return diags.failParse(3551 if (target == Symbol.Id.null) {
3376 path,3552 // If this is not an SHF_ALLOC section, then let's let this
3377 "unsupported symbol at index {d} required for relocation",3553 // slide for now, because it probably doesn't affect the final
3378 .{rel.info.sym},3554 // binary's functionality for this section to be a bit broken.
3379 );3555 if (!loc_sec.shdr.flags.shf.ALLOC) continue;
3556 return diags.failParse(
3557 path,
3558 "unsupported symbol at index {d} required for relocation",
3559 .{rel.info.sym},
3560 );
3561 }
3380 elf.addRelocAssumeCapacity(3562 elf.addRelocAssumeCapacity(
3381 loc_node,3563 loc_node,
3382 rel.offset - loc_sec.shdr.addr,3564 rel.offset - loc_sec.shdr.addr,
...@@ -3531,17 +3713,21 @@ fn createInitFiniArraySection(...@@ -3531,17 +3713,21 @@ fn createInitFiniArraySection(
3531 @"type": std.elf.SHT,3713 @"type": std.elf.SHT,
3532) !void {3714) !void {
3533 assert(shndx.* == .UNDEF);3715 assert(shndx.* == .UNDEF);
3716 const gpa = elf.base.comp.gpa;
3534 const addr_align: std.mem.Alignment = switch (elf.identClass()) {3717 const addr_align: std.mem.Alignment = switch (elf.identClass()) {
3535 .NONE, _ => unreachable,3718 .NONE, _ => unreachable,
3536 .@"32" => .@"4",3719 .@"32" => .@"4",
3537 .@"64" => .@"8",3720 .@"64" => .@"8",
3538 };3721 };
3722 assert(elf.section_by_name.count() == elf.shdrs.items.len);
3723 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
3539 shndx.* = try elf.addSection(elf.ni.data_rel_ro, .{3724 shndx.* = try elf.addSection(elf.ni.data_rel_ro, .{
3540 .name = "." ++ name,3725 .name = "." ++ name,
3541 .type = @"type",3726 .type = @"type",
3542 .flags = .{ .WRITE = true, .ALLOC = true },3727 .flags = .{ .WRITE = true, .ALLOC = true },
3543 .node_align = addr_align,3728 .node_align = addr_align,
3544 });3729 });
3730 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
3545 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);3731 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
3546 _ = elf.addGlobalSymbolAssumeCapacity(.{3732 _ = elf.addGlobalSymbolAssumeCapacity(.{
3547 .node = shndx.get(elf).ni,3733 .node = shndx.get(elf).ni,
...@@ -3879,9 +4065,11 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -3879,9 +4065,11 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
3879 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);4065 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
3880 const allocator = bfa.allocator();4066 const allocator = bfa.allocator();
38814067
3882 const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf)});4068 const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf).slice(elf)});
3883 defer allocator.free(rela_name);4069 defer allocator.free(rela_name);
38844070
4071 assert(elf.section_by_name.count() == elf.shdrs.items.len);
4072 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
3885 const rela_shndx = try elf.addSection(.none, .{4073 const rela_shndx = try elf.addSection(.none, .{
3886 .name = rela_name,4074 .name = rela_name,
3887 .type = .RELA,4075 .type = .RELA,
...@@ -3898,6 +4086,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -3898,6 +4086,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
3898 },4086 },
3899 .node_align = elf.mf.flags.block_size,4087 .node_align = elf.mf.flags.block_size,
3900 });4088 });
4089 elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {});
3901 shndx.get(elf).rela_shndx = rela_shndx;4090 shndx.get(elf).rela_shndx = rela_shndx;
3902 }4091 }
3903 break :rela .{ shndx.get(elf).rela_shndx, len };4092 break :rela .{ shndx.get(elf).rela_shndx, len };
...@@ -4272,7 +4461,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {...@@ -4272,7 +4461,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4272 return comp.link_diags.fail(4461 return comp.link_diags.fail(
4273 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",4462 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",
4274 .{4463 .{
4275 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),4464 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
4276 ii.path(elf).fmtEscapeString(),4465 ii.path(elf).fmtEscapeString(),
4277 fmtMemberString(ii.member(elf)),4466 fmtMemberString(ii.member(elf)),
4278 e,4467 e,
...@@ -4325,13 +4514,13 @@ fn idleProgNode(...@@ -4325,13 +4514,13 @@ fn idleProgNode(
4325 var name: [std.Progress.Node.max_name_len]u8 = undefined;4514 var name: [std.Progress.Node.max_name_len]u8 = undefined;
4326 return prog_node.start(name: switch (node) {4515 return prog_node.start(name: switch (node) {
4327 else => |tag| @tagName(tag),4516 else => |tag| @tagName(tag),
4328 .section => |shndx| shndx.name(elf),4517 .section => |shndx| shndx.name(elf).slice(elf),
4329 .input_section => |isi| {4518 .input_section => |isi| {
4330 const ii = isi.input(elf);4519 const ii = isi.input(elf);
4331 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{4520 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
4332 ii.path(elf).fmtEscapeString(),4521 ii.path(elf).fmtEscapeString(),
4333 fmtMemberString(ii.member(elf)),4522 fmtMemberString(ii.member(elf)),
4334 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),4523 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
4335 }) catch &name;4524 }) catch &name;
4336 },4525 },
4337 .nav => |nmi| {4526 .nav => |nmi| {
...@@ -4885,7 +5074,7 @@ fn updateExportsInner(...@@ -4885,7 +5074,7 @@ fn updateExportsInner(
4885 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) {5074 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) {
4886 .nav => |nav| .{5075 .nav => |nav| .{
4887 (try elf.navMapIndex(zcu, nav)).symbol(elf),5076 (try elf.navMapIndex(zcu, nav)).symbol(elf),
4888 navType(ip, ip.getNav(nav).resolved.?, elf.base.comp.config.any_non_single_threaded),5077 elf.navType(ip.getNav(nav).resolved.?),
4889 },5078 },
4890 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },5079 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
4891 };5080 };
...@@ -4987,13 +5176,13 @@ pub fn printNode(...@@ -4987,13 +5176,13 @@ pub fn printNode(
4987 try w.writeByte(')');5176 try w.writeByte(')');
4988 },5177 },
4989 },5178 },
4990 .section => |shndx| try w.print("({s})", .{shndx.name(elf)}),5179 .section => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}),
4991 .input_section => |isi| {5180 .input_section => |isi| {
4992 const ii = isi.input(elf);5181 const ii = isi.input(elf);
4993 try w.print("({f}{f}, {s})", .{5182 try w.print("({f}{f}, {s})", .{
4994 ii.path(elf).fmtEscapeString(),5183 ii.path(elf).fmtEscapeString(),
4995 fmtMemberString(ii.member(elf)),5184 fmtMemberString(ii.member(elf)),
4996 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),5185 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
4997 });5186 });
4998 },5187 },
4999 .nav => |nmi| {5188 .nav => |nmi| {