diff options
| -rw-r--r-- | lib/std/os.zig | 1 | ||||
| -rw-r--r-- | lib/std/os/plan9.zig | 89 | ||||
| -rw-r--r-- | lib/std/os/plan9/errno.zig | 76 | ||||
| -rw-r--r-- | lib/std/os/plan9/x86_64.zig | 2 | ||||
| -rw-r--r-- | lib/std/start.zig | 1 | ||||
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 11 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 38 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 8 | ||||
| -rw-r--r-- | src/codegen.zig | 16 | ||||
| -rw-r--r-- | src/link/Elf.zig | 1 | ||||
| -rw-r--r-- | src/link/Plan9.zig | 497 |
11 files changed, 609 insertions, 131 deletions
diff --git a/lib/std/os.zig b/lib/std/os.zig index ee28c158db..0b5a2f7e71 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -67,6 +67,7 @@ else if (builtin.link_libc or is_windows) std.c else switch (builtin.os.tag) { .linux => linux, + .plan9 => plan9, .wasi => wasi, .uefi => uefi, else => struct {}, diff --git a/lib/std/os/plan9.zig b/lib/std/os/plan9.zig index 6d052a3573..1f46915b27 100644 --- a/lib/std/os/plan9.zig +++ b/lib/std/os/plan9.zig @@ -5,6 +5,78 @@ pub const syscall_bits = switch (builtin.cpu.arch) { .x86_64 => @import("plan9/x86_64.zig"), else => @compileError("more plan9 syscall implementations (needs more inline asm in stage2"), }; +pub const E = @import("plan9/errno.zig").E; +/// Get the errno from a syscall return value, or 0 for no error. +pub fn getErrno(r: usize) E { + const signed_r = @bitCast(isize, r); + const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0; + return @intToEnum(E, int); +} +pub const SIG = struct { + /// hangup + pub const HUP = 1; + /// interrupt + pub const INT = 2; + /// quit + pub const QUIT = 3; + /// illegal instruction (not reset when caught) + pub const ILL = 4; + /// used by abort + pub const ABRT = 5; + /// floating point exception + pub const FPE = 6; + /// kill (cannot be caught or ignored) + pub const KILL = 7; + /// segmentation violation + pub const SEGV = 8; + /// write on a pipe with no one to read it + pub const PIPE = 9; + /// alarm clock + pub const ALRM = 10; + /// software termination signal from kill + pub const TERM = 11; + /// user defined signal 1 + pub const USR1 = 12; + /// user defined signal 2 + pub const USR2 = 13; + /// bus error + pub const BUS = 14; + // The following symbols must be defined, but the signals needn't be supported + /// child process terminated or stopped + pub const CHLD = 15; + /// continue if stopped + pub const CONT = 16; + /// stop + pub const STOP = 17; + /// interactive stop + pub const TSTP = 18; + /// read from ctl tty by member of background + pub const TTIN = 19; + /// write to ctl tty by member of background + pub const TTOU = 20; +}; +pub const sigset_t = c_long; +pub const empty_sigset = 0; +pub const siginfo_t = c_long; // TODO plan9 doesn't have sigaction_fn. Sigaction is not a union, but we incude it here to be compatible. +pub const Sigaction = extern struct { + pub const handler_fn = *const fn (c_int) callconv(.C) void; + pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; + + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, + mask: sigset_t, + flags: c_int, +}; +// TODO implement sigaction +// right now it is just a shim to allow using start.zig code +pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize { + _ = oact; + _ = act; + _ = sig; + return 0; +} pub const SYS = enum(usize) { SYSR1 = 0, _ERRSTR = 1, @@ -64,6 +136,10 @@ pub fn pwrite(fd: usize, buf: [*]const u8, count: usize, offset: usize) usize { return syscall_bits.syscall4(.PWRITE, fd, @ptrToInt(buf), count, offset); } +pub fn pread(fd: usize, buf: [*]const u8, count: usize, offset: usize) usize { + return syscall_bits.syscall4(.PREAD, fd, @ptrToInt(buf), count, offset); +} + pub fn open(path: [*:0]const u8, omode: OpenMode) usize { return syscall_bits.syscall2(.OPEN, @ptrToInt(path), @enumToInt(omode)); } @@ -72,8 +148,19 @@ pub fn create(path: [*:0]const u8, omode: OpenMode, perms: usize) usize { return syscall_bits.syscall3(.CREATE, @ptrToInt(path), @enumToInt(omode), perms); } -pub fn exits(status: ?[*:0]const u8) void { +pub fn exit(status: u8) noreturn { + if (status == 0) { + exits(null); + } else { + // TODO plan9 does not have exit codes. You either exit with 0 or a string + const arr: [1:0]u8 = .{status}; + exits(&arr); + } +} + +pub fn exits(status: ?[*:0]const u8) noreturn { _ = syscall_bits.syscall1(.EXITS, if (status) |s| @ptrToInt(s) else 0); + unreachable; } pub fn close(fd: usize) usize { diff --git a/lib/std/os/plan9/errno.zig b/lib/std/os/plan9/errno.zig new file mode 100644 index 0000000000..94197beca3 --- /dev/null +++ b/lib/std/os/plan9/errno.zig @@ -0,0 +1,76 @@ +//! Ported from /sys/include/ape/errno.h +pub const E = enum(u16) { + SUCCESS = 0, + DOM = 1000, + RANGE = 1001, + PLAN9 = 1002, + + @"2BIG" = 1, + ACCES = 2, + AGAIN = 3, + // WOULDBLOCK = 3, // TODO errno.h has 2 names for 3 + BADF = 4, + BUSY = 5, + CHILD = 6, + DEADLK = 7, + EXIST = 8, + FAULT = 9, + FBIG = 10, + INTR = 11, + INVAL = 12, + IO = 13, + ISDIR = 14, + MFILE = 15, + MLINK = 16, + NAMETOOLONG = 17, + NFILE = 18, + NODEV = 19, + NOENT = 20, + NOEXEC = 21, + NOLCK = 22, + NOMEM = 23, + NOSPC = 24, + NOSYS = 25, + NOTDIR = 26, + NOTEMPTY = 27, + NOTTY = 28, + NXIO = 29, + PERM = 30, + PIPE = 31, + ROFS = 32, + SPIPE = 33, + SRCH = 34, + XDEV = 35, + + // bsd networking software + NOTSOCK = 36, + PROTONOSUPPORT = 37, + // PROTOTYPE = 37, // TODO errno.h has two names for 37 + CONNREFUSED = 38, + AFNOSUPPORT = 39, + NOBUFS = 40, + OPNOTSUPP = 41, + ADDRINUSE = 42, + DESTADDRREQ = 43, + MSGSIZE = 44, + NOPROTOOPT = 45, + SOCKTNOSUPPORT = 46, + PFNOSUPPORT = 47, + ADDRNOTAVAIL = 48, + NETDOWN = 49, + NETUNREACH = 50, + NETRESET = 51, + CONNABORTED = 52, + ISCONN = 53, + NOTCONN = 54, + SHUTDOWN = 55, + TOOMANYREFS = 56, + TIMEDOUT = 57, + HOSTDOWN = 58, + HOSTUNREACH = 59, + GREG = 60, + + // These added in 1003.1b-1993 + CANCELED = 61, + INPROGRESS = 62, +}; diff --git a/lib/std/os/plan9/x86_64.zig b/lib/std/os/plan9/x86_64.zig index 0f9263296f..c68fdc4f0f 100644 --- a/lib/std/os/plan9/x86_64.zig +++ b/lib/std/os/plan9/x86_64.zig @@ -66,7 +66,7 @@ pub fn syscall4(sys: plan9.SYS, arg0: usize, arg1: usize, arg2: usize, arg3: usi : [arg0] "{r8}" (arg0), [arg1] "{r9}" (arg1), [arg2] "{r10}" (arg2), - [arg2] "{r11}" (arg3), + [arg3] "{r11}" (arg3), [syscall_number] "{rbp}" (@enumToInt(sys)), : "rcx", "rax", "rbp", "r11", "memory" ); diff --git a/lib/std/start.zig b/lib/std/start.zig index 4177c9ba01..f5d2688efb 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -18,7 +18,6 @@ const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start"; // Until then, we have simplified logic here for self-hosted. TODO remove this once // self-hosted is capable enough to handle all of the real start.zig logic. pub const simplified_logic = - (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .plan9) or builtin.zig_backend == .stage2_x86 or builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_arm or diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index bf945e6983..dd752555b7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4335,14 +4335,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier }, }); } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - const decl_block_index = try p9.seeDecl(func.owner_decl); - const decl_block = p9.getDeclBlock(decl_block_index); - const ptr_bits = self.target.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const got_addr = p9.bases.data; - const got_index = decl_block.got_index.?; - const fn_got_addr = got_addr + got_index * ptr_bytes; - try self.genSetReg(Type.usize, .x30, .{ .memory = fn_got_addr }); + const atom_index = try p9.seeDecl(func.owner_decl); + const atom = p9.getAtom(atom_index); + try self.genSetReg(Type.usize, .x30, .{ .memory = atom.getOffsetTableAddress(p9) }); } else unreachable; _ = try self.addInst(.{ diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6d98ecce4f..6e13a55008 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -130,6 +130,8 @@ const Owner = union(enum) { } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| { const atom = try coff_file.getOrCreateAtomForDecl(decl_index); return coff_file.getAtom(atom).getSymbolIndex().?; + } else if (ctx.bin_file.cast(link.File.Plan9)) |p9_file| { + return p9_file.seeDecl(decl_index); } else unreachable; }, .lazy_sym => |lazy_sym| { @@ -141,6 +143,9 @@ const Owner = union(enum) { const atom = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); return coff_file.getAtom(atom).getSymbolIndex().?; + } else if (ctx.bin_file.cast(link.File.Plan9)) |p9_file| { + return p9_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); } else unreachable; }, } @@ -8115,16 +8120,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); try self.asmRegister(.{ ._, .call }, .rax); } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - const decl_block_index = try p9.seeDecl(owner_decl); - const decl_block = p9.getDeclBlock(decl_block_index); - const ptr_bits = self.target.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const got_addr = p9.bases.data; - const got_index = decl_block.got_index.?; - const fn_got_addr = got_addr + got_index * ptr_bytes; + const atom_index = try p9.seeDecl(owner_decl); + const atom = p9.getAtom(atom_index); try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ .base = .{ .reg = .ds }, - .disp = @intCast(i32, fn_got_addr), + .disp = @intCast(i32, atom.getOffsetTableAddress(p9)), })); } else unreachable; } else if (func_value.getExternFunc(mod)) |extern_func| { @@ -10092,6 +10092,28 @@ fn genLazySymbolRef( ), else => unreachable, } + } else if (self.bin_file.cast(link.File.Plan9)) |p9_file| { + const atom_index = p9_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + var atom = p9_file.getAtom(atom_index); + _ = atom.getOrCreateOffsetTableEntry(p9_file); + const got_addr = atom.getOffsetTableAddress(p9_file); + const got_mem = + Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }); + switch (tag) { + .lea, .mov => try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), got_mem), + .call => try self.asmMemory(.{ ._, .call }, got_mem), + else => unreachable, + } + switch (tag) { + .lea, .call => {}, + .mov => try self.asmRegisterMemory( + .{ ._, tag }, + reg.to64(), + Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }), + ), + else => unreachable, + } } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 506092ff17..78ff918715 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -118,6 +118,14 @@ pub fn emitMir(emit: *Emit) Error!void { .pcrel = true, .length = 2, }); + } else if (emit.bin_file.cast(link.File.Plan9)) |p9_file| { + const atom_index = symbol.atom_index; + try p9_file.addReloc(atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct + .target = symbol.sym_index, // we set sym_index to just be the atom index + .offset = @intCast(u32, end_offset - 4), + .addend = 0, + .pcrel = true, + }); } else return emit.fail("TODO implement linker reloc for {s}", .{ @tagName(emit.bin_file.tag), }), diff --git a/src/codegen.zig b/src/codegen.zig index b39c3c5ec0..d446200a3b 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -852,10 +852,9 @@ fn genDeclRef( const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; return GenResult.mcv(.{ .load_got = sym_index }); } else if (bin_file.cast(link.File.Plan9)) |p9| { - const decl_block_index = try p9.seeDecl(decl_index); - const decl_block = p9.getDeclBlock(decl_block_index); - const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; - return GenResult.mcv(.{ .memory = got_addr }); + const atom_index = try p9.seeDecl(decl_index); + const atom = p9.getAtom(atom_index); + return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(p9) }); } else { return GenResult.fail(bin_file.allocator, src_loc, "TODO genDeclRef for target {}", .{target}); } @@ -880,12 +879,9 @@ fn genUnnamedConst( return GenResult.mcv(.{ .load_direct = local_sym_index }); } else if (bin_file.cast(link.File.Coff)) |_| { return GenResult.mcv(.{ .load_direct = local_sym_index }); - } else if (bin_file.cast(link.File.Plan9)) |p9| { - const ptr_bits = target.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const got_index = local_sym_index; // the plan9 backend returns the got_index - const got_addr = p9.bases.data + got_index * ptr_bytes; - return GenResult.mcv(.{ .memory = got_addr }); + } else if (bin_file.cast(link.File.Plan9)) |_| { + const atom_index = local_sym_index; // plan9 returns the atom_index + return GenResult.mcv(.{ .load_direct = atom_index }); } else { return GenResult.fail(bin_file.allocator, src_loc, "TODO genUnnamedConst for target {}", .{target}); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 0863a22fac..15ba9ebecc 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -341,6 +341,7 @@ pub fn deinit(self: *Elf) void { self.atoms.deinit(gpa); self.atom_by_index_table.deinit(gpa); + self.lazy_syms.deinit(gpa); { var it = self.unnamed_const_atoms.valueIterator(); diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index c08754b57a..6433fb2762 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -79,7 +79,9 @@ data_decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, []u8) = .{}, /// with `Decl` `main`, and lives as long as that `Decl`. unnamed_const_atoms: UnnamedConstTable = .{}, -relocs: std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Reloc)) = .{}, +lazy_syms: LazySymbolTable = .{}, + +relocs: std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Reloc)) = .{}, hdr: aout.ExecHdr = undefined, // relocs: std. @@ -94,13 +96,14 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{}, syms_index_free_list: std.ArrayListUnmanaged(usize) = .{}, -decl_blocks: std.ArrayListUnmanaged(DeclBlock) = .{}, +atoms: std.ArrayListUnmanaged(Atom) = .{}, decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, const Reloc = struct { - target: Module.Decl.Index, + target: Atom.Index, offset: u64, addend: u32, + pcrel: bool = false, }; const Bases = struct { @@ -109,11 +112,28 @@ const Bases = struct { data: u64, }; -const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(struct { info: DeclBlock, code: []const u8 })); +const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); + +const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); + +const LazySymbolMetadata = struct { + const State = enum { unused, pending_flush, flushed }; + text_atom: Atom.Index = undefined, + rodata_atom: Atom.Index = undefined, + text_state: State = .unused, + rodata_state: State = .unused, + + fn numberOfAtoms(self: LazySymbolMetadata) u32 { + var n: u32 = 0; + if (self.text_state != .unused) n += 1; + if (self.rodata_state != .unused) n += 1; + return n; + } +}; pub const PtrWidth = enum { p32, p64 }; -pub const DeclBlock = struct { +pub const Atom = struct { type: aout.Sym.Type, /// offset in the text or data sects offset: ?u64, @@ -121,12 +141,60 @@ pub const DeclBlock = struct { sym_index: ?usize, /// offset into got got_index: ?usize, + /// We include the code here to be use in relocs + /// In the case of unnamed_const_atoms and lazy_syms, this atom owns the code. + /// But, in the case of function and data decls, they own the code and this field + /// is just a pointer for convience. + code: CodePtr, + + const CodePtr = struct { + code_ptr: ?[*]u8, + other: union { + code_len: usize, + decl_index: Module.Decl.Index, + }, + fn getCode(self: CodePtr, plan9: *const Plan9) []u8 { + const mod = plan9.base.options.module.?; + return if (self.code_ptr) |p| p[0..self.other.code_len] else blk: { + const decl_index = self.other.decl_index; + const decl = mod.declPtr(decl_index); + if (decl.ty.zigTypeTag(mod) == .Fn) { + const table = plan9.fn_decl_table.get(decl.getFileScope(mod)).?.functions; + const output = table.get(decl_index).?; + break :blk output.code; + } else { + break :blk plan9.data_decl_table.get(decl_index).?; + } + }; + } + fn getOwnedCode(self: CodePtr) ?[]u8 { + return if (self.code_ptr) |p| p[0..self.other.code_len] else null; + } + }; pub const Index = u32; + + pub fn getOrCreateOffsetTableEntry(self: *Atom, plan9: *Plan9) usize { + if (self.got_index == null) self.got_index = plan9.allocateGotIndex(); + return self.got_index.?; + } + + pub fn getOrCreateSymbolTableEntry(self: *Atom, plan9: *Plan9) !usize { + if (self.sym_index == null) self.sym_index = try plan9.allocateSymbolIndex(); + return self.sym_index.?; + } + + // asserts that self.got_index != null + pub fn getOffsetTableAddress(self: Atom, plan9: *Plan9) u64 { + const ptr_bytes = @divExact(plan9.base.options.target.ptrBitWidth(), 8); + const got_addr = plan9.bases.data; + const got_index = self.got_index.?; + return got_addr + got_index * ptr_bytes; + } }; const DeclMetadata = struct { - index: DeclBlock.Index, + index: Atom.Index, exports: std.ArrayListUnmanaged(usize) = .{}, fn getExport(m: DeclMetadata, p9: *const Plan9, name: []const u8) ?usize { @@ -286,7 +354,7 @@ pub fn updateFunc(self: *Plan9, mod: *Module, func_index: Module.Fn.Index, air: const decl = mod.declPtr(decl_index); self.freeUnnamedConsts(decl_index); - _ = try self.seeDecl(decl_index); + const atom_idx = try self.seeDecl(decl_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -320,6 +388,10 @@ pub fn updateFunc(self: *Plan9, mod: *Module, func_index: Module.Fn.Index, air: return; }, }; + self.getAtomPtr(atom_idx).code = .{ + .code_ptr = null, + .other = .{ .decl_index = decl_index }, + }; const out: FnDeclOutput = .{ .code = code, .lineinfo = try dbg_line_buffer.toOwnedSlice(), @@ -351,12 +423,13 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index }); const sym_index = try self.allocateSymbolIndex(); - - const info: DeclBlock = .{ + const new_atom_idx = try self.createAtom(); + var info: Atom = .{ .type = .d, .offset = null, .sym_index = sym_index, .got_index = self.allocateGotIndex(), + .code = undefined, // filled in later }; const sym: aout.Sym = .{ .value = undefined, @@ -368,7 +441,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), tv, &code_buffer, .{ .none = {}, }, .{ - .parent_atom_index = @enumToInt(decl_index), + .parent_atom_index = new_atom_idx, }); const code = switch (res) { .ok => code_buffer.items, @@ -382,9 +455,12 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I // duped_code is freed when the unnamed const is freed var duped_code = try self.base.allocator.dupe(u8, code); errdefer self.base.allocator.free(duped_code); - try unnamed_consts.append(self.base.allocator, .{ .info = info, .code = duped_code }); - // we return the got_index to codegen so that it can reference to the place of the data in the got - return @intCast(u32, info.got_index.?); + const new_atom = self.getAtomPtr(new_atom_idx); + new_atom.* = info; + new_atom.code = .{ .code_ptr = duped_code.ptr, .other = .{ .code_len = duped_code.len } }; + try unnamed_consts.append(self.base.allocator, new_atom_idx); + // we return the new_atom_idx to codegen + return new_atom_idx; } pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void { @@ -399,7 +475,7 @@ pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !vo } } - _ = try self.seeDecl(decl_index); + const atom_idx = try self.seeDecl(decl_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -409,7 +485,7 @@ pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !vo .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ .none = {} }, .{ - .parent_atom_index = @enumToInt(decl_index), + .parent_atom_index = @intCast(Atom.Index, atom_idx), }); const code = switch (res) { .ok => code_buffer.items, @@ -421,6 +497,7 @@ pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !vo }; try self.data_decl_table.ensureUnusedCapacity(self.base.allocator, 1); const duped_code = try self.base.allocator.dupe(u8, code); + self.getAtomPtr(self.decls.get(decl_index).?.index).code = .{ .code_ptr = null, .other = .{ .decl_index = decl_index } }; if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| { self.base.allocator.free(old_entry.value); } @@ -433,22 +510,22 @@ fn updateFinish(self: *Plan9, decl_index: Module.Decl.Index) !void { const is_fn = (decl.ty.zigTypeTag(mod) == .Fn); const sym_t: aout.Sym.Type = if (is_fn) .t else .d; - const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index); + const atom = self.getAtomPtr(self.decls.get(decl_index).?.index); // write the internal linker metadata - decl_block.type = sym_t; + atom.type = sym_t; // write the symbol // we already have the got index const sym: aout.Sym = .{ .value = undefined, // the value of stuff gets filled in in flushModule - .type = decl_block.type, + .type = atom.type, .name = try self.base.allocator.dupe(u8, mod.intern_pool.stringToSlice(decl.name)), }; - if (decl_block.sym_index) |s| { + if (atom.sym_index) |s| { self.syms.items[s] = sym; } else { const s = try self.allocateSymbolIndex(); - decl_block.sym_index = s; + atom.sym_index = s; self.syms.items[s] = sym; } } @@ -461,6 +538,7 @@ fn allocateSymbolIndex(self: *Plan9) !usize { return self.syms.items.len - 1; } } + fn allocateGotIndex(self: *Plan9) usize { if (self.got_index_free_list.popOrNull()) |i| { return i; @@ -495,7 +573,7 @@ pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void { } } -// counts decls and unnamed consts +// counts decls, unnamed consts, and lazy syms fn atomCount(self: *Plan9) usize { var fn_decl_count: usize = 0; var itf_files = self.fn_decl_table.iterator(); @@ -510,7 +588,12 @@ fn atomCount(self: *Plan9) usize { while (it_unc.next()) |unnamed_consts| { unnamed_const_count += unnamed_consts.value_ptr.items.len; } - return data_decl_count + fn_decl_count + unnamed_const_count; + var lazy_atom_count: usize = 0; + var it_lazy = self.lazy_syms.iterator(); + while (it_lazy.next()) |kv| { + lazy_atom_count += kv.value_ptr.numberOfAtoms(); + } + return data_decl_count + fn_decl_count + unnamed_const_count + lazy_atom_count; } pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { @@ -532,7 +615,32 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; - assert(self.got_len == self.atomCount() + self.got_index_free_list.items.len); + // finish up the lazy syms + if (self.lazy_syms.getPtr(.none)) |metadata| { + // Most lazy symbols can be updated on first use, but + // anyerror needs to wait for everything to be flushed. + if (metadata.text_state != .unused) self.updateLazySymbolAtom( + File.LazySymbol.initDecl(.code, null, mod), + metadata.text_atom, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + if (metadata.rodata_state != .unused) self.updateLazySymbolAtom( + File.LazySymbol.initDecl(.const_data, null, mod), + metadata.rodata_atom, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + } + for (self.lazy_syms.values()) |*metadata| { + if (metadata.text_state != .unused) metadata.text_state = .flushed; + if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; + } + // make sure the got table is good + const atom_count = self.atomCount(); + assert(self.got_len == atom_count + 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); @@ -562,7 +670,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No var it = fentry.value_ptr.functions.iterator(); while (it.next()) |entry| { const decl_index = entry.key_ptr.*; - const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index); + const decl = mod.declPtr(decl_index); + const atom = self.getAtomPtr(self.decls.get(decl_index).?.index); const out = entry.value_ptr.*; { // connect the previous decl to the next @@ -580,14 +689,14 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No iovecs_i += 1; const off = self.getAddr(text_i, .t); text_i += out.code.len; - decl_block.offset = off; + atom.offset = off; + log.debug("write text decl {*} ({}), lines {d} to {d}.;__GOT+0x{x} vaddr: 0x{x}", .{ decl, decl.name.fmt(&mod.intern_pool), out.start_line + 1, out.end_line, atom.got_index.? * 8, off }); if (!self.sixtyfour_bit) { - mem.writeIntNative(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off)); - mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); } - self.syms.items[decl_block.sym_index.?].value = off; + self.syms.items[atom.sym_index.?].value = off; if (mod.decl_exports.get(decl_index)) |exports| { try self.addDeclExports(mod, decl_index, exports.items); } @@ -597,9 +706,30 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No // 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); } + // the text lazy symbols + { + var it = self.lazy_syms.iterator(); + while (it.next()) |kv| { + const meta = kv.value_ptr; + const text_atom = if (meta.text_state != .unused) self.getAtomPtr(meta.text_atom) else continue; + const code = text_atom.code.getOwnedCode().?; + 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; + text_atom.offset = off; + if (!self.sixtyfour_bit) { + mem.writeInt(u32, got_table[text_atom.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); + } else { + mem.writeInt(u64, got_table[text_atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + } + self.syms.items[text_atom.sym_index.?].value = off; + } + } + // etext symbol + self.syms.items[2].value = self.getAddr(text_i, .t); // global offset table is in data iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len }; iovecs_i += 1; @@ -609,7 +739,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No var it = self.data_decl_table.iterator(); while (it.next()) |entry| { const decl_index = entry.key_ptr.*; - const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index); + const atom = self.getAtomPtr(self.decls.get(decl_index).?.index); const code = entry.value_ptr.*; foff += code.len; @@ -617,13 +747,13 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No iovecs_i += 1; const off = self.getAddr(data_i, .d); data_i += code.len; - decl_block.offset = off; + atom.offset = off; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); } - self.syms.items[decl_block.sym_index.?].value = off; + self.syms.items[atom.sym_index.?].value = off; if (mod.decl_exports.get(decl_index)) |exports| { try self.addDeclExports(mod, decl_index, exports.items); } @@ -631,28 +761,48 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No // write the unnamed constants after the other data decls var it_unc = self.unnamed_const_atoms.iterator(); while (it_unc.next()) |unnamed_consts| { - for (unnamed_consts.value_ptr.items) |*unnamed_const| { - const code = unnamed_const.code; - log.debug("write unnamed const: ({s})", .{self.syms.items[unnamed_const.info.sym_index.?].name}); + for (unnamed_consts.value_ptr.items) |atom_idx| { + const atom = self.getAtomPtr(atom_idx); + const code = atom.code.getOwnedCode().?; // unnamed consts must own their code + log.debug("write unnamed const: ({s})", .{self.syms.items[atom.sym_index.?].name}); foff += code.len; iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len }; iovecs_i += 1; const off = self.getAddr(data_i, .d); data_i += code.len; - unnamed_const.info.offset = off; + atom.offset = off; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[unnamed_const.info.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[unnamed_const.info.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); } - self.syms.items[unnamed_const.info.sym_index.?].value = off; + self.syms.items[atom.sym_index.?].value = off; + } + } + // the lazy data symbols + var it_lazy = self.lazy_syms.iterator(); + while (it_lazy.next()) |kv| { + const meta = kv.value_ptr; + const data_atom = if (meta.rodata_state != .unused) self.getAtomPtr(meta.rodata_atom) else continue; + const code = data_atom.code.getOwnedCode().?; // lazy symbols must own their code + foff += code.len; + iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len }; + iovecs_i += 1; + const off = self.getAddr(data_i, .d); + data_i += code.len; + data_atom.offset = off; + if (!self.sixtyfour_bit) { + mem.writeInt(u32, got_table[data_atom.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); + } else { + mem.writeInt(u64, got_table[data_atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); } + self.syms.items[data_atom.sym_index.?].value = off; } // edata symbol self.syms.items[0].value = self.getAddr(data_i, .b); + // end + self.syms.items[1].value = self.getAddr(data_i, .b); } - // edata - self.syms.items[1].value = self.getAddr(0x0, .b); var sym_buf = std.ArrayList(u8).init(self.base.allocator); try self.writeSyms(&sym_buf); const syms = try sym_buf.toOwnedSlice(); @@ -682,33 +832,31 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No { var it = self.relocs.iterator(); while (it.next()) |kv| { - const source_decl_index = kv.key_ptr.*; - const source_decl = mod.declPtr(source_decl_index); + const source_atom_index = kv.key_ptr.*; + const source_atom = self.getAtom(source_atom_index); + const source_atom_symbol = self.syms.items[source_atom.sym_index.?]; for (kv.value_ptr.items) |reloc| { - const target_decl_index = reloc.target; - const target_decl_block = self.getDeclBlock(self.decls.get(target_decl_index).?.index); - const target_decl_offset = target_decl_block.offset.?; + const target_atom_index = reloc.target; + const target_atom = self.getAtomPtr(target_atom_index); + const target_symbol = self.syms.items[target_atom.sym_index.?]; + const target_offset = target_atom.offset.?; const offset = reloc.offset; const addend = reloc.addend; - const code = blk: { - const is_fn = source_decl.ty.zigTypeTag(mod) == .Fn; - if (is_fn) { - const table = self.fn_decl_table.get(source_decl.getFileScope(mod)).?.functions; - const output = table.get(source_decl_index).?; - break :blk output.code; - } else { - const code = self.data_decl_table.get(source_decl_index).?; - break :blk code; - } - }; + const code = source_atom.code.getCode(self); - if (!self.sixtyfour_bit) { - mem.writeInt(u32, code[@intCast(usize, offset)..][0..4], @intCast(u32, target_decl_offset + addend), self.base.options.target.cpu.arch.endian()); + if (reloc.pcrel) { + const disp = @intCast(i32, target_offset) - @intCast(i32, source_atom.offset.?) - 4 - @intCast(i32, offset); + mem.writeInt(i32, code[@intCast(usize, offset)..][0..4], @intCast(i32, disp), self.base.options.target.cpu.arch.endian()); } else { - mem.writeInt(u64, code[@intCast(usize, offset)..][0..8], target_decl_offset + addend, self.base.options.target.cpu.arch.endian()); + if (!self.sixtyfour_bit) { + mem.writeInt(u32, code[@intCast(usize, offset)..][0..4], @intCast(u32, target_offset + addend), self.base.options.target.cpu.arch.endian()); + } else { + mem.writeInt(u64, code[@intCast(usize, offset)..][0..8], target_offset + addend, self.base.options.target.cpu.arch.endian()); + } } + log.debug("relocating the address of '{s}' + {d} into '{s}' + {d} (({s}[{d}] = 0x{x} + 0x{x})", .{ target_symbol.name, addend, source_atom_symbol.name, offset, source_atom_symbol.name, offset, target_offset, addend }); } } } @@ -722,7 +870,7 @@ fn addDeclExports( exports: []const *Module.Export, ) !void { const metadata = self.decls.getPtr(decl_index).?; - const decl_block = self.getDeclBlock(metadata.index); + const atom = self.getAtom(metadata.index); for (exports) |exp| { const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); @@ -739,8 +887,8 @@ fn addDeclExports( } } const sym = .{ - .value = decl_block.offset.?, - .type = decl_block.type.toGlobal(), + .value = atom.offset.?, + .type = atom.type.toGlobal(), .name = try self.base.allocator.dupe(u8, exp_name), }; @@ -780,12 +928,12 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { } if (self.decls.fetchRemove(decl_index)) |const_kv| { var kv = const_kv; - const decl_block = self.getDeclBlock(kv.value.index); - if (decl_block.got_index) |i| { + const atom = self.getAtom(kv.value.index); + if (atom.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_block.sym_index) |i| { + if (atom.sym_index) |i| { self.syms_index_free_list.append(self.base.allocator, i) catch {}; self.syms.items[i] = aout.Sym.undefined_symbol; } @@ -793,40 +941,42 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { } self.freeUnnamedConsts(decl_index); { - const relocs = self.relocs.getPtr(decl_index) orelse return; + const atom_index = self.decls.get(decl_index).?.index; + const relocs = self.relocs.getPtr(atom_index) orelse return; relocs.clearAndFree(self.base.allocator); - assert(self.relocs.remove(decl_index)); + assert(self.relocs.remove(atom_index)); } } fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void { const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; - for (unnamed_consts.items) |c| { - self.base.allocator.free(self.syms.items[c.info.sym_index.?].name); - self.base.allocator.free(c.code); - self.syms.items[c.info.sym_index.?] = aout.Sym.undefined_symbol; - self.syms_index_free_list.append(self.base.allocator, c.info.sym_index.?) catch {}; + for (unnamed_consts.items) |atom_idx| { + const atom = self.getAtom(atom_idx); + self.base.allocator.free(self.syms.items[atom.sym_index.?].name); + self.syms.items[atom.sym_index.?] = aout.Sym.undefined_symbol; + self.syms_index_free_list.append(self.base.allocator, atom.sym_index.?) catch {}; } unnamed_consts.clearAndFree(self.base.allocator); } -fn createDeclBlock(self: *Plan9) !DeclBlock.Index { +fn createAtom(self: *Plan9) !Atom.Index { const gpa = self.base.allocator; - const index = @intCast(DeclBlock.Index, self.decl_blocks.items.len); - const decl_block = try self.decl_blocks.addOne(gpa); - decl_block.* = .{ + const index = @intCast(Atom.Index, self.atoms.items.len); + const atom = try self.atoms.addOne(gpa); + atom.* = .{ .type = .t, .offset = null, .sym_index = null, .got_index = null, + .code = undefined, }; return index; } -pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !DeclBlock.Index { +pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !Atom.Index { const gop = try self.decls.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { - const index = try self.createDeclBlock(); - self.getDeclBlockPtr(index).got_index = self.allocateGotIndex(); + const index = try self.createAtom(); + self.getAtomPtr(index).got_index = self.allocateGotIndex(); gop.value_ptr.* = .{ .index = index, .exports = .{}, @@ -846,6 +996,88 @@ pub fn updateDeclExports( _ = module; _ = exports; } + +pub fn getOrCreateAtomForLazySymbol(self: *Plan9, sym: File.LazySymbol) !Atom.Index { + const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(self.base.options.module.?)); + errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); + + if (!gop.found_existing) gop.value_ptr.* = .{}; + + const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { + .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state }, + .const_data => .{ .atom = &gop.value_ptr.rodata_atom, .state = &gop.value_ptr.rodata_state }, + }; + switch (metadata.state.*) { + .unused => metadata.atom.* = try self.createAtom(), + .pending_flush => return metadata.atom.*, + .flushed => {}, + } + metadata.state.* = .pending_flush; + const atom = metadata.atom.*; + _ = try self.getAtomPtr(atom).getOrCreateSymbolTableEntry(self); + _ = self.getAtomPtr(atom).getOrCreateOffsetTableEntry(self); + // anyerror needs to be deferred until flushModule + if (sym.getDecl(self.base.options.module.?) != .none) { + try self.updateLazySymbolAtom(sym, atom); + } + return atom; +} + +fn updateLazySymbolAtom(self: *Plan9, sym: File.LazySymbol, atom_index: Atom.Index) !void { + const gpa = self.base.allocator; + const mod = self.base.options.module.?; + + var required_alignment: u32 = undefined; + var code_buffer = std.ArrayList(u8).init(gpa); + defer code_buffer.deinit(); + + // create the symbol for the name + const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ + @tagName(sym.kind), + sym.ty.fmt(mod), + }); + + const symbol: aout.Sym = .{ + .value = undefined, + .type = if (sym.kind == .code) .t else .d, + .name = name, + }; + self.syms.items[self.getAtomPtr(atom_index).sym_index.?] = symbol; + + // generate the code + const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl| + mod.declPtr(owner_decl).srcLoc(mod) + else + Module.SrcLoc{ + .file_scope = undefined, + .parent_decl_node = undefined, + .lazy = .unneeded, + }; + const res = try codegen.generateLazySymbol( + &self.base, + src, + sym, + &required_alignment, + &code_buffer, + .none, + .{ .parent_atom_index = @intCast(Atom.Index, atom_index) }, + ); + const code = switch (res) { + .ok => code_buffer.items, + .fail => |em| { + log.err("{s}", .{em.msg}); + return error.CodegenFail; + }, + }; + // duped_code is freed when the atom is freed + var duped_code = try self.base.allocator.dupe(u8, code); + errdefer self.base.allocator.free(duped_code); + self.getAtomPtr(atom_index).code = .{ + .code_ptr = duped_code.ptr, + .other = .{ .code_len = duped_code.len }, + }; +} + pub fn deinit(self: *Plan9) void { const gpa = self.base.allocator; { @@ -861,6 +1093,14 @@ pub fn deinit(self: *Plan9) void { self.freeUnnamedConsts(kv.key_ptr.*); } self.unnamed_const_atoms.deinit(gpa); + var it_lzc = self.lazy_syms.iterator(); + while (it_lzc.next()) |kv| { + if (kv.value_ptr.text_state != .unused) + gpa.free(self.syms.items[self.getAtom(kv.value_ptr.text_atom).sym_index.?].name); + if (kv.value_ptr.rodata_state != .unused) + gpa.free(self.syms.items[self.getAtom(kv.value_ptr.rodata_atom).sym_index.?].name); + } + self.lazy_syms.deinit(gpa); var itf_files = self.fn_decl_table.iterator(); while (itf_files.next()) |ent| { // get the submap @@ -883,7 +1123,12 @@ pub fn deinit(self: *Plan9) void { self.syms_index_free_list.deinit(gpa); self.file_segments.deinit(gpa); self.path_arena.deinit(); - self.decl_blocks.deinit(gpa); + for (self.atoms.items) |a| { + if (a.code.getOwnedCode()) |c| { + gpa.free(c); + } + } + self.atoms.deinit(gpa); { var it = self.decls.iterator(); @@ -911,7 +1156,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option self.bases = defaultBaseAddrs(options.target.cpu.arch); - // first 3 symbols in our table are edata, end, etext + // first 4 symbols in our table are edata, end, etext, and got try self.syms.appendSlice(self.base.allocator, &.{ .{ .value = 0xcafebabe, @@ -928,13 +1173,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option .type = .T, .name = "etext", }, + // we include the global offset table to make it easier for debugging + .{ + .value = self.getAddr(0, .d), // the global offset table starts at 0 + .type = .d, + .name = "__GOT", + }, }); return self; } pub fn writeSym(self: *Plan9, w: anytype, sym: aout.Sym) !void { - log.debug("write sym{{name: {s}, value: {x}}}", .{ sym.name, sym.value }); + // log.debug("write sym{{name: {s}, value: {x}}}", .{ sym.name, 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)); @@ -950,6 +1201,11 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const mod = self.base.options.module.?; const ip = &mod.intern_pool; const writer = buf.writer(); + // write the first four symbols (edata, etext, end, __GOT) + try self.writeSym(writer, self.syms.items[0]); + try self.writeSym(writer, self.syms.items[1]); + try self.writeSym(writer, self.syms.items[2]); + try self.writeSym(writer, self.syms.items[3]); // write the f symbols { var it = self.file_segments.iterator(); @@ -968,8 +1224,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { while (it.next()) |entry| { const decl_index = entry.key_ptr.*; const decl_metadata = self.decls.get(decl_index).?; - const decl_block = self.getDeclBlock(decl_metadata.index); - const sym = self.syms.items[decl_block.sym_index.?]; + const atom = self.getAtom(decl_metadata.index); + const sym = self.syms.items[atom.sym_index.?]; try self.writeSym(writer, sym); if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { for (exports.items) |e| if (decl_metadata.getExport(self, ip.stringToSlice(e.opts.name))) |exp_i| { @@ -978,6 +1234,27 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { } } } + // the data lazy symbols + { + var it = self.lazy_syms.iterator(); + while (it.next()) |kv| { + const meta = kv.value_ptr; + const data_atom = if (meta.rodata_state != .unused) self.getAtomPtr(meta.rodata_atom) else continue; + const sym = self.syms.items[data_atom.sym_index.?]; + try self.writeSym(writer, sym); + } + } + // unnamed consts + { + var it = self.unnamed_const_atoms.iterator(); + while (it.next()) |kv| { + const consts = kv.value_ptr; + for (consts.items) |atom_index| { + const sym = self.syms.items[self.getAtom(atom_index).sym_index.?]; + try self.writeSym(writer, sym); + } + } + } // 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 @@ -994,8 +1271,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { while (submap_it.next()) |entry| { const decl_index = entry.key_ptr.*; const decl_metadata = self.decls.get(decl_index).?; - const decl_block = self.getDeclBlock(decl_metadata.index); - const sym = self.syms.items[decl_block.sym_index.?]; + const atom = self.getAtom(decl_metadata.index); + const sym = self.syms.items[atom.sym_index.?]; try self.writeSym(writer, sym); if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { for (exports.items) |e| if (decl_metadata.getExport(self, ip.stringToSlice(e.opts.name))) |exp_i| { @@ -1007,6 +1284,16 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { } } } + // the text lazy symbols + { + var it = self.lazy_syms.iterator(); + while (it.next()) |kv| { + const meta = kv.value_ptr; + const text_atom = if (meta.text_state != .unused) self.getAtomPtr(meta.text_atom) else continue; + const sym = self.syms.items[text_atom.sym_index.?]; + try self.writeSym(writer, sym); + } + } } } @@ -1024,6 +1311,7 @@ pub fn getDeclVAddr( ) !u64 { const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); + // we might already know the vaddr if (decl.ty.zigTypeTag(mod) == .Fn) { var start = self.bases.text; var it_file = self.fn_decl_table.iterator(); @@ -1043,23 +1331,28 @@ pub fn getDeclVAddr( start += kv.value_ptr.len; } } + const atom_index = try self.seeDecl(decl_index); // the parent_atom_index in this case is just the decl_index of the parent - const gop = try self.relocs.getOrPut(self.base.allocator, @intToEnum(Module.Decl.Index, reloc_info.parent_atom_index)); - if (!gop.found_existing) { - gop.value_ptr.* = .{}; - } - try gop.value_ptr.append(self.base.allocator, .{ - .target = decl_index, + try self.addReloc(reloc_info.parent_atom_index, .{ + .target = atom_index, .offset = reloc_info.offset, .addend = reloc_info.addend, }); - return 0; + return 0xcafebabe; +} + +pub fn addReloc(self: *Plan9, parent_index: Atom.Index, reloc: Reloc) !void { + const gop = try self.relocs.getOrPut(self.base.allocator, parent_index); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(self.base.allocator, reloc); } -pub fn getDeclBlock(self: *const Plan9, index: DeclBlock.Index) DeclBlock { - return self.decl_blocks.items[index]; +pub fn getAtom(self: *const Plan9, index: Atom.Index) Atom { + return self.atoms.items[index]; } -fn getDeclBlockPtr(self: *Plan9, index: DeclBlock.Index) *DeclBlock { - return &self.decl_blocks.items[index]; +fn getAtomPtr(self: *Plan9, index: Atom.Index) *Atom { + return &self.atoms.items[index]; } |
