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(...@@ -2884,51 +2884,99 @@ fn initHeaders(
2884 .@"64" => .@"8",2884 .@"64" => .@"8",
2885 };2885 };
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
2912 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =2887 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
2913 switch (machine) {2888 switch (machine) {
2914 else => @panic(@tagName(machine)),2889 else => @panic(@tagName(machine)),
2915 .X86_64 => .{ 16, .@"16", true },2890 .X86_64 => .{ 16, .@"16", true },
2916 .LOONGARCH => .{ 32, .@"4", false },2891 .LOONGARCH => .{ 32, .@"4", false },
2917 };2892 };
2918 const expected_nodes_len = expected_nodes_len: switch (@"type") {2893
2919 .NONE, .CORE, _ => unreachable,2894 const shnum: u32 = shnum: {
2920 .REL => {2895 var shnum: u32 = 1; // reserved ("null") shdr
2921 // Each phdr is actually going to be an shdr.2896 shnum += 1; // .symtab
2922 defer phnum = 0;2897 shnum += 1; // .shstrtab
2923 break :expected_nodes_len 5 + phnum;2898 shnum += 1; // .strtab
2924 },2899 shnum += @intFromBool(maybe_interp != null); // .interp
2925 .EXEC, .DYN => break :expected_nodes_len 9 +2900 shnum += 1; // .rodata
2926 phnum * 2 - 1 + // each phdr also has a matching shdr, except for the PT_PHDR phdr2901 shnum += 1; // .text
2927 @as(usize, 4) * @intFromBool(have_dynamic_section) + // .dynstr, .dynsym, .rela.dyn, .rela.plt2902 shnum += 1; // .data
2928 @intFromBool(plt_sec),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;
2929 };2919 };
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
2930 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);2977 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
2931 try elf.shdrs.ensureTotalCapacity(gpa, shnum);2978 try elf.shdrs.ensureTotalCapacity(gpa, shnum);
2979 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum);
2932 try elf.phdrs.resize(gpa, phnum);2980 try elf.phdrs.resize(gpa, phnum);
2933 try elf.symtab.ensureTotalCapacity(gpa, 1);2981 try elf.symtab.ensureTotalCapacity(gpa, 1);
2934 elf.nodes.appendAssumeCapacity(.file);2982 elf.nodes.appendAssumeCapacity(.file);
...@@ -2980,7 +3028,7 @@ fn initHeaders(...@@ -2980,7 +3028,7 @@ fn initHeaders(
2980 ehdr.phentsize = @sizeOf(ElfN.Phdr);3028 ehdr.phentsize = @sizeOf(ElfN.Phdr);
2981 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);3029 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
2982 ehdr.shentsize = @sizeOf(ElfN.Shdr);3030 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`
2984 ehdr.shstrndx = std.elf.SHN_UNDEF;3032 ehdr.shstrndx = std.elf.SHN_UNDEF;
2985 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);3033 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
2986 },3034 },
...@@ -3000,8 +3048,8 @@ fn initHeaders(...@@ -3000,8 +3048,8 @@ fn initHeaders(
3000 .moved = true,3048 .moved = true,
3001 .bubbles_moved = false,3049 .bubbles_moved = false,
3002 }));3050 }));
3003 elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });3051 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3004 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;3052 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
30053053
3006 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{3054 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
3007 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),3055 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),
...@@ -3010,32 +3058,32 @@ fn initHeaders(...@@ -3010,32 +3058,32 @@ fn initHeaders(
3010 .resized = true,3058 .resized = true,
3011 .bubbles_moved = false,3059 .bubbles_moved = false,
3012 }));3060 }));
3013 elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });3061 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3014 elf.phdrs.items[phdr_phndx] = elf.ni.phdr;3062 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;
30153063
3016 assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{3064 assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3017 .alignment = elf.mf.flags.block_size,3065 .alignment = elf.mf.flags.block_size,
3018 .moved = true,3066 .moved = true,
3019 .bubbles_moved = false,3067 .bubbles_moved = false,
3020 }));3068 }));
3021 elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });3069 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3022 elf.phdrs.items[text_phndx] = elf.ni.text;3070 elf.phdrs.items[phndx.text] = elf.ni.text;
30233071
3024 assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{3072 assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3025 .alignment = elf.mf.flags.block_size,3073 .alignment = elf.mf.flags.block_size,
3026 .moved = true,3074 .moved = true,
3027 .bubbles_moved = false,3075 .bubbles_moved = false,
3028 }));3076 }));
3029 elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });3077 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3030 elf.phdrs.items[data_phndx] = elf.ni.data;3078 elf.phdrs.items[phndx.data] = elf.ni.data;
30313079
3032 assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{3080 assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
3033 .alignment = elf.mf.flags.block_size,3081 .alignment = elf.mf.flags.block_size,
3034 .moved = true,3082 .moved = true,
3035 .bubbles_moved = false,3083 .bubbles_moved = false,
3036 }));3084 }));
3037 elf.nodes.appendAssumeCapacity(.{ .segment = relro_phndx });3085 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3038 elf.phdrs.items[relro_phndx] = elf.ni.data_rel_ro;3086 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
30393087
3040 break :ph_vaddr switch (elf.ehdrField(.type)) {3088 break :ph_vaddr switch (elf.ehdrField(.type)) {
3041 .NONE, .CORE, _ => unreachable,3089 .NONE, .CORE, _ => unreachable,
...@@ -3058,7 +3106,7 @@ fn initHeaders(...@@ -3058,7 +3106,7 @@ fn initHeaders(
30583106
3059 if (@"type" != .REL) {3107 if (@"type" != .REL) {
3060 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));3108 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];
3062 ph_phdr.* = .{3110 ph_phdr.* = .{
3063 .type = .PHDR,3111 .type = .PHDR,
3064 .offset = 0,3112 .offset = 0,
...@@ -3072,7 +3120,7 @@ fn initHeaders(...@@ -3072,7 +3120,7 @@ fn initHeaders(
3072 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);3120 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
30733121
3074 if (maybe_interp) |_| {3122 if (maybe_interp) |_| {
3075 const ph_interp = &phdr[interp_phndx];3123 const ph_interp = &phdr[phndx.interp];
3076 ph_interp.* = .{3124 ph_interp.* = .{
3077 .type = .INTERP,3125 .type = .INTERP,
3078 .offset = 0,3126 .offset = 0,
...@@ -3087,7 +3135,7 @@ fn initHeaders(...@@ -3087,7 +3135,7 @@ fn initHeaders(
3087 }3135 }
30883136
3089 _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf);3137 _, 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];
3091 ph_rodata.* = .{3139 ph_rodata.* = .{
3092 .type = if (rodata_size == 0) .NULL else .LOAD,3140 .type = if (rodata_size == 0) .NULL else .LOAD,
3093 .offset = 0,3141 .offset = 0,
...@@ -3102,7 +3150,7 @@ fn initHeaders(...@@ -3102,7 +3150,7 @@ fn initHeaders(
3102 ph_vaddr += @intCast(rodata_size);3150 ph_vaddr += @intCast(rodata_size);
31033151
3104 _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf);3152 _, 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];
3106 ph_text.* = .{3154 ph_text.* = .{
3107 .type = if (text_size == 0) .NULL else .LOAD,3155 .type = if (text_size == 0) .NULL else .LOAD,
3108 .offset = 0,3156 .offset = 0,
...@@ -3117,7 +3165,7 @@ fn initHeaders(...@@ -3117,7 +3165,7 @@ fn initHeaders(
3117 ph_vaddr += @intCast(text_size);3165 ph_vaddr += @intCast(text_size);
31183166
3119 _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf);3167 _, 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];
3121 ph_data.* = .{3169 ph_data.* = .{
3122 .type = if (data_size == 0) .NULL else .LOAD,3170 .type = if (data_size == 0) .NULL else .LOAD,
3123 .offset = 0,3171 .offset = 0,
...@@ -3132,7 +3180,7 @@ fn initHeaders(...@@ -3132,7 +3180,7 @@ fn initHeaders(
3132 ph_vaddr += @intCast(data_size);3180 ph_vaddr += @intCast(data_size);
31333181
3134 if (comp.config.any_non_single_threaded) {3182 if (comp.config.any_non_single_threaded) {
3135 const ph_tls = &phdr[tls_phndx];3183 const ph_tls = &phdr[phndx.tls];
3136 ph_tls.* = .{3184 ph_tls.* = .{
3137 .type = .TLS,3185 .type = .TLS,
3138 .offset = 0,3186 .offset = 0,
...@@ -3147,7 +3195,7 @@ fn initHeaders(...@@ -3147,7 +3195,7 @@ fn initHeaders(
3147 }3195 }
31483196
3149 if (have_dynamic_section) {3197 if (have_dynamic_section) {
3150 const ph_dynamic = &phdr[dynamic_phndx];3198 const ph_dynamic = &phdr[phndx.dynamic];
3151 ph_dynamic.* = .{3199 ph_dynamic.* = .{
3152 .type = .DYNAMIC,3200 .type = .DYNAMIC,
3153 .offset = 0,3201 .offset = 0,
...@@ -3161,7 +3209,7 @@ fn initHeaders(...@@ -3161,7 +3209,7 @@ fn initHeaders(
3161 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);3209 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);
3162 }3210 }
31633211
3164 const ph_relro = &phdr[relro_phndx];3212 const ph_relro = &phdr[phndx.relro];
3165 ph_relro.* = .{3213 ph_relro.* = .{
3166 .type = .GNU_RELRO,3214 .type = .GNU_RELRO,
3167 .offset = 0,3215 .offset = 0,
...@@ -3312,8 +3360,8 @@ fn initHeaders(...@@ -3312,8 +3360,8 @@ fn initHeaders(
3312 .resized = true,3360 .resized = true,
3313 .bubbles_moved = false,3361 .bubbles_moved = false,
3314 });3362 });
3315 elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });3363 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp });
3316 elf.phdrs.items[interp_phndx] = interp_ni;3364 elf.phdrs.items[phndx.interp] = interp_ni;
33173365
3318 const sec_interp_shndx = try elf.addSection(interp_ni, .{3366 const sec_interp_shndx = try elf.addSection(interp_ni, .{
3319 .name = ".interp",3367 .name = ".interp",
...@@ -3331,8 +3379,8 @@ fn initHeaders(...@@ -3331,8 +3379,8 @@ fn initHeaders(
3331 .moved = true,3379 .moved = true,
3332 .bubbles_moved = false,3380 .bubbles_moved = false,
3333 });3381 });
3334 elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx });3382 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic });
3335 elf.phdrs.items[dynamic_phndx] = dynamic_ni;3383 elf.phdrs.items[phndx.dynamic] = dynamic_ni;
33363384
3337 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{3385 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{
3338 .name = ".dynstr",3386 .name = ".dynstr",
...@@ -3473,8 +3521,8 @@ fn initHeaders(...@@ -3473,8 +3521,8 @@ fn initHeaders(
3473 .moved = true,3521 .moved = true,
3474 .bubbles_moved = false,3522 .bubbles_moved = false,
3475 });3523 });
3476 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });3524 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
3477 elf.phdrs.items[tls_phndx] = elf.ni.tls;3525 elf.phdrs.items[phndx.tls] = elf.ni.tls;
3478 }3526 }
34793527
3480 // Populate reserved GOT words.3528 // Populate reserved GOT words.
...@@ -3641,10 +3689,11 @@ fn initHeaders(...@@ -3641,10 +3689,11 @@ fn initHeaders(
3641 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },3689 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
3642 .addralign = elf.mf.flags.block_size,3690 .addralign = elf.mf.flags.block_size,
3643 });3691 });
3692
3644 assert(elf.nodes.len == expected_nodes_len);3693 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);3696 for (0..shnum) |shndx_raw| {
3647 for (0..elf.shdrs.items.len) |shndx_raw| {
3648 const shndx: Section.Index = @enumFromInt(shndx_raw);3697 const shndx: Section.Index = @enumFromInt(shndx_raw);
3649 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});3698 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
3650 }3699 }