Merge pull request #24094 from jacobly0/x86_64-ld-scripts

link: support static archives that are linker scripts
This commit is contained in:
Andrew Kelley 2025-06-06 16:25:17 -04:00 committed by GitHub
commit 98646e5cf8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 103 additions and 102 deletions

View File

@ -2294,9 +2294,10 @@ fn resolvePathInputLib(
const test_path: Path = pq.path;
// In the case of shared libraries, they might actually be "linker scripts"
// that contain references to other libraries.
if (pq.query.allow_so_scripts and target.ofmt == .elf and
Compilation.classifyFileExt(test_path.sub_path) == .shared_library)
{
if (pq.query.allow_so_scripts and target.ofmt == .elf and switch (Compilation.classifyFileExt(test_path.sub_path)) {
.static_library, .shared_library => true,
else => false,
}) {
var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
error.FileNotFound => return .no_match,
else => |e| fatal("unable to search for {s} library '{'}': {s}", .{
@ -2304,14 +2305,13 @@ fn resolvePathInputLib(
}),
};
errdefer file.close();
try ld_script_bytes.resize(gpa, @sizeOf(std.elf.Elf64_Ehdr));
try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));
const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{'}': {s}", .{
test_path, @errorName(err),
});
elf_file: {
if (n != ld_script_bytes.items.len) break :elf_file;
if (!mem.eql(u8, ld_script_bytes.items[0..4], "\x7fELF")) break :elf_file;
// Appears to be an ELF file.
const buf = ld_script_bytes.items[0..n];
if (mem.startsWith(u8, buf, std.elf.MAGIC) or mem.startsWith(u8, buf, std.elf.ARMAG)) {
// Appears to be an ELF or archive file.
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
}
const stat = file.stat() catch |err|
@ -2319,10 +2319,10 @@ fn resolvePathInputLib(
const size = std.math.cast(u32, stat.size) orelse
fatal("{}: linker script too big", .{test_path});
try ld_script_bytes.resize(gpa, size);
const buf = ld_script_bytes.items[n..];
const n2 = file.preadAll(buf, n) catch |err|
const buf2 = ld_script_bytes.items[n..];
const n2 = file.preadAll(buf2, n) catch |err|
fatal("failed to read {}: {s}", .{ test_path, @errorName(err) });
if (n2 != buf.len) fatal("failed to read {}: unexpected end of file", .{test_path});
if (n2 != buf2.len) fatal("failed to read {}: unexpected end of file", .{test_path});
var diags = Diags.init(gpa);
defer diags.deinit();
const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items);

View File

@ -99,7 +99,7 @@ copy_rel: CopyRelSection = .{},
rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .empty,
/// SHT_GROUP sections
/// Applies only to a relocatable.
comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .empty,
group_sections: std.ArrayListUnmanaged(GroupSection) = .empty,
resolver: SymbolResolver = .{},
@ -510,7 +510,7 @@ pub fn deinit(self: *Elf) void {
self.copy_rel.deinit(gpa);
self.rela_dyn.deinit(gpa);
self.rela_plt.deinit(gpa);
self.comdat_group_sections.deinit(gpa);
self.group_sections.deinit(gpa);
self.dump_argv_list.deinit(gpa);
}
@ -919,7 +919,7 @@ fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
&self.sections,
self.shstrtab.items,
self.merge_sections.items,
self.comdat_group_sections.items,
self.group_sections.items,
self.zigObjectPtr(),
self.files,
);
@ -1315,16 +1315,16 @@ pub fn resolveSymbols(self: *Elf) !void {
}
{
// Dedup comdat groups.
// Dedup groups.
var table = std.StringHashMap(Ref).init(self.base.comp.gpa);
defer table.deinit();
for (self.objects.items) |index| {
try self.file(index).?.object.resolveComdatGroups(self, &table);
try self.file(index).?.object.resolveGroups(self, &table);
}
for (self.objects.items) |index| {
self.file(index).?.object.markComdatGroupsDead(self);
self.file(index).?.object.markGroupsDead(self);
}
}
@ -3125,7 +3125,7 @@ pub fn sortShdrs(
sections: *std.MultiArrayList(Section),
shstrtab: []const u8,
merge_sections: []Merge.Section,
comdat_group_sections: []ComdatGroupSection,
comdat_group_sections: []GroupSection,
zig_object_ptr: ?*ZigObject,
files: std.MultiArrayList(File.Entry),
) !void {
@ -4446,8 +4446,8 @@ pub fn atom(self: *Elf, ref: Ref) ?*Atom {
return file_ptr.atom(ref.index);
}
pub fn comdatGroup(self: *Elf, ref: Ref) *ComdatGroup {
return self.file(ref.file).?.comdatGroup(ref.index);
pub fn group(self: *Elf, ref: Ref) *Group {
return self.file(ref.file).?.group(ref.index);
}
pub fn symbol(self: *Elf, ref: Ref) ?*Symbol {
@ -4814,7 +4814,7 @@ fn fmtDumpState(
object.fmtCies(self),
object.fmtFdes(self),
object.fmtSymtab(self),
object.fmtComdatGroups(self),
object.fmtGroups(self),
});
}
@ -4852,9 +4852,9 @@ fn fmtDumpState(
try writer.print("{}\n", .{self.got.fmt(self)});
try writer.print("{}\n", .{self.plt.fmt(self)});
try writer.writeAll("Output COMDAT groups\n");
for (self.comdat_group_sections.items) |cg| {
try writer.print(" shdr({d}) : COMDAT({})\n", .{ cg.shndx, cg.cg_ref });
try writer.writeAll("Output groups\n");
for (self.group_sections.items) |cg| {
try writer.print(" shdr({d}) : GROUP({})\n", .{ cg.shndx, cg.cg_ref });
}
try writer.writeAll("\nOutput merge sections\n");
@ -4934,25 +4934,26 @@ const default_entry_addr = 0x8000000;
pub const base_tag: link.File.Tag = .elf;
pub const ComdatGroup = struct {
pub const Group = struct {
signature_off: u32,
file_index: File.Index,
shndx: u32,
members_start: u32,
members_len: u32,
is_comdat: bool,
alive: bool = true,
pub fn file(cg: ComdatGroup, elf_file: *Elf) File {
pub fn file(cg: Group, elf_file: *Elf) File {
return elf_file.file(cg.file_index).?;
}
pub fn signature(cg: ComdatGroup, elf_file: *Elf) [:0]const u8 {
pub fn signature(cg: Group, elf_file: *Elf) [:0]const u8 {
return cg.file(elf_file).object.getString(cg.signature_off);
}
pub fn comdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 {
pub fn members(cg: Group, elf_file: *Elf) []const u32 {
const object = cg.file(elf_file).object;
return object.comdat_group_data.items[cg.members_start..][0..cg.members_len];
return object.group_data.items[cg.members_start..][0..cg.members_len];
}
pub const Index = u32;
@ -5310,7 +5311,7 @@ const Air = @import("../Air.zig");
const Archive = @import("Elf/Archive.zig");
const AtomList = @import("Elf/AtomList.zig");
const Compilation = @import("../Compilation.zig");
const ComdatGroupSection = synthetic_sections.ComdatGroupSection;
const GroupSection = synthetic_sections.GroupSection;
const CopyRelSection = synthetic_sections.CopyRelSection;
const Diags = @import("../link.zig").Diags;
const DynamicSection = synthetic_sections.DynamicSection;

View File

@ -20,8 +20,8 @@ atoms: std.ArrayListUnmanaged(Atom) = .empty,
atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .empty,
atoms_extra: std.ArrayListUnmanaged(u32) = .empty,
comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup) = .empty,
comdat_group_data: std.ArrayListUnmanaged(u32) = .empty,
groups: std.ArrayListUnmanaged(Elf.Group) = .empty,
group_data: std.ArrayListUnmanaged(u32) = .empty,
input_merge_sections: std.ArrayListUnmanaged(Merge.InputSection) = .empty,
input_merge_sections_indexes: std.ArrayListUnmanaged(Merge.InputSection.Index) = .empty,
@ -49,8 +49,8 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
self.atoms.deinit(gpa);
self.atoms_indexes.deinit(gpa);
self.atoms_extra.deinit(gpa);
self.comdat_groups.deinit(gpa);
self.comdat_group_data.deinit(gpa);
self.groups.deinit(gpa);
self.group_data.deinit(gpa);
self.relocs.deinit(gpa);
self.fdes.deinit(gpa);
self.cies.deinit(gpa);
@ -304,22 +304,22 @@ fn initAtoms(
}
const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
if (group_members[0] != elf.GRP_COMDAT) {
return diags.failParse(path, "corrupt section group: unknown SHT_GROUP format", .{});
switch (group_members[0]) {
0, elf.GRP_COMDAT => {
const group_start: u32 = @intCast(self.group_data.items.len);
try self.group_data.appendUnalignedSlice(gpa, group_members[1..]);
self.group(try self.addGroup(gpa)).* = .{
.signature_off = group_signature,
.file_index = self.index,
.shndx = shndx,
.members_start = group_start,
.members_len = @intCast(group_nmembers - 1),
.is_comdat = group_members[0] == elf.GRP_COMDAT,
};
},
else => return diags.failParse(path, "corrupt section group: unknown SHT_GROUP format", .{}),
}
const group_start: u32 = @intCast(self.comdat_group_data.items.len);
try self.comdat_group_data.appendUnalignedSlice(gpa, group_members[1..]);
const comdat_group_index = try self.addComdatGroup(gpa);
const comdat_group = self.comdatGroup(comdat_group_index);
comdat_group.* = .{
.signature_off = group_signature,
.file_index = self.index,
.shndx = shndx,
.members_start = group_start,
.members_len = @intCast(group_nmembers - 1),
};
},
elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"),
@ -986,28 +986,28 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
}
}
pub fn resolveComdatGroups(self: *Object, elf_file: *Elf, table: anytype) !void {
for (self.comdat_groups.items, 0..) |*cg, cgi| {
const signature = cg.signature(elf_file);
pub fn resolveGroups(self: *Object, elf_file: *Elf, table: anytype) !void {
for (self.groups.items, 0..) |*g, gi| {
const signature = g.signature(elf_file);
const gop = try table.getOrPut(signature);
if (!gop.found_existing) {
gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index };
gop.value_ptr.* = .{ .index = @intCast(gi), .file = self.index };
continue;
}
const current = elf_file.comdatGroup(gop.value_ptr.*);
cg.alive = false;
const current = elf_file.group(gop.value_ptr.*);
g.alive = false;
if (self.index < current.file_index) {
current.alive = false;
cg.alive = true;
gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index };
g.alive = true;
gop.value_ptr.* = .{ .index = @intCast(gi), .file = self.index };
}
}
}
pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void {
for (self.comdat_groups.items) |cg| {
if (cg.alive) continue;
for (cg.comdatGroupMembers(elf_file)) |shndx| {
pub fn markGroupsDead(self: *Object, elf_file: *Elf) void {
for (self.groups.items) |g| {
if (g.alive) continue;
for (g.members(elf_file)) |shndx| {
const atom_index = self.atoms_indexes.items[shndx];
if (self.atom(atom_index)) |atom_ptr| {
atom_ptr.alive = false;
@ -1421,15 +1421,15 @@ fn inputMergeSection(self: *Object, index: Merge.InputSection.Index) ?*Merge.Inp
return &self.input_merge_sections.items[index];
}
fn addComdatGroup(self: *Object, gpa: Allocator) !Elf.ComdatGroup.Index {
const index = @as(Elf.ComdatGroup.Index, @intCast(self.comdat_groups.items.len));
_ = try self.comdat_groups.addOne(gpa);
fn addGroup(self: *Object, gpa: Allocator) !Elf.Group.Index {
const index: Elf.Group.Index = @intCast(self.groups.items.len);
_ = try self.groups.addOne(gpa);
return index;
}
pub fn comdatGroup(self: *Object, index: Elf.ComdatGroup.Index) *Elf.ComdatGroup {
assert(index < self.comdat_groups.items.len);
return &self.comdat_groups.items[index];
pub fn group(self: *Object, index: Elf.Group.Index) *Elf.Group {
assert(index < self.groups.items.len);
return &self.groups.items[index];
}
pub fn format(
@ -1550,14 +1550,14 @@ fn formatFdes(
}
}
pub fn fmtComdatGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatComdatGroups) {
pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatGroups) {
return .{ .data = .{
.object = self,
.elf_file = elf_file,
} };
}
fn formatComdatGroups(
fn formatGroups(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
@ -1567,13 +1567,13 @@ fn formatComdatGroups(
_ = options;
const object = ctx.object;
const elf_file = ctx.elf_file;
try writer.writeAll(" COMDAT groups\n");
for (object.comdat_groups.items, 0..) |cg, cg_index| {
try writer.print(" COMDAT({d})", .{cg_index});
if (!cg.alive) try writer.writeAll(" : [*]");
try writer.writeAll(" groups\n");
for (object.groups.items, 0..) |g, g_index| {
try writer.print(" {s}({d})", .{ if (g.is_comdat) "COMDAT" else "GROUP", g_index });
if (!g.alive) try writer.writeAll(" : [*]");
try writer.writeByte('\n');
const cg_members = cg.comdatGroupMembers(elf_file);
for (cg_members) |shndx| {
const g_members = g.members(elf_file);
for (g_members) |shndx| {
const atom_index = object.atoms_indexes.items[shndx];
const atom_ptr = object.atom(atom_index) orelse continue;
try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) });

View File

@ -198,10 +198,10 @@ pub const File = union(enum) {
};
}
pub fn comdatGroup(file: File, ind: Elf.ComdatGroup.Index) *Elf.ComdatGroup {
pub fn group(file: File, ind: Elf.Group.Index) *Elf.Group {
return switch (file) {
.linker_defined, .shared_object, .zig_object => unreachable,
.object => |x| x.comdatGroup(ind),
.object => |x| x.group(ind),
};
}

View File

@ -19,7 +19,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) !void {
&elf_file.sections,
elf_file.shstrtab.items,
elf_file.merge_sections.items,
elf_file.comdat_group_sections.items,
elf_file.group_sections.items,
elf_file.zigObjectPtr(),
elf_file.files,
);
@ -152,7 +152,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation) !void {
&elf_file.sections,
elf_file.shstrtab.items,
elf_file.merge_sections.items,
elf_file.comdat_group_sections.items,
elf_file.group_sections.items,
elf_file.zigObjectPtr(),
elf_file.files,
);
@ -233,19 +233,19 @@ fn initSections(elf_file: *Elf) !void {
);
}
try initComdatGroups(elf_file);
try initGroups(elf_file);
try elf_file.initSymtab();
try elf_file.initShStrtab();
}
fn initComdatGroups(elf_file: *Elf) !void {
fn initGroups(elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
for (elf_file.objects.items) |index| {
const object = elf_file.file(index).?.object;
for (object.comdat_groups.items, 0..) |cg, cg_index| {
for (object.groups.items, 0..) |cg, cg_index| {
if (!cg.alive) continue;
const cg_sec = try elf_file.comdat_group_sections.addOne(gpa);
const cg_sec = try elf_file.group_sections.addOne(gpa);
cg_sec.* = .{
.shndx = try elf_file.addSection(.{
.name = try elf_file.insertShString(".group"),
@ -292,12 +292,12 @@ fn updateSectionSizes(elf_file: *Elf) !void {
}
try elf_file.updateSymtabSize();
updateComdatGroupsSizes(elf_file);
updateGroupsSizes(elf_file);
elf_file.updateShStrtabSize();
}
fn updateComdatGroupsSizes(elf_file: *Elf) void {
for (elf_file.comdat_group_sections.items) |cg| {
fn updateGroupsSizes(elf_file: *Elf) void {
for (elf_file.group_sections.items) |cg| {
const shdr = &elf_file.sections.items(.shdr)[cg.shndx];
shdr.sh_size = cg.size(elf_file);
shdr.sh_link = elf_file.section_indexes.symtab.?;
@ -436,21 +436,21 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
}
try writeComdatGroups(elf_file);
try writeGroups(elf_file);
try elf_file.writeSymtab();
try elf_file.writeShStrtab();
}
fn writeComdatGroups(elf_file: *Elf) !void {
fn writeGroups(elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
for (elf_file.comdat_group_sections.items) |cgs| {
for (elf_file.group_sections.items) |cgs| {
const shdr = elf_file.sections.items(.shdr)[cgs.shndx];
const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
defer buffer.deinit();
try cgs.write(elf_file, buffer.writer());
assert(buffer.items.len == sh_size);
log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{
log.debug("writing group from 0x{x} to 0x{x}", .{
shdr.sh_offset,
shdr.sh_offset + shdr.sh_size,
});

View File

@ -1484,33 +1484,33 @@ pub const VerneedSection = struct {
}
};
pub const ComdatGroupSection = struct {
pub const GroupSection = struct {
shndx: u32,
cg_ref: Elf.Ref,
fn comdatGroup(cgs: ComdatGroupSection, elf_file: *Elf) *Elf.ComdatGroup {
fn group(cgs: GroupSection, elf_file: *Elf) *Elf.Group {
const cg_file = elf_file.file(cgs.cg_ref.file).?;
return cg_file.object.comdatGroup(cgs.cg_ref.index);
return cg_file.object.group(cgs.cg_ref.index);
}
pub fn symbol(cgs: ComdatGroupSection, elf_file: *Elf) *Symbol {
const cg = cgs.comdatGroup(elf_file);
pub fn symbol(cgs: GroupSection, elf_file: *Elf) *Symbol {
const cg = cgs.group(elf_file);
const object = cg.file(elf_file).object;
const shdr = object.shdrs.items[cg.shndx];
return &object.symbols.items[shdr.sh_info];
}
pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize {
const cg = cgs.comdatGroup(elf_file);
const members = cg.comdatGroupMembers(elf_file);
pub fn size(cgs: GroupSection, elf_file: *Elf) usize {
const cg = cgs.group(elf_file);
const members = cg.members(elf_file);
return (members.len + 1) * @sizeOf(u32);
}
pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void {
const cg = cgs.comdatGroup(elf_file);
pub fn write(cgs: GroupSection, elf_file: *Elf, writer: anytype) !void {
const cg = cgs.group(elf_file);
const object = cg.file(elf_file).object;
const members = cg.comdatGroupMembers(elf_file);
try writer.writeInt(u32, elf.GRP_COMDAT, .little);
const members = cg.members(elf_file);
try writer.writeInt(u32, if (cg.is_comdat) elf.GRP_COMDAT else 0, .little);
for (members) |shndx| {
const shdr = object.shdrs.items[shndx];
switch (shdr.sh_type) {