srctree

Jakub Konka parent fad5e7a9 a94d5895
elf: do not prealloc input objects, pread selectively

inlinesplit
src/link/Elf.zig added: 348, removed: 290, total 58
@@ -35,6 +35,10 @@ llvm_object: ?*LlvmObject = null,
/// Index of each input file also encodes the priority or precedence of one input file
/// over another.
files: std.MultiArrayList(File.Entry) = .{},
/// Long-lived list of all file descriptors.
/// We store them globally rather than per actual File so that we can re-use
/// one file handle per every object file within an archive.
file_handles: std.ArrayListUnmanaged(File.Handle) = .{},
zig_object_index: ?File.Index = null,
linker_defined_index: ?File.Index = null,
objects: std.ArrayListUnmanaged(File.Index) = .{},
@@ -444,6 +448,11 @@ pub fn deinit(self: *Elf) void {
 
if (self.llvm_object) |llvm_object| llvm_object.deinit();
 
for (self.file_handles.items) |fh| {
fh.close();
}
self.file_handles.deinit(gpa);
 
for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
.null => {},
.zig_object => data.zig_object.deinit(gpa),
@@ -1244,9 +1253,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
for (self.objects.items) |index| {
try self.file(index).?.object.init(self);
}
for (self.shared_objects.items) |index| {
try self.file(index).?.shared_object.init(self);
}
 
if (comp.link_errors.items.len > 0) return error.FlushFailure;
 
@@ -1463,7 +1469,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
for (files.items) |index| {
const file_ptr = self.file(index).?;
try file_ptr.updateArStrtab(gpa, &ar_strtab);
file_ptr.updateArSize();
try file_ptr.updateArSize(self);
}
 
// Update file offsets of contributing objects.
@@ -1515,7 +1521,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
// Write object files
for (files.items) |index| {
if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
try self.file(index).?.writeAr(buffer.writer());
try self.file(index).?.writeAr(self, buffer.writer());
}
 
assert(buffer.items.len == total_size);
@@ -1902,13 +1908,13 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {
defer tracy.end();
 
const gpa = self.base.comp.gpa;
const in_file = try std.fs.cwd().openFile(path, .{});
defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
const handle = try std.fs.cwd().openFile(path, .{});
const fh = try self.addFileHandle(handle);
 
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .object = .{
.path = try gpa.dupe(u8, path),
.data = data,
.file_handle = fh,
.index = index,
} });
try self.objects.append(gpa, index);
@@ -1922,12 +1928,12 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
defer tracy.end();
 
const gpa = self.base.comp.gpa;
const in_file = try std.fs.cwd().openFile(path, .{});
defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
var archive = Archive{ .path = try gpa.dupe(u8, path), .data = data };
const handle = try std.fs.cwd().openFile(path, .{});
const fh = try self.addFileHandle(handle);
 
var archive = Archive{};
defer archive.deinit(gpa);
try archive.parse(self);
try archive.parse(self, path, fh);
 
const objects = try archive.objects.toOwnedSlice(gpa);
defer gpa.free(objects);
@@ -1948,13 +1954,12 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
defer tracy.end();
 
const gpa = self.base.comp.gpa;
const in_file = try std.fs.cwd().openFile(lib.path, .{});
defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
const handle = try std.fs.cwd().openFile(lib.path, .{});
defer handle.close();
 
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .shared_object = .{
.path = try gpa.dupe(u8, lib.path),
.data = data,
.index = index,
.needed = lib.needed,
.alive = lib.needed,
@@ -1962,7 +1967,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
try self.shared_objects.append(gpa, index);
 
const shared_object = self.file(index).?.shared_object;
try shared_object.parse(self);
try shared_object.parse(self, handle);
}
 
fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
@@ -2135,7 +2140,7 @@ fn resolveSymbols(self: *Elf) void {
const cg = self.comdatGroup(cg_index);
const cg_owner = self.comdatGroupOwner(cg.owner);
if (cg_owner.file != index) {
for (object.comdatGroupMembers(cg.shndx)) |shndx| {
for (cg.comdatGroupMembers(self)) |shndx| {
const atom_index = object.atoms.items[shndx];
if (self.atom(atom_index)) |atom_ptr| {
atom_ptr.flags.alive = false;
@@ -5862,6 +5867,19 @@ pub fn file(self: *Elf, index: File.Index) ?File {
};
}
 
pub fn addFileHandle(self: *Elf, handle: std.fs.File) !File.HandleIndex {
const gpa = self.base.comp.gpa;
const index: File.HandleIndex = @intCast(self.file_handles.items.len);
const fh = try self.file_handles.addOne(gpa);
fh.* = handle;
return index;
}
 
pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle {
assert(index < self.file_handles.items.len);
return self.file_handles.items[index];
}
 
/// Returns pointer-to-symbol described at sym_index.
pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol {
return &self.symbols.items[sym_index];
@@ -6395,6 +6413,15 @@ fn fmtDumpState(
}
}
 
/// Caller owns the memory.
pub fn preadAllAlloc(allocator: Allocator, handle: std.fs.File, offset: usize, size: usize) ![]u8 {
const buffer = try allocator.alloc(u8, size);
errdefer allocator.free(buffer);
const amt = try handle.preadAll(buffer, offset);
if (amt != size) return error.InputOutput;
return buffer;
}
 
/// Binary search
pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
if (!@hasDecl(@TypeOf(predicate), "predicate"))
@@ -6441,12 +6468,22 @@ pub const base_tag: link.File.Tag = .elf;
 
const ComdatGroupOwner = struct {
file: File.Index = 0,
 
const Index = u32;
};
 
pub const ComdatGroup = struct {
owner: ComdatGroupOwner.Index,
shndx: u16,
file: File.Index,
shndx: u32,
members_start: u32,
members_len: u32,
 
pub fn comdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 {
const object = elf_file.file(cg.file).?.object;
return object.comdat_group_data.items[cg.members_start..][0..cg.members_len];
}
 
pub const Index = u32;
};
 
 
src/link/Elf/Archive.zig added: 348, removed: 290, total 58
@@ -1,8 +1,5 @@
path: []const u8,
data: []const u8,
 
objects: std.ArrayListUnmanaged(Object) = .{},
strtab: []const u8 = &[0]u8{},
strtab: std.ArrayListUnmanaged(u8) = .{},
 
pub fn isArchive(path: []const u8) !bool {
const file = try std.fs.cwd().openFile(path, .{});
@@ -14,68 +11,79 @@ pub fn isArchive(path: []const u8) !bool {
}
 
pub fn deinit(self: *Archive, allocator: Allocator) void {
allocator.free(self.path);
allocator.free(self.data);
self.objects.deinit(allocator);
self.strtab.deinit(allocator);
}
 
pub fn parse(self: *Archive, elf_file: *Elf) !void {
pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: File.HandleIndex) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const handle = elf_file.fileHandle(handle_index);
const size = (try handle.stat()).size;
 
var stream = std.io.fixedBufferStream(self.data);
const reader = stream.reader();
const reader = handle.reader();
_ = try reader.readBytesNoEof(elf.ARMAG.len);
 
var pos: usize = elf.ARMAG.len;
while (true) {
if (stream.pos >= self.data.len) break;
if (!mem.isAligned(stream.pos, 2)) stream.pos += 1;
if (pos >= size) break;
if (!mem.isAligned(pos, 2)) {
try handle.seekBy(1);
pos += 1;
}
 
const hdr = try reader.readStruct(elf.ar_hdr);
pos += @sizeOf(elf.ar_hdr);
 
if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
try elf_file.reportParseError(self.path, "invalid archive header delimiter: {s}", .{
try elf_file.reportParseError(path, "invalid archive header delimiter: {s}", .{
std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
});
return error.MalformedArchive;
}
 
const size = try hdr.size();
const obj_size = try hdr.size();
defer {
_ = stream.seekBy(size) catch {};
_ = handle.seekBy(obj_size) catch {};
pos += obj_size;
}
 
if (hdr.isSymtab() or hdr.isSymtab64()) continue;
if (hdr.isStrtab()) {
self.strtab = self.data[stream.pos..][0..size];
try self.strtab.resize(gpa, obj_size);
const amt = try handle.preadAll(self.strtab.items, pos);
if (amt != obj_size) return error.InputOutput;
continue;
}
if (hdr.isSymdef() or hdr.isSymdefSorted()) continue;
 
const name = if (hdr.name()) |name|
try gpa.dupe(u8, name)
name
else if (try hdr.nameOffset()) |off|
try gpa.dupe(u8, self.getString(off))
self.getString(off)
else
unreachable;
 
const object = Object{
.archive = try gpa.dupe(u8, self.path),
.path = name,
.data = try gpa.dupe(u8, self.data[stream.pos..][0..size]),
.archive = .{
.path = try gpa.dupe(u8, path),
.offset = pos,
},
.path = try gpa.dupe(u8, name),
.file_handle = handle_index,
.index = undefined,
.alive = false,
};
 
log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path });
log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path });
 
try self.objects.append(gpa, object);
}
}
 
fn getString(self: Archive, off: u32) []const u8 {
assert(off < self.strtab.len);
const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0);
assert(off < self.strtab.items.len);
const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
return name[0 .. name.len - 1];
}
 
