| ... | @@ -33,102 +33,27 @@ pub const Options = struct { | ... | @@ -33,102 +33,27 @@ pub const Options = struct { |
| 33 | program_code_size_hint: u64 = 256 * 1024, | 33 | program_code_size_hint: u64 = 256 * 1024, |
| 34 | }; | 34 | }; |
| 35 | | 35 | |
| 36 | /// Attempts incremental linking, if the file already exists. | | |
| 37 | /// If incremental linking fails, falls back to truncating the file and rewriting it. | | |
| 38 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. | | |
| 39 | /// This operation is not atomic. | | |
| 40 | pub fn openBinFilePath( | | |
| 41 | allocator: *Allocator, | | |
| 42 | dir: fs.Dir, | | |
| 43 | sub_path: []const u8, | | |
| 44 | options: Options, | | |
| 45 | ) !*File { | | |
| 46 | const cbe = options.object_format == .c; | | |
| 47 | const file = try dir.createFile(sub_path, .{ .truncate = cbe, .read = true, .mode = determineMode(options) }); | | |
| 48 | errdefer file.close(); | | |
| 49 | | | |
| 50 | if (cbe) { | | |
| 51 | var bin_file = try allocator.create(File.C); | | |
| 52 | errdefer allocator.destroy(bin_file); | | |
| 53 | bin_file.* = try openCFile(allocator, file, options); | | |
| 54 | return &bin_file.base; | | |
| 55 | } else { | | |
| 56 | var bin_file = try allocator.create(File.Elf); | | |
| 57 | errdefer allocator.destroy(bin_file); | | |
| 58 | bin_file.* = try openBinFile(allocator, file, options); | | |
| 59 | bin_file.owns_file_handle = true; | | |
| 60 | return &bin_file.base; | | |
| 61 | } | | |
| 62 | } | | |
| 63 | | | |
| 64 | /// Atomically overwrites the old file, if present. | | |
| 65 | pub fn writeFilePath( | | |
| 66 | allocator: *Allocator, | | |
| 67 | dir: fs.Dir, | | |
| 68 | sub_path: []const u8, | | |
| 69 | module: Module, | | |
| 70 | errors: *std.ArrayList(Module.ErrorMsg), | | |
| 71 | ) !void { | | |
| 72 | const options: Options = .{ | | |
| 73 | .target = module.target, | | |
| 74 | .output_mode = module.output_mode, | | |
| 75 | .link_mode = module.link_mode, | | |
| 76 | .object_format = module.object_format, | | |
| 77 | .symbol_count_hint = module.decls.items.len, | | |
| 78 | .optimize_mode = module.optimize_mode, | | |
| 79 | }; | | |
| 80 | const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) }); | | |
| 81 | defer af.deinit(); | | |
| 82 | | | |
| 83 | const elf_file = try createElfFile(allocator, af.file, options); | | |
| 84 | for (module.decls.items) |decl| { | | |
| 85 | try elf_file.updateDecl(module, decl, errors); | | |
| 86 | } | | |
| 87 | try elf_file.flush(); | | |
| 88 | if (elf_file.error_flags.no_entry_point_found) { | | |
| 89 | try errors.ensureCapacity(errors.items.len + 1); | | |
| 90 | errors.appendAssumeCapacity(.{ | | |
| 91 | .byte_offset = 0, | | |
| 92 | .msg = try std.fmt.allocPrint(errors.allocator, "no entry point found", .{}), | | |
| 93 | }); | | |
| 94 | } | | |
| 95 | try af.finish(); | | |
| 96 | return result; | | |
| 97 | } | | |
| 98 | | | |
| 99 | fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C { | | |
| 100 | return File.C{ | | |
| 101 | .base = .{ | | |
| 102 | .tag = .c, | | |
| 103 | .options = options, | | |
| 104 | }, | | |
| 105 | .allocator = allocator, | | |
| 106 | .file = file, | | |
| 107 | .main = std.ArrayList(u8).init(allocator), | | |
| 108 | .header = std.ArrayList(u8).init(allocator), | | |
| 109 | .constants = std.ArrayList(u8).init(allocator), | | |
| 110 | .called = std.StringHashMap(void).init(allocator), | | |
| 111 | }; | | |
| 112 | } | | |
| 113 | | | |
| 114 | /// Attempts incremental linking, if the file already exists. | | |
| 115 | /// If incremental linking fails, falls back to truncating the file and rewriting it. | | |
| 116 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | | |
| 117 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. | | |
| 118 | /// This operation is not atomic. | | |
| 119 | pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf { | | |
| 120 | return openBinFileInner(allocator, file, options) catch |err| switch (err) { | | |
| 121 | error.IncrFailed => { | | |
| 122 | return createElfFile(allocator, file, options); | | |
| 123 | }, | | |
| 124 | else => |e| return e, | | |
| 125 | }; | | |
| 126 | } | | |
| 127 | | | |
| 128 | pub const File = struct { | 36 | pub const File = struct { |
| 129 | tag: Tag, | 37 | tag: Tag, |
| 130 | options: Options, | 38 | options: Options, |
| 131 | | 39 | |
| | 40 | /// Attempts incremental linking, if the file already exists. If |
| | 41 | /// incremental linking fails, falls back to truncating the file and |
| | 42 | /// rewriting it. A malicious file is detected as incremental link failure |
| | 43 | /// and does not cause Illegal Behavior. This operation is not atomic. |
| | 44 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { |
| | 45 | switch (options.object_format) { |
| | 46 | .unknown => unreachable, |
| | 47 | .coff => return error.TODOImplementCoff, |
| | 48 | .elf => return Elf.openPath(allocator, dir, sub_path, options), |
| | 49 | .macho => return error.TODOImplementMacho, |
| | 50 | .wasm => return error.TODOImplementWasm, |
| | 51 | .c => return C.openPath(allocator, dir, sub_path, options), |
| | 52 | .hex => return error.TODOImplementHex, |
| | 53 | .raw => return error.TODOImplementRaw, |
| | 54 | } |
| | 55 | } |
| | 56 | |
| 132 | pub fn cast(base: *File, comptime T: type) ?*T { | 57 | pub fn cast(base: *File, comptime T: type) ?*T { |
| 133 | if (base.tag != T.base_tag) | 58 | if (base.tag != T.base_tag) |
| 134 | return null; | 59 | return null; |
| ... | @@ -248,6 +173,31 @@ pub const File = struct { | ... | @@ -248,6 +173,31 @@ pub const File = struct { |
| 248 | need_noreturn: bool = false, | 173 | need_noreturn: bool = false, |
| 249 | error_msg: *Module.ErrorMsg = undefined, | 174 | error_msg: *Module.ErrorMsg = undefined, |
| 250 | | 175 | |
| | 176 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { |
| | 177 | assert(options.object_format == .c); |
| | 178 | |
| | 179 | const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = determineMode(options) }); |
| | 180 | errdefer file.close(); |
| | 181 | |
| | 182 | var c_file = try allocator.create(C); |
| | 183 | errdefer allocator.destroy(c_file); |
| | 184 | |
| | 185 | c_file.* = File.C{ |
| | 186 | .base = .{ |
| | 187 | .tag = .c, |
| | 188 | .options = options, |
| | 189 | }, |
| | 190 | .allocator = allocator, |
| | 191 | .file = file, |
| | 192 | .main = std.ArrayList(u8).init(allocator), |
| | 193 | .header = std.ArrayList(u8).init(allocator), |
| | 194 | .constants = std.ArrayList(u8).init(allocator), |
| | 195 | .called = std.StringHashMap(void).init(allocator), |
| | 196 | }; |
| | 197 | |
| | 198 | return &c_file.base; |
| | 199 | } |
| | 200 | |
| 251 | pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void { | 201 | pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void { |
| 252 | self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args); | 202 | self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args); |
| 253 | return error.CGenFailure; | 203 | return error.CGenFailure; |
| ... | @@ -452,6 +402,107 @@ pub const File = struct { | ... | @@ -452,6 +402,107 @@ pub const File = struct { |
| 452 | sym_index: ?u32 = null, | 402 | sym_index: ?u32 = null, |
| 453 | }; | 403 | }; |
| 454 | | 404 | |
| | 405 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { |
| | 406 | assert(options.object_format == .elf); |
| | 407 | |
| | 408 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) }); |
| | 409 | errdefer file.close(); |
| | 410 | |
| | 411 | var elf_file = try allocator.create(Elf); |
| | 412 | errdefer allocator.destroy(elf_file); |
| | 413 | |
| | 414 | elf_file.* = openFile(allocator, file, options) catch |err| switch (err) { |
| | 415 | error.IncrFailed => try createFile(allocator, file, options), |
| | 416 | else => |e| return e, |
| | 417 | }; |
| | 418 | |
| | 419 | elf_file.owns_file_handle = true; |
| | 420 | return &elf_file.base; |
| | 421 | } |
| | 422 | |
| | 423 | /// Returns error.IncrFailed if incremental update could not be performed. |
| | 424 | fn openFile(allocator: *Allocator, file: fs.File, options: Options) !Elf { |
| | 425 | switch (options.output_mode) { |
| | 426 | .Exe => {}, |
| | 427 | .Obj => {}, |
| | 428 | .Lib => return error.IncrFailed, |
| | 429 | } |
| | 430 | var self: Elf = .{ |
| | 431 | .base = .{ |
| | 432 | .tag = .elf, |
| | 433 | .options = options, |
| | 434 | }, |
| | 435 | .allocator = allocator, |
| | 436 | .file = file, |
| | 437 | .owns_file_handle = false, |
| | 438 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| | 439 | 32 => .p32, |
| | 440 | 64 => .p64, |
| | 441 | else => return error.UnsupportedELFArchitecture, |
| | 442 | }, |
| | 443 | }; |
| | 444 | errdefer self.deinit(); |
| | 445 | |
| | 446 | // TODO implement reading the elf file |
| | 447 | return error.IncrFailed; |
| | 448 | //try self.populateMissingMetadata(); |
| | 449 | //return self; |
| | 450 | } |
| | 451 | |
| | 452 | /// Truncates the existing file contents and overwrites the contents. |
| | 453 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| | 454 | fn createFile(allocator: *Allocator, file: fs.File, options: Options) !Elf { |
| | 455 | switch (options.output_mode) { |
| | 456 | .Exe => {}, |
| | 457 | .Obj => {}, |
| | 458 | .Lib => return error.TODOImplementWritingLibFiles, |
| | 459 | } |
| | 460 | var self: Elf = .{ |
| | 461 | .base = .{ |
| | 462 | .tag = .elf, |
| | 463 | .options = options, |
| | 464 | }, |
| | 465 | .allocator = allocator, |
| | 466 | .file = file, |
| | 467 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| | 468 | 32 => .p32, |
| | 469 | 64 => .p64, |
| | 470 | else => return error.UnsupportedELFArchitecture, |
| | 471 | }, |
| | 472 | .shdr_table_dirty = true, |
| | 473 | .owns_file_handle = false, |
| | 474 | }; |
| | 475 | errdefer self.deinit(); |
| | 476 | |
| | 477 | // Index 0 is always a null symbol. |
| | 478 | try self.local_symbols.append(allocator, .{ |
| | 479 | .st_name = 0, |
| | 480 | .st_info = 0, |
| | 481 | .st_other = 0, |
| | 482 | .st_shndx = 0, |
| | 483 | .st_value = 0, |
| | 484 | .st_size = 0, |
| | 485 | }); |
| | 486 | |
| | 487 | // There must always be a null section in index 0 |
| | 488 | try self.sections.append(allocator, .{ |
| | 489 | .sh_name = 0, |
| | 490 | .sh_type = elf.SHT_NULL, |
| | 491 | .sh_flags = 0, |
| | 492 | .sh_addr = 0, |
| | 493 | .sh_offset = 0, |
| | 494 | .sh_size = 0, |
| | 495 | .sh_link = 0, |
| | 496 | .sh_info = 0, |
| | 497 | .sh_addralign = 0, |
| | 498 | .sh_entsize = 0, |
| | 499 | }); |
| | 500 | |
| | 501 | try self.populateMissingMetadata(); |
| | 502 | |
| | 503 | return self; |
| | 504 | } |
| | 505 | |
| 455 | pub fn deinit(self: *Elf) void { | 506 | pub fn deinit(self: *Elf) void { |
| 456 | self.sections.deinit(self.allocator); | 507 | self.sections.deinit(self.allocator); |
| 457 | self.program_headers.deinit(self.allocator); | 508 | self.program_headers.deinit(self.allocator); |
| ... | @@ -1939,110 +1990,6 @@ pub const File = struct { | ... | @@ -1939,110 +1990,6 @@ pub const File = struct { |
| 1939 | }; | 1990 | }; |
| 1940 | }; | 1991 | }; |
| 1941 | | 1992 | |
| 1942 | /// Truncates the existing file contents and overwrites the contents. | | |
| 1943 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | | |
| 1944 | pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf { | | |
| 1945 | switch (options.output_mode) { | | |
| 1946 | .Exe => {}, | | |
| 1947 | .Obj => {}, | | |
| 1948 | .Lib => return error.TODOImplementWritingLibFiles, | | |
| 1949 | } | | |
| 1950 | switch (options.object_format) { | | |
| 1951 | .c => unreachable, | | |
| 1952 | .unknown => unreachable, // TODO remove this tag from the enum | | |
| 1953 | .coff => return error.TODOImplementWritingCOFF, | | |
| 1954 | .elf => {}, | | |
| 1955 | .macho => return error.TODOImplementWritingMachO, | | |
| 1956 | .wasm => return error.TODOImplementWritingWasmObjects, | | |
| 1957 | .hex => return error.TODOImplementWritingHex, | | |
| 1958 | .raw => return error.TODOImplementWritingRaw, | | |
| 1959 | } | | |
| 1960 | | | |
| 1961 | var self: File.Elf = .{ | | |
| 1962 | .base = .{ | | |
| 1963 | .tag = .elf, | | |
| 1964 | .options = options, | | |
| 1965 | }, | | |
| 1966 | .allocator = allocator, | | |
| 1967 | .file = file, | | |
| 1968 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { | | |
| 1969 | 32 => .p32, | | |
| 1970 | 64 => .p64, | | |
| 1971 | else => return error.UnsupportedELFArchitecture, | | |
| 1972 | }, | | |
| 1973 | .shdr_table_dirty = true, | | |
| 1974 | .owns_file_handle = false, | | |
| 1975 | }; | | |
| 1976 | errdefer self.deinit(); | | |
| 1977 | | | |
| 1978 | // Index 0 is always a null symbol. | | |
| 1979 | try self.local_symbols.append(allocator, .{ | | |
| 1980 | .st_name = 0, | | |
| 1981 | .st_info = 0, | | |
| 1982 | .st_other = 0, | | |
| 1983 | .st_shndx = 0, | | |
| 1984 | .st_value = 0, | | |
| 1985 | .st_size = 0, | | |
| 1986 | }); | | |
| 1987 | | | |
| 1988 | // There must always be a null section in index 0 | | |
| 1989 | try self.sections.append(allocator, .{ | | |
| 1990 | .sh_name = 0, | | |
| 1991 | .sh_type = elf.SHT_NULL, | | |
| 1992 | .sh_flags = 0, | | |
| 1993 | .sh_addr = 0, | | |
| 1994 | .sh_offset = 0, | | |
| 1995 | .sh_size = 0, | | |
| 1996 | .sh_link = 0, | | |
| 1997 | .sh_info = 0, | | |
| 1998 | .sh_addralign = 0, | | |
| 1999 | .sh_entsize = 0, | | |
| 2000 | }); | | |
| 2001 | | | |
| 2002 | try self.populateMissingMetadata(); | | |
| 2003 | | | |
| 2004 | return self; | | |
| 2005 | } | | |
| 2006 | | | |
| 2007 | /// Returns error.IncrFailed if incremental update could not be performed. | | |
| 2008 | fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf { | | |
| 2009 | switch (options.output_mode) { | | |
| 2010 | .Exe => {}, | | |
| 2011 | .Obj => {}, | | |
| 2012 | .Lib => return error.IncrFailed, | | |
| 2013 | } | | |
| 2014 | switch (options.object_format) { | | |
| 2015 | .unknown => unreachable, // TODO remove this tag from the enum | | |
| 2016 | .c => unreachable, | | |
| 2017 | .coff => return error.IncrFailed, | | |
| 2018 | .elf => {}, | | |
| 2019 | .macho => return error.IncrFailed, | | |
| 2020 | .wasm => return error.IncrFailed, | | |
| 2021 | .hex => return error.IncrFailed, | | |
| 2022 | .raw => return error.IncrFailed, | | |
| 2023 | } | | |
| 2024 | var self: File.Elf = .{ | | |
| 2025 | .base = .{ | | |
| 2026 | .tag = .elf, | | |
| 2027 | .options = options, | | |
| 2028 | }, | | |
| 2029 | .allocator = allocator, | | |
| 2030 | .file = file, | | |
| 2031 | .owns_file_handle = false, | | |
| 2032 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { | | |
| 2033 | 32 => .p32, | | |
| 2034 | 64 => .p64, | | |
| 2035 | else => return error.UnsupportedELFArchitecture, | | |
| 2036 | }, | | |
| 2037 | }; | | |
| 2038 | errdefer self.deinit(); | | |
| 2039 | | | |
| 2040 | // TODO implement reading the elf file | | |
| 2041 | return error.IncrFailed; | | |
| 2042 | //try self.populateMissingMetadata(); | | |
| 2043 | //return self; | | |
| 2044 | } | | |
| 2045 | | | |
| 2046 | /// Saturating multiplication | 1993 | /// Saturating multiplication |
| 2047 | fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { | 1994 | fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| 2048 | const T = @TypeOf(a, b); | 1995 | const T = @TypeOf(a, b); |