| ... | ... | @@ -33,102 +33,27 @@ pub const Options = struct { |
| 33 | 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 | 36 | pub const File = struct { |
| 129 | 37 | tag: Tag, |
| 130 | 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 | 57 | pub fn cast(base: *File, comptime T: type) ?*T { |
| 133 | 58 | if (base.tag != T.base_tag) |
| 134 | 59 | return null; |
| ... | ... | @@ -248,6 +173,31 @@ pub const File = struct { |
| 248 | 173 | need_noreturn: bool = false, |
| 249 | 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 | 201 | pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void { |
| 252 | 202 | self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args); |
| 253 | 203 | return error.CGenFailure; |
| ... | ... | @@ -452,6 +402,107 @@ pub const File = struct { |
| 452 | 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 | 506 | pub fn deinit(self: *Elf) void { |
| 456 | 507 | self.sections.deinit(self.allocator); |
| 457 | 508 | self.program_headers.deinit(self.allocator); |
| ... | ... | @@ -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 | 1993 | /// Saturating multiplication |
| 2047 | 1994 | fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| 2048 | 1995 | const T = @TypeOf(a, b); |