@@ -86,7 +94,7 @@ pub fn setArHdr(opts: struct {
name: []const u8,
name_off: u32,
},
size: u32,
size: usize,
}) elf.ar_hdr {
var hdr: elf.ar_hdr = .{
.ar_name = undefined,
 
src/link/Elf/Atom.zig added: 348, removed: 290, total 58
@@ -22,6 +22,12 @@ output_section_index: u16 = 0,
/// Index of the input section containing this atom's relocs.
relocs_section_index: u32 = 0,
 
/// Start index of the relocations belonging to this atom.
rel_index: u32 = 0,
 
/// Number of relocations belonging to this atom.
rel_num: u32 = 0,
 
/// Index of this atom in the linker's atoms table.
atom_index: Index = 0,
 
@@ -52,7 +58,7 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {
return elf_file.file(self.file_index);
}
 
pub fn inputShdr(self: Atom, elf_file: *Elf) Object.ElfShdr {
pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {
return switch (self.file(elf_file).?) {
.object => |x| x.shdrs.items[self.input_section_index],
.zig_object => |x| x.inputShdr(self.atom_index, elf_file),
@@ -289,7 +295,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{};
return switch (self.file(elf_file).?) {
.zig_object => |x| x.relocs.items[shndx].items,
.object => |x| x.getRelocs(shndx),
.object => |x| x.relocs.items[self.rel_index..][0..self.rel_num],
else => unreachable,
};
}
 
src/link/Elf/Object.zig added: 348, removed: 290, total 58
@@ -1,10 +1,10 @@
archive: ?[]const u8 = null,
archive: ?InArchive = null,
path: []const u8,
data: []const u8,
file_handle: File.HandleIndex,
index: File.Index,
 
header: ?elf.Elf64_Ehdr = null,
shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},
shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
 
symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
@@ -12,9 +12,12 @@ first_global: ?Symbol.Index = null,
symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},
comdat_group_data: std.ArrayListUnmanaged(u32) = .{},
relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
 
fdes: std.ArrayListUnmanaged(Fde) = .{},
cies: std.ArrayListUnmanaged(Cie) = .{},
eh_frame_data: std.ArrayListUnmanaged(u8) = .{},
 
alive: bool = true,
num_dynrelocs: u32 = 0,
@@ -35,24 +38,30 @@ pub fn isObject(path: []const u8) !bool {
}
 
pub fn deinit(self: *Object, allocator: Allocator) void {
if (self.archive) |path| allocator.free(path);
if (self.archive) |*ar| allocator.free(ar.path);
allocator.free(self.path);
allocator.free(self.data);
self.shdrs.deinit(allocator);
self.symtab.deinit(allocator);
self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
self.atoms.deinit(allocator);
self.comdat_groups.deinit(allocator);
self.comdat_group_data.deinit(allocator);
self.relocs.deinit(allocator);
self.fdes.deinit(allocator);
self.cies.deinit(allocator);
self.eh_frame_data.deinit(allocator);
}
 
pub fn parse(self: *Object, elf_file: *Elf) !void {
var stream = std.io.fixedBufferStream(self.data);
const reader = stream.reader();
const gpa = elf_file.base.comp.gpa;
const offset = if (self.archive) |ar| ar.offset else 0;
const handle = elf_file.fileHandle(self.file_handle);
const file_size = (try handle.stat()).size;
 
self.header = try reader.readStruct(elf.Elf64_Ehdr);
const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr));
defer gpa.free(header_buffer);
self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;
 
const target = elf_file.base.comp.root_mod.resolved_target.result;
if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {
@@ -66,12 +75,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
 
if (self.header.?.e_shnum == 0) return;
 
const comp = elf_file.base.comp;
const gpa = comp.gpa;
 
if (self.data.len < self.header.?.e_shoff or
self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr))
{
const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
if (file_size < offset + shoff or file_size < offset + shoff + shsize) {
try elf_file.reportParseError2(
self.index,
"corrupt header: section header table extends past the end of file",
@@ -80,25 +87,23 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
return error.MalformedObject;
}
 
const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
const shdrs = @as(
[*]align(1) const elf.Elf64_Shdr,
@ptrCast(self.data.ptr + shoff),
)[0..self.header.?.e_shnum];
try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize);
defer gpa.free(shdrs_buffer);
const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
try self.shdrs.appendUnalignedSlice(gpa, shdrs);
 
for (shdrs) |shdr| {
for (self.shdrs.items) |shdr| {
if (shdr.sh_type != elf.SHT_NOBITS) {
if (self.data.len < shdr.sh_offset or self.data.len < shdr.sh_offset + shdr.sh_size) {
if (file_size < offset + shdr.sh_offset or file_size < offset + shdr.sh_offset + shdr.sh_size) {
try elf_file.reportParseError2(self.index, "corrupt section: extends past the end of file", .{});
return error.MalformedObject;
}
}
self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
}
 
const shstrtab = self.shdrContents(self.header.?.e_shstrndx);
for (shdrs) |shdr| {
const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx);
defer gpa.free(shstrtab);
for (self.shdrs.items) |shdr| {
if (shdr.sh_name >= shstrtab.len) {
try elf_file.reportParseError2(self.index, "corrupt section name offset", .{});
return error.MalformedObject;
@@ -112,10 +117,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
} else null;
 
if (symtab_index) |index| {
const shdr = shdrs[index];
const shdr = self.shdrs.items[index];
self.first_global = shdr.sh_info;
 
const raw_symtab = self.shdrContents(index);
const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index);
defer gpa.free(raw_symtab);
const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {
try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{});
return error.MalformedObject;
@@ -123,7 +129,9 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
 
const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
try self.strtab.appendSlice(gpa, self.shdrContents(@as(u16, @intCast(shdr.sh_link))));
const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link);
defer gpa.free(strtab);
try self.strtab.appendSlice(gpa, strtab);
 
try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
for (symtab) |sym| {
@@ -138,22 +146,23 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
}
 
pub fn init(self: *Object, elf_file: *Elf) !void {
try self.initAtoms(elf_file);
try self.initSymtab(elf_file);
const gpa = elf_file.base.comp.gpa;
const handle = elf_file.fileHandle(self.file_handle);
 
try self.initAtoms(gpa, handle, elf_file);
try self.initSymtab(gpa, elf_file);
 
for (self.shdrs.items, 0..) |shdr, i| {
const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
if (!atom.flags.alive) continue;
if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
try self.parseEhFrame(@as(u16, @intCast(i)), elf_file);
try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);
}
}
 
fn initAtoms(self: *Object, elf_file: *Elf) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
const shdrs = self.shdrs.items;
try self.atoms.resize(gpa, shdrs.len);
try self.atoms.resize(allocator, shdrs.len);
@memset(self.atoms.items, 0);
 
for (shdrs, 0..) |shdr, i| {
@@ -177,8 +186,9 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
break :blk self.getString(group_info_sym.st_name);
};
 
const shndx = @as(u16, @intCast(i));
const group_raw_data = self.shdrContents(shndx);
const shndx = @as(u32, @intCast(i));
const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
defer allocator.free(group_raw_data);
const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32));
const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
 
@@ -188,14 +198,20 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
continue;
}
 
