authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-01 09:56:54+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-07 13:05:31+01:00
log2e5b6d814e5c71acf268c242c9519c3e3674bcba
treec8b057beb7a416dd1ae41fc0a1ac4db60a4aeaef
parent0e8bb9188f77ba39d8d4d86d6a5abe378009c5df
signaturelock-open Commit is signed but in an unrecognized format.

Elf2(refactor): clean up phdr and node counting


1 files changed, 112 insertions(+), 63 deletions(-)

src/link/Elf2.zig+112-63
......@@ -2884,51 +2884,99 @@ fn initHeaders(
28842884 .@"64" => .@"8",
28852885 };
28862886
2887 const shnum: u32 = 1;
2888 var phnum: u32 = 0;
2889 const phdr_phndx = phnum;
2890 phnum += 1;
2891 const interp_phndx = if (maybe_interp) |_| phndx: {
2892 defer phnum += 1;
2893 break :phndx phnum;
2894 } else undefined;
2895 const rodata_phndx = phnum;
2896 phnum += 1;
2897 const text_phndx = phnum;
2898 phnum += 1;
2899 const data_phndx = phnum;
2900 phnum += 1;
2901 const tls_phndx = if (comp.config.any_non_single_threaded) phndx: {
2902 defer phnum += 1;
2903 break :phndx phnum;
2904 } else undefined;
2905 const dynamic_phndx = if (have_dynamic_section) phndx: {
2906 defer phnum += 1;
2907 break :phndx phnum;
2908 } else undefined;
2909 const relro_phndx = phnum;
2910 phnum += 1;
2911
29122887 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
29132888 switch (machine) {
29142889 else => @panic(@tagName(machine)),
29152890 .X86_64 => .{ 16, .@"16", true },
29162891 .LOONGARCH => .{ 32, .@"4", false },
29172892 };
2918 const expected_nodes_len = expected_nodes_len: switch (@"type") {
2919 .NONE, .CORE, _ => unreachable,
2920 .REL => {
2921 // Each phdr is actually going to be an shdr.
2922 defer phnum = 0;
2923 break :expected_nodes_len 5 + phnum;
2924 },
2925 .EXEC, .DYN => break :expected_nodes_len 9 +
2926 phnum * 2 - 1 + // each phdr also has a matching shdr, except for the PT_PHDR phdr
2927 @as(usize, 4) * @intFromBool(have_dynamic_section) + // .dynstr, .dynsym, .rela.dyn, .rela.plt
2928 @intFromBool(plt_sec),
2893
2894 const shnum: u32 = shnum: {
2895 var shnum: u32 = 1; // reserved ("null") shdr
2896 shnum += 1; // .symtab
2897 shnum += 1; // .shstrtab
2898 shnum += 1; // .strtab
2899 shnum += @intFromBool(maybe_interp != null); // .interp
2900 shnum += 1; // .rodata
2901 shnum += 1; // .text
2902 shnum += 1; // .data
2903 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata
2904 shnum += 1; // .data.rel.ro
2905 if (have_dynamic_section) {
2906 shnum += 1; // .dynamic
2907 shnum += 1; // .dynstr
2908 shnum += 1; // .dynsym
2909 shnum += 1; // .rela.dyn
2910 shnum += 1; // .rela.plt
2911 }
2912 if (@"type" != .REL) {
2913 shnum += 1; // .got
2914 shnum += 1; // .got.plt
2915 shnum += 1; // .plt
2916 shnum += @intFromBool(plt_sec); // .plt_sec
2917 }
2918 break :shnum shnum;
29292919 };
2920
2921 const phndx: struct {
2922 phdr: u32,
2923 interp: u32,
2924 rodata: u32,
2925 text: u32,
2926 data: u32,
2927 tls: u32,
2928 dynamic: u32,
2929 relro: u32,
2930 }, const phnum: u32 = ph: {
2931 switch (@"type") {
2932 .NONE, .CORE, _ => unreachable,
2933 .REL => break :ph .{ undefined, 0 },
2934 .EXEC, .DYN => {},
2935 }
2936 var phnum: u32 = 0;
2937 break :ph .{ .{
2938 .phdr = phndx: {
2939 defer phnum += 1;
2940 break :phndx phnum;
2941 },
2942 .interp = if (maybe_interp) |_| phndx: {
2943 defer phnum += 1;
2944 break :phndx phnum;
2945 } else undefined,
2946 .rodata = phndx: {
2947 defer phnum += 1;
2948 break :phndx phnum;
2949 },
2950 .text = phndx: {
2951 defer phnum += 1;
2952 break :phndx phnum;
2953 },
2954 .data = phndx: {
2955 defer phnum += 1;
2956 break :phndx phnum;
2957 },
2958 .tls = if (comp.config.any_non_single_threaded) phndx: {
2959 defer phnum += 1;
2960 break :phndx phnum;
2961 } else undefined,
2962 .dynamic = if (have_dynamic_section) phndx: {
2963 defer phnum += 1;
2964 break :phndx phnum;
2965 } else undefined,
2966 .relro = phndx: {
2967 defer phnum += 1;
2968 break :phndx phnum;
2969 },
2970 }, phnum };
2971 };
2972
2973 const expected_nodes_len = 3 + // `.file`, `.ehdr`, and `.shdr` nodes
2974 (shnum - 1) + // -1 because the null shdr does not have a `.section` node
2975 phnum;
2976
29302977 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
29312978 try elf.shdrs.ensureTotalCapacity(gpa, shnum);
2979 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum);
29322980 try elf.phdrs.resize(gpa, phnum);
29332981 try elf.symtab.ensureTotalCapacity(gpa, 1);
29342982 elf.nodes.appendAssumeCapacity(.file);
......@@ -2980,7 +3028,7 @@ fn initHeaders(
29803028 ehdr.phentsize = @sizeOf(ElfN.Phdr);
29813029 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
29823030 ehdr.shentsize = @sizeOf(ElfN.Shdr);
2983 ehdr.shnum = if (shnum < std.elf.SHN_LORESERVE) shnum else 0;
3031 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`
29843032 ehdr.shstrndx = std.elf.SHN_UNDEF;
29853033 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
29863034 },
......@@ -3000,8 +3048,8 @@ fn initHeaders(
30003048 .moved = true,
30013049 .bubbles_moved = false,
30023050 }));
3003 elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });
3004 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;
3051 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3052 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
30053053
30063054 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
30073055 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),
......@@ -3010,32 +3058,32 @@ fn initHeaders(
30103058 .resized = true,
30113059 .bubbles_moved = false,
30123060 }));
3013 elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });
3014 elf.phdrs.items[phdr_phndx] = elf.ni.phdr;
3061 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3062 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;
30153063
30163064 assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
30173065 .alignment = elf.mf.flags.block_size,
30183066 .moved = true,
30193067 .bubbles_moved = false,
30203068 }));
3021 elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });
3022 elf.phdrs.items[text_phndx] = elf.ni.text;
3069 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3070 elf.phdrs.items[phndx.text] = elf.ni.text;
30233071
30243072 assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
30253073 .alignment = elf.mf.flags.block_size,
30263074 .moved = true,
30273075 .bubbles_moved = false,
30283076 }));
3029 elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });
3030 elf.phdrs.items[data_phndx] = elf.ni.data;
3077 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3078 elf.phdrs.items[phndx.data] = elf.ni.data;
30313079
30323080 assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
30333081 .alignment = elf.mf.flags.block_size,
30343082 .moved = true,
30353083 .bubbles_moved = false,
30363084 }));
3037 elf.nodes.appendAssumeCapacity(.{ .segment = relro_phndx });
3038 elf.phdrs.items[relro_phndx] = elf.ni.data_rel_ro;
3085 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3086 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
30393087
30403088 break :ph_vaddr switch (elf.ehdrField(.type)) {
30413089 .NONE, .CORE, _ => unreachable,
......@@ -3058,7 +3106,7 @@ fn initHeaders(
30583106
30593107 if (@"type" != .REL) {
30603108 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));
3061 const ph_phdr = &phdr[phdr_phndx];
3109 const ph_phdr = &phdr[phndx.phdr];
30623110 ph_phdr.* = .{
30633111 .type = .PHDR,
30643112 .offset = 0,
......@@ -3072,7 +3120,7 @@ fn initHeaders(
30723120 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
30733121
30743122 if (maybe_interp) |_| {
3075 const ph_interp = &phdr[interp_phndx];
3123 const ph_interp = &phdr[phndx.interp];
30763124 ph_interp.* = .{
30773125 .type = .INTERP,
30783126 .offset = 0,
......@@ -3087,7 +3135,7 @@ fn initHeaders(
30873135 }
30883136
30893137 _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf);
3090 const ph_rodata = &phdr[rodata_phndx];
3138 const ph_rodata = &phdr[phndx.rodata];
30913139 ph_rodata.* = .{
30923140 .type = if (rodata_size == 0) .NULL else .LOAD,
30933141 .offset = 0,
......@@ -3102,7 +3150,7 @@ fn initHeaders(
31023150 ph_vaddr += @intCast(rodata_size);
31033151
31043152 _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf);
3105 const ph_text = &phdr[text_phndx];
3153 const ph_text = &phdr[phndx.text];
31063154 ph_text.* = .{
31073155 .type = if (text_size == 0) .NULL else .LOAD,
31083156 .offset = 0,
......@@ -3117,7 +3165,7 @@ fn initHeaders(
31173165 ph_vaddr += @intCast(text_size);
31183166
31193167 _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf);
3120 const ph_data = &phdr[data_phndx];
3168 const ph_data = &phdr[phndx.data];
31213169 ph_data.* = .{
31223170 .type = if (data_size == 0) .NULL else .LOAD,
31233171 .offset = 0,
......@@ -3132,7 +3180,7 @@ fn initHeaders(
31323180 ph_vaddr += @intCast(data_size);
31333181
31343182 if (comp.config.any_non_single_threaded) {
3135 const ph_tls = &phdr[tls_phndx];
3183 const ph_tls = &phdr[phndx.tls];
31363184 ph_tls.* = .{
31373185 .type = .TLS,
31383186 .offset = 0,
......@@ -3147,7 +3195,7 @@ fn initHeaders(
31473195 }
31483196
31493197 if (have_dynamic_section) {
3150 const ph_dynamic = &phdr[dynamic_phndx];
3198 const ph_dynamic = &phdr[phndx.dynamic];
31513199 ph_dynamic.* = .{
31523200 .type = .DYNAMIC,
31533201 .offset = 0,
......@@ -3161,7 +3209,7 @@ fn initHeaders(
31613209 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);
31623210 }
31633211
3164 const ph_relro = &phdr[relro_phndx];
3212 const ph_relro = &phdr[phndx.relro];
31653213 ph_relro.* = .{
31663214 .type = .GNU_RELRO,
31673215 .offset = 0,
......@@ -3312,8 +3360,8 @@ fn initHeaders(
33123360 .resized = true,
33133361 .bubbles_moved = false,
33143362 });
3315 elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
3316 elf.phdrs.items[interp_phndx] = interp_ni;
3363 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp });
3364 elf.phdrs.items[phndx.interp] = interp_ni;
33173365
33183366 const sec_interp_shndx = try elf.addSection(interp_ni, .{
33193367 .name = ".interp",
......@@ -3331,8 +3379,8 @@ fn initHeaders(
33313379 .moved = true,
33323380 .bubbles_moved = false,
33333381 });
3334 elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx });
3335 elf.phdrs.items[dynamic_phndx] = dynamic_ni;
3382 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic });
3383 elf.phdrs.items[phndx.dynamic] = dynamic_ni;
33363384
33373385 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{
33383386 .name = ".dynstr",
......@@ -3473,8 +3521,8 @@ fn initHeaders(
34733521 .moved = true,
34743522 .bubbles_moved = false,
34753523 });
3476 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
3477 elf.phdrs.items[tls_phndx] = elf.ni.tls;
3524 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
3525 elf.phdrs.items[phndx.tls] = elf.ni.tls;
34783526 }
34793527
34803528 // Populate reserved GOT words.
......@@ -3641,10 +3689,11 @@ fn initHeaders(
36413689 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
36423690 .addralign = elf.mf.flags.block_size,
36433691 });
3692
36443693 assert(elf.nodes.len == expected_nodes_len);
3694 assert(elf.shdrs.items.len == shnum);
36453695
3646 try elf.section_by_name.ensureUnusedCapacity(gpa, elf.shdrs.items.len);
3647 for (0..elf.shdrs.items.len) |shndx_raw| {
3696 for (0..shnum) |shndx_raw| {
36483697 const shndx: Section.Index = @enumFromInt(shndx_raw);
36493698 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
36503699 }