authorgravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2021-08-29 17:25:18-04:00
committergravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2021-09-20 16:37:56-04:00
logf697e0a326b06b7dcf641fbd61110be756407dcf
treea4cd4845e8ca601cca1d08d5f16cb004bfbe127a
parent84ab03a875b2a1b9d38f094242ddbd54f133c1a5

plan9 linker: link lineinfo and filenames


3 files changed, 312 insertions(+), 70 deletions(-)

src/codegen.zig+31-15
...@@ -52,16 +52,23 @@ pub const DebugInfoOutput = union(enum) {...@@ -52,16 +52,23 @@ pub const DebugInfoOutput = union(enum) {
52 /// assume all numbers/variables are bytes52 /// assume all numbers/variables are bytes
53 /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset53 /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset
54 /// x when x < 65 -> add x to line offset54 /// x when x < 65 -> add x to line offset
55 /// x when x < 129 -> subtract 64 from x and add it to the line offset55 /// x when x < 129 -> subtract 64 from x and subtract it from the line offset
56 /// x -> subtract 129 from x, multiply it by the quanta of the instruction size56 /// x -> subtract 129 from x, multiply it by the quanta of the instruction size
57 /// (1 on x86_64), and add it to the pc57 /// (1 on x86_64), and add it to the pc
58 /// after every opcode, add the quanta of the instruction size to the pc58 /// after every opcode, add the quanta of the instruction size to the pc
59 plan9: struct {59 plan9: struct {
60 /// the actual opcodes60 /// the actual opcodes
61 dbg_line: *std.ArrayList(u8),61 dbg_line: *std.ArrayList(u8),
62 /// what line the debuginfo starts on
63 /// 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
64 start_line: *?u32,
62 /// what the line count ends on after codegen65 /// what the line count ends on after codegen
63 /// 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 decl66 /// 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
64 end_line: *u32,67 end_line: *u32,
68 /// the last pc change op
69 /// This is very useful for adding quanta
70 /// to it if its not actually the last one.
71 pcop_change_index: *?u32,
65 },72 },
66 none,73 none,
67};74};
...@@ -946,7 +953,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -946,7 +953,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
946953
947 fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {954 fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
948 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);955 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
949 const delta_pc = self.code.items.len - self.prev_di_pc;956 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
950 switch (self.debug_output) {957 switch (self.debug_output) {
951 .dwarf => |dbg_out| {958 .dwarf => |dbg_out| {
952 // TODO Look into using the DWARF special opcodes to compress this data.959 // TODO Look into using the DWARF special opcodes to compress this data.
...@@ -960,30 +967,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -960,30 +967,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
960 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;967 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
961 }968 }
962 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);969 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
970 self.prev_di_pc = self.code.items.len;
971 self.prev_di_line = line;
972 self.prev_di_column = column;
973 self.prev_di_pc = self.code.items.len;
963 },974 },
964 .plan9 => |dbg_out| {975 .plan9 => |dbg_out| {
976 if (delta_pc <= 0) return; // only do this when the pc changes
965 // we have already checked the target in the linker to make sure it is compatable977 // we have already checked the target in the linker to make sure it is compatable
966 const quant = @import("link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;978 const quant = @import("link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
967979
968 // increasing the line number980 // increasing the line number
969 if (delta_line > 0 and delta_line < 65) {981 try @import("link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
970 try dbg_out.dbg_line.append(@intCast(u8, delta_line));
971 } else if (delta_line < 0 and delta_line > -65) {
972 try dbg_out.dbg_line.append(@intCast(u8, -delta_line + 64));
973 } else if (delta_line != 0) {
974 try dbg_out.dbg_line.writer().writeIntBig(i32, delta_line);
975 }
976 // increasing the pc982 // increasing the pc
977 if (delta_pc - quant != 0) {983 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
978 try dbg_out.dbg_line.append(@intCast(u8, delta_pc - quant + 129));984 if (d_pc_p9 > 0) {
979 }985 // minus one becaue if its the last one, we want to leave space to change the line which is one quanta
986 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
987 if (dbg_out.pcop_change_index.*) |pci|
988 dbg_out.dbg_line.items[pci] += 1;
989 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
990 } else if (d_pc_p9 == 0) {
991 // we don't need to do anything, because adding the quant does it for us
992 } else unreachable;
993 if (dbg_out.start_line.* == null)
994 dbg_out.start_line.* = self.prev_di_line;
980 dbg_out.end_line.* = line;995 dbg_out.end_line.* = line;
996 // only do this if the pc changed
997 self.prev_di_line = line;
998 self.prev_di_column = column;
999 self.prev_di_pc = self.code.items.len;
981 },1000 },
982 .none => {},1001 .none => {},
983 }1002 }
984 self.prev_di_line = line;
985 self.prev_di_column = column;
986 self.prev_di_pc = self.code.items.len;
987 }1003 }
9881004
989 /// Asserts there is already capacity to insert into top branch inst_table.1005 /// Asserts there is already capacity to insert into top branch inst_table.
src/link/Plan9.zig+273-55
...@@ -20,6 +20,14 @@ const Allocator = std.mem.Allocator;...@@ -20,6 +20,14 @@ const Allocator = std.mem.Allocator;
20const log = std.log.scoped(.link);20const log = std.log.scoped(.link);
21const assert = std.debug.assert;21const assert = std.debug.assert;
2222
23const FnDeclOutput = struct {
24 code: []const u8,
25 /// this might have to be modified in the linker, so thats why its mutable
26 lineinfo: []u8,
27 start_line: u32,
28 end_line: u32,
29};
30
23base: link.File,31base: link.File,
24sixtyfour_bit: bool,32sixtyfour_bit: bool,
25error_flags: File.ErrorFlags = File.ErrorFlags{},33error_flags: File.ErrorFlags = File.ErrorFlags{},
...@@ -27,9 +35,31 @@ bases: Bases,...@@ -27,9 +35,31 @@ bases: Bases,
2735
28/// A symbol's value is just casted down when compiling36/// A symbol's value is just casted down when compiling
29/// for a 32 bit target.37/// for a 32 bit target.
38/// Does not represent the order or amount of symbols in the file
39/// it is just useful for storing symbols. Some other symbols are in
40/// file_segments.
30syms: std.ArrayListUnmanaged(aout.Sym) = .{},41syms: std.ArrayListUnmanaged(aout.Sym) = .{},
3142
32fn_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},43/// The plan9 a.out format requires segments of
44/// filenames to be deduplicated, so we use this map to
45/// de duplicate it. The value is the value of the path
46/// component
47file_segments: std.StringArrayHashMapUnmanaged(u16) = .{},
48/// The value of a 'f' symbol increments by 1 every time, so that no 2 'f'
49/// symbols have the same value.
50file_segments_i: u16 = 1,
51
52path_arena: std.heap.ArenaAllocator,
53
54/// maps a file scope to a hash map of decl to codegen output
55/// this is useful for line debuginfo, since it makes sense to sort by file
56/// The debugger looks for the first file (aout.Sym.Type.z) preceeding the text symbol
57/// of the function to know what file it came from.
58/// If we group the decls by file, it makes it really easy to do this (put the symbol in the correct place)
59fn_decl_table: std.AutoArrayHashMapUnmanaged(
60 *Module.Scope.File,
61 struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(*Module.Decl, FnDeclOutput) = .{} },
62) = .{},
33data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},63data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
3464
35hdr: aout.ExecHdr = undefined,65hdr: aout.ExecHdr = undefined,
...@@ -110,8 +140,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {...@@ -110,8 +140,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
110 33...64 => true,140 33...64 => true,
111 else => return error.UnsupportedP9Architecture,141 else => return error.UnsupportedP9Architecture,
112 };142 };
143
144 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
145
113 const self = try gpa.create(Plan9);146 const self = try gpa.create(Plan9);
114 self.* = .{147 self.* = .{
148 .path_arena = arena_allocator,
115 .base = .{149 .base = .{
116 .tag = .plan9,150 .tag = .plan9,
117 .options = options,151 .options = options,
...@@ -122,9 +156,66 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {...@@ -122,9 +156,66 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
122 .bases = undefined,156 .bases = undefined,
123 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),157 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
124 };158 };
159 // a / will always be in a file path
160 try self.file_segments.put(self.base.allocator, "/", 1);
125 return self;161 return self;
126}162}
127163
164fn putFn(self: *Plan9, decl: *Module.Decl, out: FnDeclOutput) !void {
165 const gpa = self.base.allocator;
166 const fn_map_res = try self.fn_decl_table.getOrPut(gpa, decl.namespace.file_scope);
167 if (fn_map_res.found_existing) {
168 try fn_map_res.value_ptr.functions.put(gpa, decl, out);
169 } else {
170 const file = decl.namespace.file_scope;
171 const arena = &self.path_arena.allocator;
172 // each file gets a symbol
173 fn_map_res.value_ptr.* = .{
174 .sym_index = blk: {
175 try self.syms.append(gpa, undefined);
176 break :blk @intCast(u32, self.syms.items.len - 1);
177 },
178 };
179 try fn_map_res.value_ptr.functions.put(gpa, decl, out);
180
181 var a = std.ArrayList(u8).init(arena);
182 errdefer a.deinit();
183 // every 'z' starts with 0
184 try a.append(0);
185 // path component value of '/'
186 try a.writer().writeIntBig(u16, 1);
187
188 // getting the full file path
189 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
190 const dir = file.pkg.root_src_directory.path orelse try std.os.getcwd(&buf);
191 const sub_path = try std.fs.path.join(arena, &.{ dir, file.sub_file_path });
192 try self.addPathComponents(sub_path, &a);
193
194 // null terminate
195 try a.append(0);
196 const final = a.toOwnedSlice();
197 self.syms.items[fn_map_res.value_ptr.sym_index] = .{
198 .type = .z,
199 .value = 1,
200 .name = final,
201 };
202 }
203}
204
205fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !void {
206 const sep = std.fs.path.sep;
207 var it = std.mem.tokenize(u8, path, &.{sep});
208 while (it.next()) |component| {
209 if (self.file_segments.get(component)) |num| {
210 try a.writer().writeIntBig(u16, num);
211 } else {
212 self.file_segments_i += 1;
213 try self.file_segments.put(self.base.allocator, component, self.file_segments_i);
214 try a.writer().writeIntBig(u16, self.file_segments_i);
215 }
216 }
217}
218
128pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {219pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
129 if (build_options.skip_non_native and builtin.object_format != .plan9) {220 if (build_options.skip_non_native and builtin.object_format != .plan9) {
130 @panic("Attempted to compile for object format that was disabled by build configuration");221 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -139,7 +230,9 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -139,7 +230,9 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
139 defer code_buffer.deinit();230 defer code_buffer.deinit();
140 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);231 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
141 defer dbg_line_buffer.deinit();232 defer dbg_line_buffer.deinit();
142 var end_line: u32 = 0;233 var start_line: ?u32 = null;
234 var end_line: u32 = undefined;
235 var pcop_change_index: ?u32 = null;
143236
144 const res = try codegen.generateFunction(237 const res = try codegen.generateFunction(
145 &self.base,238 &self.base,
...@@ -152,6 +245,8 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -152,6 +245,8 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
152 .plan9 = .{245 .plan9 = .{
153 .dbg_line = &dbg_line_buffer,246 .dbg_line = &dbg_line_buffer,
154 .end_line = &end_line,247 .end_line = &end_line,
248 .start_line = &start_line,
249 .pcop_change_index = &pcop_change_index,
155 },250 },
156 },251 },
157 );252 );
...@@ -163,7 +258,13 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -163,7 +258,13 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
163 return;258 return;
164 },259 },
165 };260 };
166 try self.fn_decl_table.put(self.base.allocator, decl, code);261 const out: FnDeclOutput = .{
262 .code = code,
263 .lineinfo = dbg_line_buffer.toOwnedSlice(),
264 .start_line = start_line.?,
265 .end_line = end_line,
266 };
267 try self.putFn(decl, out);
167 return self.updateFinish(decl);268 return self.updateFinish(decl);
168}269}
169270
...@@ -242,6 +343,30 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {...@@ -242,6 +343,30 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {
242 return self.flushModule(comp);343 return self.flushModule(comp);
243}344}
244345
346pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void {
347 if (delta_line > 0 and delta_line < 65) {
348 const toappend = @intCast(u8, delta_line);
349 try l.append(toappend);
350 } else if (delta_line < 0 and delta_line > -65) {
351 const toadd: u8 = @intCast(u8, -delta_line + 64);
352 try l.append(toadd);
353 } else if (delta_line != 0) {
354 try l.append(0);
355 try l.writer().writeIntBig(i32, delta_line);
356 }
357}
358
359fn declCount(self: *Plan9) u64 {
360 var fn_decl_count: u64 = 0;
361 var itf_files = self.fn_decl_table.iterator();
362 while (itf_files.next()) |ent| {
363 // get the submap
364 var submap = ent.value_ptr.functions;
365 fn_decl_count += submap.count();
366 }
367 return self.data_decl_table.count() + fn_decl_count;
368}
369
245pub fn flushModule(self: *Plan9, comp: *Compilation) !void {370pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
246 if (build_options.skip_non_native and builtin.object_format != .plan9) {371 if (build_options.skip_non_native and builtin.object_format != .plan9) {
247 @panic("Attempted to compile for object format that was disabled by build configuration");372 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -257,13 +382,13 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {...@@ -257,13 +382,13 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
257382
258 const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;383 const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
259384
260 assert(self.got_len == self.fn_decl_table.count() + self.data_decl_table.count() + self.got_index_free_list.items.len);385 assert(self.got_len == self.declCount() + self.got_index_free_list.items.len);
261 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;386 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
262 var got_table = try self.base.allocator.alloc(u8, got_size);387 var got_table = try self.base.allocator.alloc(u8, got_size);
263 defer self.base.allocator.free(got_table);388 defer self.base.allocator.free(got_table);
264389
265 // + 3 for header, got, symbols390 // + 4 for header, got, symbols, linecountinfo
266 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3);391 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.declCount() + 4);
267 defer self.base.allocator.free(iovecs);392 defer self.base.allocator.free(iovecs);
268393
269 const file = self.base.file.?;394 const file = self.base.file.?;
...@@ -276,30 +401,52 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {...@@ -276,30 +401,52 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
276 iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };401 iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };
277 var iovecs_i: usize = 1;402 var iovecs_i: usize = 1;
278 var text_i: u64 = 0;403 var text_i: u64 = 0;
404
405 var linecountinfo = std.ArrayList(u8).init(self.base.allocator);
406 defer linecountinfo.deinit();
279 // text407 // text
280 {408 {
281 var it = self.fn_decl_table.iterator();409 var linecount: u32 = 0;
282 while (it.next()) |entry| {410 var it_file = self.fn_decl_table.iterator();
283 const decl = entry.key_ptr.*;411 while (it_file.next()) |fentry| {
284 const code = entry.value_ptr.*;412 var it = fentry.value_ptr.functions.iterator();
285 log.debug("write text decl {*} ({s})", .{ decl, decl.name });413 while (it.next()) |entry| {
286 foff += code.len;414 const decl = entry.key_ptr.*;
287 iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };415 const out = entry.value_ptr.*;
288 iovecs_i += 1;416 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line, out.end_line });
289 const off = self.getAddr(text_i, .t);417 {
290 text_i += code.len;418 // connect the previous decl to the next
291 decl.link.plan9.offset = off;419 const delta_line = @intCast(i32, out.start_line) - @intCast(i32, linecount);
292 if (!self.sixtyfour_bit) {420
293 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));421 try changeLine(&linecountinfo, delta_line);
294 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());422 // TODO change the pc too (maybe?)
295 } else {423
296 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());424 // write out the actual info that was generated in codegen now
297 }425 try linecountinfo.appendSlice(out.lineinfo);
298 self.syms.items[decl.link.plan9.sym_index.?].value = off;426 linecount = out.end_line;
299 if (mod.decl_exports.get(decl)) |exports| {427 }
300 try self.addDeclExports(mod, decl, exports);428 foff += out.code.len;
429 iovecs[iovecs_i] = .{ .iov_base = out.code.ptr, .iov_len = out.code.len };
430 iovecs_i += 1;
431 const off = self.getAddr(text_i, .t);
432 text_i += out.code.len;
433 decl.link.plan9.offset = off;
434 if (!self.sixtyfour_bit) {
435 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
436 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
437 } else {
438 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
439 }
440 self.syms.items[decl.link.plan9.sym_index.?].value = off;
441 if (mod.decl_exports.get(decl)) |exports| {
442 try self.addDeclExports(mod, decl, exports);
443 }
301 }444 }
302 }445 }
446 if (linecountinfo.items.len & 1 == 1) {
447 // just a nop to make it even, the plan9 linker does this
448 try linecountinfo.append(129);
449 }
303 // etext symbol450 // etext symbol
304 self.syms.items[2].value = self.getAddr(text_i, .t);451 self.syms.items[2].value = self.getAddr(text_i, .t);
305 }452 }
...@@ -337,20 +484,23 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {...@@ -337,20 +484,23 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
337 // edata484 // edata
338 self.syms.items[1].value = self.getAddr(0x0, .b);485 self.syms.items[1].value = self.getAddr(0x0, .b);
339 var sym_buf = std.ArrayList(u8).init(self.base.allocator);486 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
340 defer sym_buf.deinit();
341 try self.writeSyms(&sym_buf);487 try self.writeSyms(&sym_buf);
342 assert(2 + self.fn_decl_table.count() + self.data_decl_table.count() == iovecs_i); // we didn't write all the decls488 const syms = sym_buf.toOwnedSlice();
343 iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len };489 defer self.base.allocator.free(syms);
490 assert(2 + self.declCount() == iovecs_i); // we didn't write all the decls
491 iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len };
492 iovecs_i += 1;
493 iovecs[iovecs_i] = .{ .iov_base = linecountinfo.items.ptr, .iov_len = linecountinfo.items.len };
344 iovecs_i += 1;494 iovecs_i += 1;
345 // generate the header495 // generate the header
346 self.hdr = .{496 self.hdr = .{
347 .magic = self.magic,497 .magic = self.magic,
348 .text = @intCast(u32, text_i),498 .text = @intCast(u32, text_i),
349 .data = @intCast(u32, data_i),499 .data = @intCast(u32, data_i),
350 .syms = @intCast(u32, sym_buf.items.len),500 .syms = @intCast(u32, syms.len),
351 .bss = 0,501 .bss = 0,
352 .pcsz = 0,
353 .spsz = 0,502 .spsz = 0,
503 .pcsz = @intCast(u32, linecountinfo.items.len),
354 .entry = @intCast(u32, self.entry_val.?),504 .entry = @intCast(u32, self.entry_val.?),
355 };505 };
356 std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);506 std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);
...@@ -397,7 +547,15 @@ pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {...@@ -397,7 +547,15 @@ pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
397 // in the deleteUnusedDecl function.547 // in the deleteUnusedDecl function.
398 const is_fn = (decl.val.tag() == .function);548 const is_fn = (decl.val.tag() == .function);
399 if (is_fn) {549 if (is_fn) {
400 _ = self.fn_decl_table.swapRemove(decl);550 var symidx_and_submap =
551 self.fn_decl_table.get(decl.namespace.file_scope).?;
552 var submap = symidx_and_submap.functions;
553 _ = submap.swapRemove(decl);
554 if (submap.count() == 0) {
555 self.syms.items[symidx_and_submap.sym_index] = aout.Sym.undefined_symbol;
556 self.syms_index_free_list.append(self.base.allocator, symidx_and_submap.sym_index) catch {};
557 submap.deinit(self.base.allocator);
558 }
401 } else {559 } else {
402 _ = self.data_decl_table.swapRemove(decl);560 _ = self.data_decl_table.swapRemove(decl);
403 }561 }
...@@ -407,7 +565,7 @@ pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {...@@ -407,7 +565,7 @@ pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
407 }565 }
408 if (decl.link.plan9.sym_index) |i| {566 if (decl.link.plan9.sym_index) |i| {
409 self.syms_index_free_list.append(self.base.allocator, i) catch {};567 self.syms_index_free_list.append(self.base.allocator, i) catch {};
410 self.syms.items[i] = undefined;568 self.syms.items[i] = aout.Sym.undefined_symbol;
411 }569 }
412}570}
413571
...@@ -436,19 +594,29 @@ pub fn updateDeclExports(...@@ -436,19 +594,29 @@ pub fn updateDeclExports(
436 _ = exports;594 _ = exports;
437}595}
438pub fn deinit(self: *Plan9) void {596pub fn deinit(self: *Plan9) void {
439 var itf = self.fn_decl_table.iterator();597 const gpa = self.base.allocator;
440 while (itf.next()) |entry| {598 var itf_files = self.fn_decl_table.iterator();
441 self.base.allocator.free(entry.value_ptr.*);599 while (itf_files.next()) |ent| {
600 // get the submap
601 var submap = ent.value_ptr.functions;
602 defer submap.deinit(gpa);
603 var itf = submap.iterator();
604 while (itf.next()) |entry| {
605 gpa.free(entry.value_ptr.code);
606 gpa.free(entry.value_ptr.lineinfo);
607 }
442 }608 }
443 self.fn_decl_table.deinit(self.base.allocator);609 self.fn_decl_table.deinit(gpa);
444 var itd = self.data_decl_table.iterator();610 var itd = self.data_decl_table.iterator();
445 while (itd.next()) |entry| {611 while (itd.next()) |entry| {
446 self.base.allocator.free(entry.value_ptr.*);612 gpa.free(entry.value_ptr.*);
447 }613 }
448 self.data_decl_table.deinit(self.base.allocator);614 self.data_decl_table.deinit(gpa);
449 self.syms.deinit(self.base.allocator);615 self.syms.deinit(gpa);
450 self.got_index_free_list.deinit(self.base.allocator);616 self.got_index_free_list.deinit(gpa);
451 self.syms_index_free_list.deinit(self.base.allocator);617 self.syms_index_free_list.deinit(gpa);
618 self.file_segments.deinit(gpa);
619 self.path_arena.deinit();
452}620}
453621
454pub const Export = ?usize;622pub const Export = ?usize;
...@@ -458,7 +626,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -458,7 +626,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
458 return error.LLVMBackendDoesNotSupportPlan9;626 return error.LLVMBackendDoesNotSupportPlan9;
459 assert(options.object_format == .plan9);627 assert(options.object_format == .plan9);
460 const file = try options.emit.?.directory.handle.createFile(sub_path, .{628 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
461 .truncate = false,
462 .read = true,629 .read = true,
463 .mode = link.determineMode(options),630 .mode = link.determineMode(options),
464 });631 });
...@@ -492,21 +659,72 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -492,21 +659,72 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
492 return self;659 return self;
493}660}
494661
662pub fn writeSym(self: *Plan9, w: anytype, sym: aout.Sym) !void {
663 log.debug("write sym.name: {s}", .{sym.name});
664 log.debug("write sym.value: {x}", .{sym.value});
665 if (sym.type == .bad) return; // we don't want to write free'd symbols
666 if (!self.sixtyfour_bit) {
667 try w.writeIntBig(u32, @intCast(u32, sym.value));
668 } else {
669 try w.writeIntBig(u64, sym.value);
670 }
671 try w.writeByte(@enumToInt(sym.type));
672 try w.writeAll(sym.name);
673 try w.writeByte(0);
674}
495pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {675pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
496 const writer = buf.writer();676 const writer = buf.writer();
497 for (self.syms.items) |sym| {677 // write the f symbols
498 log.debug("sym.name: {s}", .{sym.name});678 {
499 log.debug("sym.value: {x}", .{sym.value});679 var it = self.file_segments.iterator();
500 if (mem.eql(u8, sym.name, "_start"))680 while (it.next()) |entry| {
501 self.entry_val = sym.value;681 try self.writeSym(writer, .{
502 if (!self.sixtyfour_bit) {682 .type = .f,
503 try writer.writeIntBig(u32, @intCast(u32, sym.value));683 .value = entry.value_ptr.*,
504 } else {684 .name = entry.key_ptr.*,
505 try writer.writeIntBig(u64, sym.value);685 });
686 }
687 }
688 // write the data symbols
689 {
690 var it = self.data_decl_table.iterator();
691 while (it.next()) |entry| {
692 const decl = entry.key_ptr.*;
693 const sym = self.syms.items[decl.link.plan9.sym_index.?];
694 try self.writeSym(writer, sym);
695 if (self.base.options.module.?.decl_exports.get(decl)) |exports| {
696 for (exports) |e| {
697 try self.writeSym(writer, self.syms.items[e.link.plan9.?]);
698 }
699 }
700 }
701 }
702 // text symbols are the hardest:
703 // the file of a text symbol is the .z symbol before it
704 // so we have to write everything in the right order
705 {
706 var it_file = self.fn_decl_table.iterator();
707 while (it_file.next()) |fentry| {
708 var symidx_and_submap = fentry.value_ptr;
709 // write the z symbol
710 try self.writeSym(writer, self.syms.items[symidx_and_submap.sym_index]);
711
712 // write all the decls come from the file of the z symbol
713 var submap_it = symidx_and_submap.functions.iterator();
714 while (submap_it.next()) |entry| {
715 const decl = entry.key_ptr.*;
716 const sym = self.syms.items[decl.link.plan9.sym_index.?];
717 try self.writeSym(writer, sym);
718 if (self.base.options.module.?.decl_exports.get(decl)) |exports| {
719 for (exports) |e| {
720 const s = self.syms.items[e.link.plan9.?];
721 if (mem.eql(u8, s.name, "_start"))
722 self.entry_val = s.value;
723 try self.writeSym(writer, s);
724 }
725 }
726 }
506 }727 }
507 try writer.writeByte(@enumToInt(sym.type));
508 try writer.writeAll(sym.name);
509 try writer.writeByte(0);
510 }728 }
511}729}
512730
src/link/Plan9/aout.zig+8
...@@ -34,6 +34,12 @@ pub const Sym = struct {...@@ -34,6 +34,12 @@ pub const Sym = struct {
34 type: Type,34 type: Type,
35 name: []const u8,35 name: []const u8,
3636
37 pub const undefined_symbol: Sym = .{
38 .value = undefined,
39 .type = .bad,
40 .name = "undefined_symbol",
41 };
42
37 /// The type field is one of the following characters with the43 /// The type field is one of the following characters with the
38 /// high bit set:44 /// high bit set:
39 /// T text segment symbol45 /// T text segment symbol
...@@ -65,6 +71,8 @@ pub const Sym = struct {...@@ -65,6 +71,8 @@ pub const Sym = struct {
65 z = 0x80 | 'z',71 z = 0x80 | 'z',
66 Z = 0x80 | 'Z',72 Z = 0x80 | 'Z',
67 m = 0x80 | 'm',73 m = 0x80 | 'm',
74 /// represents an undefined symbol, to be removed in flush
75 bad = 0,
6876
69 pub fn toGlobal(self: Type) Type {77 pub fn toGlobal(self: Type) Type {
70 return switch (self) {78 return switch (self) {