self-hosted: link: global offset table support for decls

This commit is contained in:
Andrew Kelley 2020-05-12 20:11:47 -04:00
parent fda0eef9fb
commit e3a0fac1a7
4 changed files with 198 additions and 100 deletions

View File

@ -50,3 +50,4 @@ pub fn add(self: *Package, name: []const u8, package: *Package) !void {
const std = @import("std");
const mem = std.mem;
const assert = std.debug.assert;
const Package = @This();

View File

@ -5,13 +5,9 @@ const ir = @import("ir.zig");
const Type = @import("type.zig").Type;
const Value = @import("value.zig").Value;
const Target = std.Target;
const Allocator = mem.Allocator;
pub fn generateSymbol(
typed_value: ir.TypedValue,
module: ir.Module,
code: *std.ArrayList(u8),
errors: *std.ArrayList(ir.ErrorMsg),
) !void {
pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !?*ir.ErrorMsg {
switch (typed_value.ty.zigTypeTag()) {
.Fn => {
const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
@ -21,14 +17,14 @@ pub fn generateSymbol(
.mod_fn = module_fn,
.code = code,
.inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator),
.errors = errors,
.err_msg = null,
};
defer function.inst_table.deinit();
for (module_fn.body.instructions) |inst| {
const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
error.CodegenFail => {
assert(function.errors.items.len != 0);
assert(function.err_msg != null);
break;
},
else => |e| return e,
@ -36,7 +32,7 @@ pub fn generateSymbol(
try function.inst_table.putNoClobber(inst, new_inst);
}
return Symbol{ .errors = function.errors.toOwnedSlice() };
return function.err_msg;
},
else => @panic("TODO implement generateSymbol for non-function decls"),
}
@ -47,7 +43,7 @@ const Function = struct {
mod_fn: *const ir.Module.Fn,
code: *std.ArrayList(u8),
inst_table: std.AutoHashMap(*ir.Inst, MCValue),
errors: *std.ArrayList(ir.ErrorMsg),
err_msg: ?*ir.ErrorMsg,
const MCValue = union(enum) {
none,
@ -428,11 +424,8 @@ const Function = struct {
fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {
@setCold(true);
try self.errors.ensureCapacity(self.errors.items.len + 1);
self.errors.appendAssumeCapacity(.{
.byte_offset = src,
.msg = try std.fmt.allocPrint(self.errors.allocator, format, args),
});
assert(self.err_msg == null);
self.err_msg = try ir.ErrorMsg.create(self.code.allocator, src, format, args);
return error.CodegenFail;
}
};

View File

@ -201,7 +201,7 @@ pub const Module = struct {
decl_table: std.AutoHashMap(Decl.Hash, *Decl),
optimize_mode: std.builtin.Mode,
link_error_flags: link.ElfFile.ErrorFlags = .{},
link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{},
/// We optimize memory usage for a compilation with no compile errors by storing the
/// error messages and mapping outside of `Decl`.
@ -247,7 +247,7 @@ pub const Module = struct {
/// The most recent value of the Decl after a successful semantic analysis.
/// The tag for this union is determined by the tag value of the analysis field.
typed_value: union {
never_succeeded,
never_succeeded: void,
most_recent: TypedValue.Managed,
},
/// Represents the "shallow" analysis status. For example, for decls that are functions,
@ -278,12 +278,11 @@ pub const Module = struct {
complete,
},
/// Represents the position of the code, if any, in the output file.
/// Represents the position of the code in the output file.
/// This is populated regardless of semantic analysis and code generation.
/// This value is `undefined` if the type has no runtime bits.
link: link.ElfFile.Decl,
link: link.ElfFile.Decl = link.ElfFile.Decl.empty,
/// The set of other decls whose typed_value could possibly change if this Decl's
/// The shallow set of other decls whose typed_value could possibly change if this Decl's
/// typed_value is modified.
/// TODO look into using a lightweight map/set data structure rather than a linear array.
dependants: ArrayListUnmanaged(*Decl) = .{},
@ -368,11 +367,11 @@ pub const Module = struct {
/// Reference to external memory, not owned by ZIRModule.
sub_file_path: []const u8,
source: union {
unloaded,
unloaded: void,
bytes: [:0]const u8,
},
contents: union {
not_available,
not_available: void,
module: *text.Module,
},
status: enum {
@ -575,9 +574,9 @@ pub const Module = struct {
try self.failed_files.ensureCapacity(self.failed_files.size + 1);
var keep_source = false;
const source = try self.root_pkg_dir.readFileAllocOptions(
const source = try self.root_pkg.root_src_dir.readFileAllocOptions(
self.allocator,
self.root_src_path,
self.root_pkg.root_src_path,
std.math.maxInt(u32),
1,
0,
@ -628,20 +627,7 @@ pub const Module = struct {
while (self.analysis_queue.popOrNull()) |work_item| {
switch (work_item) {
.decl => |decl| switch (decl.analysis) {
.success => |typed_value| {
var arena = decl.arena.promote(self.allocator);
const update_result = self.bin_file.updateDecl(
self.*,
typed_value,
decl.export_node,
decl.fullyQualifiedNameHash(),
&arena.allocator,
);
decl.arena = arena.state;
if (try update_result) |err_msg| {
decl.analysis = .{ .codegen_failure = err_msg };
}
},
.success => try self.bin_file.updateDecl(self, decl),
},
}
}
@ -653,22 +639,22 @@ pub const Module = struct {
return kv.value;
} else {
const new_decl = blk: {
var decl_arena = std.heap.ArenaAllocator.init(self.allocator);
errdefer decl_arena.deinit();
const new_decl = try decl_arena.allocator.create(Decl);
const name = try mem.dupeZ(&decl_arena.allocator, u8, old_inst.name);
try self.decl_table.ensureCapacity(self.decl_table.size + 1);
const new_decl = try self.allocator.create(Decl);
errdefer self.allocator.destroy(new_decl);
const name = try mem.dupeZ(self.allocator, u8, old_inst.name);
errdefer self.allocator.free(name);
new_decl.* = .{
.arena = decl_arena.state,
.name = name,
.src = old_inst.src,
.analysis = .in_progress,
.scope = scope.findZIRModule(),
.src = old_inst.src,
.typed_value = .{ .never_succeeded = {} },
.analysis = .initial_in_progress,
};
try self.decl_table.putNoClobber(hash, new_decl);
self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
break :blk new_decl;
};
swapRemoveElem(self.allocator, *Scope.ZIRModule, root_scope, self.failed_decls);
var decl_scope: Scope.DeclAnalysis = .{
.base = .{ .parent = scope },
.decl = new_decl,
@ -1838,6 +1824,7 @@ pub fn main() anyerror!void {
const bin_path = args[2];
const debug_error_trace = true;
const output_zir = true;
const object_format: ?std.builtin.ObjectFormat = null;
const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
@ -1845,9 +1832,9 @@ pub fn main() anyerror!void {
.target = native_info.target,
.output_mode = .Exe,
.link_mode = .Static,
.object_format = options.object_format orelse native_info.target.getObjectFormat(),
.object_format = object_format orelse native_info.target.getObjectFormat(),
});
defer bin_file.deinit(allocator);
defer bin_file.deinit();
var module = blk: {
const root_pkg = try Package.create(allocator, std.fs.cwd(), ".", src_path);
@ -1857,7 +1844,9 @@ pub fn main() anyerror!void {
errdefer allocator.destroy(root_scope);
root_scope.* = .{
.sub_file_path = root_pkg.root_src_path,
.contents = .unloaded,
.source = .{ .unloaded = {} },
.contents = .{ .not_available = {} },
.status = .unloaded,
};
break :blk Module{
@ -1866,7 +1855,13 @@ pub fn main() anyerror!void {
.root_scope = root_scope,
.bin_file = &bin_file,
.optimize_mode = .Debug,
.decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(allocator),
.decl_table = std.AutoHashMap(Module.Decl.Hash, *Module.Decl).init(allocator),
.decl_exports = std.AutoHashMap(*Module.Decl, []*Module.Export).init(allocator),
.export_owners = std.AutoHashMap(*Module.Decl, []*Module.Export).init(allocator),
.failed_decls = std.AutoHashMap(*Module.Decl, *ErrorMsg).init(allocator),
.failed_fns = std.AutoHashMap(*Module.Fn, *ErrorMsg).init(allocator),
.failed_files = std.AutoHashMap(*Module.Scope.ZIRModule, *ErrorMsg).init(allocator),
.failed_exports = std.AutoHashMap(*Module.Export, *ErrorMsg).init(allocator),
};
};
defer module.deinit();

View File

@ -94,35 +94,40 @@ pub const ElfFile = struct {
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
/// Same order as in the file.
sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
shdr_table_offset: ?u64 = null,
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
/// Same order as in the file.
program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
phdr_table_offset: ?u64 = null,
/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
phdr_load_re_index: ?u16 = null,
/// The index into the program headers of the global offset table.
/// It needs PT_LOAD and Read flags.
phdr_got_index: ?u16 = null,
entry_addr: ?u64 = null,
shstrtab: std.ArrayListUnmanaged(u8) = .{},
shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
shstrtab_index: ?u16 = null,
text_section_index: ?u16 = null,
symtab_section_index: ?u16 = null,
got_section_index: ?u16 = null,
/// The same order as in the file
symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
/// Same order as in the file.
offset_table: std.ArrayListUnmanaged(aoeu) = .{},
/// Same order as in the file. The value is the absolute vaddr value.
/// If the vaddr of the executable program header changes, the entire
/// offset table needs to be rewritten.
offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
/// This means the entire read-only executable program code needs to be rewritten.
phdr_load_re_dirty: bool = false,
phdr_table_dirty: bool = false,
shdr_table_dirty: bool = false,
shstrtab_dirty: bool = false,
symtab_dirty: bool = false,
offset_table_count_dirty: bool = false,
symbol_count_dirty: bool = false,
error_flags: ErrorFlags = ErrorFlags{},
@ -130,18 +135,25 @@ pub const ElfFile = struct {
no_entry_point_found: bool = false,
};
/// TODO it's too bad this optional takes up double the memory it should
pub const Decl = struct {
/// Each decl always gets a local symbol with the fully qualified name.
/// The vaddr and size are found here directly.
/// The file offset is found by computing the vaddr offset from the section vaddr
/// the symbol references, and adding that to the file offset of the section.
local_sym_index: ?usize = null,
/// If this field is 0, it means the codegen size = 0 and there is no symbol or
/// offset table entry.
local_sym_index: u32,
/// This field is undefined for symbols with size = 0.
offset_table_index: u32,
pub const empty = Decl{
.local_sym_index = 0,
.offset_table_index = undefined,
};
};
/// TODO it's too bad this optional takes up double the memory it should
pub const Export = struct {
sym_index: ?usize = null,
sym_index: usize,
};
pub fn deinit(self: *ElfFile) void {
@ -250,33 +262,57 @@ pub const ElfFile = struct {
.p32 => true,
.p64 => false,
};
const ptr_size: u8 = switch (self.ptr_width) {
.p32 => 4,
.p64 => 8,
};
if (self.phdr_load_re_index == null) {
self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
const file_size = self.options.program_code_size_hint;
const p_align = 0x1000;
const off = self.findFreeSpace(file_size, p_align);
//std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
try self.program_headers.append(.{
try self.program_headers.append(self.allocator, .{
.p_type = elf.PT_LOAD,
.p_offset = off,
.p_filesz = file_size,
.p_vaddr = default_entry_addr,
.p_paddr = default_entry_addr,
.p_memsz = 0,
.p_memsz = file_size,
.p_align = p_align,
.p_flags = elf.PF_X | elf.PF_R,
});
self.entry_addr = null;
self.phdr_load_re_dirty = true;
self.phdr_table_dirty = true;
}
if (self.phdr_got_index == null) {
self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
const off = self.findFreeSpace(file_size, ptr_size);
//std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
// TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
// we'll need to re-use that function anyway, in case the GOT grows and overlaps something
// else in virtual memory.
const default_got_addr = 0x80000000;
try self.program_headers.append(self.allocator, .{
.p_type = elf.PT_LOAD,
.p_offset = off,
.p_filesz = file_size,
.p_vaddr = default_got_addr,
.p_paddr = default_got_addr,
.p_memsz = file_size,
.p_align = ptr_size,
.p_flags = elf.PF_R,
});
self.phdr_table_dirty = true;
}
if (self.shstrtab_index == null) {
self.shstrtab_index = @intCast(u16, self.sections.items.len);
assert(self.shstrtab.items.len == 0);
try self.shstrtab.append(0); // need a 0 at position 0
try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
const off = self.findFreeSpace(self.shstrtab.items.len, 1);
//std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
try self.sections.append(.{
try self.sections.append(self.allocator, .{
.sh_name = try self.makeString(".shstrtab"),
.sh_type = elf.SHT_STRTAB,
.sh_flags = 0,
@ -295,7 +331,7 @@ pub const ElfFile = struct {
self.text_section_index = @intCast(u16, self.sections.items.len);
const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
try self.sections.append(.{
try self.sections.append(self.allocator, .{
.sh_name = try self.makeString(".text"),
.sh_type = elf.SHT_PROGBITS,
.sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
@ -309,6 +345,24 @@ pub const ElfFile = struct {
});
self.shdr_table_dirty = true;
}
if (self.got_section_index == null) {
self.got_section_index = @intCast(u16, self.sections.items.len);
const phdr = &self.program_headers.items[self.phdr_got_index.?];
try self.sections.append(self.allocator, .{
.sh_name = try self.makeString(".got"),
.sh_type = elf.SHT_PROGBITS,
.sh_flags = elf.SHF_ALLOC,
.sh_addr = phdr.p_vaddr,
.sh_offset = phdr.p_offset,
.sh_size = phdr.p_filesz,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = phdr.p_align,
.sh_entsize = ptr_size,
});
self.shdr_table_dirty = true;
}
if (self.symtab_section_index == null) {
self.symtab_section_index = @intCast(u16, self.sections.items.len);
const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
@ -317,7 +371,7 @@ pub const ElfFile = struct {
const off = self.findFreeSpace(file_size, min_align);
//std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
try self.sections.append(.{
try self.sections.append(self.allocator, .{
.sh_name = try self.makeString(".symtab"),
.sh_type = elf.SHT_SYMTAB,
.sh_flags = 0,
@ -330,14 +384,14 @@ pub const ElfFile = struct {
.sh_addralign = min_align,
.sh_entsize = each_size,
});
self.symtab_dirty = true;
self.shdr_table_dirty = true;
try self.writeAllSymbols();
}
const shsize: u64 = switch (ptr_width) {
const shsize: u64 = switch (self.ptr_width) {
.p32 => @sizeOf(elf.Elf32_Shdr),
.p64 => @sizeOf(elf.Elf64_Shdr),
};
const shalign: u16 = switch (ptr_width) {
const shalign: u16 = switch (self.ptr_width) {
.p32 => @alignOf(elf.Elf32_Shdr),
.p64 => @alignOf(elf.Elf64_Shdr),
};
@ -345,11 +399,11 @@ pub const ElfFile = struct {
self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
self.shdr_table_dirty = true;
}
const phsize: u64 = switch (ptr_width) {
const phsize: u64 = switch (self.ptr_width) {
.p32 => @sizeOf(elf.Elf32_Phdr),
.p64 => @sizeOf(elf.Elf64_Phdr),
};
const phalign: u16 = switch (ptr_width) {
const phalign: u16 = switch (self.ptr_width) {
.p32 => @alignOf(elf.Elf32_Phdr),
.p64 => @alignOf(elf.Elf64_Phdr),
};
@ -399,7 +453,7 @@ pub const ElfFile = struct {
try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
},
}
self.phdr_table_offset = false;
self.phdr_table_dirty = false;
}
{
@ -432,11 +486,10 @@ pub const ElfFile = struct {
self.shdr_table_offset = self.findFreeSpace(needed_size, phalign);
}
const allocator = self.sections.allocator;
switch (self.ptr_width) {
.p32 => {
const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
defer allocator.free(buf);
const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
defer self.allocator.free(buf);
for (buf) |*shdr, i| {
shdr.* = sectHeaderTo32(self.sections.items[i]);
@ -447,8 +500,8 @@ pub const ElfFile = struct {
try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
},
.p64 => {
const buf = try allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
defer allocator.free(buf);
const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
defer self.allocator.free(buf);
for (buf) |*shdr, i| {
shdr.* = self.sections.items[i];
@ -460,6 +513,7 @@ pub const ElfFile = struct {
try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
},
}
self.shdr_table_dirty = false;
}
if (self.entry_addr == null and self.options.output_mode == .Exe) {
self.error_flags.no_entry_point_found = true;
@ -470,11 +524,11 @@ pub const ElfFile = struct {
// TODO find end pos and truncate
// The point of flush() is to commit changes, so nothing should be dirty after this.
assert(!self.phdr_load_re_dirty);
assert(!self.phdr_table_dirty);
assert(!self.shdr_table_dirty);
assert(!self.shstrtab_dirty);
assert(!self.symtab_dirty);
assert(!self.symbol_count_dirty);
assert(!self.offset_table_count_dirty);
}
fn writeElfHeader(self: *ElfFile) !void {
@ -608,6 +662,7 @@ pub const ElfFile = struct {
const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
const shdr = &self.sections.items[self.text_section_index.?];
// TODO Also detect virtual address collisions.
const text_capacity = self.allocatedSize(shdr.sh_offset);
// TODO instead of looping here, maintain a free list and a pointer to the end.
const end_vaddr = blk: {
@ -664,7 +719,7 @@ pub const ElfFile = struct {
defer code.deinit();
const typed_value = decl.typed_value.most_recent.typed_value;
const err_msg = try codegen.generateSymbol(typed_value, module, &code, module.allocator);
const err_msg = try codegen.generateSymbol(typed_value, module, &code);
if (err_msg != null) |em| {
decl.analysis = .codegen_failure;
_ = try module.failed_decls.put(decl, em);
@ -678,26 +733,31 @@ pub const ElfFile = struct {
else => elf.STT_OBJECT,
};
if (decl.link.local_sym_index) |local_sym_index| {
const local_sym = &self.symbols.items[local_sym_index];
if (decl.link.local_sym_index != 0) {
const local_sym = &self.symbols.items[decl.link.local_sym_index];
const existing_block = self.findAllocatedTextBlock(local_sym);
const file_offset = if (code_size > existing_block.size_capacity) fo: {
const new_block = self.allocateTextBlock(code_size);
const new_block = try self.allocateTextBlock(code_size);
local_sym.st_value = new_block.vaddr;
local_sym.st_size = code_size;
try self.writeOffsetTableEntry(decl.link.offset_table_index);
break :fo new_block.file_offset;
} else existing_block.file_offset;
local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(u8, decl.name));
local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
// TODO this write could be avoided if no fields of the symbol were changed.
try self.writeSymbol(local_sym_index);
try self.writeSymbol(decl.link.local_sym_index);
break :blk file_offset;
} else {
try self.symbols.ensureCapacity(self.symbols.items.len + 1);
try self.offset_table.ensureCapacity(self.offset_table.items.len + 1);
const decl_name = mem.spanZ(u8, decl.name);
const name_str_index = try self.makeString(decl_name);
const new_block = self.allocateTextBlock(code_size);
const new_block = try self.allocateTextBlock(code_size);
const local_sym_index = self.symbols.items.len;
const offset_table_index = self.offset_table.items.len;
self.symbols.appendAssumeCapacity(self.allocator, .{
.st_name = name_str_index,
@ -708,10 +768,17 @@ pub const ElfFile = struct {
.st_size = code_size,
});
errdefer self.symbols.shrink(self.symbols.items.len - 1);
self.offset_table.appendAssumeCapacity(self.allocator, new_block.vaddr);
errdefer self.offset_table.shrink(self.offset_table.items.len - 1);
try self.writeSymbol(local_sym_index);
try self.writeOffsetTableEntry(offset_table_index);
self.symbol_count_dirty = true;
decl.link.local_sym_index = local_sym_index;
self.offset_table_count_dirty = true;
decl.link = .{
.local_sym_index = local_sym_index,
.offset_table_index = offset_table_index,
};
break :blk new_block.file_offset;
}
@ -839,6 +906,45 @@ pub const ElfFile = struct {
}
}
fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
const shdr = &self.sections.items[self.got_section_index.?];
const phdr = &self.program_headers.items[self.phdr_got_index.?];
if (self.offset_table_count_dirty) {
// TODO Also detect virtual address collisions.
const allocated_size = self.allocatedSize(shdr.sh_offset);
const needed_size = self.symbols.items.len * shdr.sh_entsize;
if (needed_size > allocated_size) {
// Must move the entire got section.
const new_offset = self.findFreeSpace(needed_size, shdr.sh_entsize);
const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size);
if (amt != text_size) return error.InputOutput;
shdr.sh_offset = new_offset;
}
shdr.sh_size = needed_size;
phdr.p_memsz = needed_size;
phdr.p_filesz = needed_size;
self.shdr_table_dirty = true; // TODO look into making only the one section dirty
self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
self.offset_table_count_dirty = false;
}
const endian = self.options.target.cpu.arch.endian();
const off = shdr.sh_offset + shdr.sh_entsize * index;
switch (self.ptr_width) {
.p32 => {
var buf: [4]u8 = undefined;
mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
try self.file.pwriteAll(&buf, off);
},
.p64 => {
var buf: [8]u8 = undefined;
mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
try self.file.pwriteAll(&buf, off);
},
}
}
fn writeSymbol(self: *ElfFile, index: usize) !void {
const syms_sect = &self.sections.items[self.symtab_section_index.?];
// Make sure we are not pointlessly writing symbol data that will have to get relocated
@ -849,6 +955,9 @@ pub const ElfFile = struct {
if (needed_size > allocated_size) {
return self.writeAllSymbols();
}
syms_sect.sh_info = @intCast(u32, self.symbols.items.len);
self.shdr_table_dirty = true; // TODO look into only writing one section
self.symbol_count_dirty = false;
}
const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
switch (self.ptr_width) {
@ -896,12 +1005,13 @@ pub const ElfFile = struct {
//std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
syms_sect.sh_size = needed_size;
syms_sect.sh_info = @intCast(u32, self.symbols.items.len);
const allocator = self.symbols.allocator;
self.symbol_count_dirty = false;
self.shdr_table_dirty = true; // TODO look into only writing one section
const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
switch (self.ptr_width) {
.p32 => {
const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len);
defer allocator.free(buf);
const buf = try self.allocator.alloc(elf.Elf32_Sym, self.symbols.items.len);
defer self.allocator.free(buf);
for (buf) |*sym, i| {
sym.* = .{
@ -919,8 +1029,8 @@ pub const ElfFile = struct {
try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);
},
.p64 => {
const buf = try allocator.alloc(elf.Elf64_Sym, self.symbols.items.len);
defer allocator.free(buf);
const buf = try self.allocator.alloc(elf.Elf64_Sym, self.symbols.items.len);
defer self.allocator.free(buf);
for (buf) |*sym, i| {
sym.* = .{
@ -961,12 +1071,11 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
.allocator = allocator,
.file = file,
.options = options,
.ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) {
.ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
32 => .p32,
64 => .p64,
else => return error.UnsupportedELFArchitecture,
},
.symtab_dirty = true,
.shdr_table_dirty = true,
};
errdefer self.deinit();
@ -1018,7 +1127,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
.allocator = allocator,
.file = file,
.options = options,
.ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) {
.ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
32 => .p32,
64 => .p64,
else => return error.UnsupportedELFArchitecture,