diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 10127627c..72a4e1b41 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -23,6 +23,8 @@ root_pkg: *Package, /// Module owns this resource. root_scope: *Scope.ZIRModule, bin_file: link.ElfFile, +bin_file_dir: std.fs.Dir, +bin_file_path: []const u8, /// It's rare for a decl to be exported, so we save memory by having a sparse map of /// Decl pointers to details about them being exported. /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. @@ -456,6 +458,8 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { .allocator = gpa, .root_pkg = options.root_pkg, .root_scope = root_scope, + .bin_file_dir = bin_file_dir, + .bin_file_path = options.bin_file_path, .bin_file = bin_file, .optimize_mode = options.optimize_mode, .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa), @@ -551,6 +555,18 @@ pub fn update(self: *Module) !void { self.link_error_flags = self.bin_file.error_flags; } +/// Having the file open for writing is problematic as far as executing the +/// binary is concerned. This will remove the write flag, or close the file, +/// or whatever is needed so that it can be executed. +/// After this, one must call` makeFileWritable` before calling `update`. +pub fn makeBinFileExecutable(self: *Module) !void { + return self.bin_file.makeExecutable(); +} + +pub fn makeBinFileWritable(self: *Module) !void { + return self.bin_file.makeWritable(self.bin_file_dir, self.bin_file_path); +} + pub fn totalErrorCount(self: *Module) usize { return self.failed_decls.size + self.failed_files.size + @@ -759,7 +775,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { const new_contents_hash = Decl.hashSimpleName(src_decl.contents); if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) { // TODO recursive dependency management - std.debug.warn("noticed that '{}' changed\n", .{src_decl.name}); + //std.debug.warn("noticed that '{}' changed\n", .{src_decl.name}); self.decl_table.removeAssertDiscard(name_hash); const saved_link = decl.link; decl.destroy(self.allocator); diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 8b24ef533..a0c7ec849 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -91,7 +91,7 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF pub const ElfFile = struct { allocator: *Allocator, - file: fs.File, + file: ?fs.File, owns_file_handle: bool, options: Options, ptr_width: enum { p32, p64 }, @@ -170,8 +170,27 @@ pub const ElfFile = struct { self.local_symbols.deinit(self.allocator); self.global_symbols.deinit(self.allocator); self.offset_table.deinit(self.allocator); - if (self.owns_file_handle) - self.file.close(); + if (self.owns_file_handle) { + if (self.file) |f| f.close(); + } + } + + pub fn makeExecutable(self: *ElfFile) !void { + assert(self.owns_file_handle); + if (self.file) |f| { + f.close(); + self.file = null; + } + } + + pub fn makeWritable(self: *ElfFile, dir: fs.Dir, sub_path: []const u8) !void { + assert(self.owns_file_handle); + if (self.file != null) return; + self.file = try dir.createFile(sub_path, .{ + .truncate = false, + .read = true, + .mode = determineMode(self.options), + }); } // `alloc_num / alloc_den` is the factor of padding when allocation @@ -467,7 +486,7 @@ pub const ElfFile = struct { bswapAllFields(elf.Elf32_Phdr, phdr); } } - try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); + try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); }, .p64 => { const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len); @@ -479,7 +498,7 @@ pub const ElfFile = struct { bswapAllFields(elf.Elf64_Phdr, phdr); } } - try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); + try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); }, } self.phdr_table_dirty = false; @@ -498,7 +517,7 @@ pub const ElfFile = struct { shstrtab_sect.sh_size = needed_size; //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size }); - try self.file.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); + try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); if (!self.shdr_table_dirty) { // Then it won't get written with the others and we need to do it. try self.writeSectHeader(self.shstrtab_index.?); @@ -534,7 +553,7 @@ pub const ElfFile = struct { bswapAllFields(elf.Elf32_Shdr, shdr); } } - try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); + try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); }, .p64 => { const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len); @@ -547,7 +566,7 @@ pub const ElfFile = struct { bswapAllFields(elf.Elf64_Shdr, shdr); } } - try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); + try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); }, } self.shdr_table_dirty = false; @@ -687,7 +706,7 @@ pub const ElfFile = struct { assert(index == e_ehsize); - try self.file.pwriteAll(hdr_buf[0..index], 0); + try self.file.?.pwriteAll(hdr_buf[0..index], 0); } const AllocatedBlock = struct { @@ -718,7 +737,7 @@ pub const ElfFile = struct { // Must move the entire text section. const new_offset = self.findFreeSpace(needed_size, 0x1000); const text_size = (last_start + last_size) - phdr.p_vaddr; - const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, text_size); + const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size); if (amt != text_size) return error.InputOutput; shdr.sh_offset = new_offset; } @@ -876,7 +895,7 @@ pub const ElfFile = struct { } }; - try self.file.pwriteAll(code, file_offset); + try self.file.?.pwriteAll(code, file_offset); // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{}; @@ -962,14 +981,14 @@ pub const ElfFile = struct { if (foreign_endian) { bswapAllFields(elf.Elf32_Phdr, &phdr[0]); } - return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset); + return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); }, 64 => { var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; if (foreign_endian) { bswapAllFields(elf.Elf64_Phdr, &phdr[0]); } - return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset); + return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); }, else => return error.UnsupportedArchitecture, } @@ -985,14 +1004,14 @@ pub const ElfFile = struct { if (foreign_endian) { bswapAllFields(elf.Elf32_Shdr, &shdr[0]); } - return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset); + return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); }, 64 => { var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; if (foreign_endian) { bswapAllFields(elf.Elf64_Shdr, &shdr[0]); } - return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset); + return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); }, else => return error.UnsupportedArchitecture, } @@ -1012,7 +1031,7 @@ pub const ElfFile = struct { if (needed_size > allocated_size) { // Must move the entire got section. const new_offset = self.findFreeSpace(needed_size, entry_size); - const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size); + const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size); if (amt != shdr.sh_size) return error.InputOutput; shdr.sh_offset = new_offset; } @@ -1031,12 +1050,12 @@ pub const ElfFile = struct { .p32 => { var buf: [4]u8 = undefined; mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); - try self.file.pwriteAll(&buf, off); + try self.file.?.pwriteAll(&buf, off); }, .p64 => { var buf: [8]u8 = undefined; mem.writeInt(u64, &buf, self.offset_table.items[index], endian); - try self.file.pwriteAll(&buf, off); + try self.file.?.pwriteAll(&buf, off); }, } } @@ -1059,7 +1078,7 @@ pub const ElfFile = struct { // Move all the symbols to a new file location. const new_offset = self.findFreeSpace(needed_size, sym_align); const existing_size = @as(u64, syms_sect.sh_info) * sym_size; - const amt = try self.file.copyRangeAll(syms_sect.sh_offset, self.file, new_offset, existing_size); + const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size); if (amt != existing_size) return error.InputOutput; syms_sect.sh_offset = new_offset; } @@ -1084,7 +1103,7 @@ pub const ElfFile = struct { bswapAllFields(elf.Elf32_Sym, &sym[0]); } const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index; - try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); + try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); }, .p64 => { var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]}; @@ -1092,7 +1111,7 @@ pub const ElfFile = struct { bswapAllFields(elf.Elf64_Sym, &sym[0]); } const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index; - try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); + try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); }, } } @@ -1124,7 +1143,7 @@ pub const ElfFile = struct { bswapAllFields(elf.Elf32_Sym, sym); } } - try self.file.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); + try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); }, .p64 => { const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len); @@ -1143,7 +1162,7 @@ pub const ElfFile = struct { bswapAllFields(elf.Elf64_Sym, sym); } } - try self.file.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); + try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); }, } } diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 40d046a25..4ef4acc24 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -447,11 +447,17 @@ fn buildOutputType( while (watch) { try stderr.print("🦎 ", .{}); + if (output_mode == .Exe) { + try module.makeBinFileExecutable(); + } if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)}); continue; }) |line| { if (mem.eql(u8, line, "update")) { + if (output_mode == .Exe) { + try module.makeBinFileWritable(); + } try updateModule(gpa, &module, zir_out_path); } else if (mem.eql(u8, line, "exit")) { break;