Elf: implement linksection

Closes #24330
This commit is contained in:
Jacob Young 2025-09-11 05:20:45 -04:00 committed by Andrew Kelley
parent 8e9f2f02d2
commit 09bc118c9f
2 changed files with 142 additions and 1 deletions

View file

@ -1140,7 +1140,109 @@ fn getNavShdrIndex(
const ptr_size = elf_file.ptrWidthBytes(); const ptr_size = elf_file.ptrWidthBytes();
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const nav_val = zcu.navValue(nav_index); const nav_val = zcu.navValue(nav_index);
if (ip.isFunctionType(nav_val.typeOf(zcu).toIntern())) { const is_func = ip.isFunctionType(nav_val.typeOf(zcu).toIntern());
if (ip.getNav(nav_index).getLinkSection().unwrap()) |@"linksection"| {
const section_name = @"linksection".toSlice(ip);
if (elf_file.sectionByName(section_name)) |osec| {
if (is_func) {
elf_file.sections.items(.shdr)[osec].sh_flags |= elf.SHF_EXECINSTR;
} else {
elf_file.sections.items(.shdr)[osec].sh_flags |= elf.SHF_WRITE;
}
return osec;
}
const osec = try elf_file.addSection(.{
.type = elf.SHT_PROGBITS,
.flags = elf.SHF_ALLOC | @as(u64, if (is_func) elf.SHF_EXECINSTR else elf.SHF_WRITE),
.name = try elf_file.insertShString(section_name),
.addralign = 1,
});
const section_index = try self.addSectionSymbol(gpa, try self.addString(gpa, section_name), osec);
if (std.mem.eql(u8, section_name, ".text")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR;
self.text_index = section_index;
} else if (std.mem.startsWith(u8, section_name, ".text.")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR;
} else if (std.mem.eql(u8, section_name, ".rodata")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC;
self.rodata_index = section_index;
} else if (std.mem.startsWith(u8, section_name, ".rodata.")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC;
} else if (std.mem.eql(u8, section_name, ".data.rel.ro")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
self.data_relro_index = section_index;
} else if (std.mem.eql(u8, section_name, ".data")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
self.data_index = section_index;
} else if (std.mem.startsWith(u8, section_name, ".data.")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
} else if (std.mem.eql(u8, section_name, ".bss")) {
const shdr = &elf_file.sections.items(.shdr)[osec];
shdr.sh_type = elf.SHT_NOBITS;
shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
self.bss_index = section_index;
} else if (std.mem.startsWith(u8, section_name, ".bss.")) {
const shdr = &elf_file.sections.items(.shdr)[osec];
shdr.sh_type = elf.SHT_NOBITS;
shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
} else if (std.mem.eql(u8, section_name, ".tdata")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
self.tdata_index = section_index;
} else if (std.mem.startsWith(u8, section_name, ".tdata.")) {
elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
} else if (std.mem.eql(u8, section_name, ".tbss")) {
const shdr = &elf_file.sections.items(.shdr)[osec];
shdr.sh_type = elf.SHT_NOBITS;
shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
self.tbss_index = section_index;
} else if (std.mem.startsWith(u8, section_name, ".tbss.")) {
const shdr = &elf_file.sections.items(.shdr)[osec];
shdr.sh_type = elf.SHT_NOBITS;
shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
} else if (std.mem.eql(u8, section_name, ".eh_frame")) {
const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result;
const shdr = &elf_file.sections.items(.shdr)[osec];
if (target.cpu.arch == .x86_64) shdr.sh_type = elf.SHT_X86_64_UNWIND;
shdr.sh_flags = elf.SHF_ALLOC;
self.eh_frame_index = section_index;
} else if (std.mem.eql(u8, section_name, ".debug_info")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
self.debug_info_index = section_index;
} else if (std.mem.eql(u8, section_name, ".debug_abbrev")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
self.debug_abbrev_index = section_index;
} else if (std.mem.eql(u8, section_name, ".debug_aranges")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
self.debug_aranges_index = section_index;
} else if (std.mem.eql(u8, section_name, ".debug_str")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
self.debug_str_index = section_index;
} else if (std.mem.eql(u8, section_name, ".debug_line")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
self.debug_line_index = section_index;
} else if (std.mem.eql(u8, section_name, ".debug_line_str")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
self.debug_line_str_index = section_index;
} else if (std.mem.eql(u8, section_name, ".debug_loclists")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
self.debug_loclists_index = section_index;
} else if (std.mem.eql(u8, section_name, ".debug_rnglists")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
self.debug_rnglists_index = section_index;
} else if (std.mem.startsWith(u8, section_name, ".debug")) {
elf_file.sections.items(.shdr)[osec].sh_flags = 0;
} else if (std.mem.eql(u8, section_name, ".init_array") or std.mem.startsWith(u8, section_name, ".init_array.")) {
const shdr = &elf_file.sections.items(.shdr)[osec];
shdr.sh_type = elf.SHT_INIT_ARRAY;
shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
} else if (std.mem.eql(u8, section_name, ".fini_array") or std.mem.startsWith(u8, section_name, ".fini_array.")) {
const shdr = &elf_file.sections.items(.shdr)[osec];
shdr.sh_type = elf.SHT_FINI_ARRAY;
shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
}
return osec;
}
if (is_func) {
if (self.text_index) |symbol_index| if (self.text_index) |symbol_index|
return self.symbol(symbol_index).outputShndx(elf_file).?; return self.symbol(symbol_index).outputShndx(elf_file).?;
const osec = try elf_file.addSection(.{ const osec = try elf_file.addSection(.{

View file

@ -73,6 +73,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
elf_step.dependOn(testLinkingC(b, .{ .target = musl_target })); elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));
elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target })); elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));
elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target })); elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));
elf_step.dependOn(testLinksection(b, .{ .target = musl_target }));
elf_step.dependOn(testMergeStrings(b, .{ .target = musl_target })); elf_step.dependOn(testMergeStrings(b, .{ .target = musl_target }));
elf_step.dependOn(testMergeStrings2(b, .{ .target = musl_target })); elf_step.dependOn(testMergeStrings2(b, .{ .target = musl_target }));
// https://github.com/ziglang/zig/issues/17451 // https://github.com/ziglang/zig/issues/17451
@ -165,6 +166,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));
elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));
elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
elf_step.dependOn(testLinksection(b, .{ .use_llvm = false, .target = default_target }));
elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu })); elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu }));
elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl })); elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl }));
@ -2439,6 +2441,43 @@ fn testLinkingZig(b: *Build, opts: Options) *Step {
return test_step; return test_step;
} }
fn testLinksection(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "linksection", opts);
const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes =
\\export var test_global: u32 linksection(".TestGlobal") = undefined;
\\export fn testFn() linksection(".TestFn") callconv(.c) void {
\\ TestGenericFn("A").f();
\\}
\\fn TestGenericFn(comptime suffix: []const u8) type {
\\ return struct {
\\ fn f() linksection(".TestGenFn" ++ suffix) void {}
\\ };
\\}
});
const check = obj.checkObject();
check.checkInSymtab();
check.checkContains("SECTION LOCAL DEFAULT .TestGlobal");
check.checkInSymtab();
check.checkContains("SECTION LOCAL DEFAULT .TestFn");
check.checkInSymtab();
check.checkContains("SECTION LOCAL DEFAULT .TestGenFnA");
check.checkInSymtab();
check.checkContains("OBJECT GLOBAL DEFAULT test_global");
check.checkInSymtab();
check.checkContains("FUNC GLOBAL DEFAULT testFn");
if (opts.optimize == .Debug) {
check.checkInSymtab();
check.checkContains("FUNC LOCAL DEFAULT main.TestGenericFn(");
}
test_step.dependOn(&check.step);
return test_step;
}
// Adapted from https://github.com/rui314/mold/blob/main/test/elf/mergeable-strings.sh // Adapted from https://github.com/rui314/mold/blob/main/test/elf/mergeable-strings.sh
fn testMergeStrings(b: *Build, opts: Options) *Step { fn testMergeStrings(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "merge-strings", opts); const test_step = addTestStep(b, "merge-strings", opts);