diff options
| author | Veikka Tuominen <git@vexu.eu> | 2021-09-21 19:38:12 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-09-21 19:38:12 +0300 |
| commit | a2dd0c387dd9e08c0147490b0667146758b6a43b (patch) | |
| tree | 8891c006cb48c551361af4291925b90f6678b067 /src | |
| parent | d722f0cc627c34f2c18681202a512d9aaa58fb18 (diff) | |
| parent | f697e0a326b06b7dcf641fbd61110be756407dcf (diff) | |
| download | zig-a2dd0c387dd9e08c0147490b0667146758b6a43b.tar.gz zig-a2dd0c387dd9e08c0147490b0667146758b6a43b.zip | |
Merge pull request #9652 from g-w1/p9d
plan9: emit debug info
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 4 | ||||
| -rw-r--r-- | src/codegen.zig | 67 | ||||
| -rw-r--r-- | src/link/Plan9.zig | 396 | ||||
| -rw-r--r-- | src/link/Plan9/aout.zig | 17 |
4 files changed, 414 insertions, 70 deletions
diff --git a/src/Module.zig b/src/Module.zig index 6a183db21f..fd275b507f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -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 => {}, } diff --git a/src/codegen.zig b/src/codegen.zig index bf2166d797..54c1400211 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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 { diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index bf49a238b6..384345ff67 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -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; } diff --git a/src/link/Plan9/aout.zig b/src/link/Plan9/aout.zig index f6dff7437c..f3570f880c 100644 --- a/src/link/Plan9/aout.zig +++ b/src/link/Plan9/aout.zig @@ -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, + }; +} |
