authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-16 15:44:20-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-16 15:44:20-04:00
log017ecc5148da3f3f50f5666d635c22dfb6bfffb2
tree8705593aa531877e839b0aabe963789273c16cd4
parentb0375978ba54bc80c691eaf118f3a78e7f8920a7

self hosted repl: close executables between updates

This allows the executable to be executed

3 files changed, 65 insertions(+), 24 deletions(-)

src-self-hosted/Module.zig+17-1
...@@ -23,6 +23,8 @@ root_pkg: *Package,...@@ -23,6 +23,8 @@ root_pkg: *Package,
23/// Module owns this resource.23/// Module owns this resource.
24root_scope: *Scope.ZIRModule,24root_scope: *Scope.ZIRModule,
25bin_file: link.ElfFile,25bin_file: link.ElfFile,
26bin_file_dir: std.fs.Dir,
27bin_file_path: []const u8,
26/// It's rare for a decl to be exported, so we save memory by having a sparse map of28/// It's rare for a decl to be exported, so we save memory by having a sparse map of
27/// Decl pointers to details about them being exported.29/// Decl pointers to details about them being exported.
28/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.30/// 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 {...@@ -456,6 +458,8 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
456 .allocator = gpa,458 .allocator = gpa,
457 .root_pkg = options.root_pkg,459 .root_pkg = options.root_pkg,
458 .root_scope = root_scope,460 .root_scope = root_scope,
461 .bin_file_dir = bin_file_dir,
462 .bin_file_path = options.bin_file_path,
459 .bin_file = bin_file,463 .bin_file = bin_file,
460 .optimize_mode = options.optimize_mode,464 .optimize_mode = options.optimize_mode,
461 .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa),465 .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa),
...@@ -551,6 +555,18 @@ pub fn update(self: *Module) !void {...@@ -551,6 +555,18 @@ pub fn update(self: *Module) !void {
551 self.link_error_flags = self.bin_file.error_flags;555 self.link_error_flags = self.bin_file.error_flags;
552}556}
553557
558/// Having the file open for writing is problematic as far as executing the
559/// binary is concerned. This will remove the write flag, or close the file,
560/// or whatever is needed so that it can be executed.
561/// After this, one must call` makeFileWritable` before calling `update`.
562pub fn makeBinFileExecutable(self: *Module) !void {
563 return self.bin_file.makeExecutable();
564}
565
566pub fn makeBinFileWritable(self: *Module) !void {
567 return self.bin_file.makeWritable(self.bin_file_dir, self.bin_file_path);
568}
569
554pub fn totalErrorCount(self: *Module) usize {570pub fn totalErrorCount(self: *Module) usize {
555 return self.failed_decls.size +571 return self.failed_decls.size +
556 self.failed_files.size +572 self.failed_files.size +
...@@ -759,7 +775,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -759,7 +775,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
759 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);775 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
760 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {776 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
761 // TODO recursive dependency management777 // TODO recursive dependency management
762 std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});778 //std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});
763 self.decl_table.removeAssertDiscard(name_hash);779 self.decl_table.removeAssertDiscard(name_hash);
764 const saved_link = decl.link;780 const saved_link = decl.link;
765 decl.destroy(self.allocator);781 decl.destroy(self.allocator);
src-self-hosted/link.zig+42-23
...@@ -91,7 +91,7 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF...@@ -91,7 +91,7 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF
9191
92pub const ElfFile = struct {92pub const ElfFile = struct {
93 allocator: *Allocator,93 allocator: *Allocator,
94 file: fs.File,94 file: ?fs.File,
95 owns_file_handle: bool,95 owns_file_handle: bool,
96 options: Options,96 options: Options,
97 ptr_width: enum { p32, p64 },97 ptr_width: enum { p32, p64 },
...@@ -170,8 +170,27 @@ pub const ElfFile = struct {...@@ -170,8 +170,27 @@ pub const ElfFile = struct {
170 self.local_symbols.deinit(self.allocator);170 self.local_symbols.deinit(self.allocator);
171 self.global_symbols.deinit(self.allocator);171 self.global_symbols.deinit(self.allocator);
172 self.offset_table.deinit(self.allocator);172 self.offset_table.deinit(self.allocator);
173 if (self.owns_file_handle)173 if (self.owns_file_handle) {
174 self.file.close();174 if (self.file) |f| f.close();
175 }
176 }
177
178 pub fn makeExecutable(self: *ElfFile) !void {
179 assert(self.owns_file_handle);
180 if (self.file) |f| {
181 f.close();
182 self.file = null;
183 }
184 }
185
186 pub fn makeWritable(self: *ElfFile, dir: fs.Dir, sub_path: []const u8) !void {
187 assert(self.owns_file_handle);
188 if (self.file != null) return;
189 self.file = try dir.createFile(sub_path, .{
190 .truncate = false,
191 .read = true,
192 .mode = determineMode(self.options),
193 });
175 }194 }
176195
177 // `alloc_num / alloc_den` is the factor of padding when allocation196 // `alloc_num / alloc_den` is the factor of padding when allocation
...@@ -467,7 +486,7 @@ pub const ElfFile = struct {...@@ -467,7 +486,7 @@ pub const ElfFile = struct {
467 bswapAllFields(elf.Elf32_Phdr, phdr);486 bswapAllFields(elf.Elf32_Phdr, phdr);
468 }487 }
469 }488 }
470 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);489 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
471 },490 },
472 .p64 => {491 .p64 => {
473 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);492 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
...@@ -479,7 +498,7 @@ pub const ElfFile = struct {...@@ -479,7 +498,7 @@ pub const ElfFile = struct {
479 bswapAllFields(elf.Elf64_Phdr, phdr);498 bswapAllFields(elf.Elf64_Phdr, phdr);
480 }499 }
481 }500 }
482 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);501 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
483 },502 },
484 }503 }
485 self.phdr_table_dirty = false;504 self.phdr_table_dirty = false;
...@@ -498,7 +517,7 @@ pub const ElfFile = struct {...@@ -498,7 +517,7 @@ pub const ElfFile = struct {
498 shstrtab_sect.sh_size = needed_size;517 shstrtab_sect.sh_size = needed_size;
499 //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });518 //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
500519
501 try self.file.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);520 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
502 if (!self.shdr_table_dirty) {521 if (!self.shdr_table_dirty) {
503 // Then it won't get written with the others and we need to do it.522 // Then it won't get written with the others and we need to do it.
504 try self.writeSectHeader(self.shstrtab_index.?);523 try self.writeSectHeader(self.shstrtab_index.?);
...@@ -534,7 +553,7 @@ pub const ElfFile = struct {...@@ -534,7 +553,7 @@ pub const ElfFile = struct {
534 bswapAllFields(elf.Elf32_Shdr, shdr);553 bswapAllFields(elf.Elf32_Shdr, shdr);
535 }554 }
536 }555 }
537 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);556 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
538 },557 },
539 .p64 => {558 .p64 => {
540 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);559 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
...@@ -547,7 +566,7 @@ pub const ElfFile = struct {...@@ -547,7 +566,7 @@ pub const ElfFile = struct {
547 bswapAllFields(elf.Elf64_Shdr, shdr);566 bswapAllFields(elf.Elf64_Shdr, shdr);
548 }567 }
549 }568 }
550 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);569 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
551 },570 },
552 }571 }
553 self.shdr_table_dirty = false;572 self.shdr_table_dirty = false;
...@@ -687,7 +706,7 @@ pub const ElfFile = struct {...@@ -687,7 +706,7 @@ pub const ElfFile = struct {
687706
688 assert(index == e_ehsize);707 assert(index == e_ehsize);
689708
690 try self.file.pwriteAll(hdr_buf[0..index], 0);709 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
691 }710 }
692711
693 const AllocatedBlock = struct {712 const AllocatedBlock = struct {
...@@ -718,7 +737,7 @@ pub const ElfFile = struct {...@@ -718,7 +737,7 @@ pub const ElfFile = struct {
718 // Must move the entire text section.737 // Must move the entire text section.
719 const new_offset = self.findFreeSpace(needed_size, 0x1000);738 const new_offset = self.findFreeSpace(needed_size, 0x1000);
720 const text_size = (last_start + last_size) - phdr.p_vaddr;739 const text_size = (last_start + last_size) - phdr.p_vaddr;
721 const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, text_size);740 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
722 if (amt != text_size) return error.InputOutput;741 if (amt != text_size) return error.InputOutput;
723 shdr.sh_offset = new_offset;742 shdr.sh_offset = new_offset;
724 }743 }
...@@ -876,7 +895,7 @@ pub const ElfFile = struct {...@@ -876,7 +895,7 @@ pub const ElfFile = struct {
876 }895 }
877 };896 };
878897
879 try self.file.pwriteAll(code, file_offset);898 try self.file.?.pwriteAll(code, file_offset);
880899
881 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.900 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
882 const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{};901 const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{};
...@@ -962,14 +981,14 @@ pub const ElfFile = struct {...@@ -962,14 +981,14 @@ pub const ElfFile = struct {
962 if (foreign_endian) {981 if (foreign_endian) {
963 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);982 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
964 }983 }
965 return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset);984 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
966 },985 },
967 64 => {986 64 => {
968 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};987 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
969 if (foreign_endian) {988 if (foreign_endian) {
970 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);989 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
971 }990 }
972 return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset);991 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
973 },992 },
974 else => return error.UnsupportedArchitecture,993 else => return error.UnsupportedArchitecture,
975 }994 }
...@@ -985,14 +1004,14 @@ pub const ElfFile = struct {...@@ -985,14 +1004,14 @@ pub const ElfFile = struct {
985 if (foreign_endian) {1004 if (foreign_endian) {
986 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);1005 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
987 }1006 }
988 return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset);1007 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
989 },1008 },
990 64 => {1009 64 => {
991 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};1010 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
992 if (foreign_endian) {1011 if (foreign_endian) {
993 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);1012 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
994 }1013 }
995 return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset);1014 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
996 },1015 },
997 else => return error.UnsupportedArchitecture,1016 else => return error.UnsupportedArchitecture,
998 }1017 }
...@@ -1012,7 +1031,7 @@ pub const ElfFile = struct {...@@ -1012,7 +1031,7 @@ pub const ElfFile = struct {
1012 if (needed_size > allocated_size) {1031 if (needed_size > allocated_size) {
1013 // Must move the entire got section.1032 // Must move the entire got section.
1014 const new_offset = self.findFreeSpace(needed_size, entry_size);1033 const new_offset = self.findFreeSpace(needed_size, entry_size);
1015 const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size);1034 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1016 if (amt != shdr.sh_size) return error.InputOutput;1035 if (amt != shdr.sh_size) return error.InputOutput;
1017 shdr.sh_offset = new_offset;1036 shdr.sh_offset = new_offset;
1018 }1037 }
...@@ -1031,12 +1050,12 @@ pub const ElfFile = struct {...@@ -1031,12 +1050,12 @@ pub const ElfFile = struct {
1031 .p32 => {1050 .p32 => {
1032 var buf: [4]u8 = undefined;1051 var buf: [4]u8 = undefined;
1033 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);1052 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1034 try self.file.pwriteAll(&buf, off);1053 try self.file.?.pwriteAll(&buf, off);
1035 },1054 },
1036 .p64 => {1055 .p64 => {
1037 var buf: [8]u8 = undefined;1056 var buf: [8]u8 = undefined;
1038 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);1057 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1039 try self.file.pwriteAll(&buf, off);1058 try self.file.?.pwriteAll(&buf, off);
1040 },1059 },
1041 }1060 }
1042 }1061 }
...@@ -1059,7 +1078,7 @@ pub const ElfFile = struct {...@@ -1059,7 +1078,7 @@ pub const ElfFile = struct {
1059 // Move all the symbols to a new file location.1078 // Move all the symbols to a new file location.
1060 const new_offset = self.findFreeSpace(needed_size, sym_align);1079 const new_offset = self.findFreeSpace(needed_size, sym_align);
1061 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;1080 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1062 const amt = try self.file.copyRangeAll(syms_sect.sh_offset, self.file, new_offset, existing_size);1081 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1063 if (amt != existing_size) return error.InputOutput;1082 if (amt != existing_size) return error.InputOutput;
1064 syms_sect.sh_offset = new_offset;1083 syms_sect.sh_offset = new_offset;
1065 }1084 }
...@@ -1084,7 +1103,7 @@ pub const ElfFile = struct {...@@ -1084,7 +1103,7 @@ pub const ElfFile = struct {
1084 bswapAllFields(elf.Elf32_Sym, &sym[0]);1103 bswapAllFields(elf.Elf32_Sym, &sym[0]);
1085 }1104 }
1086 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;1105 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1087 try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1106 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1088 },1107 },
1089 .p64 => {1108 .p64 => {
1090 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};1109 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
...@@ -1092,7 +1111,7 @@ pub const ElfFile = struct {...@@ -1092,7 +1111,7 @@ pub const ElfFile = struct {
1092 bswapAllFields(elf.Elf64_Sym, &sym[0]);1111 bswapAllFields(elf.Elf64_Sym, &sym[0]);
1093 }1112 }
1094 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;1113 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1095 try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1114 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1096 },1115 },
1097 }1116 }
1098 }1117 }
...@@ -1124,7 +1143,7 @@ pub const ElfFile = struct {...@@ -1124,7 +1143,7 @@ pub const ElfFile = struct {
1124 bswapAllFields(elf.Elf32_Sym, sym);1143 bswapAllFields(elf.Elf32_Sym, sym);
1125 }1144 }
1126 }1145 }
1127 try self.file.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);1146 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1128 },1147 },
1129 .p64 => {1148 .p64 => {
1130 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);1149 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
...@@ -1143,7 +1162,7 @@ pub const ElfFile = struct {...@@ -1143,7 +1162,7 @@ pub const ElfFile = struct {
1143 bswapAllFields(elf.Elf64_Sym, sym);1162 bswapAllFields(elf.Elf64_Sym, sym);
1144 }1163 }
1145 }1164 }
1146 try self.file.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);1165 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1147 },1166 },
1148 }1167 }
1149 }1168 }
src-self-hosted/main.zig+6
...@@ -447,11 +447,17 @@ fn buildOutputType(...@@ -447,11 +447,17 @@ fn buildOutputType(
447447
448 while (watch) {448 while (watch) {
449 try stderr.print("🦎 ", .{});449 try stderr.print("🦎 ", .{});
450 if (output_mode == .Exe) {
451 try module.makeBinFileExecutable();
452 }
450 if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {453 if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
451 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});454 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
452 continue;455 continue;
453 }) |line| {456 }) |line| {
454 if (mem.eql(u8, line, "update")) {457 if (mem.eql(u8, line, "update")) {
458 if (output_mode == .Exe) {
459 try module.makeBinFileWritable();
460 }
455 try updateModule(gpa, &module, zir_out_path);461 try updateModule(gpa, &module, zir_out_path);
456 } else if (mem.eql(u8, line, "exit")) {462 } else if (mem.eql(u8, line, "exit")) {
457 break;463 break;