stage2: add DWARF info for the main compilation unit
This commit is contained in:
parent
f962315363
commit
f1c1b8c02d
@ -9,7 +9,7 @@ const leb = @import("debug/leb128.zig");
|
||||
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
usingnamespace @import("dwarf_bits.zig");
|
||||
pub usingnamespace @import("dwarf_bits.zig");
|
||||
|
||||
const PcRange = struct {
|
||||
start: u64,
|
||||
|
@ -680,3 +680,12 @@ pub const LANG_HP_Basic91 = 0x8004;
|
||||
pub const LANG_HP_Pascal91 = 0x8005;
|
||||
pub const LANG_HP_IMacro = 0x8006;
|
||||
pub const LANG_HP_Assembler = 0x8007;
|
||||
|
||||
pub const UT_compile = 0x01;
|
||||
pub const UT_type = 0x02;
|
||||
pub const UT_partial = 0x03;
|
||||
pub const UT_skeleton = 0x04;
|
||||
pub const UT_split_compile = 0x05;
|
||||
pub const UT_split_type = 0x06;
|
||||
pub const UT_lo_user = 0x80;
|
||||
pub const UT_hi_user = 0xff;
|
||||
|
@ -75,6 +75,8 @@ next_anon_name_index: usize = 0,
|
||||
/// contains Decls that need to be deleted if they end up having no references to them.
|
||||
deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
|
||||
|
||||
/// Owned by Module.
|
||||
root_name: []u8,
|
||||
keep_source_files_loaded: bool,
|
||||
|
||||
pub const InnerError = error{ OutOfMemory, AnalysisFail };
|
||||
@ -772,6 +774,7 @@ pub const AllErrors = struct {
|
||||
|
||||
pub const InitOptions = struct {
|
||||
target: std.Target,
|
||||
root_name: []const u8,
|
||||
root_pkg: *Package,
|
||||
output_mode: std.builtin.OutputMode,
|
||||
bin_file_dir: ?std.fs.Dir = null,
|
||||
@ -783,8 +786,13 @@ pub const InitOptions = struct {
|
||||
};
|
||||
|
||||
pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
const root_name = try gpa.dupe(u8, options.root_name);
|
||||
errdefer gpa.free(root_name);
|
||||
|
||||
const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
|
||||
const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
|
||||
.root_name = root_name,
|
||||
.root_src_dir_path = options.root_pkg.root_src_dir_path,
|
||||
.target = options.target,
|
||||
.output_mode = options.output_mode,
|
||||
.link_mode = options.link_mode orelse .Static,
|
||||
@ -821,6 +829,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
|
||||
return Module{
|
||||
.gpa = gpa,
|
||||
.root_name = root_name,
|
||||
.root_pkg = options.root_pkg,
|
||||
.root_scope = root_scope,
|
||||
.bin_file_dir = bin_file_dir,
|
||||
@ -834,6 +843,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
pub fn deinit(self: *Module) void {
|
||||
self.bin_file.destroy();
|
||||
const gpa = self.gpa;
|
||||
self.gpa.free(self.root_name);
|
||||
self.deletion_set.deinit(gpa);
|
||||
self.work_queue.deinit();
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
pub const Table = std.StringHashMap(*Package);
|
||||
|
||||
/// This should be used for file operations.
|
||||
root_src_dir: std.fs.Dir,
|
||||
/// Relative to `root_src_dir`.
|
||||
root_src_path: []const u8,
|
||||
/// This is for metadata purposes, for example putting into debug information.
|
||||
root_src_dir_path: []u8,
|
||||
/// Relative to `root_src_dir` and `root_src_dir_path`.
|
||||
root_src_path: []u8,
|
||||
table: Table,
|
||||
|
||||
/// No references to `root_src_dir` and `root_src_path` are kept.
|
||||
@ -18,8 +21,11 @@ pub fn create(
|
||||
errdefer allocator.destroy(ptr);
|
||||
const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);
|
||||
errdefer allocator.free(root_src_path_dupe);
|
||||
const root_src_dir_path = try mem.dupe(allocator, u8, root_src_dir);
|
||||
errdefer allocator.free(root_src_dir_path);
|
||||
ptr.* = .{
|
||||
.root_src_dir = try base_dir.openDir(root_src_dir, .{}),
|
||||
.root_src_dir_path = root_src_dir_path,
|
||||
.root_src_path = root_src_path_dupe,
|
||||
.table = Table.init(allocator),
|
||||
};
|
||||
@ -30,6 +36,7 @@ pub fn destroy(self: *Package) void {
|
||||
const allocator = self.table.allocator;
|
||||
self.root_src_dir.close();
|
||||
allocator.free(self.root_src_path);
|
||||
allocator.free(self.root_src_dir_path);
|
||||
{
|
||||
var it = self.table.iterator();
|
||||
while (it.next()) |kv| {
|
||||
@ -41,10 +48,9 @@ pub fn destroy(self: *Package) void {
|
||||
}
|
||||
|
||||
pub fn add(self: *Package, name: []const u8, package: *Package) !void {
|
||||
try self.table.ensureCapacity(self.table.items().len + 1);
|
||||
const name_dupe = try mem.dupe(self.table.allocator, u8, name);
|
||||
errdefer self.table.allocator.deinit(name_dupe);
|
||||
const entry = try self.table.put(name_dupe, package);
|
||||
assert(entry == null);
|
||||
self.table.putAssumeCapacityNoClobber(name_dupe, package);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
@ -8,6 +8,11 @@ const fs = std.fs;
|
||||
const elf = std.elf;
|
||||
const codegen = @import("codegen.zig");
|
||||
const c_codegen = @import("codegen/c.zig");
|
||||
const log = std.log;
|
||||
const DW = std.dwarf;
|
||||
|
||||
// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
|
||||
// zig fmt: off
|
||||
|
||||
const default_entry_addr = 0x8000000;
|
||||
|
||||
@ -17,6 +22,8 @@ pub const Options = struct {
|
||||
link_mode: std.builtin.LinkMode,
|
||||
object_format: std.builtin.ObjectFormat,
|
||||
optimize_mode: std.builtin.Mode,
|
||||
root_name: []const u8,
|
||||
root_src_dir_path: []const u8,
|
||||
/// Used for calculating how much space to reserve for symbols in case the binary file
|
||||
/// does not already have a symbol table.
|
||||
symbol_count_hint: u64 = 32,
|
||||
@ -319,12 +326,18 @@ pub const File = struct {
|
||||
phdr_got_index: ?u16 = null,
|
||||
entry_addr: ?u64 = null,
|
||||
|
||||
debug_strtab: std.ArrayListUnmanaged(u8) = 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,
|
||||
debug_info_section_index: ?u16 = null,
|
||||
debug_abbrev_section_index: ?u16 = null,
|
||||
debug_str_section_index: ?u16 = null,
|
||||
|
||||
debug_abbrev_table_offset: ?u64 = null,
|
||||
|
||||
/// The same order as in the file. ELF requires global symbols to all be after the
|
||||
/// local symbols, they cannot be mixed. So we must buffer all the global symbols and
|
||||
@ -345,7 +358,10 @@ pub const File = struct {
|
||||
phdr_table_dirty: bool = false,
|
||||
shdr_table_dirty: bool = false,
|
||||
shstrtab_dirty: bool = false,
|
||||
debug_strtab_dirty: bool = false,
|
||||
offset_table_count_dirty: bool = false,
|
||||
debug_info_section_dirty: bool = false,
|
||||
debug_abbrev_section_dirty: bool = false,
|
||||
|
||||
error_flags: ErrorFlags = ErrorFlags{},
|
||||
|
||||
@ -434,6 +450,7 @@ pub const File = struct {
|
||||
self.sections.deinit(self.allocator);
|
||||
self.program_headers.deinit(self.allocator);
|
||||
self.shstrtab.deinit(self.allocator);
|
||||
self.debug_strtab.deinit(self.allocator);
|
||||
self.local_symbols.deinit(self.allocator);
|
||||
self.global_symbols.deinit(self.allocator);
|
||||
self.global_symbol_free_list.deinit(self.allocator);
|
||||
@ -545,6 +562,14 @@ pub const File = struct {
|
||||
return @intCast(u32, result);
|
||||
}
|
||||
|
||||
fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
|
||||
try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1);
|
||||
const result = self.debug_strtab.items.len;
|
||||
self.debug_strtab.appendSliceAssumeCapacity(bytes);
|
||||
self.debug_strtab.appendAssumeCapacity(0);
|
||||
return @intCast(u32, result);
|
||||
}
|
||||
|
||||
fn getString(self: *Elf, str_off: u32) []const u8 {
|
||||
assert(str_off < self.shstrtab.items.len);
|
||||
return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
|
||||
@ -572,7 +597,7 @@ pub const File = struct {
|
||||
const file_size = self.base.options.program_code_size_hint;
|
||||
const p_align = 0x1000;
|
||||
const off = self.findFreeSpace(file_size, p_align);
|
||||
std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
|
||||
log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
|
||||
try self.program_headers.append(self.allocator, .{
|
||||
.p_type = elf.PT_LOAD,
|
||||
.p_offset = off,
|
||||
@ -593,7 +618,7 @@ pub const File = struct {
|
||||
// page align.
|
||||
const p_align = 0x1000;
|
||||
const off = self.findFreeSpace(file_size, p_align);
|
||||
std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
|
||||
log.debug(.link, "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.
|
||||
@ -615,7 +640,7 @@ pub const File = struct {
|
||||
assert(self.shstrtab.items.len == 0);
|
||||
try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
|
||||
const off = self.findFreeSpace(self.shstrtab.items.len, 1);
|
||||
std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
|
||||
log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
|
||||
try self.sections.append(self.allocator, .{
|
||||
.sh_name = try self.makeString(".shstrtab"),
|
||||
.sh_type = elf.SHT_STRTAB,
|
||||
@ -631,6 +656,27 @@ pub const File = struct {
|
||||
self.shstrtab_dirty = true;
|
||||
self.shdr_table_dirty = true;
|
||||
}
|
||||
if (self.debug_str_section_index == null) {
|
||||
self.debug_str_section_index = @intCast(u16, self.sections.items.len);
|
||||
assert(self.debug_strtab.items.len == 0);
|
||||
try self.debug_strtab.append(self.allocator, 0); // need a 0 at position 0
|
||||
const off = self.findFreeSpace(self.debug_strtab.items.len, 1);
|
||||
log.debug(.link, "found debug_strtab free space 0x{x} to 0x{x}\n", .{ off, off + self.debug_strtab.items.len });
|
||||
try self.sections.append(self.allocator, .{
|
||||
.sh_name = try self.makeString(".debug_str"),
|
||||
.sh_type = elf.SHT_PROGBITS,
|
||||
.sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
|
||||
.sh_addr = 0,
|
||||
.sh_offset = off,
|
||||
.sh_size = self.debug_strtab.items.len,
|
||||
.sh_link = 0,
|
||||
.sh_info = 0,
|
||||
.sh_addralign = 1,
|
||||
.sh_entsize = 1,
|
||||
});
|
||||
self.debug_strtab_dirty = true;
|
||||
self.shdr_table_dirty = true;
|
||||
}
|
||||
if (self.text_section_index == null) {
|
||||
self.text_section_index = @intCast(u16, self.sections.items.len);
|
||||
const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
|
||||
@ -673,7 +719,7 @@ pub const File = struct {
|
||||
const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
|
||||
const file_size = self.base.options.symbol_count_hint * each_size;
|
||||
const off = self.findFreeSpace(file_size, min_align);
|
||||
std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
|
||||
log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
|
||||
|
||||
try self.sections.append(self.allocator, .{
|
||||
.sh_name = try self.makeString(".symtab"),
|
||||
@ -691,6 +737,56 @@ pub const File = struct {
|
||||
self.shdr_table_dirty = true;
|
||||
try self.writeSymbol(0);
|
||||
}
|
||||
if (self.debug_info_section_index == null) {
|
||||
self.debug_info_section_index = @intCast(u16, self.sections.items.len);
|
||||
|
||||
const file_size_hint = 200;
|
||||
const p_align = 1;
|
||||
const off = self.findFreeSpace(file_size_hint, p_align);
|
||||
log.debug(.link, "found .debug_info free space 0x{x} to 0x{x}\n", .{
|
||||
off,
|
||||
off + file_size_hint,
|
||||
});
|
||||
try self.sections.append(self.allocator, .{
|
||||
.sh_name = try self.makeString(".debug_info"),
|
||||
.sh_type = elf.SHT_PROGBITS,
|
||||
.sh_flags = 0,
|
||||
.sh_addr = 0,
|
||||
.sh_offset = off,
|
||||
.sh_size = file_size_hint,
|
||||
.sh_link = 0,
|
||||
.sh_info = 0,
|
||||
.sh_addralign = p_align,
|
||||
.sh_entsize = 0,
|
||||
});
|
||||
self.shdr_table_dirty = true;
|
||||
self.debug_info_section_dirty = true;
|
||||
}
|
||||
if (self.debug_abbrev_section_index == null) {
|
||||
self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
|
||||
|
||||
const file_size_hint = 128;
|
||||
const p_align = 1;
|
||||
const off = self.findFreeSpace(file_size_hint, p_align);
|
||||
log.debug(.link, "found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
|
||||
off,
|
||||
off + file_size_hint,
|
||||
});
|
||||
try self.sections.append(self.allocator, .{
|
||||
.sh_name = try self.makeString(".debug_abbrev"),
|
||||
.sh_type = elf.SHT_PROGBITS,
|
||||
.sh_flags = 0,
|
||||
.sh_addr = 0,
|
||||
.sh_offset = off,
|
||||
.sh_size = file_size_hint,
|
||||
.sh_link = 0,
|
||||
.sh_info = 0,
|
||||
.sh_addralign = p_align,
|
||||
.sh_entsize = 0,
|
||||
});
|
||||
self.shdr_table_dirty = true;
|
||||
self.debug_abbrev_section_dirty = true;
|
||||
}
|
||||
const shsize: u64 = switch (self.ptr_width) {
|
||||
.p32 => @sizeOf(elf.Elf32_Shdr),
|
||||
.p64 => @sizeOf(elf.Elf64_Shdr),
|
||||
@ -726,12 +822,141 @@ pub const File = struct {
|
||||
|
||||
/// Commit pending changes and write headers.
|
||||
pub fn flush(self: *Elf) !void {
|
||||
const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const target_endian = self.base.options.target.cpu.arch.endian();
|
||||
const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
|
||||
|
||||
// Unfortunately these have to be buffered and done at the end because ELF does not allow
|
||||
// mixing local and global symbols within a symbol table.
|
||||
try self.writeAllGlobalSymbols();
|
||||
|
||||
if (self.debug_abbrev_section_dirty) {
|
||||
const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];
|
||||
|
||||
// These are LEB encoded but since the values are all less than 127
|
||||
// we can simply append these bytes.
|
||||
const abbrev_buf = [_]u8{
|
||||
1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
|
||||
//DW.AT_stmt_list, DW.FORM_data4, TODO
|
||||
DW.AT_low_pc , DW.FORM_addr,
|
||||
DW.AT_high_pc , DW.FORM_addr,
|
||||
DW.AT_name , DW.FORM_strp,
|
||||
DW.AT_comp_dir , DW.FORM_strp,
|
||||
DW.AT_producer , DW.FORM_strp,
|
||||
DW.AT_language , DW.FORM_data2,
|
||||
0, 0, // table sentinel
|
||||
|
||||
0, 0, 0, // section sentinel
|
||||
};
|
||||
|
||||
const needed_size = abbrev_buf.len;
|
||||
const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
|
||||
if (needed_size > allocated_size) {
|
||||
debug_abbrev_sect.sh_size = 0; // free the space
|
||||
debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
|
||||
}
|
||||
debug_abbrev_sect.sh_size = needed_size;
|
||||
log.debug(.link, ".debug_abbrev start=0x{x} end=0x{x}\n", .{
|
||||
debug_abbrev_sect.sh_offset,
|
||||
debug_abbrev_sect.sh_offset + needed_size,
|
||||
});
|
||||
|
||||
const abbrev_offset = 0;
|
||||
self.debug_abbrev_table_offset = abbrev_offset;
|
||||
try self.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
|
||||
if (!self.shdr_table_dirty) {
|
||||
// Then it won't get written with the others and we need to do it.
|
||||
try self.writeSectHeader(self.debug_abbrev_section_index.?);
|
||||
}
|
||||
|
||||
self.debug_abbrev_section_dirty = false;
|
||||
}
|
||||
if (self.debug_info_section_dirty) {
|
||||
const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
|
||||
|
||||
var di_buf = std.ArrayList(u8).init(self.allocator);
|
||||
defer di_buf.deinit();
|
||||
|
||||
// Enough for a 64-bit header and main compilation unit without resizing.
|
||||
try di_buf.ensureCapacity(100);
|
||||
|
||||
// initial length - length of the .debug_info contribution for this compilation unit,
|
||||
// not including the initial length itself.
|
||||
// We have to come back and write it later after we know the size.
|
||||
const init_len_index = di_buf.items.len;
|
||||
switch (self.ptr_width) {
|
||||
.p32 => di_buf.items.len += 4,
|
||||
.p64 => di_buf.items.len += 12,
|
||||
}
|
||||
const after_init_len = di_buf.items.len;
|
||||
mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 5, target_endian); // DWARF version
|
||||
di_buf.appendAssumeCapacity(DW.UT_compile);
|
||||
const abbrev_offset = self.debug_abbrev_table_offset.?;
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
di_buf.appendAssumeCapacity(4); // address size
|
||||
mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
|
||||
},
|
||||
.p64 => {
|
||||
di_buf.appendAssumeCapacity(8); // address size
|
||||
mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
|
||||
},
|
||||
}
|
||||
// Write the form for the compile unit, which must match the abbrev table above.
|
||||
const name_strp = try self.makeDebugString(self.base.options.root_name);
|
||||
const comp_dir_strp = try self.makeDebugString(self.base.options.root_src_dir_path);
|
||||
const producer_strp = try self.makeDebugString("zig (TODO version here)");
|
||||
// Currently only one compilation unit is supported, so the address range is simply
|
||||
// identical to the main program header virtual address and memory size.
|
||||
const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
|
||||
const low_pc = text_phdr.p_vaddr;
|
||||
const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
|
||||
|
||||
di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header
|
||||
//DW.AT_stmt_list, DW.FORM_data4, TODO line information
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
|
||||
// We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
|
||||
// http://dwarfstd.org/ShowIssue.php?issue=171115.1
|
||||
// Until then we say it is C99.
|
||||
mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
|
||||
|
||||
const init_len = di_buf.items.len - after_init_len;
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
|
||||
},
|
||||
.p64 => {
|
||||
// initial length - length of the .debug_info contribution for this compilation unit,
|
||||
// not including the initial length itself.
|
||||
di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
|
||||
mem.writeInt(u64, di_buf.items[init_len_index + 4..][0..8], init_len, target_endian);
|
||||
},
|
||||
}
|
||||
|
||||
const needed_size = di_buf.items.len;
|
||||
const allocated_size = self.allocatedSize(debug_info_sect.sh_offset);
|
||||
if (needed_size > allocated_size) {
|
||||
debug_info_sect.sh_size = 0; // free the space
|
||||
debug_info_sect.sh_offset = self.findFreeSpace(needed_size, 1);
|
||||
}
|
||||
debug_info_sect.sh_size = needed_size;
|
||||
log.debug(.link, ".debug_info start=0x{x} end=0x{x}\n", .{
|
||||
debug_info_sect.sh_offset,
|
||||
debug_info_sect.sh_offset + needed_size,
|
||||
});
|
||||
|
||||
try self.file.?.pwriteAll(di_buf.items, debug_info_sect.sh_offset);
|
||||
if (!self.shdr_table_dirty) {
|
||||
// Then it won't get written with the others and we need to do it.
|
||||
try self.writeSectHeader(self.debug_info_section_index.?);
|
||||
}
|
||||
|
||||
self.debug_info_section_dirty = false;
|
||||
}
|
||||
|
||||
if (self.phdr_table_dirty) {
|
||||
const phsize: u64 = switch (self.ptr_width) {
|
||||
.p32 => @sizeOf(elf.Elf32_Phdr),
|
||||
@ -789,7 +1014,7 @@ pub const File = struct {
|
||||
shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
|
||||
}
|
||||
shstrtab_sect.sh_size = needed_size;
|
||||
std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
|
||||
log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
|
||||
|
||||
try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
|
||||
if (!self.shdr_table_dirty) {
|
||||
@ -799,6 +1024,27 @@ pub const File = struct {
|
||||
self.shstrtab_dirty = false;
|
||||
}
|
||||
}
|
||||
{
|
||||
const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
|
||||
if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {
|
||||
const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
|
||||
const needed_size = self.debug_strtab.items.len;
|
||||
|
||||
if (needed_size > allocated_size) {
|
||||
debug_strtab_sect.sh_size = 0; // free the space
|
||||
debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
|
||||
}
|
||||
debug_strtab_sect.sh_size = needed_size;
|
||||
log.debug(.link, "debug_strtab start=0x{x} end=0x{x}\n", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
|
||||
|
||||
try self.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
|
||||
if (!self.shdr_table_dirty) {
|
||||
// Then it won't get written with the others and we need to do it.
|
||||
try self.writeSectHeader(self.debug_str_section_index.?);
|
||||
}
|
||||
self.debug_strtab_dirty = false;
|
||||
}
|
||||
}
|
||||
if (self.shdr_table_dirty) {
|
||||
const shsize: u64 = switch (self.ptr_width) {
|
||||
.p32 => @sizeOf(elf.Elf32_Shdr),
|
||||
@ -835,7 +1081,7 @@ pub const File = struct {
|
||||
|
||||
for (buf) |*shdr, i| {
|
||||
shdr.* = self.sections.items[i];
|
||||
std.log.debug(.link, "writing section {}\n", .{shdr.*});
|
||||
log.debug(.link, "writing section {}\n", .{shdr.*});
|
||||
if (foreign_endian) {
|
||||
bswapAllFields(elf.Elf64_Shdr, shdr);
|
||||
}
|
||||
@ -846,7 +1092,7 @@ pub const File = struct {
|
||||
self.shdr_table_dirty = false;
|
||||
}
|
||||
if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
|
||||
std.log.debug(.link, "no_entry_point_found = true\n", .{});
|
||||
log.debug(.link, "no_entry_point_found = true\n", .{});
|
||||
self.error_flags.no_entry_point_found = true;
|
||||
} else {
|
||||
self.error_flags.no_entry_point_found = false;
|
||||
@ -854,14 +1100,25 @@ pub const File = struct {
|
||||
}
|
||||
|
||||
// The point of flush() is to commit changes, so nothing should be dirty after this.
|
||||
assert(!self.debug_info_section_dirty);
|
||||
assert(!self.debug_abbrev_section_dirty);
|
||||
assert(!self.phdr_table_dirty);
|
||||
assert(!self.shdr_table_dirty);
|
||||
assert(!self.shstrtab_dirty);
|
||||
assert(!self.debug_strtab_dirty);
|
||||
assert(!self.offset_table_count_dirty);
|
||||
const syms_sect = &self.sections.items[self.symtab_section_index.?];
|
||||
assert(syms_sect.sh_info == self.local_symbols.items.len);
|
||||
}
|
||||
|
||||
fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
|
||||
const target_endian = self.base.options.target.cpu.arch.endian();
|
||||
switch (self.ptr_width) {
|
||||
.p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian),
|
||||
.p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
|
||||
}
|
||||
}
|
||||
|
||||
fn writeElfHeader(self: *Elf) !void {
|
||||
var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
|
||||
|
||||
@ -1122,6 +1379,11 @@ pub const File = struct {
|
||||
phdr.p_memsz = needed_size;
|
||||
phdr.p_filesz = needed_size;
|
||||
|
||||
// The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
|
||||
// range of the compilation unit. When we expand the text section, this range changes,
|
||||
// so the .debug_info section becomes dirty.
|
||||
self.debug_info_section_dirty = true;
|
||||
|
||||
self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
|
||||
self.shdr_table_dirty = true; // TODO look into making only the one section dirty
|
||||
}
|
||||
@ -1160,10 +1422,10 @@ pub const File = struct {
|
||||
try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
|
||||
|
||||
if (self.local_symbol_free_list.popOrNull()) |i| {
|
||||
std.log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
|
||||
log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
|
||||
decl.link.local_sym_index = i;
|
||||
} else {
|
||||
std.log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
|
||||
log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
|
||||
decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
|
||||
_ = self.local_symbols.addOneAssumeCapacity();
|
||||
}
|
||||
@ -1231,11 +1493,11 @@ pub const File = struct {
|
||||
!mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
|
||||
if (need_realloc) {
|
||||
const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
|
||||
std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
|
||||
log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
|
||||
if (vaddr != local_sym.st_value) {
|
||||
local_sym.st_value = vaddr;
|
||||
|
||||
std.log.debug(.link, " (writing new offset table entry)\n", .{});
|
||||
log.debug(.link, " (writing new offset table entry)\n", .{});
|
||||
self.offset_table.items[decl.link.offset_table_index] = vaddr;
|
||||
try self.writeOffsetTableEntry(decl.link.offset_table_index);
|
||||
}
|
||||
@ -1253,7 +1515,7 @@ pub const File = struct {
|
||||
const decl_name = mem.spanZ(decl.name);
|
||||
const name_str_index = try self.makeString(decl_name);
|
||||
const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
|
||||
std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
|
||||
log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
|
||||
errdefer self.freeTextBlock(&decl.link);
|
||||
|
||||
local_sym.* = .{
|
||||
|
@ -94,6 +94,8 @@ pub fn main() !void {
|
||||
return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
|
||||
} else if (mem.eql(u8, cmd, "version")) {
|
||||
// Need to set up the build script to give the version as a comptime value.
|
||||
// TODO when you solve this, also take a look at link.zig, there is a placeholder
|
||||
// that says "TODO version here".
|
||||
std.debug.print("TODO version command not implemented yet\n", .{});
|
||||
return error.Unimplemented;
|
||||
} else if (mem.eql(u8, cmd, "zen")) {
|
||||
@ -492,6 +494,7 @@ fn buildOutputType(
|
||||
defer root_pkg.destroy();
|
||||
|
||||
var module = try Module.init(gpa, .{
|
||||
.root_name = root_name,
|
||||
.target = target_info.target,
|
||||
.output_mode = output_mode,
|
||||
.root_pkg = root_pkg,
|
||||
|
@ -433,6 +433,7 @@ pub const TestContext = struct {
|
||||
defer allocator.free(bin_name);
|
||||
|
||||
var module = try Module.init(allocator, .{
|
||||
.root_name = "test_case",
|
||||
.target = target,
|
||||
// TODO: support tests for object file building, and library builds
|
||||
// and linking. This will require a rework to support multi-file
|
||||
|
Loading…
x
Reference in New Issue
Block a user