const group_start = @as(u32, @intCast(self.comdat_group_data.items.len));
try self.comdat_group_data.appendUnalignedSlice(allocator, group_members[1..]);
 
const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature);
const comdat_group_index = try elf_file.addComdatGroup();
const comdat_group = elf_file.comdatGroup(comdat_group_index);
comdat_group.* = .{
.owner = gop.index,
.file = self.index,
.shndx = shndx,
.members_start = group_start,
.members_len = @intCast(group_nmembers - 1),
};
try self.comdat_groups.append(gpa, comdat_group_index);
try self.comdat_groups.append(allocator, comdat_group_index);
},
 
elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"),
@@ -210,7 +226,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
else => {
const shndx = @as(u16, @intCast(i));
if (self.skipShdr(shndx, elf_file)) continue;
try self.addAtom(shdr, shndx, elf_file);
try self.addAtom(allocator, handle, shdr, shndx, elf_file);
},
}
}
@@ -220,14 +236,19 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
elf.SHT_REL, elf.SHT_RELA => {
const atom_index = self.atoms.items[shdr.sh_info];
if (elf_file.atom(atom_index)) |atom| {
atom.relocs_section_index = @as(u16, @intCast(i));
const relocs = try self.preadRelocsAlloc(allocator, handle, @intCast(i));
defer allocator.free(relocs);
atom.relocs_section_index = @intCast(i);
atom.rel_index = @intCast(self.relocs.items.len);
atom.rel_num = @intCast(relocs.len);
try self.relocs.appendUnalignedSlice(allocator, relocs);
}
},
else => {},
};
}
 
fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOfMemory}!void {
fn addAtom(self: *Object, allocator: Allocator, handle: std.fs.File, shdr: elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void {
const atom_index = try elf_file.addAtom();
const atom = elf_file.atom(atom_index).?;
atom.atom_index = atom_index;
@@ -237,7 +258,8 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf
self.atoms.items[shndx] = atom_index;
 
if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
const data = self.shdrContents(shndx);
const data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
defer allocator.free(data);
const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
atom.size = chdr.ch_size;
atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign);
@@ -247,7 +269,7 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf
}
}
 
fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 {
fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u16 {
const name = blk: {
const name = self.getString(shdr.sh_name);
if (elf_file.base.isRelocatable()) break :blk name;
@@ -310,12 +332,10 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
return ignore;
}
 
fn initSymtab(self: *Object, elf_file: *Elf) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
const first_global = self.first_global orelse self.symtab.items.len;
 
try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len);
try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len);
 
for (self.symtab.items[0..first_global], 0..) |sym, i| {
const index = try elf_file.addSymbol();
@@ -335,19 +355,24 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void {
}
}
 
fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {
fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: u32, elf_file: *Elf) !void {
const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u16, @intCast(i)),
elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),
else => {},
} else {
// TODO: convert into an error
log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()});
return;
};
 
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const raw = self.shdrContents(shndx);
const relocs = self.getRelocs(relocs_shndx);
const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);
defer allocator.free(raw);
const data_start = @as(u32, @intCast(self.eh_frame_data.items.len));
try self.eh_frame_data.appendSlice(allocator, raw);
const relocs = try self.preadRelocsAlloc(allocator, handle, relocs_shndx);
defer allocator.free(relocs);
const rel_start = @as(u32, @intCast(self.relocs.items.len));
try self.relocs.appendUnalignedSlice(allocator, relocs);
const fdes_start = self.fdes.items.len;
const cies_start = self.cies.items.len;
 
@@ -355,22 +380,20 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {
while (try it.next()) |rec| {
const rel_range = filterRelocs(relocs, rec.offset, rec.size + 4);
switch (rec.tag) {
.cie => try self.cies.append(gpa, .{
.offset = rec.offset,
.cie => try self.cies.append(allocator, .{
.offset = data_start + rec.offset,
.size = rec.size,
.rel_index = @as(u32, @intCast(rel_range.start)),
.rel_index = rel_start + @as(u32, @intCast(rel_range.start)),
.rel_num = @as(u32, @intCast(rel_range.len)),
.rel_section_index = relocs_shndx,
.input_section_index = shndx,
.file_index = self.index,
}),
.fde => try self.fdes.append(gpa, .{
.offset = rec.offset,
.fde => try self.fdes.append(allocator, .{
.offset = data_start + rec.offset,
.size = rec.size,
.cie_index = undefined,
.rel_index = @as(u32, @intCast(rel_range.start)),
.rel_index = rel_start + @as(u32, @intCast(rel_range.start)),
.rel_num = @as(u32, @intCast(rel_range.len)),
.rel_section_index = relocs_shndx,
.input_section_index = shndx,
.file_index = self.index,
}),
@@ -773,21 +796,30 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf
}
}
 
pub fn updateArSize(self: *Object) void {
self.output_ar_state.size = self.data.len;
pub fn updateArSize(self: *Object, elf_file: *Elf) !void {
const handle = elf_file.fileHandle(self.file_handle);
const size = (try handle.stat()).size;
self.output_ar_state.size = size;
}
 
pub fn writeAr(self: Object, writer: anytype) !void {
pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void {
const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
const name = self.path;
const hdr = Archive.setArHdr(.{
.name = if (name.len <= Archive.max_member_name_len)
.{ .name = name }
else
.{ .name_off = self.output_ar_state.name_off },
.size = @intCast(self.data.len),
.size = size,
});
try writer.writeAll(mem.asBytes(&hdr));
try writer.writeAll(self.data);
const handle = elf_file.fileHandle(self.file_handle);
const gpa = elf_file.base.comp.gpa;
const data = try gpa.alloc(u8, size);
defer gpa.free(data);
const amt = try handle.preadAll(data, 0);
if (amt != size) return error.InputOutput;
try writer.writeAll(data);
}
 
pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
@@ -859,12 +891,6 @@ pub fn globals(self: Object) []const Symbol.Index {
return self.symbols.items[start..];
}
 
pub fn shdrContents(self: Object, index: u32) []const u8 {
assert(index < self.shdrs.items.len);
const shdr = self.shdrs.items[index];
return self.data[shdr.sh_offset..][0..shdr.sh_size];
}
 
/// Returns atom's code and optionally uncompresses data if required (for compressed sections).
/// Caller owns the memory.
pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
@@ -872,8 +898,11 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)
const gpa = comp.gpa;
const atom_ptr = elf_file.atom(atom_index).?;
assert(atom_ptr.file_index == self.index);
const data = self.shdrContents(atom_ptr.input_section_index);
const shdr = atom_ptr.inputShdr(elf_file);
const handle = elf_file.fileHandle(self.file_handle);
const data = try self.preadShdrContentsAlloc(gpa, handle, atom_ptr.input_section_index);
defer if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) gpa.free(data);
 
if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
switch (chdr.ch_type) {
@@ -892,31 +921,35 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)
},
else => @panic("TODO unhandled compression scheme"),
}
} else return gpa.dupe(u8, data);
}
}
 
pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 {
const raw = self.shdrContents(index);
const nmembers = @divExact(raw.len, @sizeOf(u32));
const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers];
return members;
return data;
}
 
pub fn asFile(self: *Object) File {
return .{ .object = self };
}
 
pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela {
const raw = self.shdrContents(shndx);
const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
}
 
pub fn getString(self: Object, off: u32) [:0]const u8 {
assert(off < self.strtab.items.len);
return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
}
 
/// Caller owns the memory.
fn preadShdrContentsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, index: u32) ![]u8 {
assert(index < self.shdrs.items.len);
const offset = if (self.archive) |ar| ar.offset else 0;
const shdr = self.shdrs.items[index];
return Elf.preadAllAlloc(allocator, handle, offset + shdr.sh_offset, shdr.sh_size);
}
 
/// Caller owns the memory.
fn preadRelocsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela {
const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);
const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
}
 
pub fn format(
self: *Object,
comptime unused_fmt_string: []const u8,
@@ -1053,7 +1086,7 @@ fn formatComdatGroups(
const cg_owner = elf_file.comdatGroupOwner(cg.owner);
if (cg_owner.file != object.index) continue;
try writer.print(" COMDAT({d})\n", .{cg_index});
const cg_members = object.comdatGroupMembers(cg.shndx);
const cg_members = cg.comdatGroupMembers(elf_file);
for (cg_members) |shndx| {
const atom_index = object.atoms.items[shndx];
const atom = elf_file.atom(atom_index) orelse continue;
@@ -1074,40 +1107,17 @@ fn formatPath(
) !void {
_ = unused_fmt_string;
_ = options;
if (object.archive) |path| {
try writer.writeAll(path);
if (object.archive) |ar| {
try writer.writeAll(ar.path);
try writer.writeByte('(');
try writer.writeAll(object.path);
try writer.writeByte(')');
} else try writer.writeAll(object.path);
}
 
pub const ElfShdr = struct {
sh_name: u32,
sh_type: u32,
sh_flags: u64,
sh_addr: u64,
sh_offset: usize,
sh_size: usize,
sh_link: u32,
sh_info: u32,
sh_addralign: u64,
sh_entsize: u64,
 
pub fn fromElf64Shdr(shdr: elf.Elf64_Shdr) error{Overflow}!ElfShdr {
return .{
.sh_name = shdr.sh_name,
.sh_type = shdr.sh_type,
.sh_flags = shdr.sh_flags,
.sh_addr = shdr.sh_addr,
.sh_offset = math.cast(usize, shdr.sh_offset) orelse return error.Overflow,
.sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow,
.sh_link = shdr.sh_link,
.sh_info = shdr.sh_info,
.sh_addralign = shdr.sh_addralign,
.sh_entsize = shdr.sh_entsize,
};
}
const InArchive = struct {
path: []const u8,
offset: u64,
};
 
const Object = @This();
 
src/link/Elf/SharedObject.zig added: 348, removed: 290, total 58
@@ -1,22 +1,18 @@
path: []const u8,
data: []const u8,
index: File.Index,
 
header: ?elf.Elf64_Ehdr = null,
shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},
shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
 
symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
/// Version symtab contains version strings of the symbols if present.
versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
verstrings: std.ArrayListUnmanaged(u32) = .{},
 
symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
aliases: ?std.ArrayListUnmanaged(u32) = null,
 
dynsym_sect_index: ?u16 = null,
dynamic_sect_index: ?u16 = null,
versym_sect_index: ?u16 = null,
verdef_sect_index: ?u16 = null,
dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .{},
 
needed: bool,
alive: bool,
@@ -36,23 +32,24 @@ pub fn isSharedObject(path: []const u8) !bool {
 
pub fn deinit(self: *SharedObject, allocator: Allocator) void {
allocator.free(self.path);
allocator.free(self.data);
self.shdrs.deinit(allocator);
self.symtab.deinit(allocator);
self.strtab.deinit(allocator);
self.versyms.deinit(allocator);
self.verstrings.deinit(allocator);
self.symbols.deinit(allocator);
if (self.aliases) |*aliases| aliases.deinit(allocator);
self.shdrs.deinit(allocator);
self.dynamic_table.deinit(allocator);
}
 
pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
var stream = std.io.fixedBufferStream(self.data);
const reader = stream.reader();
const file_size = (try handle.stat()).size;
 
self.header = try reader.readStruct(elf.Elf64_Ehdr);
const header_buffer = try Elf.preadAllAlloc(gpa, handle, 0, @sizeOf(elf.Elf64_Ehdr));
defer gpa.free(header_buffer);
self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;
 
const target = elf_file.base.comp.root_mod.resolved_target.result;
if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {
@@ -64,9 +61,10 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
return error.InvalidCpuArch;
}
 
if (self.data.len < self.header.?.e_shoff or
self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr))
{
const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
const shnum = std.math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
if (file_size < shoff or file_size < shoff + shsize) {
try elf_file.reportParseError2(
self.index,
"corrupted header: section header table extends past the end of file",
@@ -75,45 +73,84 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
return error.MalformedObject;
}
 
const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, shoff, shsize);
defer gpa.free(shdrs_buffer);
const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
try self.shdrs.appendUnalignedSlice(gpa, shdrs);
 
const shdrs = @as(
[*]align(1) const elf.Elf64_Shdr,
@ptrCast(self.data.ptr + shoff),
)[0..self.header.?.e_shnum];
try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
 
for (shdrs, 0..) |shdr, i| {
var dynsym_sect_index: ?u32 = null;
var dynamic_sect_index: ?u32 = null;
var versym_sect_index: ?u32 = null;
var verdef_sect_index: ?u32 = null;
for (self.shdrs.items, 0..) |shdr, i| {
if (shdr.sh_type != elf.SHT_NOBITS) {
if (self.data.len < shdr.sh_offset or self.data.len < shdr.sh_offset + shdr.sh_size) {
if (file_size < shdr.sh_offset or file_size < shdr.sh_offset + shdr.sh_size) {
try elf_file.reportParseError2(self.index, "corrupted section header", .{});
return error.MalformedObject;
}
}
self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
switch (shdr.sh_type) {
elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)),
elf.SHT_DYNAMIC => self.dynamic_sect_index = @as(u16, @intCast(i)),
elf.SHT_GNU_VERSYM => self.versym_sect_index = @as(u16, @intCast(i)),
elf.SHT_GNU_VERDEF => self.verdef_sect_index = @as(u16, @intCast(i)),
elf.SHT_DYNSYM => dynsym_sect_index = @intCast(i),
elf.SHT_DYNAMIC => dynamic_sect_index = @intCast(i),
elf.SHT_GNU_VERSYM => versym_sect_index = @intCast(i),
elf.SHT_GNU_VERDEF => verdef_sect_index = @intCast(i),
else => {},
}
}
 
try self.parseVersions(elf_file);
if (dynamic_sect_index) |index| {
const shdr = self.shdrs.items[index];
const raw = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
defer gpa.free(raw);
const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn));
const dyntab = @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num];
try self.dynamic_table.appendUnalignedSlice(gpa, dyntab);
}
 
const symtab = if (dynsym_sect_index) |index| blk: {
const shdr = self.shdrs.items[index];
const buffer = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
const nsyms = @divExact(buffer.len, @sizeOf(elf.Elf64_Sym));
break :blk @as([*]align(1) const elf.Elf64_Sym, @ptrCast(buffer.ptr))[0..nsyms];
} else &[0]elf.Elf64_Sym{};
defer gpa.free(symtab);
 
const strtab = if (dynsym_sect_index) |index| blk: {
const symtab_shdr = self.shdrs.items[index];
const shdr = self.shdrs.items[symtab_shdr.sh_link];
const buffer = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
break :blk buffer;
} else &[0]u8{};
defer gpa.free(strtab);
 
try self.parseVersions(elf_file, handle, .{
.symtab = symtab,
.verdef_sect_index = verdef_sect_index,
.versym_sect_index = versym_sect_index,
});
 
try self.initSymtab(elf_file, .{
.symtab = symtab,
.strtab = strtab,
});
}
 
fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
fn parseVersions(self: *SharedObject, elf_file: *Elf, handle: std.fs.File, opts: struct {
symtab: []align(1) const elf.Elf64_Sym,
verdef_sect_index: ?u32,
versym_sect_index: ?u32,
}) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const symtab = self.getSymtabRaw();
 
try self.verstrings.resize(gpa, 2);
self.verstrings.items[elf.VER_NDX_LOCAL] = 0;
self.verstrings.items[elf.VER_NDX_GLOBAL] = 0;
 
if (self.verdef_sect_index) |shndx| {
const verdefs = self.shdrContents(shndx);
if (opts.verdef_sect_index) |shndx| {
const shdr = self.shdrs.items[shndx];
const verdefs = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
defer gpa.free(verdefs);
const nverdefs = self.verdefNum();
try self.verstrings.resize(gpa, self.verstrings.items.len + nverdefs);
 
@@ -131,10 +168,12 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
}
}
 
try self.versyms.ensureTotalCapacityPrecise(gpa, symtab.len);
try self.versyms.ensureTotalCapacityPrecise(gpa, opts.symtab.len);
 
if (self.versym_sect_index) |shndx| {
const versyms_raw = self.shdrContents(shndx);
if (opts.versym_sect_index) |shndx| {
const shdr = self.shdrs.items[shndx];
const versyms_raw = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size);
defer gpa.free(versyms_raw);
const nversyms = @divExact(versyms_raw.len, @sizeOf(elf.Elf64_Versym));
const versyms = @as([*]align(1) const elf.Elf64_Versym, @ptrCast(versyms_raw.ptr))[0..nversyms];
for (versyms) |ver| {
@@ -144,22 +183,23 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
ver;
self.versyms.appendAssumeCapacity(normalized_ver);
}
} else for (0..symtab.len) |_| {
} else for (0..opts.symtab.len) |_| {
self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL);
}
}
 
