self hosted repl: close executables between updates

This allows the executable to be executed
master
Andrew Kelley 2020-05-16 15:44:20 -04:00
parent b0375978ba
commit 017ecc5148
3 changed files with 65 additions and 24 deletions

View File

@ -23,6 +23,8 @@ root_pkg: *Package,
/// Module owns this resource. /// Module owns this resource.
root_scope: *Scope.ZIRModule, root_scope: *Scope.ZIRModule,
bin_file: link.ElfFile, 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 /// 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. /// 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. /// 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, .allocator = gpa,
.root_pkg = options.root_pkg, .root_pkg = options.root_pkg,
.root_scope = root_scope, .root_scope = root_scope,
.bin_file_dir = bin_file_dir,
.bin_file_path = options.bin_file_path,
.bin_file = bin_file, .bin_file = bin_file,
.optimize_mode = options.optimize_mode, .optimize_mode = options.optimize_mode,
.decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa), .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; 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 { pub fn totalErrorCount(self: *Module) usize {
return self.failed_decls.size + return self.failed_decls.size +
self.failed_files.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); const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) { if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
// TODO recursive dependency management // 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); self.decl_table.removeAssertDiscard(name_hash);
const saved_link = decl.link; const saved_link = decl.link;
decl.destroy(self.allocator); decl.destroy(self.allocator);

View File

@ -91,7 +91,7 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF
pub const ElfFile = struct { pub const ElfFile = struct {
allocator: *Allocator, allocator: *Allocator,
file: fs.File, file: ?fs.File,
owns_file_handle: bool, owns_file_handle: bool,
options: Options, options: Options,
ptr_width: enum { p32, p64 }, ptr_width: enum { p32, p64 },
@ -170,8 +170,27 @@ pub const ElfFile = struct {
self.local_symbols.deinit(self.allocator); self.local_symbols.deinit(self.allocator);
self.global_symbols.deinit(self.allocator); self.global_symbols.deinit(self.allocator);
self.offset_table.deinit(self.allocator); self.offset_table.deinit(self.allocator);
if (self.owns_file_handle) if (self.owns_file_handle) {
self.file.close(); 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 // `alloc_num / alloc_den` is the factor of padding when allocation
@ -467,7 +486,7 @@ pub const ElfFile = struct {
bswapAllFields(elf.Elf32_Phdr, phdr); 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 => { .p64 => {
const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len); 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); 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; self.phdr_table_dirty = false;
@ -498,7 +517,7 @@ pub const ElfFile = struct {
shstrtab_sect.sh_size = needed_size; 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 }); //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) { if (!self.shdr_table_dirty) {
// Then it won't get written with the others and we need to do it. // Then it won't get written with the others and we need to do it.
try self.writeSectHeader(self.shstrtab_index.?); try self.writeSectHeader(self.shstrtab_index.?);
@ -534,7 +553,7 @@ pub const ElfFile = struct {
bswapAllFields(elf.Elf32_Shdr, shdr); 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 => { .p64 => {
const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len); 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); 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; self.shdr_table_dirty = false;
@ -687,7 +706,7 @@ pub const ElfFile = struct {
assert(index == e_ehsize); 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 { const AllocatedBlock = struct {
@ -718,7 +737,7 @@ pub const ElfFile = struct {
// Must move the entire text section. // Must move the entire text section.
const new_offset = self.findFreeSpace(needed_size, 0x1000); const new_offset = self.findFreeSpace(needed_size, 0x1000);
const text_size = (last_start + last_size) - phdr.p_vaddr; 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; if (amt != text_size) return error.InputOutput;
shdr.sh_offset = new_offset; 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. // 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{}; const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{};
@ -962,14 +981,14 @@ pub const ElfFile = struct {
if (foreign_endian) { if (foreign_endian) {
bswapAllFields(elf.Elf32_Phdr, &phdr[0]); bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
} }
return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset); return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
}, },
64 => { 64 => {
var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
if (foreign_endian) { if (foreign_endian) {
bswapAllFields(elf.Elf64_Phdr, &phdr[0]); 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, else => return error.UnsupportedArchitecture,
} }
@ -985,14 +1004,14 @@ pub const ElfFile = struct {
if (foreign_endian) { if (foreign_endian) {
bswapAllFields(elf.Elf32_Shdr, &shdr[0]); bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
} }
return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset); return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
}, },
64 => { 64 => {
var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
if (foreign_endian) { if (foreign_endian) {
bswapAllFields(elf.Elf64_Shdr, &shdr[0]); 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, else => return error.UnsupportedArchitecture,
} }
@ -1012,7 +1031,7 @@ pub const ElfFile = struct {
if (needed_size > allocated_size) { if (needed_size > allocated_size) {
// Must move the entire got section. // Must move the entire got section.
const new_offset = self.findFreeSpace(needed_size, entry_size); 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; if (amt != shdr.sh_size) return error.InputOutput;
shdr.sh_offset = new_offset; shdr.sh_offset = new_offset;
} }
@ -1031,12 +1050,12 @@ pub const ElfFile = struct {
.p32 => { .p32 => {
var buf: [4]u8 = undefined; var buf: [4]u8 = undefined;
mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); 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 => { .p64 => {
var buf: [8]u8 = undefined; var buf: [8]u8 = undefined;
mem.writeInt(u64, &buf, self.offset_table.items[index], endian); 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. // Move all the symbols to a new file location.
const new_offset = self.findFreeSpace(needed_size, sym_align); const new_offset = self.findFreeSpace(needed_size, sym_align);
const existing_size = @as(u64, syms_sect.sh_info) * sym_size; 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; if (amt != existing_size) return error.InputOutput;
syms_sect.sh_offset = new_offset; syms_sect.sh_offset = new_offset;
} }
@ -1084,7 +1103,7 @@ pub const ElfFile = struct {
bswapAllFields(elf.Elf32_Sym, &sym[0]); bswapAllFields(elf.Elf32_Sym, &sym[0]);
} }
const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index; 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 => { .p64 => {
var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]}; 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]); bswapAllFields(elf.Elf64_Sym, &sym[0]);
} }
const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index; 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); 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 => { .p64 => {
const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len); 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); 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);
}, },
} }
} }

View File

@ -447,11 +447,17 @@ fn buildOutputType(
while (watch) { while (watch) {
try stderr.print("🦎 ", .{}); try stderr.print("🦎 ", .{});
if (output_mode == .Exe) {
try module.makeBinFileExecutable();
}
if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)}); try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
continue; continue;
}) |line| { }) |line| {
if (mem.eql(u8, line, "update")) { if (mem.eql(u8, line, "update")) {
if (output_mode == .Exe) {
try module.makeBinFileWritable();
}
try updateModule(gpa, &module, zir_out_path); try updateModule(gpa, &module, zir_out_path);
} else if (mem.eql(u8, line, "exit")) { } else if (mem.eql(u8, line, "exit")) {
break; break;