Merge pull request #9652 from g-w1/p9d

plan9: emit debug info
This commit is contained in:
Veikka Tuominen 2021-09-21 19:38:12 +03:00 committed by GitHub
commit a2dd0c387d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 414 additions and 70 deletions

View File

@ -3707,7 +3707,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
},
.plan9 => {
// TODO implement for plan9
// TODO Look into detecting when this would be unnecessary by storing enough state
// in `Decl` to notice that the line number did not change.
mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
},
.c, .wasm, .spirv => {},
}

View File

@ -48,6 +48,28 @@ pub const DebugInfoOutput = union(enum) {
dbg_info: *std.ArrayList(u8),
dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
},
/// the plan9 debuginfo output is a bytecode with 4 opcodes
/// assume all numbers/variables are bytes
/// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset
/// x when x < 65 -> add x to line offset
/// x when x < 129 -> subtract 64 from x and subtract it from the line offset
/// x -> subtract 129 from x, multiply it by the quanta of the instruction size
/// (1 on x86_64), and add it to the pc
/// after every opcode, add the quanta of the instruction size to the pc
plan9: struct {
/// the actual opcodes
dbg_line: *std.ArrayList(u8),
/// what line the debuginfo starts on
/// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
start_line: *?u32,
/// what the line count ends on after codegen
/// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
end_line: *u32,
/// the last pc change op
/// This is very useful for adding quanta
/// to it if its not actually the last one.
pcop_change_index: *?u32,
},
none,
};
@ -915,6 +937,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
},
.plan9 => {},
.none => {},
}
}
@ -925,15 +948,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
},
.plan9 => {},
.none => {},
}
}
fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
const delta_pc: usize = self.code.items.len - self.prev_di_pc;
switch (self.debug_output) {
.dwarf => |dbg_out| {
const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
const delta_pc = self.code.items.len - self.prev_di_pc;
// TODO Look into using the DWARF special opcodes to compress this data.
// It lets you emit single-byte opcodes that add different numbers to
// both the PC and the line number at the same time.
@ -945,12 +969,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
}
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
self.prev_di_pc = self.code.items.len;
self.prev_di_line = line;
self.prev_di_column = column;
self.prev_di_pc = self.code.items.len;
},
.plan9 => |dbg_out| {
if (delta_pc <= 0) return; // only do this when the pc changes
// we have already checked the target in the linker to make sure it is compatable
const quant = @import("link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
// increasing the line number
try @import("link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
// increasing the pc
const d_pc_p9 = @intCast(i64, delta_pc) - quant;
if (d_pc_p9 > 0) {
// minus one becaue if its the last one, we want to leave space to change the line which is one quanta
try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
if (dbg_out.pcop_change_index.*) |pci|
dbg_out.dbg_line.items[pci] += 1;
dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
} else if (d_pc_p9 == 0) {
// we don't need to do anything, because adding the quant does it for us
} else unreachable;
if (dbg_out.start_line.* == null)
dbg_out.start_line.* = self.prev_di_line;
dbg_out.end_line.* = line;
// only do this if the pc changed
self.prev_di_line = line;
self.prev_di_column = column;
self.prev_di_pc = self.code.items.len;
},
.none => {},
}
self.prev_di_line = line;
self.prev_di_column = column;
self.prev_di_pc = self.code.items.len;
}
/// Asserts there is already capacity to insert into top branch inst_table.
@ -1034,6 +1085,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
},
.plan9 => {},
.none => {},
}
}
@ -2459,6 +2511,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
},
.plan9 => {},
.none => {},
}
},
@ -2493,6 +2546,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
else => {},
}
},
.plan9 => {},
.none => {},
}
},
@ -2929,6 +2983,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
if (self.air.value(callee)) |func_value| {
if (func_value.castTag(.function)) |func_payload| {
try p9.seeDecl(func_payload.data.owner_decl);
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
const got_addr = p9.bases.data;
@ -2976,6 +3031,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
if (self.air.value(callee)) |func_value| {
if (func_value.castTag(.function)) |func_payload| {
try p9.seeDecl(func_payload.data.owner_decl);
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
const got_addr = p9.bases.data;
@ -4923,6 +4979,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
return MCValue{ .memory = got_addr };
} else if (self.bin_file.cast(link.File.Plan9)) |p9| {
try p9.seeDecl(decl);
const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
return MCValue{ .memory = got_addr };
} else {

View File

@ -20,6 +20,14 @@ const Allocator = std.mem.Allocator;
const log = std.log.scoped(.link);
const assert = std.debug.assert;
const FnDeclOutput = struct {
code: []const u8,
/// this might have to be modified in the linker, so thats why its mutable
lineinfo: []u8,
start_line: u32,
end_line: u32,
};
base: link.File,
sixtyfour_bit: bool,
error_flags: File.ErrorFlags = File.ErrorFlags{},
@ -27,16 +35,45 @@ bases: Bases,
/// A symbol's value is just casted down when compiling
/// for a 32 bit target.
/// Does not represent the order or amount of symbols in the file
/// it is just useful for storing symbols. Some other symbols are in
/// file_segments.
syms: std.ArrayListUnmanaged(aout.Sym) = .{},
fn_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
/// The plan9 a.out format requires segments of
/// filenames to be deduplicated, so we use this map to
/// de duplicate it. The value is the value of the path
/// component
file_segments: std.StringArrayHashMapUnmanaged(u16) = .{},
/// The value of a 'f' symbol increments by 1 every time, so that no 2 'f'
/// symbols have the same value.
file_segments_i: u16 = 1,
path_arena: std.heap.ArenaAllocator,
/// maps a file scope to a hash map of decl to codegen output
/// this is useful for line debuginfo, since it makes sense to sort by file
/// The debugger looks for the first file (aout.Sym.Type.z) preceeding the text symbol
/// of the function to know what file it came from.
/// If we group the decls by file, it makes it really easy to do this (put the symbol in the correct place)
fn_decl_table: std.AutoArrayHashMapUnmanaged(
*Module.Scope.File,
struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(*Module.Decl, FnDeclOutput) = .{} },
) = .{},
data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
hdr: aout.ExecHdr = undefined,
magic: u32,
entry_val: ?u64 = null,
got_len: usize = 0,
// A list of all the free got indexes, so when making a new decl
// don't make a new one, just use one from here.
got_index_free_list: std.ArrayListUnmanaged(u64) = .{},
syms_index_free_list: std.ArrayListUnmanaged(u64) = .{},
const Bases = struct {
text: u64,
@ -103,8 +140,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
33...64 => true,
else => return error.UnsupportedP9Architecture,
};
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
const self = try gpa.create(Plan9);
self.* = .{
.path_arena = arena_allocator,
.base = .{
.tag = .plan9,
.options = options,
@ -113,21 +154,102 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
},
.sixtyfour_bit = sixtyfour_bit,
.bases = undefined,
.magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
};
// a / will always be in a file path
try self.file_segments.put(self.base.allocator, "/", 1);
return self;
}
fn putFn(self: *Plan9, decl: *Module.Decl, out: FnDeclOutput) !void {
const gpa = self.base.allocator;
const fn_map_res = try self.fn_decl_table.getOrPut(gpa, decl.namespace.file_scope);
if (fn_map_res.found_existing) {
try fn_map_res.value_ptr.functions.put(gpa, decl, out);
} else {
const file = decl.namespace.file_scope;
const arena = &self.path_arena.allocator;
// each file gets a symbol
fn_map_res.value_ptr.* = .{
.sym_index = blk: {
try self.syms.append(gpa, undefined);
break :blk @intCast(u32, self.syms.items.len - 1);
},
};
try fn_map_res.value_ptr.functions.put(gpa, decl, out);
var a = std.ArrayList(u8).init(arena);
errdefer a.deinit();
// every 'z' starts with 0
try a.append(0);
// path component value of '/'
try a.writer().writeIntBig(u16, 1);
// getting the full file path
var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const dir = file.pkg.root_src_directory.path orelse try std.os.getcwd(&buf);
const sub_path = try std.fs.path.join(arena, &.{ dir, file.sub_file_path });
try self.addPathComponents(sub_path, &a);
// null terminate
try a.append(0);
const final = a.toOwnedSlice();
self.syms.items[fn_map_res.value_ptr.sym_index] = .{
.type = .z,
.value = 1,
.name = final,
};
}
}
fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !void {
const sep = std.fs.path.sep;
var it = std.mem.tokenize(u8, path, &.{sep});
while (it.next()) |component| {
if (self.file_segments.get(component)) |num| {
try a.writer().writeIntBig(u16, num);
} else {
self.file_segments_i += 1;
try self.file_segments.put(self.base.allocator, component, self.file_segments_i);
try a.writer().writeIntBig(u16, self.file_segments_i);
}
}
}
pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
if (build_options.skip_non_native and builtin.object_format != .plan9) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
const decl = func.owner_decl;
try self.seeDecl(decl);
log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
defer code_buffer.deinit();
const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ .none = .{} });
var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
defer dbg_line_buffer.deinit();
var start_line: ?u32 = null;
var end_line: u32 = undefined;
var pcop_change_index: ?u32 = null;
const res = try codegen.generateFunction(
&self.base,
decl.srcLoc(),
func,
air,
liveness,
&code_buffer,
.{
.plan9 = .{
.dbg_line = &dbg_line_buffer,
.end_line = &end_line,
.start_line = &start_line,
.pcop_change_index = &pcop_change_index,
},
},
);
const code = switch (res) {
.appended => code_buffer.toOwnedSlice(),
.fail => |em| {
@ -136,7 +258,13 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
return;
},
};
try self.fn_decl_table.put(self.base.allocator, decl, code);
const out: FnDeclOutput = .{
.code = code,
.lineinfo = dbg_line_buffer.toOwnedSlice(),
.start_line = start_line.?,
.end_line = end_line,
};
try self.putFn(decl, out);
return self.updateFinish(decl);
}
@ -151,6 +279,8 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
}
}
try self.seeDecl(decl);
log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
@ -192,8 +322,12 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
if (decl.link.plan9.sym_index) |s| {
self.syms.items[s] = sym;
} else {
try self.syms.append(self.base.allocator, sym);
decl.link.plan9.sym_index = self.syms.items.len - 1;
if (self.syms_index_free_list.popOrNull()) |i| {
decl.link.plan9.sym_index = i;
} else {
try self.syms.append(self.base.allocator, sym);
decl.link.plan9.sym_index = self.syms.items.len - 1;
}
}
}
@ -209,6 +343,30 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {
return self.flushModule(comp);
}
pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void {
if (delta_line > 0 and delta_line < 65) {
const toappend = @intCast(u8, delta_line);
try l.append(toappend);
} else if (delta_line < 0 and delta_line > -65) {
const toadd: u8 = @intCast(u8, -delta_line + 64);
try l.append(toadd);
} else if (delta_line != 0) {
try l.append(0);
try l.writer().writeIntBig(i32, delta_line);
}
}
fn declCount(self: *Plan9) u64 {
var fn_decl_count: u64 = 0;
var itf_files = self.fn_decl_table.iterator();
while (itf_files.next()) |ent| {
// get the submap
var submap = ent.value_ptr.functions;
fn_decl_count += submap.count();
}
return self.data_decl_table.count() + fn_decl_count;
}
pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
if (build_options.skip_non_native and builtin.object_format != .plan9) {
@panic("Attempted to compile for object format that was disabled by build configuration");
@ -224,15 +382,13 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
// TODO I changed this assert from == to >= but this code all needs to be audited; see
// the comment in `freeDecl`.
assert(self.got_len >= self.fn_decl_table.count() + self.data_decl_table.count());
assert(self.got_len == self.declCount() + self.got_index_free_list.items.len);
const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
var got_table = try self.base.allocator.alloc(u8, got_size);
defer self.base.allocator.free(got_table);
// + 2 for header, got, symbols
var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3);
// + 4 for header, got, symbols, linecountinfo
var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.declCount() + 4);
defer self.base.allocator.free(iovecs);
const file = self.base.file.?;
@ -245,30 +401,52 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };
var iovecs_i: usize = 1;
var text_i: u64 = 0;
var linecountinfo = std.ArrayList(u8).init(self.base.allocator);
defer linecountinfo.deinit();
// text
{
var it = self.fn_decl_table.iterator();
while (it.next()) |entry| {
const decl = entry.key_ptr.*;
const code = entry.value_ptr.*;
log.debug("write text decl {*} ({s})", .{ decl, decl.name });
foff += code.len;
iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };
iovecs_i += 1;
const off = self.getAddr(text_i, .t);
text_i += code.len;
decl.link.plan9.offset = off;
if (!self.sixtyfour_bit) {
mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
} else {
mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
}
self.syms.items[decl.link.plan9.sym_index.?].value = off;
if (mod.decl_exports.get(decl)) |exports| {
try self.addDeclExports(mod, decl, exports);
var linecount: u32 = 0;
var it_file = self.fn_decl_table.iterator();
while (it_file.next()) |fentry| {
var it = fentry.value_ptr.functions.iterator();
while (it.next()) |entry| {
const decl = entry.key_ptr.*;
const out = entry.value_ptr.*;
log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line, out.end_line });
{
// connect the previous decl to the next
const delta_line = @intCast(i32, out.start_line) - @intCast(i32, linecount);
try changeLine(&linecountinfo, delta_line);
// TODO change the pc too (maybe?)
// write out the actual info that was generated in codegen now
try linecountinfo.appendSlice(out.lineinfo);
linecount = out.end_line;
}
foff += out.code.len;
iovecs[iovecs_i] = .{ .iov_base = out.code.ptr, .iov_len = out.code.len };
iovecs_i += 1;
const off = self.getAddr(text_i, .t);
text_i += out.code.len;
decl.link.plan9.offset = off;
if (!self.sixtyfour_bit) {
mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
} else {
mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
}
self.syms.items[decl.link.plan9.sym_index.?].value = off;
if (mod.decl_exports.get(decl)) |exports| {
try self.addDeclExports(mod, decl, exports);
}
}
}
if (linecountinfo.items.len & 1 == 1) {
// just a nop to make it even, the plan9 linker does this
try linecountinfo.append(129);
}
// etext symbol
self.syms.items[2].value = self.getAddr(text_i, .t);
}
@ -306,20 +484,23 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
// edata
self.syms.items[1].value = self.getAddr(0x0, .b);
var sym_buf = std.ArrayList(u8).init(self.base.allocator);
defer sym_buf.deinit();
try self.writeSyms(&sym_buf);
assert(2 + self.fn_decl_table.count() + self.data_decl_table.count() == iovecs_i); // we didn't write all the decls
iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len };
const syms = sym_buf.toOwnedSlice();
defer self.base.allocator.free(syms);
assert(2 + self.declCount() == iovecs_i); // we didn't write all the decls
iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len };
iovecs_i += 1;
iovecs[iovecs_i] = .{ .iov_base = linecountinfo.items.ptr, .iov_len = linecountinfo.items.len };
iovecs_i += 1;
// generate the header
self.hdr = .{
.magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
.magic = self.magic,
.text = @intCast(u32, text_i),
.data = @intCast(u32, data_i),
.syms = @intCast(u32, sym_buf.items.len),
.syms = @intCast(u32, syms.len),
.bss = 0,
.pcsz = 0,
.spsz = 0,
.pcsz = @intCast(u32, linecountinfo.items.len),
.entry = @intCast(u32, self.entry_val.?),
};
std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);
@ -360,18 +541,43 @@ fn addDeclExports(
}
pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
// TODO this is not the correct check for being function body,
// it could just be a function pointer.
// TODO audit the lifetimes of decls table entries. It's possible to get
// allocateDeclIndexes and then freeDecl without any updateDecl in between.
// However that is planned to change, see the TODO comment in Module.zig
// in the deleteUnusedDecl function.
const is_fn = (decl.ty.zigTypeTag() == .Fn);
const is_fn = (decl.val.tag() == .function);
if (is_fn) {
_ = self.fn_decl_table.swapRemove(decl);
var symidx_and_submap =
self.fn_decl_table.get(decl.namespace.file_scope).?;
var submap = symidx_and_submap.functions;
_ = submap.swapRemove(decl);
if (submap.count() == 0) {
self.syms.items[symidx_and_submap.sym_index] = aout.Sym.undefined_symbol;
self.syms_index_free_list.append(self.base.allocator, symidx_and_submap.sym_index) catch {};
submap.deinit(self.base.allocator);
}
} else {
_ = self.data_decl_table.swapRemove(decl);
}
if (decl.link.plan9.got_index) |i| {
// TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
self.got_index_free_list.append(self.base.allocator, i) catch {};
}
if (decl.link.plan9.sym_index) |i| {
self.syms_index_free_list.append(self.base.allocator, i) catch {};
self.syms.items[i] = aout.Sym.undefined_symbol;
}
}
pub fn seeDecl(self: *Plan9, decl: *Module.Decl) !void {
if (decl.link.plan9.got_index == null) {
if (self.got_index_free_list.popOrNull()) |i| {
decl.link.plan9.got_index = i;
} else {
self.got_len += 1;
decl.link.plan9.got_index = self.got_len - 1;
}
}
}
pub fn updateDeclExports(
@ -380,6 +586,7 @@ pub fn updateDeclExports(
decl: *Module.Decl,
exports: []const *Module.Export,
) !void {
try self.seeDecl(decl);
// we do all the things in flush
_ = self;
_ = module;
@ -387,17 +594,29 @@ pub fn updateDeclExports(
_ = exports;
}
pub fn deinit(self: *Plan9) void {
var itf = self.fn_decl_table.iterator();
while (itf.next()) |entry| {
self.base.allocator.free(entry.value_ptr.*);
const gpa = self.base.allocator;
var itf_files = self.fn_decl_table.iterator();
while (itf_files.next()) |ent| {
// get the submap
var submap = ent.value_ptr.functions;
defer submap.deinit(gpa);
var itf = submap.iterator();
while (itf.next()) |entry| {
gpa.free(entry.value_ptr.code);
gpa.free(entry.value_ptr.lineinfo);
}
}
self.fn_decl_table.deinit(self.base.allocator);
self.fn_decl_table.deinit(gpa);
var itd = self.data_decl_table.iterator();
while (itd.next()) |entry| {
self.base.allocator.free(entry.value_ptr.*);
gpa.free(entry.value_ptr.*);
}
self.data_decl_table.deinit(self.base.allocator);
self.syms.deinit(self.base.allocator);
self.data_decl_table.deinit(gpa);
self.syms.deinit(gpa);
self.got_index_free_list.deinit(gpa);
self.syms_index_free_list.deinit(gpa);
self.file_segments.deinit(gpa);
self.path_arena.deinit();
}
pub const Export = ?usize;
@ -407,7 +626,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
return error.LLVMBackendDoesNotSupportPlan9;
assert(options.object_format == .plan9);
const file = try options.emit.?.directory.handle.createFile(sub_path, .{
.truncate = false,
.read = true,
.mode = link.determineMode(options),
});
@ -441,27 +659,77 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
return self;
}
pub fn writeSym(self: *Plan9, w: anytype, sym: aout.Sym) !void {
log.debug("write sym.name: {s}", .{sym.name});
log.debug("write sym.value: {x}", .{sym.value});
if (sym.type == .bad) return; // we don't want to write free'd symbols
if (!self.sixtyfour_bit) {
try w.writeIntBig(u32, @intCast(u32, sym.value));
} else {
try w.writeIntBig(u64, sym.value);
}
try w.writeByte(@enumToInt(sym.type));
try w.writeAll(sym.name);
try w.writeByte(0);
}
pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
const writer = buf.writer();
for (self.syms.items) |sym| {
log.debug("sym.name: {s}", .{sym.name});
log.debug("sym.value: {x}", .{sym.value});
if (mem.eql(u8, sym.name, "_start"))
self.entry_val = sym.value;
if (!self.sixtyfour_bit) {
try writer.writeIntBig(u32, @intCast(u32, sym.value));
} else {
try writer.writeIntBig(u64, sym.value);
// write the f symbols
{
var it = self.file_segments.iterator();
while (it.next()) |entry| {
try self.writeSym(writer, .{
.type = .f,
.value = entry.value_ptr.*,
.name = entry.key_ptr.*,
});
}
}
// write the data symbols
{
var it = self.data_decl_table.iterator();
while (it.next()) |entry| {
const decl = entry.key_ptr.*;
const sym = self.syms.items[decl.link.plan9.sym_index.?];
try self.writeSym(writer, sym);
if (self.base.options.module.?.decl_exports.get(decl)) |exports| {
for (exports) |e| {
try self.writeSym(writer, self.syms.items[e.link.plan9.?]);
}
}
}
}
// text symbols are the hardest:
// the file of a text symbol is the .z symbol before it
// so we have to write everything in the right order
{
var it_file = self.fn_decl_table.iterator();
while (it_file.next()) |fentry| {
var symidx_and_submap = fentry.value_ptr;
// write the z symbol
try self.writeSym(writer, self.syms.items[symidx_and_submap.sym_index]);
// write all the decls come from the file of the z symbol
var submap_it = symidx_and_submap.functions.iterator();
while (submap_it.next()) |entry| {
const decl = entry.key_ptr.*;
const sym = self.syms.items[decl.link.plan9.sym_index.?];
try self.writeSym(writer, sym);
if (self.base.options.module.?.decl_exports.get(decl)) |exports| {
for (exports) |e| {
const s = self.syms.items[e.link.plan9.?];
if (mem.eql(u8, s.name, "_start"))
self.entry_val = s.value;
try self.writeSym(writer, s);
}
}
}
}
try writer.writeByte(@enumToInt(sym.type));
try writer.writeAll(sym.name);
try writer.writeByte(0);
}
}
/// this will be removed, moved to updateFinish
pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
if (decl.link.plan9.got_index == null) {
self.got_len += 1;
decl.link.plan9.got_index = self.got_len - 1;
}
_ = self;
_ = decl;
}

View File

@ -34,6 +34,12 @@ pub const Sym = struct {
type: Type,
name: []const u8,
pub const undefined_symbol: Sym = .{
.value = undefined,
.type = .bad,
.name = "undefined_symbol",
};
/// The type field is one of the following characters with the
/// high bit set:
/// T text segment symbol
@ -65,6 +71,8 @@ pub const Sym = struct {
z = 0x80 | 'z',
Z = 0x80 | 'Z',
m = 0x80 | 'm',
/// represents an undefined symbol, to be removed in flush
bad = 0,
pub fn toGlobal(self: Type) Type {
return switch (self) {
@ -112,3 +120,12 @@ pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {
else => error.ArchNotSupportedByPlan9,
};
}
/// gets the quantization of pc for the arch
pub fn getPCQuant(arch: std.Target.Cpu.Arch) !u8 {
return switch (arch) {
.i386, .x86_64 => 1,
.powerpc, .powerpc64, .mips, .sparc, .arm, .aarch64 => 4,
else => error.ArchNotSupportedByPlan9,
};
}