pub fn init(self: *SharedObject, elf_file: *Elf) !void {
fn initSymtab(self: *SharedObject, elf_file: *Elf, opts: struct {
symtab: []align(1) const elf.Elf64_Sym,
strtab: []const u8,
}) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const symtab = self.getSymtabRaw();
const strtab = self.getStrtabRaw();
 
try self.strtab.appendSlice(gpa, strtab);
try self.symtab.ensureTotalCapacityPrecise(gpa, symtab.len);
try self.symbols.ensureTotalCapacityPrecise(gpa, symtab.len);
try self.strtab.appendSlice(gpa, opts.strtab);
try self.symtab.ensureTotalCapacityPrecise(gpa, opts.symtab.len);
try self.symbols.ensureTotalCapacityPrecise(gpa, opts.symtab.len);
 
for (symtab, 0..) |sym, i| {
for (opts.symtab, 0..) |sym, i| {
const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0;
const name = self.getString(sym.st_name);
// We need to garble up the name so that we don't pick this symbol
@@ -250,11 +290,6 @@ pub fn writeSymtab(self: SharedObject, elf_file: *Elf) void {
}
}
 
pub fn shdrContents(self: SharedObject, index: u16) []const u8 {
const shdr = self.shdrs.items[index];
return self.data[shdr.sh_offset..][0..shdr.sh_size];
}
 
pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 {
const off = self.verstrings.items[index & elf.VERSYM_VERSION];
return self.getString(off);
@@ -264,16 +299,8 @@ pub fn asFile(self: *SharedObject) File {
return .{ .shared_object = self };
}
 
fn dynamicTable(self: *SharedObject) []align(1) const elf.Elf64_Dyn {
const shndx = self.dynamic_sect_index orelse return &[0]elf.Elf64_Dyn{};
const raw = self.shdrContents(shndx);
const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn));
return @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num];
}
 
