authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-09-11 05:20:45-04:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-09-18 12:47:18+02:00
logb8bf2780a074ad98ccacd59fb43c16ccd14fa3e1
tree56fa28ff1bc8cfb546f0ea29e47cddaa61ea5a6f
parente14540399c1be3c052467a576bd317ea12194cdd
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Elf: implement `linksection`

Closes #24330

2 files changed, 142 insertions(+), 1 deletions(-)

src/link/Elf/ZigObject.zig+103-1
...@@ -1140,7 +1140,109 @@ fn getNavShdrIndex(...@@ -1140,7 +1140,109 @@ fn getNavShdrIndex(
1140 const ptr_size = elf_file.ptrWidthBytes();1140 const ptr_size = elf_file.ptrWidthBytes();
1141 const ip = &zcu.intern_pool;1141 const ip = &zcu.intern_pool;
1142 const nav_val = zcu.navValue(nav_index);1142 const nav_val = zcu.navValue(nav_index);
1143 if (ip.isFunctionType(nav_val.typeOf(zcu).toIntern())) {1143 const is_func = ip.isFunctionType(nav_val.typeOf(zcu).toIntern());
1144 if (ip.getNav(nav_index).getLinkSection().unwrap()) |@"linksection"| {
1145 const section_name = @"linksection".toSlice(ip);
1146 if (elf_file.sectionByName(section_name)) |osec| {
1147 if (is_func) {
1148 elf_file.sections.items(.shdr)[osec].sh_flags |= elf.SHF_EXECINSTR;
1149 } else {
1150 elf_file.sections.items(.shdr)[osec].sh_flags |= elf.SHF_WRITE;
1151 }
1152 return osec;
1153 }
1154 const osec = try elf_file.addSection(.{
1155 .type = elf.SHT_PROGBITS,
1156 .flags = elf.SHF_ALLOC | @as(u64, if (is_func) elf.SHF_EXECINSTR else elf.SHF_WRITE),
1157 .name = try elf_file.insertShString(section_name),
1158 .addralign = 1,
1159 });
1160 const section_index = try self.addSectionSymbol(gpa, try self.addString(gpa, section_name), osec);
1161 if (std.mem.eql(u8, section_name, ".text")) {
1162 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR;
1163 self.text_index = section_index;
1164 } else if (std.mem.startsWith(u8, section_name, ".text.")) {
1165 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR;
1166 } else if (std.mem.eql(u8, section_name, ".rodata")) {
1167 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC;
1168 self.rodata_index = section_index;
1169 } else if (std.mem.startsWith(u8, section_name, ".rodata.")) {
1170 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC;
1171 } else if (std.mem.eql(u8, section_name, ".data.rel.ro")) {
1172 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1173 self.data_relro_index = section_index;
1174 } else if (std.mem.eql(u8, section_name, ".data")) {
1175 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1176 self.data_index = section_index;
1177 } else if (std.mem.startsWith(u8, section_name, ".data.")) {
1178 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1179 } else if (std.mem.eql(u8, section_name, ".bss")) {
1180 const shdr = &elf_file.sections.items(.shdr)[osec];
1181 shdr.sh_type = elf.SHT_NOBITS;
1182 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1183 self.bss_index = section_index;
1184 } else if (std.mem.startsWith(u8, section_name, ".bss.")) {
1185 const shdr = &elf_file.sections.items(.shdr)[osec];
1186 shdr.sh_type = elf.SHT_NOBITS;
1187 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1188 } else if (std.mem.eql(u8, section_name, ".tdata")) {
1189 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
1190 self.tdata_index = section_index;
1191 } else if (std.mem.startsWith(u8, section_name, ".tdata.")) {
1192 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
1193 } else if (std.mem.eql(u8, section_name, ".tbss")) {
1194 const shdr = &elf_file.sections.items(.shdr)[osec];
1195 shdr.sh_type = elf.SHT_NOBITS;
1196 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
1197 self.tbss_index = section_index;
1198 } else if (std.mem.startsWith(u8, section_name, ".tbss.")) {
1199 const shdr = &elf_file.sections.items(.shdr)[osec];
1200 shdr.sh_type = elf.SHT_NOBITS;
1201 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
1202 } else if (std.mem.eql(u8, section_name, ".eh_frame")) {
1203 const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result;
1204 const shdr = &elf_file.sections.items(.shdr)[osec];
1205 if (target.cpu.arch == .x86_64) shdr.sh_type = elf.SHT_X86_64_UNWIND;
1206 shdr.sh_flags = elf.SHF_ALLOC;
1207 self.eh_frame_index = section_index;
1208 } else if (std.mem.eql(u8, section_name, ".debug_info")) {
1209 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1210 self.debug_info_index = section_index;
1211 } else if (std.mem.eql(u8, section_name, ".debug_abbrev")) {
1212 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1213 self.debug_abbrev_index = section_index;
1214 } else if (std.mem.eql(u8, section_name, ".debug_aranges")) {
1215 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1216 self.debug_aranges_index = section_index;
1217 } else if (std.mem.eql(u8, section_name, ".debug_str")) {
1218 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1219 self.debug_str_index = section_index;
1220 } else if (std.mem.eql(u8, section_name, ".debug_line")) {
1221 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1222 self.debug_line_index = section_index;
1223 } else if (std.mem.eql(u8, section_name, ".debug_line_str")) {
1224 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1225 self.debug_line_str_index = section_index;
1226 } else if (std.mem.eql(u8, section_name, ".debug_loclists")) {
1227 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1228 self.debug_loclists_index = section_index;
1229 } else if (std.mem.eql(u8, section_name, ".debug_rnglists")) {
1230 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1231 self.debug_rnglists_index = section_index;
1232 } else if (std.mem.startsWith(u8, section_name, ".debug")) {
1233 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1234 } else if (std.mem.eql(u8, section_name, ".init_array") or std.mem.startsWith(u8, section_name, ".init_array.")) {
1235 const shdr = &elf_file.sections.items(.shdr)[osec];
1236 shdr.sh_type = elf.SHT_INIT_ARRAY;
1237 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1238 } else if (std.mem.eql(u8, section_name, ".fini_array") or std.mem.startsWith(u8, section_name, ".fini_array.")) {
1239 const shdr = &elf_file.sections.items(.shdr)[osec];
1240 shdr.sh_type = elf.SHT_FINI_ARRAY;
1241 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1242 }
1243 return osec;
1244 }
1245 if (is_func) {
1144 if (self.text_index) |symbol_index|1246 if (self.text_index) |symbol_index|
1145 return self.symbol(symbol_index).outputShndx(elf_file).?;1247 return self.symbol(symbol_index).outputShndx(elf_file).?;
1146 const osec = try elf_file.addSection(.{1248 const osec = try elf_file.addSection(.{
test/link/elf.zig+39
...@@ -73,6 +73,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {...@@ -73,6 +73,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
73 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));73 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));
74 elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));74 elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));
75 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));75 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));
76 elf_step.dependOn(testLinksection(b, .{ .target = musl_target }));
76 elf_step.dependOn(testMergeStrings(b, .{ .target = musl_target }));77 elf_step.dependOn(testMergeStrings(b, .{ .target = musl_target }));
77 elf_step.dependOn(testMergeStrings2(b, .{ .target = musl_target }));78 elf_step.dependOn(testMergeStrings2(b, .{ .target = musl_target }));
78 // https://github.com/ziglang/zig/issues/1745179 // https://github.com/ziglang/zig/issues/17451
...@@ -165,6 +166,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {...@@ -165,6 +166,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
165 elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));166 elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));
166 elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));167 elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));
167 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));168 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
169 elf_step.dependOn(testLinksection(b, .{ .use_llvm = false, .target = default_target }));
168 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu }));170 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu }));
169 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl }));171 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl }));
170172
...@@ -2439,6 +2441,43 @@ fn testLinkingZig(b: *Build, opts: Options) *Step {...@@ -2439,6 +2441,43 @@ fn testLinkingZig(b: *Build, opts: Options) *Step {
2439 return test_step;2441 return test_step;
2440}2442}
24412443
2444fn testLinksection(b: *Build, opts: Options) *Step {
2445 const test_step = addTestStep(b, "linksection", opts);
2446
2447 const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes =
2448 \\export var test_global: u32 linksection(".TestGlobal") = undefined;
2449 \\export fn testFn() linksection(".TestFn") callconv(.c) void {
2450 \\ TestGenericFn("A").f();
2451 \\}
2452 \\fn TestGenericFn(comptime suffix: []const u8) type {
2453 \\ return struct {
2454 \\ fn f() linksection(".TestGenFn" ++ suffix) void {}
2455 \\ };
2456 \\}
2457 });
2458
2459 const check = obj.checkObject();
2460 check.checkInSymtab();
2461 check.checkContains("SECTION LOCAL DEFAULT .TestGlobal");
2462 check.checkInSymtab();
2463 check.checkContains("SECTION LOCAL DEFAULT .TestFn");
2464 check.checkInSymtab();
2465 check.checkContains("SECTION LOCAL DEFAULT .TestGenFnA");
2466 check.checkInSymtab();
2467 check.checkContains("OBJECT GLOBAL DEFAULT test_global");
2468 check.checkInSymtab();
2469 check.checkContains("FUNC GLOBAL DEFAULT testFn");
2470
2471 if (opts.optimize == .Debug) {
2472 check.checkInSymtab();
2473 check.checkContains("FUNC LOCAL DEFAULT main.TestGenericFn(");
2474 }
2475
2476 test_step.dependOn(&check.step);
2477
2478 return test_step;
2479}
2480
2442// Adapted from https://github.com/rui314/mold/blob/main/test/elf/mergeable-strings.sh2481// Adapted from https://github.com/rui314/mold/blob/main/test/elf/mergeable-strings.sh
2443fn testMergeStrings(b: *Build, opts: Options) *Step {2482fn testMergeStrings(b: *Build, opts: Options) *Step {
2444 const test_step = addTestStep(b, "merge-strings", opts);2483 const test_step = addTestStep(b, "merge-strings", opts);