Merge pull request #18905 from ziglang/elf-mem-pressure
elf: reduce memory pressure
This commit is contained in:
@@ -593,6 +593,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/gc.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/relocatable.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
|
||||
|
||||
639
src/link/Elf.zig
639
src/link/Elf.zig
@@ -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),
|
||||
@@ -561,7 +570,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn allocatedSize(self: *Elf, start: u64) u64 {
|
||||
pub fn allocatedSize(self: *Elf, start: u64) u64 {
|
||||
if (start == 0) return 0;
|
||||
var min_pos: u64 = std.math.maxInt(u64);
|
||||
if (self.shdr_table_offset) |off| {
|
||||
@@ -588,7 +597,7 @@ fn allocatedVirtualSize(self: *Elf, start: u64) u64 {
|
||||
return min_pos - start;
|
||||
}
|
||||
|
||||
fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
|
||||
pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
|
||||
var start: u64 = 0;
|
||||
while (self.detectAllocCollision(start, object_size)) |item_end| {
|
||||
start = mem.alignForward(u64, item_end, min_alignment);
|
||||
@@ -1066,6 +1075,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
|
||||
// --verbose-link
|
||||
if (comp.verbose_link) try self.dumpArgv(comp);
|
||||
|
||||
if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
|
||||
if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
|
||||
if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
|
||||
|
||||
const csu = try CsuObjects.init(arena, comp);
|
||||
const compiler_rt_path: ?[]const u8 = blk: {
|
||||
if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
|
||||
@@ -1073,10 +1086,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
|
||||
break :blk null;
|
||||
};
|
||||
|
||||
if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
|
||||
if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path);
|
||||
if (self.base.isObject()) return self.flushObject(comp, module_obj_path);
|
||||
|
||||
// Here we will parse input positional and library files (if referenced).
|
||||
// This will roughly match in any linker backend we support.
|
||||
var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
|
||||
@@ -1240,16 +1249,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
|
||||
// Init all objects
|
||||
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;
|
||||
|
||||
// Dedup shared objects
|
||||
{
|
||||
var seen_dsos = std.StringHashMap(void).init(gpa);
|
||||
@@ -1382,222 +1381,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
}
|
||||
|
||||
pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
|
||||
const gpa = comp.gpa;
|
||||
|
||||
var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
|
||||
defer positionals.deinit();
|
||||
|
||||
try positionals.ensureUnusedCapacity(comp.objects.len);
|
||||
positionals.appendSliceAssumeCapacity(comp.objects);
|
||||
|
||||
// This is a set of object files emitted by clang in a single `build-exe` invocation.
|
||||
// For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
|
||||
// in this set.
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
try positionals.append(.{ .path = key.status.success.object_path });
|
||||
}
|
||||
|
||||
if (module_obj_path) |path| try positionals.append(.{ .path = path });
|
||||
|
||||
for (positionals.items) |obj| {
|
||||
self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
|
||||
error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
|
||||
else => |e| try self.reportParseError(
|
||||
obj.path,
|
||||
"unexpected error: parsing input file failed with error {s}",
|
||||
.{@errorName(e)},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
|
||||
// First, we flush relocatable object file generated with our backends.
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
zig_object.resolveSymbols(self);
|
||||
zig_object.claimUnresolvedObject(self);
|
||||
|
||||
try self.initSymtab();
|
||||
try self.initShStrtab();
|
||||
try self.sortShdrs();
|
||||
try zig_object.addAtomsToRelaSections(self);
|
||||
try self.updateSectionSizesObject();
|
||||
|
||||
try self.allocateAllocSectionsObject();
|
||||
try self.allocateNonAllocSections();
|
||||
|
||||
if (build_options.enable_logging) {
|
||||
state_log.debug("{}", .{self.dumpState()});
|
||||
}
|
||||
|
||||
try self.writeSyntheticSectionsObject();
|
||||
try self.writeShdrTable();
|
||||
try self.writeElfHeader();
|
||||
|
||||
// TODO we can avoid reading in the file contents we just wrote if we give the linker
|
||||
// ability to write directly to a buffer.
|
||||
try zig_object.readFileContents(self);
|
||||
}
|
||||
|
||||
var files = std.ArrayList(File.Index).init(gpa);
|
||||
defer files.deinit();
|
||||
try files.ensureTotalCapacityPrecise(self.objects.items.len + 1);
|
||||
if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
|
||||
for (self.objects.items) |index| files.appendAssumeCapacity(index);
|
||||
|
||||
// Update ar symtab from parsed objects
|
||||
var ar_symtab: Archive.ArSymtab = .{};
|
||||
defer ar_symtab.deinit(gpa);
|
||||
|
||||
for (files.items) |index| {
|
||||
try self.file(index).?.updateArSymtab(&ar_symtab, self);
|
||||
}
|
||||
|
||||
ar_symtab.sort();
|
||||
|
||||
// Save object paths in filenames strtab.
|
||||
var ar_strtab: Archive.ArStrtab = .{};
|
||||
defer ar_strtab.deinit(gpa);
|
||||
|
||||
for (files.items) |index| {
|
||||
const file_ptr = self.file(index).?;
|
||||
try file_ptr.updateArStrtab(gpa, &ar_strtab);
|
||||
file_ptr.updateArSize();
|
||||
}
|
||||
|
||||
// Update file offsets of contributing objects.
|
||||
const total_size: usize = blk: {
|
||||
var pos: usize = elf.ARMAG.len;
|
||||
pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64);
|
||||
|
||||
if (ar_strtab.size() > 0) {
|
||||
pos = mem.alignForward(usize, pos, 2);
|
||||
pos += @sizeOf(elf.ar_hdr) + ar_strtab.size();
|
||||
}
|
||||
|
||||
for (files.items) |index| {
|
||||
const file_ptr = self.file(index).?;
|
||||
const state = switch (file_ptr) {
|
||||
.zig_object => |x| &x.output_ar_state,
|
||||
.object => |x| &x.output_ar_state,
|
||||
else => unreachable,
|
||||
};
|
||||
pos = mem.alignForward(usize, pos, 2);
|
||||
state.file_off = pos;
|
||||
pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow);
|
||||
}
|
||||
|
||||
break :blk pos;
|
||||
};
|
||||
|
||||
if (build_options.enable_logging) {
|
||||
state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(self)});
|
||||
state_log.debug("ar_strtab\n{}\n", .{ar_strtab});
|
||||
}
|
||||
|
||||
var buffer = std.ArrayList(u8).init(gpa);
|
||||
defer buffer.deinit();
|
||||
try buffer.ensureTotalCapacityPrecise(total_size);
|
||||
|
||||
// Write magic
|
||||
try buffer.writer().writeAll(elf.ARMAG);
|
||||
|
||||
// Write symtab
|
||||
try ar_symtab.write(.p64, self, buffer.writer());
|
||||
|
||||
// Write strtab
|
||||
if (ar_strtab.size() > 0) {
|
||||
if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
|
||||
try ar_strtab.write(buffer.writer());
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
assert(buffer.items.len == total_size);
|
||||
|
||||
try self.base.file.?.setEndPos(total_size);
|
||||
try self.base.file.?.pwriteAll(buffer.items, 0);
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
}
|
||||
|
||||
pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
|
||||
var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
|
||||
defer positionals.deinit();
|
||||
try positionals.ensureUnusedCapacity(comp.objects.len);
|
||||
positionals.appendSliceAssumeCapacity(comp.objects);
|
||||
|
||||
// This is a set of object files emitted by clang in a single `build-exe` invocation.
|
||||
// For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
|
||||
// in this set.
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
try positionals.append(.{ .path = key.status.success.object_path });
|
||||
}
|
||||
|
||||
if (module_obj_path) |path| try positionals.append(.{ .path = path });
|
||||
|
||||
for (positionals.items) |obj| {
|
||||
self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
|
||||
error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
|
||||
else => |e| try self.reportParseError(
|
||||
obj.path,
|
||||
"unexpected error: parsing input file failed with error {s}",
|
||||
.{@errorName(e)},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
|
||||
// Init all objects
|
||||
for (self.objects.items) |index| {
|
||||
try self.file(index).?.object.init(self);
|
||||
}
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
|
||||
// Now, we are ready to resolve the symbols across all input files.
|
||||
// We will first resolve the files in the ZigObject, next in the parsed
|
||||
// input Object files.
|
||||
self.resolveSymbols();
|
||||
self.markEhFrameAtomsDead();
|
||||
self.claimUnresolvedObject();
|
||||
|
||||
try self.initSectionsObject();
|
||||
try self.sortShdrs();
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
try zig_object.addAtomsToRelaSections(self);
|
||||
}
|
||||
for (self.objects.items) |index| {
|
||||
const object = self.file(index).?.object;
|
||||
try object.addAtomsToOutputSections(self);
|
||||
try object.addAtomsToRelaSections(self);
|
||||
}
|
||||
try self.updateSectionSizesObject();
|
||||
|
||||
try self.allocateAllocSectionsObject();
|
||||
try self.allocateNonAllocSections();
|
||||
self.allocateAtoms();
|
||||
|
||||
if (build_options.enable_logging) {
|
||||
state_log.debug("{}", .{self.dumpState()});
|
||||
}
|
||||
|
||||
try self.writeAtomsObject();
|
||||
try self.writeSyntheticSectionsObject();
|
||||
try self.writeShdrTable();
|
||||
try self.writeElfHeader();
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
}
|
||||
|
||||
/// --verbose-link output
|
||||
fn dumpArgv(self: *Elf, comp: *Compilation) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
@@ -1861,7 +1644,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
const ParseError = error{
|
||||
pub const ParseError = error{
|
||||
MalformedObject,
|
||||
MalformedArchive,
|
||||
InvalidCpuArch,
|
||||
@@ -1872,9 +1655,10 @@ const ParseError = error{
|
||||
FileSystem,
|
||||
NotSupported,
|
||||
InvalidCharacter,
|
||||
UnknownFileType,
|
||||
} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
|
||||
|
||||
fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
|
||||
pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
if (try Object.isObject(path)) {
|
||||
@@ -1902,13 +1686,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 +1706,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 +1732,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 +1745,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 {
|
||||
@@ -2084,7 +1867,7 @@ fn accessLibPath(
|
||||
/// 4. Reset state of all resolved globals since we will redo this bit on the pruned set.
|
||||
/// 5. Remove references to dead objects/shared objects
|
||||
/// 6. Re-run symbol resolution on pruned objects and shared objects sets.
|
||||
fn resolveSymbols(self: *Elf) void {
|
||||
pub fn resolveSymbols(self: *Elf) void {
|
||||
// Resolve symbols in the ZigObject. For now, we assume that it's always live.
|
||||
if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self);
|
||||
// Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
|
||||
@@ -2135,7 +1918,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;
|
||||
@@ -2168,7 +1951,7 @@ fn markLive(self: *Elf) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn markEhFrameAtomsDead(self: *Elf) void {
|
||||
pub fn markEhFrameAtomsDead(self: *Elf) void {
|
||||
for (self.objects.items) |index| {
|
||||
const file_ptr = self.file(index).?;
|
||||
if (!file_ptr.isAlive()) continue;
|
||||
@@ -2234,15 +2017,6 @@ fn claimUnresolved(self: *Elf) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn claimUnresolvedObject(self: *Elf) void {
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
zig_object.claimUnresolvedObject(self);
|
||||
}
|
||||
for (self.objects.items) |index| {
|
||||
self.file(index).?.object.claimUnresolvedObject(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// In scanRelocs we will go over all live atoms and scan their relocs.
|
||||
/// This will help us work out what synthetics to emit, GOT indirection, etc.
|
||||
/// This is also the point where we will report undefined symbols for any
|
||||
@@ -2980,7 +2754,7 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64)
|
||||
}
|
||||
}
|
||||
|
||||
fn writeShdrTable(self: *Elf) !void {
|
||||
pub fn writeShdrTable(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
const target_endian = target.cpu.arch.endian();
|
||||
@@ -3077,7 +2851,7 @@ fn writePhdrTable(self: *Elf) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn writeElfHeader(self: *Elf) !void {
|
||||
pub fn writeElfHeader(self: *Elf) !void {
|
||||
const comp = self.base.comp;
|
||||
if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
|
||||
|
||||
@@ -3653,61 +3427,7 @@ fn initSyntheticSections(self: *Elf) !void {
|
||||
try self.initShStrtab();
|
||||
}
|
||||
|
||||
fn initSectionsObject(self: *Elf) !void {
|
||||
const ptr_size = self.ptrWidthBytes();
|
||||
|
||||
for (self.objects.items) |index| {
|
||||
const object = self.file(index).?.object;
|
||||
try object.initOutputSections(self);
|
||||
try object.initRelaSections(self);
|
||||
}
|
||||
|
||||
const needs_eh_frame = for (self.objects.items) |index| {
|
||||
if (self.file(index).?.object.cies.items.len > 0) break true;
|
||||
} else false;
|
||||
if (needs_eh_frame) {
|
||||
self.eh_frame_section_index = try self.addSection(.{
|
||||
.name = ".eh_frame",
|
||||
.type = elf.SHT_PROGBITS,
|
||||
.flags = elf.SHF_ALLOC,
|
||||
.addralign = ptr_size,
|
||||
.offset = std.math.maxInt(u64),
|
||||
});
|
||||
self.eh_frame_rela_section_index = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?);
|
||||
}
|
||||
|
||||
try self.initComdatGroups();
|
||||
try self.initSymtab();
|
||||
try self.initShStrtab();
|
||||
}
|
||||
|
||||
fn initComdatGroups(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
|
||||
for (self.objects.items) |index| {
|
||||
const object = self.file(index).?.object;
|
||||
|
||||
for (object.comdat_groups.items) |cg_index| {
|
||||
const cg = self.comdatGroup(cg_index);
|
||||
const cg_owner = self.comdatGroupOwner(cg.owner);
|
||||
if (cg_owner.file != index) continue;
|
||||
|
||||
const cg_sec = try self.comdat_group_sections.addOne(gpa);
|
||||
cg_sec.* = .{
|
||||
.shndx = try self.addSection(.{
|
||||
.name = ".group",
|
||||
.type = elf.SHT_GROUP,
|
||||
.entsize = @sizeOf(u32),
|
||||
.addralign = @alignOf(u32),
|
||||
.offset = std.math.maxInt(u64),
|
||||
}),
|
||||
.cg_index = cg_index,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn initSymtab(self: *Elf) !void {
|
||||
pub fn initSymtab(self: *Elf) !void {
|
||||
const small_ptr = switch (self.ptr_width) {
|
||||
.p32 => true,
|
||||
.p64 => false,
|
||||
@@ -3732,7 +3452,7 @@ fn initSymtab(self: *Elf) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn initShStrtab(self: *Elf) !void {
|
||||
pub fn initShStrtab(self: *Elf) !void {
|
||||
if (self.shstrtab_section_index == null) {
|
||||
self.shstrtab_section_index = try self.addSection(.{
|
||||
.name = ".shstrtab",
|
||||
@@ -4038,7 +3758,7 @@ fn shdrRank(self: *Elf, shndx: u16) u8 {
|
||||
}
|
||||
}
|
||||
|
||||
fn sortShdrs(self: *Elf) !void {
|
||||
pub fn sortShdrs(self: *Elf) !void {
|
||||
const Entry = struct {
|
||||
shndx: u16,
|
||||
|
||||
@@ -4350,58 +4070,7 @@ fn updateSectionSizes(self: *Elf) !void {
|
||||
self.updateShStrtabSize();
|
||||
}
|
||||
|
||||
fn updateSectionSizesObject(self: *Elf) !void {
|
||||
for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
|
||||
const shdr = &self.shdrs.items[shndx];
|
||||
for (atom_list.items) |atom_index| {
|
||||
const atom_ptr = self.atom(atom_index) orelse continue;
|
||||
if (!atom_ptr.flags.alive) continue;
|
||||
const offset = atom_ptr.alignment.forward(shdr.sh_size);
|
||||
const padding = offset - shdr.sh_size;
|
||||
atom_ptr.value = offset;
|
||||
shdr.sh_size += padding + atom_ptr.size;
|
||||
shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1));
|
||||
}
|
||||
}
|
||||
|
||||
for (self.output_rela_sections.values()) |sec| {
|
||||
const shdr = &self.shdrs.items[sec.shndx];
|
||||
for (sec.atom_list.items) |atom_index| {
|
||||
const atom_ptr = self.atom(atom_index) orelse continue;
|
||||
if (!atom_ptr.flags.alive) continue;
|
||||
const relocs = atom_ptr.relocs(self);
|
||||
shdr.sh_size += shdr.sh_entsize * relocs.len;
|
||||
}
|
||||
|
||||
if (shdr.sh_size == 0) shdr.sh_offset = 0;
|
||||
}
|
||||
|
||||
if (self.eh_frame_section_index) |index| {
|
||||
self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
|
||||
}
|
||||
if (self.eh_frame_rela_section_index) |index| {
|
||||
const shdr = &self.shdrs.items[index];
|
||||
shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize;
|
||||
}
|
||||
|
||||
try self.updateSymtabSize();
|
||||
self.updateComdatGroupsSizes();
|
||||
self.updateShStrtabSize();
|
||||
}
|
||||
|
||||
fn updateComdatGroupsSizes(self: *Elf) void {
|
||||
for (self.comdat_group_sections.items) |cg| {
|
||||
const shdr = &self.shdrs.items[cg.shndx];
|
||||
shdr.sh_size = cg.size(self);
|
||||
shdr.sh_link = self.symtab_section_index.?;
|
||||
|
||||
const sym = self.symbol(cg.symbol(self));
|
||||
shdr.sh_info = sym.outputSymtabIndex(self) orelse
|
||||
self.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
|
||||
}
|
||||
}
|
||||
|
||||
fn updateShStrtabSize(self: *Elf) void {
|
||||
pub fn updateShStrtabSize(self: *Elf) void {
|
||||
if (self.shstrtab_section_index) |index| {
|
||||
self.shdrs.items[index].sh_size = self.shstrtab.items.len;
|
||||
}
|
||||
@@ -4486,7 +4155,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
|
||||
|
||||
/// Allocates alloc sections and creates load segments for sections
|
||||
/// extracted from input object files.
|
||||
fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
|
||||
pub fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
|
||||
// We use this struct to track maximum alignment of all TLS sections.
|
||||
// According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,
|
||||
// in-file offsets have to be aligned against the start of TLS program header.
|
||||
@@ -4633,27 +4302,8 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates alloc sections when merging relocatable objects files together.
|
||||
fn allocateAllocSectionsObject(self: *Elf) !void {
|
||||
for (self.shdrs.items) |*shdr| {
|
||||
if (shdr.sh_type == elf.SHT_NULL) continue;
|
||||
if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) {
|
||||
shdr.sh_offset = 0;
|
||||
continue;
|
||||
}
|
||||
const needed_size = shdr.sh_size;
|
||||
if (needed_size > self.allocatedSize(shdr.sh_offset)) {
|
||||
shdr.sh_size = 0;
|
||||
const new_offset = self.findFreeSpace(needed_size, shdr.sh_addralign);
|
||||
shdr.sh_offset = new_offset;
|
||||
shdr.sh_size = needed_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates non-alloc sections (debug info, symtabs, etc.).
|
||||
fn allocateNonAllocSections(self: *Elf) !void {
|
||||
pub fn allocateNonAllocSections(self: *Elf) !void {
|
||||
for (self.shdrs.items, 0..) |*shdr, shndx| {
|
||||
if (shdr.sh_type == elf.SHT_NULL) continue;
|
||||
if (shdr.sh_flags & elf.SHF_ALLOC != 0) continue;
|
||||
@@ -4752,7 +4402,7 @@ fn allocateSpecialPhdrs(self: *Elf) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn allocateAtoms(self: *Elf) void {
|
||||
pub fn allocateAtoms(self: *Elf) void {
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
zig_object.allocateTlvAtoms(self);
|
||||
}
|
||||
@@ -4849,76 +4499,7 @@ fn writeAtoms(self: *Elf) !void {
|
||||
try self.reportUndefinedSymbols(&undefs);
|
||||
}
|
||||
|
||||
fn writeAtomsObject(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
|
||||
// TODO iterate over `output_sections` directly
|
||||
for (self.shdrs.items, 0..) |shdr, shndx| {
|
||||
if (shdr.sh_type == elf.SHT_NULL) continue;
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) continue;
|
||||
|
||||
const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
|
||||
if (atom_list.items.len == 0) continue;
|
||||
|
||||
log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)});
|
||||
|
||||
// TODO really, really handle debug section separately
|
||||
const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
|
||||
const zig_object = self.zigObjectPtr().?;
|
||||
if (shndx == self.debug_info_section_index.?)
|
||||
break :blk zig_object.debug_info_section_zig_size;
|
||||
if (shndx == self.debug_abbrev_section_index.?)
|
||||
break :blk zig_object.debug_abbrev_section_zig_size;
|
||||
if (shndx == self.debug_str_section_index.?)
|
||||
break :blk zig_object.debug_str_section_zig_size;
|
||||
if (shndx == self.debug_aranges_section_index.?)
|
||||
break :blk zig_object.debug_aranges_section_zig_size;
|
||||
if (shndx == self.debug_line_section_index.?)
|
||||
break :blk zig_object.debug_line_section_zig_size;
|
||||
unreachable;
|
||||
} else 0;
|
||||
const sh_offset = shdr.sh_offset + base_offset;
|
||||
const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
|
||||
|
||||
const buffer = try gpa.alloc(u8, sh_size);
|
||||
defer gpa.free(buffer);
|
||||
const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and
|
||||
shdr.sh_flags & elf.SHF_EXECINSTR != 0)
|
||||
0xcc // int3
|
||||
else
|
||||
0;
|
||||
@memset(buffer, padding_byte);
|
||||
|
||||
for (atom_list.items) |atom_index| {
|
||||
const atom_ptr = self.atom(atom_index).?;
|
||||
assert(atom_ptr.flags.alive);
|
||||
|
||||
const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
|
||||
return error.Overflow;
|
||||
const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
|
||||
|
||||
log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{
|
||||
atom_index,
|
||||
sh_offset + offset,
|
||||
sh_offset + offset + size,
|
||||
});
|
||||
|
||||
// TODO decompress directly into provided buffer
|
||||
const out_code = buffer[offset..][0..size];
|
||||
const in_code = switch (atom_ptr.file(self).?) {
|
||||
.object => |x| try x.codeDecompressAlloc(self, atom_index),
|
||||
.zig_object => |x| try x.codeAlloc(self, atom_index),
|
||||
else => unreachable,
|
||||
};
|
||||
defer gpa.free(in_code);
|
||||
@memcpy(out_code, in_code);
|
||||
}
|
||||
|
||||
try self.base.file.?.pwriteAll(buffer, sh_offset);
|
||||
}
|
||||
}
|
||||
|
||||
fn updateSymtabSize(self: *Elf) !void {
|
||||
pub fn updateSymtabSize(self: *Elf) !void {
|
||||
var nlocals: u32 = 0;
|
||||
var nglobals: u32 = 0;
|
||||
var strsize: u32 = 0;
|
||||
@@ -5136,94 +4717,7 @@ fn writeSyntheticSections(self: *Elf) !void {
|
||||
try self.writeShStrtab();
|
||||
}
|
||||
|
||||
fn writeSyntheticSectionsObject(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
|
||||
for (self.output_rela_sections.values()) |sec| {
|
||||
if (sec.atom_list.items.len == 0) continue;
|
||||
|
||||
const shdr = self.shdrs.items[sec.shndx];
|
||||
|
||||
const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse
|
||||
return error.Overflow;
|
||||
var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
|
||||
defer relocs.deinit();
|
||||
|
||||
for (sec.atom_list.items) |atom_index| {
|
||||
const atom_ptr = self.atom(atom_index) orelse continue;
|
||||
if (!atom_ptr.flags.alive) continue;
|
||||
try atom_ptr.writeRelocs(self, &relocs);
|
||||
}
|
||||
assert(relocs.items.len == num_relocs);
|
||||
|
||||
const SortRelocs = struct {
|
||||
pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
|
||||
_ = ctx;
|
||||
return lhs.r_offset < rhs.r_offset;
|
||||
}
|
||||
};
|
||||
|
||||
mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
|
||||
|
||||
log.debug("writing {s} from 0x{x} to 0x{x}", .{
|
||||
self.getShString(shdr.sh_name),
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
});
|
||||
|
||||
try self.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
|
||||
}
|
||||
|
||||
if (self.eh_frame_section_index) |shndx| {
|
||||
const shdr = self.shdrs.items[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 eh_frame.writeEhFrameObject(self, buffer.writer());
|
||||
log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
});
|
||||
assert(buffer.items.len == sh_size);
|
||||
try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
|
||||
}
|
||||
if (self.eh_frame_rela_section_index) |shndx| {
|
||||
const shdr = self.shdrs.items[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 eh_frame.writeEhFrameRelocs(self, buffer.writer());
|
||||
assert(buffer.items.len == sh_size);
|
||||
log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
});
|
||||
try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
|
||||
}
|
||||
|
||||
try self.writeComdatGroups();
|
||||
try self.writeSymtab();
|
||||
try self.writeShStrtab();
|
||||
}
|
||||
|
||||
fn writeComdatGroups(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
for (self.comdat_group_sections.items) |cgs| {
|
||||
const shdr = self.shdrs.items[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(self, buffer.writer());
|
||||
assert(buffer.items.len == sh_size);
|
||||
log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
});
|
||||
try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
|
||||
}
|
||||
}
|
||||
|
||||
fn writeShStrtab(self: *Elf) !void {
|
||||
pub fn writeShStrtab(self: *Elf) !void {
|
||||
if (self.shstrtab_section_index) |index| {
|
||||
const shdr = self.shdrs.items[index];
|
||||
log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size });
|
||||
@@ -5231,7 +4725,7 @@ fn writeShStrtab(self: *Elf) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn writeSymtab(self: *Elf) !void {
|
||||
pub fn writeSymtab(self: *Elf) !void {
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
const gpa = self.base.comp.gpa;
|
||||
const symtab_shdr = self.shdrs.items[self.symtab_section_index.?];
|
||||
@@ -5362,7 +4856,7 @@ pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 {
|
||||
}
|
||||
|
||||
/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
|
||||
fn ptrWidthBytes(self: Elf) u8 {
|
||||
pub fn ptrWidthBytes(self: Elf) u8 {
|
||||
return switch (self.ptr_width) {
|
||||
.p32 => 4,
|
||||
.p64 => 8,
|
||||
@@ -5708,7 +5202,7 @@ fn addPhdr(self: *Elf, opts: struct {
|
||||
return index;
|
||||
}
|
||||
|
||||
fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 {
|
||||
pub fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 {
|
||||
const entsize: u64 = switch (self.ptr_width) {
|
||||
.p32 => @sizeOf(elf.Elf32_Rela),
|
||||
.p64 => @sizeOf(elf.Elf64_Rela),
|
||||
@@ -5862,6 +5356,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];
|
||||
@@ -6322,7 +5829,7 @@ fn formatPhdr(
|
||||
});
|
||||
}
|
||||
|
||||
fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
|
||||
pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
|
||||
return .{ .data = self };
|
||||
}
|
||||
|
||||
@@ -6395,6 +5902,15 @@ fn fmtDumpState(
|
||||
}
|
||||
}
|
||||
|
||||
/// Caller owns the memory.
|
||||
pub fn preadAllAlloc(allocator: Allocator, handle: std.fs.File, offset: u64, size: u64) ![]u8 {
|
||||
const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow);
|
||||
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 +5957,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;
|
||||
};
|
||||
|
||||
@@ -6542,6 +6068,7 @@ const glibc = @import("../glibc.zig");
|
||||
const link = @import("../link.zig");
|
||||
const lldMain = @import("../main.zig").lldMain;
|
||||
const musl = @import("../musl.zig");
|
||||
const relocatable = @import("Elf/relocatable.zig");
|
||||
const target_util = @import("../target.zig");
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const synthetic_sections = @import("Elf/synthetic_sections.zig");
|
||||
|
||||
@@ -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,75 @@ 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();
|
||||
_ = 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)) pos += 1;
|
||||
|
||||
const hdr = try reader.readStruct(elf.ar_hdr);
|
||||
var hdr_buffer: [@sizeOf(elf.ar_hdr)]u8 = undefined;
|
||||
{
|
||||
const amt = try handle.preadAll(&hdr_buffer, pos);
|
||||
if (amt != @sizeOf(elf.ar_hdr)) return error.InputOutput;
|
||||
}
|
||||
const hdr = @as(*align(1) const elf.ar_hdr, @ptrCast(&hdr_buffer)).*;
|
||||
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();
|
||||
defer {
|
||||
_ = stream.seekBy(size) catch {};
|
||||
}
|
||||
const obj_size = try hdr.size();
|
||||
defer 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 +90,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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,44 @@ 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 handle = elf_file.fileHandle(self.file_handle);
|
||||
|
||||
self.header = try reader.readStruct(elf.Elf64_Ehdr);
|
||||
try self.parseCommon(gpa, handle, elf_file);
|
||||
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(gpa, handle, @as(u32, @intCast(i)), elf_file);
|
||||
}
|
||||
}
|
||||
|
||||
fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
|
||||
const offset = if (self.archive) |ar| ar.offset else 0;
|
||||
const file_size = (try handle.stat()).size;
|
||||
|
||||
const header_buffer = try Elf.preadAllAlloc(allocator, handle, offset, @sizeOf(elf.Elf64_Ehdr));
|
||||
defer allocator.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 +89,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,31 +101,29 @@ 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(allocator, handle, offset + shoff, shsize);
|
||||
defer allocator.free(shdrs_buffer);
|
||||
const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
|
||||
try self.shdrs.appendUnalignedSlice(allocator, 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(allocator, handle, self.header.?.e_shstrndx);
|
||||
defer allocator.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;
|
||||
}
|
||||
}
|
||||
try self.strtab.appendSlice(gpa, shstrtab);
|
||||
try self.strtab.appendSlice(allocator, shstrtab);
|
||||
|
||||
const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
|
||||
elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
|
||||
@@ -112,10 +131,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(allocator, handle, index);
|
||||
defer allocator.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,9 +143,11 @@ 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(allocator, handle, shdr.sh_link);
|
||||
defer allocator.free(strtab);
|
||||
try self.strtab.appendSlice(allocator, strtab);
|
||||
|
||||
try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
|
||||
try self.symtab.ensureUnusedCapacity(allocator, symtab.len);
|
||||
for (symtab) |sym| {
|
||||
const out_sym = self.symtab.addOneAssumeCapacity();
|
||||
out_sym.* = sym;
|
||||
@@ -137,23 +159,9 @@ 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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 +185,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 +197,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 +225,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 +235,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 +257,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 +268,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 +331,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 +354,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 +379,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,
|
||||
}),
|
||||
@@ -759,6 +781,12 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parseAr(self: *Object, elf_file: *Elf) !void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
const handle = elf_file.fileHandle(self.file_handle);
|
||||
try self.parseCommon(gpa, handle, elf_file);
|
||||
}
|
||||
|
||||
pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
|
||||
const comp = elf_file.base.comp;
|
||||
const gpa = comp.gpa;
|
||||
@@ -773,21 +801,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 +896,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 +903,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 +926,37 @@ 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];
|
||||
const sh_offset = math.cast(u64, shdr.sh_offset) orelse return error.Overflow;
|
||||
const sh_size = math.cast(u64, shdr.sh_size) orelse return error.Overflow;
|
||||
return Elf.preadAllAlloc(allocator, handle, offset + sh_offset, 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 +1093,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 +1114,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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
565
src/link/Elf/relocatable.zig
Normal file
565
src/link/Elf/relocatable.zig
Normal file
@@ -0,0 +1,565 @@
|
||||
pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
|
||||
const gpa = comp.gpa;
|
||||
|
||||
var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
|
||||
defer positionals.deinit();
|
||||
|
||||
try positionals.ensureUnusedCapacity(comp.objects.len);
|
||||
positionals.appendSliceAssumeCapacity(comp.objects);
|
||||
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
try positionals.append(.{ .path = key.status.success.object_path });
|
||||
}
|
||||
|
||||
if (module_obj_path) |path| try positionals.append(.{ .path = path });
|
||||
|
||||
if (comp.include_compiler_rt) {
|
||||
try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path });
|
||||
}
|
||||
|
||||
for (positionals.items) |obj| {
|
||||
parsePositional(elf_file, obj.path) catch |err| switch (err) {
|
||||
error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
|
||||
error.UnknownFileType => try elf_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
|
||||
else => |e| try elf_file.reportParseError(
|
||||
obj.path,
|
||||
"unexpected error: parsing input file failed with error {s}",
|
||||
.{@errorName(e)},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
|
||||
// First, we flush relocatable object file generated with our backends.
|
||||
if (elf_file.zigObjectPtr()) |zig_object| {
|
||||
zig_object.resolveSymbols(elf_file);
|
||||
zig_object.claimUnresolvedObject(elf_file);
|
||||
|
||||
try elf_file.initSymtab();
|
||||
try elf_file.initShStrtab();
|
||||
try elf_file.sortShdrs();
|
||||
try zig_object.addAtomsToRelaSections(elf_file);
|
||||
try updateSectionSizes(elf_file);
|
||||
|
||||
try allocateAllocSections(elf_file);
|
||||
try elf_file.allocateNonAllocSections();
|
||||
|
||||
if (build_options.enable_logging) {
|
||||
state_log.debug("{}", .{elf_file.dumpState()});
|
||||
}
|
||||
|
||||
try writeSyntheticSections(elf_file);
|
||||
try elf_file.writeShdrTable();
|
||||
try elf_file.writeElfHeader();
|
||||
|
||||
// TODO we can avoid reading in the file contents we just wrote if we give the linker
|
||||
// ability to write directly to a buffer.
|
||||
try zig_object.readFileContents(elf_file);
|
||||
}
|
||||
|
||||
var files = std.ArrayList(File.Index).init(gpa);
|
||||
defer files.deinit();
|
||||
try files.ensureTotalCapacityPrecise(elf_file.objects.items.len + 1);
|
||||
if (elf_file.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
|
||||
for (elf_file.objects.items) |index| files.appendAssumeCapacity(index);
|
||||
|
||||
// Update ar symtab from parsed objects
|
||||
var ar_symtab: Archive.ArSymtab = .{};
|
||||
defer ar_symtab.deinit(gpa);
|
||||
|
||||
for (files.items) |index| {
|
||||
try elf_file.file(index).?.updateArSymtab(&ar_symtab, elf_file);
|
||||
}
|
||||
|
||||
ar_symtab.sort();
|
||||
|
||||
// Save object paths in filenames strtab.
|
||||
var ar_strtab: Archive.ArStrtab = .{};
|
||||
defer ar_strtab.deinit(gpa);
|
||||
|
||||
for (files.items) |index| {
|
||||
const file_ptr = elf_file.file(index).?;
|
||||
try file_ptr.updateArStrtab(gpa, &ar_strtab);
|
||||
try file_ptr.updateArSize(elf_file);
|
||||
}
|
||||
|
||||
// Update file offsets of contributing objects.
|
||||
const total_size: usize = blk: {
|
||||
var pos: usize = elf.ARMAG.len;
|
||||
pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64);
|
||||
|
||||
if (ar_strtab.size() > 0) {
|
||||
pos = mem.alignForward(usize, pos, 2);
|
||||
pos += @sizeOf(elf.ar_hdr) + ar_strtab.size();
|
||||
}
|
||||
|
||||
for (files.items) |index| {
|
||||
const file_ptr = elf_file.file(index).?;
|
||||
const state = switch (file_ptr) {
|
||||
.zig_object => |x| &x.output_ar_state,
|
||||
.object => |x| &x.output_ar_state,
|
||||
else => unreachable,
|
||||
};
|
||||
pos = mem.alignForward(usize, pos, 2);
|
||||
state.file_off = pos;
|
||||
pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow);
|
||||
}
|
||||
|
||||
break :blk pos;
|
||||
};
|
||||
|
||||
if (build_options.enable_logging) {
|
||||
state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(elf_file)});
|
||||
state_log.debug("ar_strtab\n{}\n", .{ar_strtab});
|
||||
}
|
||||
|
||||
var buffer = std.ArrayList(u8).init(gpa);
|
||||
defer buffer.deinit();
|
||||
try buffer.ensureTotalCapacityPrecise(total_size);
|
||||
|
||||
// Write magic
|
||||
try buffer.writer().writeAll(elf.ARMAG);
|
||||
|
||||
// Write symtab
|
||||
try ar_symtab.write(.p64, elf_file, buffer.writer());
|
||||
|
||||
// Write strtab
|
||||
if (ar_strtab.size() > 0) {
|
||||
if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
|
||||
try ar_strtab.write(buffer.writer());
|
||||
}
|
||||
|
||||
// Write object files
|
||||
for (files.items) |index| {
|
||||
if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
|
||||
try elf_file.file(index).?.writeAr(elf_file, buffer.writer());
|
||||
}
|
||||
|
||||
assert(buffer.items.len == total_size);
|
||||
|
||||
try elf_file.base.file.?.setEndPos(total_size);
|
||||
try elf_file.base.file.?.pwriteAll(buffer.items, 0);
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
}
|
||||
|
||||
pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
|
||||
var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
|
||||
defer positionals.deinit();
|
||||
try positionals.ensureUnusedCapacity(comp.objects.len);
|
||||
positionals.appendSliceAssumeCapacity(comp.objects);
|
||||
|
||||
// This is a set of object files emitted by clang in a single `build-exe` invocation.
|
||||
// For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
|
||||
// in this set.
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
try positionals.append(.{ .path = key.status.success.object_path });
|
||||
}
|
||||
|
||||
if (module_obj_path) |path| try positionals.append(.{ .path = path });
|
||||
|
||||
for (positionals.items) |obj| {
|
||||
elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
|
||||
error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
|
||||
else => |e| try elf_file.reportParseError(
|
||||
obj.path,
|
||||
"unexpected error: parsing input file failed with error {s}",
|
||||
.{@errorName(e)},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
|
||||
// Now, we are ready to resolve the symbols across all input files.
|
||||
// We will first resolve the files in the ZigObject, next in the parsed
|
||||
// input Object files.
|
||||
elf_file.resolveSymbols();
|
||||
elf_file.markEhFrameAtomsDead();
|
||||
claimUnresolved(elf_file);
|
||||
|
||||
try initSections(elf_file);
|
||||
try elf_file.sortShdrs();
|
||||
if (elf_file.zigObjectPtr()) |zig_object| {
|
||||
try zig_object.addAtomsToRelaSections(elf_file);
|
||||
}
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
try object.addAtomsToOutputSections(elf_file);
|
||||
try object.addAtomsToRelaSections(elf_file);
|
||||
}
|
||||
try updateSectionSizes(elf_file);
|
||||
|
||||
try allocateAllocSections(elf_file);
|
||||
try elf_file.allocateNonAllocSections();
|
||||
elf_file.allocateAtoms();
|
||||
|
||||
if (build_options.enable_logging) {
|
||||
state_log.debug("{}", .{elf_file.dumpState()});
|
||||
}
|
||||
|
||||
try writeAtoms(elf_file);
|
||||
try writeSyntheticSections(elf_file);
|
||||
try elf_file.writeShdrTable();
|
||||
try elf_file.writeElfHeader();
|
||||
|
||||
if (comp.link_errors.items.len > 0) return error.FlushFailure;
|
||||
}
|
||||
|
||||
fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
|
||||
if (try Object.isObject(path)) {
|
||||
try parseObject(elf_file, path);
|
||||
} else if (try Archive.isArchive(path)) {
|
||||
try parseArchive(elf_file, path);
|
||||
} else return error.UnknownFileType;
|
||||
// TODO: should we check for LD script?
|
||||
// Actually, should we even unpack an archive?
|
||||
}
|
||||
|
||||
fn parseObject(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
const handle = try std.fs.cwd().openFile(path, .{});
|
||||
const fh = try elf_file.addFileHandle(handle);
|
||||
|
||||
const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
|
||||
elf_file.files.set(index, .{ .object = .{
|
||||
.path = try gpa.dupe(u8, path),
|
||||
.file_handle = fh,
|
||||
.index = index,
|
||||
} });
|
||||
try elf_file.objects.append(gpa, index);
|
||||
|
||||
const object = elf_file.file(index).?.object;
|
||||
try object.parseAr(elf_file);
|
||||
}
|
||||
|
||||
fn parseArchive(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
const handle = try std.fs.cwd().openFile(path, .{});
|
||||
const fh = try elf_file.addFileHandle(handle);
|
||||
|
||||
var archive = Archive{};
|
||||
defer archive.deinit(gpa);
|
||||
try archive.parse(elf_file, path, fh);
|
||||
|
||||
const objects = try archive.objects.toOwnedSlice(gpa);
|
||||
defer gpa.free(objects);
|
||||
|
||||
for (objects) |extracted| {
|
||||
const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
|
||||
elf_file.files.set(index, .{ .object = extracted });
|
||||
const object = &elf_file.files.items(.data)[index].object;
|
||||
object.index = index;
|
||||
try object.parseAr(elf_file);
|
||||
try elf_file.objects.append(gpa, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn claimUnresolved(elf_file: *Elf) void {
|
||||
if (elf_file.zigObjectPtr()) |zig_object| {
|
||||
zig_object.claimUnresolvedObject(elf_file);
|
||||
}
|
||||
for (elf_file.objects.items) |index| {
|
||||
elf_file.file(index).?.object.claimUnresolvedObject(elf_file);
|
||||
}
|
||||
}
|
||||
|
||||
fn initSections(elf_file: *Elf) !void {
|
||||
const ptr_size = elf_file.ptrWidthBytes();
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
try object.initOutputSections(elf_file);
|
||||
try object.initRelaSections(elf_file);
|
||||
}
|
||||
|
||||
const needs_eh_frame = for (elf_file.objects.items) |index| {
|
||||
if (elf_file.file(index).?.object.cies.items.len > 0) break true;
|
||||
} else false;
|
||||
if (needs_eh_frame) {
|
||||
elf_file.eh_frame_section_index = try elf_file.addSection(.{
|
||||
.name = ".eh_frame",
|
||||
.type = elf.SHT_PROGBITS,
|
||||
.flags = elf.SHF_ALLOC,
|
||||
.addralign = ptr_size,
|
||||
.offset = std.math.maxInt(u64),
|
||||
});
|
||||
elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(".rela.eh_frame", elf_file.eh_frame_section_index.?);
|
||||
}
|
||||
|
||||
try initComdatGroups(elf_file);
|
||||
try elf_file.initSymtab();
|
||||
try elf_file.initShStrtab();
|
||||
}
|
||||
|
||||
fn initComdatGroups(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) |cg_index| {
|
||||
const cg = elf_file.comdatGroup(cg_index);
|
||||
const cg_owner = elf_file.comdatGroupOwner(cg.owner);
|
||||
if (cg_owner.file != index) continue;
|
||||
|
||||
const cg_sec = try elf_file.comdat_group_sections.addOne(gpa);
|
||||
cg_sec.* = .{
|
||||
.shndx = try elf_file.addSection(.{
|
||||
.name = ".group",
|
||||
.type = elf.SHT_GROUP,
|
||||
.entsize = @sizeOf(u32),
|
||||
.addralign = @alignOf(u32),
|
||||
.offset = std.math.maxInt(u64),
|
||||
}),
|
||||
.cg_index = cg_index,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn updateSectionSizes(elf_file: *Elf) !void {
|
||||
for (elf_file.output_sections.keys(), elf_file.output_sections.values()) |shndx, atom_list| {
|
||||
const shdr = &elf_file.shdrs.items[shndx];
|
||||
for (atom_list.items) |atom_index| {
|
||||
const atom_ptr = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom_ptr.flags.alive) continue;
|
||||
const offset = atom_ptr.alignment.forward(shdr.sh_size);
|
||||
const padding = offset - shdr.sh_size;
|
||||
atom_ptr.value = offset;
|
||||
shdr.sh_size += padding + atom_ptr.size;
|
||||
shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1));
|
||||
}
|
||||
}
|
||||
|
||||
for (elf_file.output_rela_sections.values()) |sec| {
|
||||
const shdr = &elf_file.shdrs.items[sec.shndx];
|
||||
for (sec.atom_list.items) |atom_index| {
|
||||
const atom_ptr = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom_ptr.flags.alive) continue;
|
||||
const relocs = atom_ptr.relocs(elf_file);
|
||||
shdr.sh_size += shdr.sh_entsize * relocs.len;
|
||||
}
|
||||
|
||||
if (shdr.sh_size == 0) shdr.sh_offset = 0;
|
||||
}
|
||||
|
||||
if (elf_file.eh_frame_section_index) |index| {
|
||||
elf_file.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(elf_file);
|
||||
}
|
||||
if (elf_file.eh_frame_rela_section_index) |index| {
|
||||
const shdr = &elf_file.shdrs.items[index];
|
||||
shdr.sh_size = eh_frame.calcEhFrameRelocs(elf_file) * shdr.sh_entsize;
|
||||
}
|
||||
|
||||
try elf_file.updateSymtabSize();
|
||||
updateComdatGroupsSizes(elf_file);
|
||||
elf_file.updateShStrtabSize();
|
||||
}
|
||||
|
||||
fn updateComdatGroupsSizes(elf_file: *Elf) void {
|
||||
for (elf_file.comdat_group_sections.items) |cg| {
|
||||
const shdr = &elf_file.shdrs.items[cg.shndx];
|
||||
shdr.sh_size = cg.size(elf_file);
|
||||
shdr.sh_link = elf_file.symtab_section_index.?;
|
||||
|
||||
const sym = elf_file.symbol(cg.symbol(elf_file));
|
||||
shdr.sh_info = sym.outputSymtabIndex(elf_file) orelse
|
||||
elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates alloc sections when merging relocatable objects files together.
|
||||
fn allocateAllocSections(elf_file: *Elf) !void {
|
||||
for (elf_file.shdrs.items) |*shdr| {
|
||||
if (shdr.sh_type == elf.SHT_NULL) continue;
|
||||
if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) {
|
||||
shdr.sh_offset = 0;
|
||||
continue;
|
||||
}
|
||||
const needed_size = shdr.sh_size;
|
||||
if (needed_size > elf_file.allocatedSize(shdr.sh_offset)) {
|
||||
shdr.sh_size = 0;
|
||||
const new_offset = elf_file.findFreeSpace(needed_size, shdr.sh_addralign);
|
||||
shdr.sh_offset = new_offset;
|
||||
shdr.sh_size = needed_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn writeAtoms(elf_file: *Elf) !void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
|
||||
// TODO iterate over `output_sections` directly
|
||||
for (elf_file.shdrs.items, 0..) |shdr, shndx| {
|
||||
if (shdr.sh_type == elf.SHT_NULL) continue;
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) continue;
|
||||
|
||||
const atom_list = elf_file.output_sections.get(@intCast(shndx)) orelse continue;
|
||||
if (atom_list.items.len == 0) continue;
|
||||
|
||||
log.debug("writing atoms in '{s}' section", .{elf_file.getShString(shdr.sh_name)});
|
||||
|
||||
// TODO really, really handle debug section separately
|
||||
const base_offset = if (elf_file.isDebugSection(@intCast(shndx))) blk: {
|
||||
const zig_object = elf_file.zigObjectPtr().?;
|
||||
if (shndx == elf_file.debug_info_section_index.?)
|
||||
break :blk zig_object.debug_info_section_zig_size;
|
||||
if (shndx == elf_file.debug_abbrev_section_index.?)
|
||||
break :blk zig_object.debug_abbrev_section_zig_size;
|
||||
if (shndx == elf_file.debug_str_section_index.?)
|
||||
break :blk zig_object.debug_str_section_zig_size;
|
||||
if (shndx == elf_file.debug_aranges_section_index.?)
|
||||
break :blk zig_object.debug_aranges_section_zig_size;
|
||||
if (shndx == elf_file.debug_line_section_index.?)
|
||||
break :blk zig_object.debug_line_section_zig_size;
|
||||
unreachable;
|
||||
} else 0;
|
||||
const sh_offset = shdr.sh_offset + base_offset;
|
||||
const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
|
||||
|
||||
const buffer = try gpa.alloc(u8, sh_size);
|
||||
defer gpa.free(buffer);
|
||||
const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and
|
||||
shdr.sh_flags & elf.SHF_EXECINSTR != 0)
|
||||
0xcc // int3
|
||||
else
|
||||
0;
|
||||
@memset(buffer, padding_byte);
|
||||
|
||||
for (atom_list.items) |atom_index| {
|
||||
const atom_ptr = elf_file.atom(atom_index).?;
|
||||
assert(atom_ptr.flags.alive);
|
||||
|
||||
const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
|
||||
return error.Overflow;
|
||||
const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
|
||||
|
||||
log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{
|
||||
atom_index,
|
||||
sh_offset + offset,
|
||||
sh_offset + offset + size,
|
||||
});
|
||||
|
||||
// TODO decompress directly into provided buffer
|
||||
const out_code = buffer[offset..][0..size];
|
||||
const in_code = switch (atom_ptr.file(elf_file).?) {
|
||||
.object => |x| try x.codeDecompressAlloc(elf_file, atom_index),
|
||||
.zig_object => |x| try x.codeAlloc(elf_file, atom_index),
|
||||
else => unreachable,
|
||||
};
|
||||
defer gpa.free(in_code);
|
||||
@memcpy(out_code, in_code);
|
||||
}
|
||||
|
||||
try elf_file.base.file.?.pwriteAll(buffer, sh_offset);
|
||||
}
|
||||
}
|
||||
|
||||
fn writeSyntheticSections(elf_file: *Elf) !void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
|
||||
for (elf_file.output_rela_sections.values()) |sec| {
|
||||
if (sec.atom_list.items.len == 0) continue;
|
||||
|
||||
const shdr = elf_file.shdrs.items[sec.shndx];
|
||||
|
||||
const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse
|
||||
return error.Overflow;
|
||||
var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
|
||||
defer relocs.deinit();
|
||||
|
||||
for (sec.atom_list.items) |atom_index| {
|
||||
const atom_ptr = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom_ptr.flags.alive) continue;
|
||||
try atom_ptr.writeRelocs(elf_file, &relocs);
|
||||
}
|
||||
assert(relocs.items.len == num_relocs);
|
||||
|
||||
const SortRelocs = struct {
|
||||
pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
|
||||
_ = ctx;
|
||||
return lhs.r_offset < rhs.r_offset;
|
||||
}
|
||||
};
|
||||
|
||||
mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
|
||||
|
||||
log.debug("writing {s} from 0x{x} to 0x{x}", .{
|
||||
elf_file.getShString(shdr.sh_name),
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
});
|
||||
|
||||
try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
|
||||
}
|
||||
|
||||
if (elf_file.eh_frame_section_index) |shndx| {
|
||||
const shdr = elf_file.shdrs.items[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 eh_frame.writeEhFrameObject(elf_file, buffer.writer());
|
||||
log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
});
|
||||
assert(buffer.items.len == sh_size);
|
||||
try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
|
||||
}
|
||||
if (elf_file.eh_frame_rela_section_index) |shndx| {
|
||||
const shdr = elf_file.shdrs.items[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 eh_frame.writeEhFrameRelocs(elf_file, buffer.writer());
|
||||
assert(buffer.items.len == sh_size);
|
||||
log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
});
|
||||
try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
|
||||
}
|
||||
|
||||
try writeComdatGroups(elf_file);
|
||||
try elf_file.writeSymtab();
|
||||
try elf_file.writeShStrtab();
|
||||
}
|
||||
|
||||
fn writeComdatGroups(elf_file: *Elf) !void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
for (elf_file.comdat_group_sections.items) |cgs| {
|
||||
const shdr = elf_file.shdrs.items[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}", .{
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
});
|
||||
try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
|
||||
}
|
||||
}
|
||||
|
||||
const assert = std.debug.assert;
|
||||
const build_options = @import("build_options");
|
||||
const eh_frame = @import("eh_frame.zig");
|
||||
const elf = std.elf;
|
||||
const link = @import("../../link.zig");
|
||||
const log = std.log.scoped(.link);
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const state_log = std.log.scoped(.link_state);
|
||||
const std = @import("std");
|
||||
|
||||
const Archive = @import("Archive.zig");
|
||||
const Compilation = @import("../../Compilation.zig");
|
||||
const Elf = @import("../Elf.zig");
|
||||
const File = @import("file.zig").File;
|
||||
const Object = @import("Object.zig");
|
||||
@@ -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];
|
||||
|
||||
@@ -24,20 +24,18 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
|
||||
const handle = macho_file.getFileHandle(handle_index);
|
||||
const offset = if (fat_arch) |ar| ar.offset else 0;
|
||||
const size = if (fat_arch) |ar| ar.size else (try handle.stat()).size;
|
||||
try handle.seekTo(offset);
|
||||
|
||||
const reader = handle.reader();
|
||||
_ = try reader.readBytesNoEof(SARMAG);
|
||||
|
||||
var pos: usize = SARMAG;
|
||||
var pos: usize = offset + SARMAG;
|
||||
while (true) {
|
||||
if (pos >= size) break;
|
||||
if (!mem.isAligned(pos, 2)) {
|
||||
try handle.seekBy(1);
|
||||
pos += 1;
|
||||
}
|
||||
if (!mem.isAligned(pos, 2)) pos += 1;
|
||||
|
||||
const hdr = try reader.readStruct(ar_hdr);
|
||||
var hdr_buffer: [@sizeOf(ar_hdr)]u8 = undefined;
|
||||
{
|
||||
const amt = try handle.preadAll(&hdr_buffer, pos);
|
||||
if (amt != @sizeOf(ar_hdr)) return error.InputOutput;
|
||||
}
|
||||
const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*;
|
||||
pos += @sizeOf(ar_hdr);
|
||||
|
||||
if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
|
||||
@@ -53,17 +51,15 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
|
||||
if (try hdr.nameLength()) |len| {
|
||||
hdr_size -= len;
|
||||
const buf = try arena.allocator().alloc(u8, len);
|
||||
try reader.readNoEof(buf);
|
||||
const amt = try handle.preadAll(buf, pos);
|
||||
if (amt != len) return error.InputOutput;
|
||||
pos += len;
|
||||
const actual_len = mem.indexOfScalar(u8, buf, @as(u8, 0)) orelse len;
|
||||
break :name buf[0..actual_len];
|
||||
}
|
||||
unreachable;
|
||||
};
|
||||
defer {
|
||||
_ = handle.seekBy(hdr_size) catch {};
|
||||
pos += hdr_size;
|
||||
}
|
||||
defer pos += hdr_size;
|
||||
|
||||
if (mem.eql(u8, name, SYMDEF) or
|
||||
mem.eql(u8, name, SYMDEF64) or
|
||||
@@ -73,7 +69,7 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
|
||||
const object = Object{
|
||||
.archive = .{
|
||||
.path = try gpa.dupe(u8, path),
|
||||
.offset = offset + pos,
|
||||
.offset = pos,
|
||||
},
|
||||
.path = try gpa.dupe(u8, name),
|
||||
.file_handle = handle_index,
|
||||
|
||||
Reference in New Issue
Block a user