fn verdefNum(self: *SharedObject) u32 {
const entries = self.dynamicTable();
for (entries) |entry| switch (entry.d_tag) {
for (self.dynamic_table.items) |entry| switch (entry.d_tag) {
elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)),
else => {},
};
@@ -281,8 +308,7 @@ fn verdefNum(self: *SharedObject) u32 {
}
 
pub fn soname(self: *SharedObject) []const u8 {
const entries = self.dynamicTable();
for (entries) |entry| switch (entry.d_tag) {
for (self.dynamic_table.items) |entry| switch (entry.d_tag) {
elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))),
else => {},
};
@@ -342,20 +368,6 @@ pub fn getString(self: SharedObject, off: u32) [:0]const u8 {
return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
}
 
pub fn getSymtabRaw(self: SharedObject) []align(1) const elf.Elf64_Sym {
const index = self.dynsym_sect_index orelse return &[0]elf.Elf64_Sym{};
const raw_symtab = self.shdrContents(index);
const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym));
const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
return symtab;
}
 
pub fn getStrtabRaw(self: SharedObject) []const u8 {
const index = self.dynsym_sect_index orelse return &[0]u8{};
const shdr = self.shdrs.items[index];
return self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
}
 
pub fn format(
self: SharedObject,
comptime unused_fmt_string: []const u8,
@@ -407,6 +419,5 @@ const mem = std.mem;
 
const Allocator = mem.Allocator;
const Elf = @import("../Elf.zig");
const ElfShdr = @import("Object.zig").ElfShdr;
const File = @import("file.zig").File;
const Symbol = @import("Symbol.zig");
 
src/link/Elf/ZigObject.zig added: 348, removed: 290, total 58
@@ -305,19 +305,16 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
}
 
