mirror of
https://github.com/ziglang/zig.git
synced 2025-12-30 01:53:16 +00:00
elf: add prelim impl of Object parsing
This commit is contained in:
parent
3df58a9583
commit
67d458370d
161
src/link/Elf.zig
161
src/link/Elf.zig
@ -9,6 +9,7 @@ llvm_object: ?*LlvmObject = null,
|
||||
files: std.MultiArrayList(File.Entry) = .{},
|
||||
zig_module_index: ?File.Index = null,
|
||||
linker_defined_index: ?File.Index = null,
|
||||
objects: std.ArrayListUnmanaged(File.Index) = .{},
|
||||
|
||||
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
|
||||
/// Same order as in the file.
|
||||
@ -51,11 +52,12 @@ got: GotSection = .{},
|
||||
text_section_index: ?u16 = null,
|
||||
rodata_section_index: ?u16 = null,
|
||||
data_section_index: ?u16 = null,
|
||||
eh_frame_section_index: ?u16 = null,
|
||||
eh_frame_hdr_section_index: ?u16 = null,
|
||||
dynamic_section_index: ?u16 = null,
|
||||
got_section_index: ?u16 = null,
|
||||
got_plt_section_index: ?u16 = null,
|
||||
plt_section_index: ?u16 = null,
|
||||
eh_frame_hdr_section_index: ?u16 = null,
|
||||
rela_dyn_section_index: ?u16 = null,
|
||||
debug_info_section_index: ?u16 = null,
|
||||
debug_abbrev_section_index: ?u16 = null,
|
||||
@ -136,6 +138,10 @@ last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFre
|
||||
/// with `Decl` `main`, and lives as long as that `Decl`.
|
||||
unnamed_consts: UnnamedConstTable = .{},
|
||||
|
||||
comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{},
|
||||
comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{},
|
||||
comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{},
|
||||
|
||||
const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));
|
||||
const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
|
||||
|
||||
@ -249,10 +255,11 @@ pub fn deinit(self: *Elf) void {
|
||||
.null => {},
|
||||
.zig_module => data.zig_module.deinit(gpa),
|
||||
.linker_defined => data.linker_defined.deinit(gpa),
|
||||
// .object => data.object.deinit(gpa),
|
||||
.object => data.object.deinit(gpa),
|
||||
// .shared_object => data.shared_object.deinit(gpa),
|
||||
};
|
||||
self.files.deinit(gpa);
|
||||
self.objects.deinit(gpa);
|
||||
|
||||
self.shdrs.deinit(gpa);
|
||||
self.phdr_to_shdr_table.deinit(gpa);
|
||||
@ -295,6 +302,9 @@ pub fn deinit(self: *Elf) void {
|
||||
}
|
||||
|
||||
self.misc_errors.deinit(gpa);
|
||||
self.comdat_groups.deinit(gpa);
|
||||
self.comdat_groups_owners.deinit(gpa);
|
||||
self.comdat_groups_table.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
|
||||
@ -828,7 +838,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
const sym_index = try zig_module.addLocal(self);
|
||||
const sym = self.symbol(sym_index);
|
||||
const esym = sym.sourceSymbol(self);
|
||||
const esym = zig_module.sourceSymbol(sym_index, self);
|
||||
const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_pkg.root_src_path));
|
||||
sym.name_offset = name_off;
|
||||
esym.st_name = name_off;
|
||||
@ -1276,14 +1286,35 @@ fn parsePositional(
|
||||
) ParseError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
_ = self;
|
||||
_ = in_file;
|
||||
_ = path;
|
||||
_ = must_link;
|
||||
_ = ctx;
|
||||
|
||||
return error.UnknownFileType;
|
||||
if (Object.isObject(in_file)) {
|
||||
try self.parseObject(in_file, path, ctx);
|
||||
} else return error.UnknownFileType;
|
||||
}
|
||||
|
||||
fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseErrorCtx) ParseError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = self.base.allocator;
|
||||
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
|
||||
const index = @as(File.Index, @intCast(self.files.slice().len));
|
||||
|
||||
var object = Object{
|
||||
.path = path,
|
||||
.data = data,
|
||||
.index = index,
|
||||
};
|
||||
errdefer object.deinit(gpa);
|
||||
try object.parse(self);
|
||||
|
||||
ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?;
|
||||
if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
|
||||
|
||||
_ = try self.files.addOne(gpa);
|
||||
self.files.set(index, .{ .object = object });
|
||||
try self.objects.append(gpa, index);
|
||||
}
|
||||
|
||||
fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
|
||||
@ -2226,6 +2257,7 @@ fn updateDeclCode(
|
||||
) !void {
|
||||
const gpa = self.base.allocator;
|
||||
const mod = self.base.options.module.?;
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
||||
@ -2234,7 +2266,7 @@ fn updateDeclCode(
|
||||
const required_alignment = decl.getAlignment(mod);
|
||||
|
||||
const sym = self.symbol(sym_index);
|
||||
const esym = sym.sourceSymbol(self);
|
||||
const esym = zig_module.sourceSymbol(sym_index, self);
|
||||
const atom_ptr = sym.atom(self).?;
|
||||
const shdr_index = sym.output_section_index;
|
||||
|
||||
@ -2447,6 +2479,7 @@ pub fn updateDecl(
|
||||
fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void {
|
||||
const gpa = self.base.allocator;
|
||||
const mod = self.base.options.module.?;
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
|
||||
var required_alignment: u32 = undefined;
|
||||
var code_buffer = std.ArrayList(u8).init(gpa);
|
||||
@ -2490,7 +2523,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
|
||||
const local_sym = self.symbol(symbol_index);
|
||||
const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?;
|
||||
local_sym.name_offset = name_str_index;
|
||||
const local_esym = local_sym.sourceSymbol(self);
|
||||
const local_esym = zig_module.sourceSymbol(symbol_index, self);
|
||||
local_esym.st_name = name_str_index;
|
||||
local_esym.st_info |= elf.STT_OBJECT;
|
||||
local_esym.st_size = code.len;
|
||||
@ -2566,7 +2599,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
|
||||
const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
|
||||
const local_sym = self.symbol(sym_index);
|
||||
local_sym.name_offset = name_str_index;
|
||||
const local_esym = local_sym.sourceSymbol(self);
|
||||
const local_esym = zig_module.sourceSymbol(sym_index, self);
|
||||
local_esym.st_name = name_str_index;
|
||||
local_esym.st_info |= elf.STT_OBJECT;
|
||||
local_esym.st_size = code.len;
|
||||
@ -2650,8 +2683,8 @@ pub fn updateDeclExports(
|
||||
};
|
||||
const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info));
|
||||
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: {
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const sym_index = try zig_module.addGlobal(exp_name, self);
|
||||
try decl_metadata.exports.append(gpa, sym_index);
|
||||
break :blk sym_index;
|
||||
@ -2660,8 +2693,8 @@ pub fn updateDeclExports(
|
||||
sym.value = decl_sym.value;
|
||||
sym.atom_index = decl_sym.atom_index;
|
||||
sym.output_section_index = decl_sym.output_section_index;
|
||||
const esym = sym.sourceSymbol(self);
|
||||
esym.* = decl_esym.*;
|
||||
const esym = zig_module.sourceSymbol(sym_index, self);
|
||||
esym.* = decl_esym;
|
||||
esym.st_info = (stb_bits << 4) | stt_bits;
|
||||
}
|
||||
}
|
||||
@ -2691,11 +2724,12 @@ pub fn deleteDeclExport(
|
||||
const metadata = self.decls.getPtr(decl_index) orelse return;
|
||||
const gpa = self.base.allocator;
|
||||
const mod = self.base.options.module.?;
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const exp_name = mod.intern_pool.stringToSlice(name);
|
||||
const sym_index = metadata.@"export"(self, exp_name) orelse return;
|
||||
log.debug("deleting export '{s}'", .{exp_name});
|
||||
const sym = self.symbol(sym_index.*);
|
||||
const esym = sym.sourceSymbol(self);
|
||||
const esym = zig_module.sourceSymbol(sym_index.*, self);
|
||||
assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant
|
||||
sym.* = .{};
|
||||
// TODO free list for esym!
|
||||
@ -3337,6 +3371,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {
|
||||
.null => null,
|
||||
.linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined },
|
||||
.zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module },
|
||||
.object => .{ .object = &self.files.items(.data)[index].object },
|
||||
};
|
||||
}
|
||||
|
||||
@ -3442,6 +3477,42 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32
|
||||
return gop.index;
|
||||
}
|
||||
|
||||
const GetOrCreateComdatGroupOwnerResult = struct {
|
||||
found_existing: bool,
|
||||
index: ComdatGroupOwner.Index,
|
||||
};
|
||||
|
||||
pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult {
|
||||
const gpa = self.base.allocator;
|
||||
const gop = try self.comdat_groups_table.getOrPut(gpa, off);
|
||||
if (!gop.found_existing) {
|
||||
const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len));
|
||||
const owner = try self.comdat_groups_owners.addOne(gpa);
|
||||
owner.* = .{};
|
||||
gop.value_ptr.* = index;
|
||||
}
|
||||
return .{
|
||||
.found_existing = gop.found_existing,
|
||||
.index = gop.value_ptr.*,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addComdatGroup(self: *Elf) !ComdatGroup.Index {
|
||||
const index = @as(ComdatGroup.Index, @intCast(self.comdat_groups.items.len));
|
||||
_ = try self.comdat_groups.addOne(self.base.allocator);
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn comdatGroup(self: *Elf, index: ComdatGroup.Index) *ComdatGroup {
|
||||
assert(index < self.comdat_groups.items.len);
|
||||
return &self.comdat_groups.items[index];
|
||||
}
|
||||
|
||||
pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupOwner {
|
||||
assert(index < self.comdat_groups_owners.items.len);
|
||||
return &self.comdat_groups_owners.items[index];
|
||||
}
|
||||
|
||||
fn reportUndefined(self: *Elf) !void {
|
||||
const gpa = self.base.allocator;
|
||||
const max_notes = 4;
|
||||
@ -3552,6 +3623,21 @@ fn fmtDumpState(
|
||||
try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path });
|
||||
try writer.print("{}\n", .{zig_module.fmtSymtab(self)});
|
||||
}
|
||||
|
||||
for (self.objects.items) |index| {
|
||||
const object = self.file(index).?.object;
|
||||
try writer.print("object({d}) : {}", .{ index, object.fmtPath() });
|
||||
if (!object.alive) try writer.writeAll(" : [*]");
|
||||
try writer.writeByte('\n');
|
||||
try writer.print("{}{}{}{}{}\n", .{
|
||||
object.fmtAtoms(self),
|
||||
object.fmtCies(self),
|
||||
object.fmtFdes(self),
|
||||
object.fmtSymtab(self),
|
||||
object.fmtComdatGroups(self),
|
||||
});
|
||||
}
|
||||
|
||||
if (self.linker_defined_index) |index| {
|
||||
const linker_defined = self.file(index).?.linker_defined;
|
||||
try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
|
||||
@ -3560,6 +3646,37 @@ fn fmtDumpState(
|
||||
try writer.print("{}\n", .{self.got.fmt(self)});
|
||||
}
|
||||
|
||||
/// Binary search
|
||||
pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
|
||||
if (!@hasDecl(@TypeOf(predicate), "predicate"))
|
||||
@compileError("Predicate is required to define fn predicate(@This(), T) bool");
|
||||
|
||||
var min: usize = 0;
|
||||
var max: usize = haystack.len;
|
||||
while (min < max) {
|
||||
const index = (min + max) / 2;
|
||||
const curr = haystack[index];
|
||||
if (predicate.predicate(curr)) {
|
||||
min = index + 1;
|
||||
} else {
|
||||
max = index;
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/// Linear search
|
||||
pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
|
||||
if (!@hasDecl(@TypeOf(predicate), "predicate"))
|
||||
@compileError("Predicate is required to define fn predicate(@This(), T) bool");
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < haystack.len) : (i += 1) {
|
||||
if (predicate.predicate(haystack[i])) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
const default_entry_addr = 0x8000000;
|
||||
|
||||
pub const base_tag: link.File.Tag = .elf;
|
||||
@ -3607,6 +3724,17 @@ const DeclMetadata = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const ComdatGroupOwner = struct {
|
||||
file: File.Index = 0,
|
||||
const Index = u32;
|
||||
};
|
||||
|
||||
pub const ComdatGroup = struct {
|
||||
owner: ComdatGroupOwner.Index,
|
||||
shndx: u16,
|
||||
pub const Index = u32;
|
||||
};
|
||||
|
||||
pub const SymtabSize = struct {
|
||||
nlocals: u32 = 0,
|
||||
nglobals: u32 = 0,
|
||||
@ -3654,6 +3782,7 @@ const LinkerDefined = @import("Elf/LinkerDefined.zig");
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||
const Module = @import("../Module.zig");
|
||||
const Object = @import("Elf/Object.zig");
|
||||
const InternPool = @import("../InternPool.zig");
|
||||
const Package = @import("../Package.zig");
|
||||
const Symbol = @import("Elf/Symbol.zig");
|
||||
|
||||
@ -46,6 +46,46 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 {
|
||||
return elf_file.strtab.getAssumeExists(self.name_offset);
|
||||
}
|
||||
|
||||
pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {
|
||||
const object = elf_file.file(self.file_index).?.object;
|
||||
return object.shdrs.items[self.input_section_index];
|
||||
}
|
||||
|
||||
pub fn codeInObject(self: Atom, elf_file: *Elf) []const u8 {
|
||||
const object = elf_file.file(self.file_index).?.object;
|
||||
return object.shdrContents(self.input_section_index);
|
||||
}
|
||||
|
||||
/// Returns atom's code and optionally uncompresses data if required (for compressed sections).
|
||||
/// Caller owns the memory.
|
||||
pub fn codeInObjectUncompressAlloc(self: Atom, elf_file: *Elf) ![]u8 {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const data = self.codeInObject(elf_file);
|
||||
const shdr = self.inputShdr(elf_file);
|
||||
if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
|
||||
const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
|
||||
switch (chdr.ch_type) {
|
||||
.ZLIB => {
|
||||
var stream = std.io.fixedBufferStream(data[@sizeOf(elf.Elf64_Chdr)..]);
|
||||
var zlib_stream = try std.compress.zlib.decompressStream(gpa, stream.reader());
|
||||
defer zlib_stream.deinit();
|
||||
const decomp = try gpa.alloc(u8, chdr.ch_size);
|
||||
const nread = try zlib_stream.reader().readAll(decomp);
|
||||
if (nread != decomp.len) {
|
||||
return error.Io;
|
||||
}
|
||||
return decomp;
|
||||
},
|
||||
else => @panic("TODO unhandled compression scheme"),
|
||||
}
|
||||
} else return gpa.dupe(u8, data);
|
||||
}
|
||||
|
||||
pub fn priority(self: Atom, elf_file: *Elf) u64 {
|
||||
const index = elf_file.file(self.file_index).?.index();
|
||||
return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index));
|
||||
}
|
||||
|
||||
/// Returns how much room there is to grow in virtual address space.
|
||||
/// File offset relocation happens transparently, so it is not included in
|
||||
/// this calculation.
|
||||
@ -247,11 +287,12 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
|
||||
self.* = .{};
|
||||
}
|
||||
|
||||
pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela {
|
||||
const file_ptr = elf_file.file(self.file_index).?;
|
||||
if (file_ptr != .zig_module) @panic("TODO");
|
||||
const zig_module = file_ptr.zig_module;
|
||||
return zig_module.relocs.items[self.relocs_section_index].items;
|
||||
pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
|
||||
return switch (elf_file.file(self.file_index).?) {
|
||||
.zig_module => |x| x.relocs.items[self.relocs_section_index].items,
|
||||
.object => |x| x.getRelocs(self.relocs_section_index),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
index: File.Index,
|
||||
symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
|
||||
symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
||||
alive: bool = true,
|
||||
|
||||
output_symtab_size: Elf.SymtabSize = .{},
|
||||
|
||||
@ -76,10 +75,6 @@ pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sourceSymbol(self: *LinkerDefined, symbol_index: Symbol.Index) *elf.Elf64_Sym {
|
||||
return &self.symtab.items[symbol_index];
|
||||
}
|
||||
|
||||
pub fn globals(self: *LinkerDefined) []const Symbol.Index {
|
||||
return self.symbols.items;
|
||||
}
|
||||
|
||||
810
src/link/Elf/Object.zig
Normal file
810
src/link/Elf/Object.zig
Normal file
@ -0,0 +1,810 @@
|
||||
archive: ?[]const u8 = null,
|
||||
path: []const u8,
|
||||
data: []const u8,
|
||||
index: File.Index,
|
||||
|
||||
header: ?elf.Elf64_Ehdr = null,
|
||||
shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
|
||||
strings: StringTable(.object_strings) = .{},
|
||||
symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
|
||||
strtab: []const u8 = &[0]u8{},
|
||||
first_global: ?u32 = null,
|
||||
|
||||
symbols: std.ArrayListUnmanaged(u32) = .{},
|
||||
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},
|
||||
|
||||
fdes: std.ArrayListUnmanaged(Fde) = .{},
|
||||
cies: std.ArrayListUnmanaged(Cie) = .{},
|
||||
|
||||
needs_exec_stack: bool = false,
|
||||
alive: bool = true,
|
||||
num_dynrelocs: u32 = 0,
|
||||
|
||||
output_symtab_size: Elf.SymtabSize = .{},
|
||||
|
||||
pub fn isObject(file: std.fs.File) bool {
|
||||
const reader = file.reader();
|
||||
const header = reader.readStruct(elf.Elf64_Ehdr) catch return false;
|
||||
defer file.seekTo(0) catch {};
|
||||
if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false;
|
||||
if (header.e_ident[elf.EI_VERSION] != 1) return false;
|
||||
if (header.e_type != elf.ET.REL) return false;
|
||||
if (header.e_version != 1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Object, allocator: Allocator) void {
|
||||
allocator.free(self.data);
|
||||
self.shdrs.deinit(allocator);
|
||||
self.strings.deinit(allocator);
|
||||
self.symbols.deinit(allocator);
|
||||
self.atoms.deinit(allocator);
|
||||
self.comdat_groups.deinit(allocator);
|
||||
self.fdes.deinit(allocator);
|
||||
self.cies.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn parse(self: *Object, elf_file: *Elf) !void {
|
||||
var stream = std.io.fixedBufferStream(self.data);
|
||||
const reader = stream.reader();
|
||||
|
||||
self.header = try reader.readStruct(elf.Elf64_Ehdr);
|
||||
|
||||
if (self.header.?.e_shnum == 0) return;
|
||||
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
const shdrs = @as(
|
||||
[*]align(1) const elf.Elf64_Shdr,
|
||||
@ptrCast(self.data.ptr + self.header.?.e_shoff),
|
||||
)[0..self.header.?.e_shnum];
|
||||
try self.shdrs.appendUnalignedSlice(gpa, shdrs);
|
||||
try self.strings.buffer.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx));
|
||||
|
||||
const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
|
||||
elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
|
||||
else => {},
|
||||
} else null;
|
||||
|
||||
if (symtab_index) |index| {
|
||||
const shdr = shdrs[index];
|
||||
self.first_global = shdr.sh_info;
|
||||
|
||||
const symtab = self.shdrContents(index);
|
||||
const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
|
||||
self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];
|
||||
self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
|
||||
}
|
||||
|
||||
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.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 shdrs = self.shdrs.items;
|
||||
try self.atoms.resize(elf_file.base.allocator, shdrs.len);
|
||||
@memset(self.atoms.items, 0);
|
||||
|
||||
for (shdrs, 0..) |shdr, i| {
|
||||
if (shdr.sh_flags & elf.SHF_EXCLUDE != 0 and
|
||||
shdr.sh_flags & elf.SHF_ALLOC == 0 and
|
||||
shdr.sh_type != elf.SHT_LLVM_ADDRSIG) continue;
|
||||
|
||||
switch (shdr.sh_type) {
|
||||
elf.SHT_GROUP => {
|
||||
if (shdr.sh_info >= self.symtab.len) {
|
||||
// TODO convert into an error
|
||||
log.debug("{}: invalid symbol index in sh_info", .{self.fmtPath()});
|
||||
continue;
|
||||
}
|
||||
const group_info_sym = self.symtab[shdr.sh_info];
|
||||
const group_signature = blk: {
|
||||
if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) {
|
||||
const sym_shdr = shdrs[group_info_sym.st_shndx];
|
||||
break :blk self.strings.getAssumeExists(sym_shdr.sh_name);
|
||||
}
|
||||
break :blk self.getString(group_info_sym.st_name);
|
||||
};
|
||||
|
||||
const shndx = @as(u16, @intCast(i));
|
||||
const group_raw_data = self.shdrContents(shndx);
|
||||
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];
|
||||
|
||||
if (group_members[0] != 0x1) { // GRP_COMDAT
|
||||
// TODO convert into an error
|
||||
log.debug("{}: unknown SHT_GROUP format", .{self.fmtPath()});
|
||||
continue;
|
||||
}
|
||||
|
||||
const group_signature_off = try self.strings.insert(elf_file.base.allocator, group_signature);
|
||||
const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off);
|
||||
const comdat_group_index = try elf_file.addComdatGroup();
|
||||
const comdat_group = elf_file.comdatGroup(comdat_group_index);
|
||||
comdat_group.* = .{
|
||||
.owner = gop.index,
|
||||
.shndx = shndx,
|
||||
};
|
||||
try self.comdat_groups.append(elf_file.base.allocator, comdat_group_index);
|
||||
},
|
||||
|
||||
elf.SHT_SYMTAB_SHNDX => @panic("TODO"),
|
||||
|
||||
elf.SHT_NULL,
|
||||
elf.SHT_REL,
|
||||
elf.SHT_RELA,
|
||||
elf.SHT_SYMTAB,
|
||||
elf.SHT_STRTAB,
|
||||
=> {},
|
||||
|
||||
else => {
|
||||
const name = self.strings.getAssumeExists(shdr.sh_name);
|
||||
const shndx = @as(u16, @intCast(i));
|
||||
|
||||
// if (mem.eql(u8, ".note.GNU-stack", name)) {
|
||||
// if (shdr.sh_flags & elf.SHF_EXECINSTR != 0) {
|
||||
// if (!elf_file.options.z_execstack or !elf_file.options.z_execstack_if_needed) {
|
||||
// elf_file.base.warn(
|
||||
// "{}: may cause segmentation fault as this file requested executable stack",
|
||||
// .{self.fmtPath()},
|
||||
// );
|
||||
// }
|
||||
// self.needs_exec_stack = true;
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (self.skipShdr(shndx, elf_file)) continue;
|
||||
try self.addAtom(shdr, shndx, name, elf_file);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Parse relocs sections if any.
|
||||
for (shdrs, 0..) |shdr, i| switch (shdr.sh_type) {
|
||||
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));
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8, elf_file: *Elf) !void {
|
||||
const atom_index = try elf_file.addAtom();
|
||||
const atom = elf_file.atom(atom_index).?;
|
||||
atom.atom_index = atom_index;
|
||||
atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name);
|
||||
atom.file_index = self.index;
|
||||
atom.input_section_index = shndx;
|
||||
self.atoms.items[shndx] = atom_index;
|
||||
|
||||
if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
|
||||
const data = self.shdrContents(shndx);
|
||||
const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
|
||||
atom.size = chdr.ch_size;
|
||||
atom.alignment = math.log2_int(u64, chdr.ch_addralign);
|
||||
} else {
|
||||
atom.size = shdr.sh_size;
|
||||
atom.alignment = math.log2_int(u64, shdr.sh_addralign);
|
||||
}
|
||||
}
|
||||
|
||||
fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
|
||||
const shdr = self.shdrs.items[index];
|
||||
const name = self.strings.getAssumeExists(shdr.sh_name);
|
||||
const ignore = blk: {
|
||||
if (mem.startsWith(u8, name, ".note")) break :blk true;
|
||||
if (mem.startsWith(u8, name, ".comment")) break :blk true;
|
||||
if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
|
||||
if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
|
||||
mem.startsWith(u8, name, ".debug")) break :blk true;
|
||||
break :blk false;
|
||||
};
|
||||
return ignore;
|
||||
}
|
||||
|
||||
fn initSymtab(self: *Object, elf_file: *Elf) !void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const first_global = self.first_global orelse self.symtab.len;
|
||||
const shdrs = self.shdrs.items;
|
||||
|
||||
try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len);
|
||||
|
||||
for (self.symtab[0..first_global], 0..) |sym, i| {
|
||||
const index = try elf_file.addSymbol();
|
||||
self.symbols.appendAssumeCapacity(index);
|
||||
const sym_ptr = elf_file.symbol(index);
|
||||
const name = blk: {
|
||||
if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) {
|
||||
const shdr = shdrs[sym.st_shndx];
|
||||
break :blk self.strings.getAssumeExists(shdr.sh_name);
|
||||
}
|
||||
break :blk self.getString(sym.st_name);
|
||||
};
|
||||
sym_ptr.value = sym.st_value;
|
||||
sym_ptr.name_offset = try elf_file.strtab.insert(gpa, name);
|
||||
sym_ptr.esym_index = @as(u32, @intCast(i));
|
||||
sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];
|
||||
sym_ptr.file_index = self.index;
|
||||
}
|
||||
|
||||
for (self.symtab[first_global..]) |sym| {
|
||||
const name = self.getString(sym.st_name);
|
||||
const off = try elf_file.strtab.insert(gpa, name);
|
||||
const gop = try elf_file.getOrPutGlobal(off);
|
||||
self.symbols.addOneAssumeCapacity().* = gop.index;
|
||||
}
|
||||
}
|
||||
|
||||
fn parseEhFrame(self: *Object, shndx: u16, 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)),
|
||||
else => {},
|
||||
} else {
|
||||
log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()});
|
||||
return;
|
||||
};
|
||||
|
||||
const gpa = elf_file.base.allocator;
|
||||
const raw = self.shdrContents(shndx);
|
||||
const relocs = self.getRelocs(relocs_shndx);
|
||||
const fdes_start = self.fdes.items.len;
|
||||
const cies_start = self.cies.items.len;
|
||||
|
||||
var it = eh_frame.Iterator{ .data = raw };
|
||||
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,
|
||||
.size = rec.size,
|
||||
.rel_index = @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,
|
||||
.size = rec.size,
|
||||
.cie_index = undefined,
|
||||
.rel_index = @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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Tie each FDE to its CIE
|
||||
for (self.fdes.items[fdes_start..]) |*fde| {
|
||||
const cie_ptr = fde.offset + 4 - fde.ciePointer(elf_file);
|
||||
const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| {
|
||||
if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));
|
||||
} else {
|
||||
// TODO convert into an error
|
||||
log.debug("{s}: no matching CIE found for FDE at offset {x}", .{
|
||||
self.fmtPath(),
|
||||
fde.offset,
|
||||
});
|
||||
continue;
|
||||
};
|
||||
fde.cie_index = cie_index;
|
||||
}
|
||||
|
||||
// Tie each FDE record to its matching atom
|
||||
const SortFdes = struct {
|
||||
pub fn lessThan(ctx: *Elf, lhs: Fde, rhs: Fde) bool {
|
||||
const lhs_atom = lhs.atom(ctx);
|
||||
const rhs_atom = rhs.atom(ctx);
|
||||
return lhs_atom.priority(ctx) < rhs_atom.priority(ctx);
|
||||
}
|
||||
};
|
||||
mem.sort(Fde, self.fdes.items[fdes_start..], elf_file, SortFdes.lessThan);
|
||||
|
||||
// Create a back-link from atom to FDEs
|
||||
var i: u32 = @as(u32, @intCast(fdes_start));
|
||||
while (i < self.fdes.items.len) {
|
||||
const fde = self.fdes.items[i];
|
||||
const atom = fde.atom(elf_file);
|
||||
atom.fde_start = i;
|
||||
i += 1;
|
||||
while (i < self.fdes.items.len) : (i += 1) {
|
||||
const next_fde = self.fdes.items[i];
|
||||
if (atom.atom_index != next_fde.atom(elf_file).atom_index) break;
|
||||
}
|
||||
atom.fde_end = i;
|
||||
}
|
||||
}
|
||||
|
||||
fn filterRelocs(
|
||||
relocs: []align(1) const elf.Elf64_Rela,
|
||||
start: u64,
|
||||
len: u64,
|
||||
) struct { start: u64, len: u64 } {
|
||||
const Predicate = struct {
|
||||
value: u64,
|
||||
|
||||
pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool {
|
||||
return rel.r_offset < self.value;
|
||||
}
|
||||
};
|
||||
const LPredicate = struct {
|
||||
value: u64,
|
||||
|
||||
pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool {
|
||||
return rel.r_offset >= self.value;
|
||||
}
|
||||
};
|
||||
|
||||
const f_start = Elf.bsearch(elf.Elf64_Rela, relocs, Predicate{ .value = start });
|
||||
const f_len = Elf.lsearch(elf.Elf64_Rela, relocs[f_start..], LPredicate{ .value = start + len });
|
||||
|
||||
return .{ .start = f_start, .len = f_len };
|
||||
}
|
||||
|
||||
pub fn scanRelocs(self: *Object, elf_file: *Elf) !void {
|
||||
for (self.atoms.items) |atom_index| {
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom.alive) continue;
|
||||
const shdr = atom.inputShdr(elf_file);
|
||||
if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) continue;
|
||||
try atom.scanRelocs(elf_file);
|
||||
}
|
||||
|
||||
for (self.cies.items) |cie| {
|
||||
for (cie.getRelocs(elf_file)) |rel| {
|
||||
const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]);
|
||||
if (sym.flags.import) {
|
||||
if (sym.getType(elf_file) != elf.STT_FUNC)
|
||||
elf_file.base.fatal("{s}: {s}: CIE referencing external data reference", .{
|
||||
self.fmtPath(),
|
||||
sym.getName(elf_file),
|
||||
});
|
||||
sym.flags.plt = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
|
||||
const first_global = self.first_global orelse return;
|
||||
for (self.globals(), 0..) |index, i| {
|
||||
const sym_idx = @as(u32, @intCast(first_global + i));
|
||||
const this_sym = self.symtab[sym_idx];
|
||||
|
||||
if (this_sym.st_shndx == elf.SHN_UNDEF) continue;
|
||||
|
||||
if (this_sym.st_shndx != elf.SHN_ABS and this_sym.st_shndx != elf.SHN_COMMON) {
|
||||
const atom_index = self.atoms.items[this_sym.st_shndx];
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom.alive) continue;
|
||||
}
|
||||
|
||||
const global = elf_file.symbol(index);
|
||||
if (self.asFile().symbolRank(this_sym, !self.alive) < global.symbolRank(elf_file)) {
|
||||
const atom = switch (this_sym.st_shndx) {
|
||||
elf.SHN_ABS, elf.SHN_COMMON => 0,
|
||||
else => self.atoms.items[this_sym.st_shndx],
|
||||
};
|
||||
global.* = .{
|
||||
.value = this_sym.st_value,
|
||||
.name = global.name,
|
||||
.atom = atom,
|
||||
.sym_idx = sym_idx,
|
||||
.file = self.index,
|
||||
.ver_idx = elf_file.default_sym_version,
|
||||
};
|
||||
if (this_sym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resetGlobals(self: *Object, elf_file: *Elf) void {
|
||||
for (self.globals()) |index| {
|
||||
const global = elf_file.symbol(index);
|
||||
const name = global.name;
|
||||
global.* = .{};
|
||||
global.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn markLive(self: *Object, elf_file: *Elf) void {
|
||||
const first_global = self.first_global orelse return;
|
||||
for (self.globals(), 0..) |index, i| {
|
||||
const sym_idx = first_global + i;
|
||||
const sym = self.symtab[sym_idx];
|
||||
if (sym.st_bind() == elf.STB_WEAK) continue;
|
||||
|
||||
const global = elf_file.symbol(index);
|
||||
const file = global.getFile(elf_file) orelse continue;
|
||||
const should_keep = sym.st_shndx == elf.SHN_UNDEF or
|
||||
(sym.st_shndx == elf.SHN_COMMON and global.sourceSymbol(elf_file).st_shndx != elf.SHN_COMMON);
|
||||
if (should_keep and !file.isAlive()) {
|
||||
file.setAlive();
|
||||
file.markLive(elf_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {
|
||||
const first_global = self.first_global orelse return;
|
||||
for (self.globals(), 0..) |index, i| {
|
||||
const sym_idx = @as(u32, @intCast(first_global + i));
|
||||
const this_sym = self.symtab[sym_idx];
|
||||
const global = elf_file.symbol(index);
|
||||
const global_file = global.getFile(elf_file) orelse continue;
|
||||
|
||||
if (self.index == global_file.getIndex() or
|
||||
this_sym.st_shndx == elf.SHN_UNDEF or
|
||||
this_sym.st_bind() == elf.STB_WEAK or
|
||||
this_sym.st_shndx == elf.SHN_COMMON) continue;
|
||||
|
||||
if (this_sym.st_shndx != elf.SHN_ABS) {
|
||||
const atom_index = self.atoms.items[this_sym.st_shndx];
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom.alive) continue;
|
||||
}
|
||||
|
||||
elf_file.base.fatal("multiple definition: {}: {}: {s}", .{
|
||||
self.fmtPath(),
|
||||
global_file.fmtPath(),
|
||||
global.getName(elf_file),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// We will create dummy shdrs per each resolved common symbols to make it
|
||||
/// play nicely with the rest of the system.
|
||||
pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
|
||||
const first_global = self.first_global orelse return;
|
||||
for (self.globals(), 0..) |index, i| {
|
||||
const sym_idx = @as(u32, @intCast(first_global + i));
|
||||
const this_sym = self.symtab[sym_idx];
|
||||
if (this_sym.st_shndx != elf.SHN_COMMON) continue;
|
||||
|
||||
const global = elf_file.symbol(index);
|
||||
const global_file = global.getFile(elf_file).?;
|
||||
if (global_file.getIndex() != self.index) {
|
||||
if (elf_file.options.warn_common) {
|
||||
elf_file.base.warn("{}: multiple common symbols: {s}", .{
|
||||
self.fmtPath(),
|
||||
global.getName(elf_file),
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
const atom_index = try elf_file.addAtom();
|
||||
try self.atoms.append(gpa, atom_index);
|
||||
|
||||
const is_tls = global.getType(elf_file) == elf.STT_TLS;
|
||||
const name = if (is_tls) ".tls_common" else ".common";
|
||||
|
||||
const atom = elf_file.atom(atom_index).?;
|
||||
atom.atom_index = atom_index;
|
||||
atom.name = try elf_file.strtab.insert(gpa, name);
|
||||
atom.file = self.index;
|
||||
atom.size = this_sym.st_size;
|
||||
const alignment = this_sym.st_value;
|
||||
atom.alignment = math.log2_int(u64, alignment);
|
||||
|
||||
var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE;
|
||||
if (is_tls) sh_flags |= elf.SHF_TLS;
|
||||
const shndx = @as(u16, @intCast(self.shdrs.items.len));
|
||||
const shdr = try self.shdrs.addOne(gpa);
|
||||
shdr.* = .{
|
||||
.sh_name = try self.strings.insert(gpa, name),
|
||||
.sh_type = elf.SHT_NOBITS,
|
||||
.sh_flags = sh_flags,
|
||||
.sh_addr = 0,
|
||||
.sh_offset = 0,
|
||||
.sh_size = this_sym.st_size,
|
||||
.sh_link = 0,
|
||||
.sh_info = 0,
|
||||
.sh_addralign = alignment,
|
||||
.sh_entsize = 0,
|
||||
};
|
||||
atom.shndx = shndx;
|
||||
|
||||
global.value = 0;
|
||||
global.atom = atom_index;
|
||||
global.flags.weak = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void {
|
||||
if (elf_file.options.strip_all) return;
|
||||
|
||||
for (self.locals()) |local_index| {
|
||||
const local = elf_file.symbol(local_index);
|
||||
if (local.atom(elf_file)) |atom| if (!atom.alive) continue;
|
||||
const s_sym = local.getSourceSymbol(elf_file);
|
||||
switch (s_sym.st_type()) {
|
||||
elf.STT_SECTION, elf.STT_NOTYPE => continue,
|
||||
else => {},
|
||||
}
|
||||
local.flags.output_symtab = true;
|
||||
self.output_symtab_size.nlocals += 1;
|
||||
self.output_symtab_size.strsize += @as(u32, @intCast(local.getName(elf_file).len + 1));
|
||||
}
|
||||
|
||||
for (self.globals()) |global_index| {
|
||||
const global = elf_file.symbol(global_index);
|
||||
if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue;
|
||||
if (global.atom(elf_file)) |atom| if (!atom.alive) continue;
|
||||
global.flags.output_symtab = true;
|
||||
if (global.isLocal()) {
|
||||
self.output_symtab_size.nlocals += 1;
|
||||
} else {
|
||||
self.output_symtab_size.nglobals += 1;
|
||||
}
|
||||
self.output_symtab_size.strsize += @as(u32, @intCast(global.getName(elf_file).len + 1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: Elf.WriteSymtabCtx) !void {
|
||||
if (elf_file.options.strip_all) return;
|
||||
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
var ilocal = ctx.ilocal;
|
||||
for (self.locals()) |local_index| {
|
||||
const local = elf_file.symbol(local_index);
|
||||
if (!local.flags.output_symtab) continue;
|
||||
const st_name = try ctx.strtab.insert(gpa, local.getName(elf_file));
|
||||
ctx.symtab[ilocal] = local.asElfSym(st_name, elf_file);
|
||||
ilocal += 1;
|
||||
}
|
||||
|
||||
var iglobal = ctx.iglobal;
|
||||
for (self.globals()) |global_index| {
|
||||
const global = elf_file.symbol(global_index);
|
||||
if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue;
|
||||
if (!global.flags.output_symtab) continue;
|
||||
const st_name = try ctx.strtab.insert(gpa, global.getName(elf_file));
|
||||
if (global.isLocal()) {
|
||||
ctx.symtab[ilocal] = global.asElfSym(st_name, elf_file);
|
||||
ilocal += 1;
|
||||
} else {
|
||||
ctx.symtab[iglobal] = global.asElfSym(st_name, elf_file);
|
||||
iglobal += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn locals(self: *Object) []const u32 {
|
||||
const end = self.first_global orelse self.symbols.items.len;
|
||||
return self.symbols.items[0..end];
|
||||
}
|
||||
|
||||
pub fn globals(self: *Object) []const u32 {
|
||||
const start = self.first_global orelse self.symbols.items.len;
|
||||
return self.symbols.items[start..];
|
||||
}
|
||||
|
||||
pub inline 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];
|
||||
}
|
||||
|
||||
fn getString(self: *Object, off: u32) [:0]const u8 {
|
||||
assert(off < self.strtab.len);
|
||||
return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 format(
|
||||
self: *Object,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = writer;
|
||||
@compileError("do not format objects directly");
|
||||
}
|
||||
|
||||
pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
|
||||
return .{ .data = .{
|
||||
.object = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const FormatContext = struct {
|
||||
object: *Object,
|
||||
elf_file: *Elf,
|
||||
};
|
||||
|
||||
fn formatSymtab(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
const object = ctx.object;
|
||||
try writer.writeAll(" locals\n");
|
||||
for (object.locals()) |index| {
|
||||
const local = ctx.elf_file.symbol(index);
|
||||
try writer.print(" {}\n", .{local.fmt(ctx.elf_file)});
|
||||
}
|
||||
try writer.writeAll(" globals\n");
|
||||
for (object.globals()) |index| {
|
||||
const global = ctx.elf_file.symbol(index);
|
||||
try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
|
||||
return .{ .data = .{
|
||||
.object = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
fn formatAtoms(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
const object = ctx.object;
|
||||
try writer.writeAll(" atoms\n");
|
||||
for (object.atoms.items) |atom_index| {
|
||||
const atom = ctx.elf_file.atom(atom_index) orelse continue;
|
||||
try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatCies) {
|
||||
return .{ .data = .{
|
||||
.object = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
fn formatCies(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
const object = ctx.object;
|
||||
try writer.writeAll(" cies\n");
|
||||
for (object.cies.items, 0..) |cie, i| {
|
||||
try writer.print(" cie({d}) : {}\n", .{ i, cie.fmt(ctx.elf_file) });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatFdes) {
|
||||
return .{ .data = .{
|
||||
.object = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
fn formatFdes(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
const object = ctx.object;
|
||||
try writer.writeAll(" fdes\n");
|
||||
for (object.fdes.items, 0..) |fde, i| {
|
||||
try writer.print(" fde({d}) : {}\n", .{ i, fde.fmt(ctx.elf_file) });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmtComdatGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatComdatGroups) {
|
||||
return .{ .data = .{
|
||||
.object = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
fn formatComdatGroups(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
const object = ctx.object;
|
||||
const elf_file = ctx.elf_file;
|
||||
try writer.writeAll(" comdat groups\n");
|
||||
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 != object.index) continue;
|
||||
for (object.comdatGroupMembers(cg.shndx)) |shndx| {
|
||||
const atom_index = object.atoms.items[shndx];
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmtPath(self: *Object) std.fmt.Formatter(formatPath) {
|
||||
return .{ .data = self };
|
||||
}
|
||||
|
||||
fn formatPath(
|
||||
object: *Object,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
if (object.archive) |path| {
|
||||
try writer.writeAll(path);
|
||||
try writer.writeByte('(');
|
||||
try writer.writeAll(object.path);
|
||||
try writer.writeByte(')');
|
||||
} else try writer.writeAll(object.path);
|
||||
}
|
||||
|
||||
const Object = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const eh_frame = @import("eh_frame.zig");
|
||||
const elf = std.elf;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.link);
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const Cie = eh_frame.Cie;
|
||||
const Elf = @import("../Elf.zig");
|
||||
const Fde = eh_frame.Fde;
|
||||
const File = @import("file.zig").File;
|
||||
const StringTable = @import("../strtab.zig").StringTable;
|
||||
const Symbol = @import("Symbol.zig");
|
||||
@ -66,22 +66,23 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
|
||||
return elf_file.file(symbol.file_index);
|
||||
}
|
||||
|
||||
pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) *elf.Elf64_Sym {
|
||||
pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
|
||||
const file_ptr = symbol.file(elf_file).?;
|
||||
switch (file_ptr) {
|
||||
.zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file),
|
||||
.linker_defined => return file_ptr.linker_defined.sourceSymbol(symbol.esym_index),
|
||||
.zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file).*,
|
||||
.linker_defined => return file_ptr.linker_defined.symtab.items[symbol.esym_index],
|
||||
.object => return file_ptr.object.symtab[symbol.esym_index],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
|
||||
const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32);
|
||||
const sym = symbol.sourceSymbol(elf_file);
|
||||
const in_archive = switch (file) {
|
||||
// .object => |x| !x.alive,
|
||||
const in_archive = switch (file_ptr) {
|
||||
.object => |x| !x.alive,
|
||||
else => false,
|
||||
};
|
||||
return file_ptr.symbolRank(sym.*, in_archive);
|
||||
return file_ptr.symbolRank(sym, in_archive);
|
||||
}
|
||||
|
||||
pub fn address(symbol: Symbol, opts: struct {
|
||||
|
||||
@ -11,8 +11,6 @@ global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
|
||||
atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{},
|
||||
relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
|
||||
|
||||
alive: bool = true,
|
||||
|
||||
output_symtab_size: Elf.SymtabSize = .{},
|
||||
|
||||
pub fn deinit(self: *ZigModule, allocator: Allocator) void {
|
||||
@ -37,7 +35,7 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !
|
||||
const symbol_ptr = elf_file.symbol(symbol_index);
|
||||
symbol_ptr.atom_index = atom_index;
|
||||
symbol_ptr.output_section_index = output_section_index;
|
||||
const local_esym = symbol_ptr.sourceSymbol(elf_file);
|
||||
const local_esym = self.sourceSymbol(symbol_ptr.index, elf_file);
|
||||
local_esym.st_shndx = output_section_index;
|
||||
const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len));
|
||||
const relocs = try self.relocs.addOne(gpa);
|
||||
@ -82,7 +80,7 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I
|
||||
pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
|
||||
for (self.locals()) |local_index| {
|
||||
const local = elf_file.symbol(local_index);
|
||||
const esym = self.sourceSymbol(local_index, elf_file);
|
||||
const esym = local.sourceSymbol(elf_file);
|
||||
switch (esym.st_type()) {
|
||||
elf.STT_SECTION, elf.STT_NOTYPE => {
|
||||
local.flags.output_symtab = false;
|
||||
|
||||
445
src/link/Elf/eh_frame.zig
Normal file
445
src/link/Elf/eh_frame.zig
Normal file
@ -0,0 +1,445 @@
|
||||
pub const Fde = struct {
|
||||
/// Includes 4byte size cell.
|
||||
offset: u64,
|
||||
size: u64,
|
||||
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,
|
||||
/// Includes 4byte size cell.
|
||||
out_offset: u64 = 0,
|
||||
|
||||
pub fn address(fde: Fde, elf_file: *Elf) u64 {
|
||||
const base: u64 = if (elf_file.eh_frame_section_index) |shndx|
|
||||
elf_file.shdrs.items[shndx].sh_addr
|
||||
else
|
||||
0;
|
||||
return base + fde.out_offset;
|
||||
}
|
||||
|
||||
pub fn data(fde: Fde, elf_file: *Elf) []const 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()];
|
||||
}
|
||||
|
||||
pub fn cie(fde: Fde, elf_file: *Elf) Cie {
|
||||
const object = elf_file.file(fde.file_index).?.object;
|
||||
return object.cies.items[fde.cie_index];
|
||||
}
|
||||
|
||||
pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 {
|
||||
return std.mem.readIntLittle(u32, fde.data(elf_file)[4..8]);
|
||||
}
|
||||
|
||||
pub fn calcSize(fde: Fde) u64 {
|
||||
return fde.size + 4;
|
||||
}
|
||||
|
||||
pub fn atom(fde: Fde, elf_file: *Elf) *Atom {
|
||||
const object = elf_file.file(fde.file_index).?.object;
|
||||
const rel = fde.relocs(elf_file)[0];
|
||||
const sym = object.symtab[rel.r_sym()];
|
||||
const atom_index = object.atoms.items[sym.st_shndx];
|
||||
return elf_file.atom(atom_index).?;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
fde: Fde,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = fde;
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = writer;
|
||||
@compileError("do not format FDEs directly");
|
||||
}
|
||||
|
||||
pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(format2) {
|
||||
return .{ .data = .{
|
||||
.fde = fde,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const FdeFormatContext = struct {
|
||||
fde: Fde,
|
||||
elf_file: *Elf,
|
||||
};
|
||||
|
||||
fn format2(
|
||||
ctx: FdeFormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
const fde = ctx.fde;
|
||||
const elf_file = ctx.elf_file;
|
||||
const base_addr = fde.address(elf_file);
|
||||
try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
|
||||
base_addr + fde.out_offset,
|
||||
fde.calcSize(),
|
||||
fde.cie_index,
|
||||
fde.atom(elf_file).name(elf_file),
|
||||
});
|
||||
if (!fde.alive) try writer.writeAll(" : [*]");
|
||||
}
|
||||
};
|
||||
|
||||
pub const Cie = struct {
|
||||
/// Includes 4byte size cell.
|
||||
offset: u64,
|
||||
size: u64,
|
||||
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.
|
||||
out_offset: u64 = 0,
|
||||
alive: bool = false,
|
||||
|
||||
pub fn address(cie: Cie, elf_file: *Elf) u64 {
|
||||
const base: u64 = if (elf_file.eh_frame_section_index) |shndx|
|
||||
elf_file.shdrs.items[shndx].sh_addr
|
||||
else
|
||||
0;
|
||||
return base + cie.out_offset;
|
||||
}
|
||||
|
||||
pub fn data(cie: Cie, elf_file: *Elf) []const 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()];
|
||||
}
|
||||
|
||||
pub fn calcSize(cie: Cie) u64 {
|
||||
return cie.size + 4;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool {
|
||||
if (!std.mem.eql(u8, cie.data(elf_file), other.data(elf_file))) return false;
|
||||
|
||||
const cie_relocs = cie.relocs(elf_file);
|
||||
const other_relocs = other.relocs(elf_file);
|
||||
if (cie_relocs.len != other_relocs.len) return false;
|
||||
|
||||
for (cie_relocs, other_relocs) |cie_rel, other_rel| {
|
||||
if (cie_rel.r_offset - cie.offset != other_rel.r_offset - other.offset) return false;
|
||||
if (cie_rel.r_type() != other_rel.r_type()) return false;
|
||||
if (cie_rel.r_addend != other_rel.r_addend) return false;
|
||||
|
||||
const cie_object = elf_file.file(cie.file_index).?.object;
|
||||
const other_object = elf_file.file(other.file_index).?.object;
|
||||
const cie_sym = cie_object.symbol(cie_rel.r_sym(), elf_file);
|
||||
const other_sym = other_object.symbol(other_rel.r_sym(), elf_file);
|
||||
if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
cie: Cie,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = cie;
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = writer;
|
||||
@compileError("do not format CIEs directly");
|
||||
}
|
||||
|
||||
pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(format2) {
|
||||
return .{ .data = .{
|
||||
.cie = cie,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const CieFormatContext = struct {
|
||||
cie: Cie,
|
||||
elf_file: *Elf,
|
||||
};
|
||||
|
||||
fn format2(
|
||||
ctx: CieFormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
const cie = ctx.cie;
|
||||
const elf_file = ctx.elf_file;
|
||||
const base_addr = cie.address(elf_file);
|
||||
try writer.print("@{x} : size({x})", .{
|
||||
base_addr + cie.out_offset,
|
||||
cie.calcSize(),
|
||||
});
|
||||
if (!cie.alive) try writer.writeAll(" : [*]");
|
||||
}
|
||||
};
|
||||
|
||||
pub const Iterator = struct {
|
||||
data: []const u8,
|
||||
pos: u64 = 0,
|
||||
|
||||
pub const Record = struct {
|
||||
tag: enum { fde, cie },
|
||||
offset: u64,
|
||||
size: u64,
|
||||
};
|
||||
|
||||
pub fn next(it: *Iterator) !?Record {
|
||||
if (it.pos >= it.data.len) return null;
|
||||
|
||||
var stream = std.io.fixedBufferStream(it.data[it.pos..]);
|
||||
const reader = stream.reader();
|
||||
|
||||
var size = try reader.readIntLittle(u32);
|
||||
if (size == 0xFFFFFFFF) @panic("TODO");
|
||||
|
||||
const id = try reader.readIntLittle(u32);
|
||||
const record = Record{
|
||||
.tag = if (id == 0) .cie else .fde,
|
||||
.offset = it.pos,
|
||||
.size = size,
|
||||
};
|
||||
it.pos += size + 4;
|
||||
|
||||
return record;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn calcEhFrameSize(elf_file: *Elf) !usize {
|
||||
var offset: u64 = 0;
|
||||
|
||||
var cies = std.ArrayList(Cie).init(elf_file.base.allocator);
|
||||
defer cies.deinit();
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
|
||||
outer: for (object.cies.items) |*cie| {
|
||||
for (cies.items) |other| {
|
||||
if (other.eql(cie.*, elf_file)) {
|
||||
// We already have a CIE record that has the exact same contents, so instead of
|
||||
// duplicating them, we mark this one dead and set its output offset to be
|
||||
// equal to that of the alive record. This way, we won't have to rewrite
|
||||
// Fde.cie_index field when committing the records to file.
|
||||
cie.out_offset = other.out_offset;
|
||||
continue :outer;
|
||||
}
|
||||
}
|
||||
cie.alive = true;
|
||||
cie.out_offset = offset;
|
||||
offset += cie.calcSize();
|
||||
try cies.append(cie.*);
|
||||
}
|
||||
}
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
for (object.fdes.items) |*fde| {
|
||||
if (!fde.alive) continue;
|
||||
fde.out_offset = offset;
|
||||
offset += fde.calcSize();
|
||||
}
|
||||
}
|
||||
|
||||
return offset + 4; // NULL terminator
|
||||
}
|
||||
|
||||
pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {
|
||||
var count: usize = 0;
|
||||
for (elf_file.objects.items) |index| {
|
||||
for (elf_file.file(index).?.object.fdes.items) |fde| {
|
||||
if (!fde.alive) continue;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return eh_frame_hdr_header_size + count * 8;
|
||||
}
|
||||
|
||||
fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, contents: []u8) !void {
|
||||
const offset = rel.r_offset - rec.offset;
|
||||
const P = @as(i64, @intCast(rec.address(elf_file) + offset));
|
||||
const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
|
||||
const A = rel.r_addend;
|
||||
|
||||
relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{
|
||||
Atom.fmtRelocType(rel.r_type()),
|
||||
offset,
|
||||
P,
|
||||
S + A,
|
||||
sym.name(elf_file),
|
||||
});
|
||||
|
||||
var where = contents[offset..];
|
||||
switch (rel.r_type()) {
|
||||
elf.R_X86_64_32 => std.mem.writeIntLittle(i32, where[0..4], @as(i32, @truncate(S + A))),
|
||||
elf.R_X86_64_64 => std.mem.writeIntLittle(i64, where[0..8], S + A),
|
||||
elf.R_X86_64_PC32 => std.mem.writeIntLittle(i32, where[0..4], @as(i32, @intCast(S - P + A))),
|
||||
elf.R_X86_64_PC64 => std.mem.writeIntLittle(i64, where[0..8], S - P + A),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr});
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
|
||||
for (object.cies.items) |cie| {
|
||||
if (!cie.alive) continue;
|
||||
|
||||
const contents = try gpa.dupe(u8, cie.data(elf_file));
|
||||
defer gpa.free(contents);
|
||||
|
||||
for (cie.relocs(elf_file)) |rel| {
|
||||
const sym = object.symbol(rel.r_sym(), elf_file);
|
||||
try resolveReloc(cie, sym, rel, elf_file, contents);
|
||||
}
|
||||
|
||||
try writer.writeAll(contents);
|
||||
}
|
||||
}
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
|
||||
for (object.fdes.items) |fde| {
|
||||
if (!fde.alive) continue;
|
||||
|
||||
const contents = try gpa.dupe(u8, fde.data(elf_file));
|
||||
defer gpa.free(contents);
|
||||
|
||||
std.mem.writeIntLittle(
|
||||
i32,
|
||||
contents[4..8],
|
||||
@as(i32, @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset)))),
|
||||
);
|
||||
|
||||
for (fde.relocs(elf_file)) |rel| {
|
||||
const sym = object.symbol(rel.r_sym(), elf_file);
|
||||
try resolveReloc(fde, sym, rel, elf_file, contents);
|
||||
}
|
||||
|
||||
try writer.writeAll(contents);
|
||||
}
|
||||
}
|
||||
|
||||
try writer.writeIntLittle(u32, 0);
|
||||
}
|
||||
|
||||
pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
|
||||
try writer.writeByte(1); // version
|
||||
try writer.writeByte(EH_PE.pcrel | EH_PE.sdata4);
|
||||
try writer.writeByte(EH_PE.udata4);
|
||||
try writer.writeByte(EH_PE.datarel | EH_PE.sdata4);
|
||||
|
||||
const eh_frame_shdr = elf_file.shdrs.items[elf_file.eh_frame_section_index.?];
|
||||
const eh_frame_hdr_shdr = elf_file.shdrs.items[elf_file.eh_frame_hdr_section_index.?];
|
||||
const num_fdes = @as(u32, @intCast(@divExact(eh_frame_hdr_shdr.sh_size - eh_frame_hdr_header_size, 8)));
|
||||
try writer.writeIntLittle(
|
||||
u32,
|
||||
@as(u32, @bitCast(@as(
|
||||
i32,
|
||||
@truncate(@as(i64, @intCast(eh_frame_shdr.sh_addr)) - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)) - 4),
|
||||
))),
|
||||
);
|
||||
try writer.writeIntLittle(u32, num_fdes);
|
||||
|
||||
const Entry = struct {
|
||||
init_addr: u32,
|
||||
fde_addr: u32,
|
||||
|
||||
pub fn lessThan(ctx: void, lhs: @This(), rhs: @This()) bool {
|
||||
_ = ctx;
|
||||
return lhs.init_addr < rhs.init_addr;
|
||||
}
|
||||
};
|
||||
|
||||
var entries = std.ArrayList(Entry).init(elf_file.base.allocator);
|
||||
defer entries.deinit();
|
||||
try entries.ensureTotalCapacityPrecise(num_fdes);
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
for (object.fdes.items) |fde| {
|
||||
if (!fde.alive) continue;
|
||||
|
||||
const relocs = fde.relocs(elf_file);
|
||||
assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
|
||||
const rel = relocs[0];
|
||||
const sym = object.symbol(rel.r_sym(), elf_file);
|
||||
const P = @as(i64, @intCast(fde.address(elf_file)));
|
||||
const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
|
||||
const A = rel.r_addend;
|
||||
entries.appendAssumeCapacity(.{
|
||||
.init_addr = @as(u32, @bitCast(@as(i32, @truncate(S + A - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)))))),
|
||||
.fde_addr = @as(
|
||||
u32,
|
||||
@bitCast(@as(i32, @truncate(P - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr))))),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std.mem.sort(Entry, entries.items, {}, Entry.lessThan);
|
||||
try writer.writeAll(std.mem.sliceAsBytes(entries.items));
|
||||
}
|
||||
|
||||
const eh_frame_hdr_header_size: u64 = 12;
|
||||
|
||||
const EH_PE = struct {
|
||||
pub const absptr = 0x00;
|
||||
pub const uleb128 = 0x01;
|
||||
pub const udata2 = 0x02;
|
||||
pub const udata4 = 0x03;
|
||||
pub const udata8 = 0x04;
|
||||
pub const sleb128 = 0x09;
|
||||
pub const sdata2 = 0x0A;
|
||||
pub const sdata4 = 0x0B;
|
||||
pub const sdata8 = 0x0C;
|
||||
pub const pcrel = 0x10;
|
||||
pub const textrel = 0x20;
|
||||
pub const datarel = 0x30;
|
||||
pub const funcrel = 0x40;
|
||||
pub const aligned = 0x50;
|
||||
pub const indirect = 0x80;
|
||||
pub const omit = 0xFF;
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const elf = std.elf;
|
||||
const relocs_log = std.log.scoped(.link_relocs);
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const Elf = @import("../Elf.zig");
|
||||
const Object = @import("Object.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
@ -1,7 +1,7 @@
|
||||
pub const File = union(enum) {
|
||||
zig_module: *ZigModule,
|
||||
linker_defined: *LinkerDefined,
|
||||
// object: *Object,
|
||||
object: *Object,
|
||||
// shared_object: *SharedObject,
|
||||
|
||||
pub fn index(file: File) Index {
|
||||
@ -25,7 +25,7 @@ pub const File = union(enum) {
|
||||
switch (file) {
|
||||
.zig_module => try writer.writeAll("(zig module)"),
|
||||
.linker_defined => try writer.writeAll("(linker defined)"),
|
||||
// .object => |x| try writer.print("{}", .{x.fmtPath()}),
|
||||
.object => |x| try writer.print("{}", .{x.fmtPath()}),
|
||||
// .shared_object => |x| try writer.writeAll(x.path),
|
||||
}
|
||||
}
|
||||
@ -95,7 +95,7 @@ pub const File = union(enum) {
|
||||
null: void,
|
||||
zig_module: ZigModule,
|
||||
linker_defined: LinkerDefined,
|
||||
// object: Object,
|
||||
object: Object,
|
||||
// shared_object: SharedObject,
|
||||
};
|
||||
};
|
||||
@ -106,7 +106,7 @@ const elf = std.elf;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Elf = @import("../Elf.zig");
|
||||
const LinkerDefined = @import("LinkerDefined.zig");
|
||||
// const Object = @import("Object.zig");
|
||||
const Object = @import("Object.zig");
|
||||
// const SharedObject = @import("SharedObject.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const ZigModule = @import("ZigModule.zig");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user