/// TODO actually create fake input shdrs and return that instead.
pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr {
pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.Elf64_Shdr {
_ = self;
const shdr = shdr: {
const atom = elf_file.atom(atom_index) orelse break :shdr Elf.null_shdr;
const shndx = atom.outputShndx() orelse break :shdr Elf.null_shdr;
var shdr = elf_file.shdrs.items[shndx];
shdr.sh_addr = 0;
shdr.sh_offset = 0;
shdr.sh_size = atom.size;
shdr.sh_addralign = atom.alignment.toByteUnits(1);
break :shdr shdr;
};
return Object.ElfShdr.fromElf64Shdr(shdr) catch unreachable;
const atom = elf_file.atom(atom_index) orelse return Elf.null_shdr;
const shndx = atom.outputShndx() orelse return Elf.null_shdr;
var shdr = elf_file.shdrs.items[shndx];
shdr.sh_addr = 0;
shdr.sh_offset = 0;
shdr.sh_size = atom.size;
shdr.sh_addralign = atom.alignment.toByteUnits(1);
return shdr;
}
 
pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void {
@@ -525,7 +522,7 @@ pub fn writeAr(self: ZigObject, writer: anytype) !void {
.{ .name = name }
else
.{ .name_off = self.output_ar_state.name_off },
.size = @intCast(self.data.items.len),
.size = self.data.items.len,
});
try writer.writeAll(mem.asBytes(&hdr));
try writer.writeAll(self.data.items);
 
src/link/Elf/eh_frame.zig added: 348, removed: 290, total 58
@@ -5,7 +5,6 @@ pub const Fde = struct {
cie_index: u32,
rel_index: u32 = 0,
rel_num: u32 = 0,
rel_section_index: u32 = 0,
input_section_index: u32 = 0,
file_index: u32 = 0,
alive: bool = true,
@@ -20,10 +19,9 @@ pub const Fde = struct {
return base + fde.out_offset;
}
 
pub fn data(fde: Fde, elf_file: *Elf) []const u8 {
pub fn data(fde: Fde, elf_file: *Elf) []u8 {
const object = elf_file.file(fde.file_index).?.object;
const contents = object.shdrContents(fde.input_section_index);
return contents[fde.offset..][0..fde.calcSize()];
return object.eh_frame_data.items[fde.offset..][0..fde.calcSize()];
}
 
pub fn cie(fde: Fde, elf_file: *Elf) Cie {
@@ -50,7 +48,7 @@ pub const Fde = struct {
 
pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
const object = elf_file.file(fde.file_index).?.object;
return object.getRelocs(fde.rel_section_index)[fde.rel_index..][0..fde.rel_num];
return object.relocs.items[fde.rel_index..][0..fde.rel_num];
}
 
pub fn format(
@@ -106,7 +104,6 @@ pub const Cie = struct {
size: usize,
rel_index: u32 = 0,
rel_num: u32 = 0,
rel_section_index: u32 = 0,
input_section_index: u32 = 0,
file_index: u32 = 0,
/// Includes 4byte size cell.
@@ -121,10 +118,9 @@ pub const Cie = struct {
return base + cie.out_offset;
}
 
pub fn data(cie: Cie, elf_file: *Elf) []const u8 {
pub fn data(cie: Cie, elf_file: *Elf) []u8 {
const object = elf_file.file(cie.file_index).?.object;
const contents = object.shdrContents(cie.input_section_index);
return contents[cie.offset..][0..cie.calcSize()];
return object.eh_frame_data.items[cie.offset..][0..cie.calcSize()];
}
 
pub fn calcSize(cie: Cie) usize {
@@ -133,7 +129,7 @@ pub const Cie = struct {
 
pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
const object = elf_file.file(cie.file_index).?.object;
return object.getRelocs(cie.rel_section_index)[cie.rel_index..][0..cie.rel_num];
return object.relocs.items[cie.rel_index..][0..cie.rel_num];
}
 
pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool {
@@ -330,9 +326,6 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file:
}
 
pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
 
relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr});
 
for (elf_file.objects.items) |index| {
@@ -341,8 +334,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
for (object.cies.items) |cie| {
if (!cie.alive) continue;
 
const contents = try gpa.dupe(u8, cie.data(elf_file));
defer gpa.free(contents);
const contents = cie.data(elf_file);
 
for (cie.relocs(elf_file)) |rel| {
const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
@@ -359,8 +351,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
for (object.fdes.items) |fde| {
if (!fde.alive) continue;
 
const contents = try gpa.dupe(u8, fde.data(elf_file));
defer gpa.free(contents);
const contents = fde.data(elf_file);
 
std.mem.writeInt(
i32,
@@ -382,9 +373,6 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
}
 
pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
 
for (elf_file.objects.items) |index| {
const object = elf_file.file(index).?.object;
 
@@ -400,8 +388,7 @@ pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {
for (object.fdes.items) |fde| {
if (!fde.alive) continue;
 
const contents = try gpa.dupe(u8, fde.data(elf_file));
defer gpa.free(contents);
const contents = fde.data(elf_file);
 
std.mem.writeInt(
i32,
 
src/link/Elf/file.zig added: 348, removed: 290, total 58
@@ -162,18 +162,18 @@ pub const File = union(enum) {
state.name_off = try ar_strtab.insert(allocator, path);
}
 
pub fn updateArSize(file: File) void {
pub fn updateArSize(file: File, elf_file: *Elf) !void {
return switch (file) {
.zig_object => |x| x.updateArSize(),
.object => |x| x.updateArSize(),
.object => |x| x.updateArSize(elf_file),
inline else => unreachable,
};
}
 
pub fn writeAr(file: File, writer: anytype) !void {
pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void {
return switch (file) {
.zig_object => |x| x.writeAr(writer),
.object => |x| x.writeAr(writer),
.object => |x| x.writeAr(elf_file, writer),
inline else => unreachable,
};
}
@@ -187,6 +187,9 @@ pub const File = union(enum) {
object: Object,
shared_object: SharedObject,
};
 
pub const Handle = std.fs.File;
pub const HandleIndex = Index;
};
 
const std = @import("std");
 
src/link/Elf/synthetic_sections.zig added: 348, removed: 290, total 58
@@ -1582,15 +1582,14 @@ pub const ComdatGroupSection = struct {
 
pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize {
const cg = elf_file.comdatGroup(cgs.cg_index);
const object = cgs.file(elf_file).?.object;
const members = object.comdatGroupMembers(cg.shndx);
const members = cg.comdatGroupMembers(elf_file);
return (members.len + 1) * @sizeOf(u32);
}
 
pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void {
const cg = elf_file.comdatGroup(cgs.cg_index);
const object = cgs.file(elf_file).?.object;
const members = object.comdatGroupMembers(cg.shndx);
const members = cg.comdatGroupMembers(elf_file);
try writer.writeInt(u32, elf.GRP_COMDAT, .little);
for (members) |shndx| {
const shdr = object.shdrs.items[shndx];