| author | |
| committer | |
| log | 2627778b251deff3426b5dc21a5a591e0c823b3b |
| tree | a82d2822d58e01f277174a275b7063f113b5a63d |
| parent | 046dce9cefa221e46f59d3f2f3132556dec6432c |
7 files changed, 104 insertions(+), 151 deletions(-)
src-self-hosted/Compilation.zig+5-4| ... | ... | @@ -1072,13 +1072,14 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { |
| 1072 | 1072 | break :blk digest; |
| 1073 | 1073 | }; |
| 1074 | 1074 | |
| 1075 | const full_object_path = if (comp.zig_cache_directory.path) |p| | |
| 1076 | try std.fs.path.join(comp.gpa, &[_][]const u8{ p, "o", &digest, o_basename }) | |
| 1075 | const components = if (comp.zig_cache_directory.path) |p| | |
| 1076 | &[_][]const u8{ p, "o", &digest, o_basename } | |
| 1077 | 1077 | else |
| 1078 | try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest, o_basename }); | |
| 1078 | &[_][]const u8{ "o", &digest, o_basename }; | |
| 1079 | ||
| 1079 | 1080 | c_object.status = .{ |
| 1080 | 1081 | .success = .{ |
| 1081 | .object_path = full_object_path, | |
| 1082 | .object_path = try std.fs.path.join(comp.gpa, components), | |
| 1082 | 1083 | .lock = ch.toOwnedLock(), |
| 1083 | 1084 | }, |
| 1084 | 1085 | }; |
src-self-hosted/link.zig+18-16| ... | ... | @@ -126,17 +126,29 @@ pub const File = struct { |
| 126 | 126 | pub fn openPath(allocator: *Allocator, options: Options) !*File { |
| 127 | 127 | const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm |
| 128 | 128 | const sub_path = if (use_lld) blk: { |
| 129 | if (options.module == null) { | |
| 130 | // No point in opening a file, we would not write anything to it. Initialize with empty. | |
| 131 | return switch (options.object_format) { | |
| 132 | .coff, .pe => &(try Coff.createEmpty(allocator, options)).base, | |
| 133 | .elf => &(try Elf.createEmpty(allocator, options)).base, | |
| 134 | .macho => &(try MachO.createEmpty(allocator, options)).base, | |
| 135 | .wasm => &(try Wasm.createEmpty(allocator, options)).base, | |
| 136 | .c => unreachable, // Reported error earlier. | |
| 137 | .hex => return error.HexObjectFormatUnimplemented, | |
| 138 | .raw => return error.RawObjectFormatUnimplemented, | |
| 139 | }; | |
| 140 | } | |
| 129 | 141 | // Open a temporary object file, not the final output file because we want to link with LLD. |
| 130 | 142 | break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ options.sub_path, options.target.oFileExt() }); |
| 131 | 143 | } else options.sub_path; |
| 132 | 144 | errdefer if (use_lld) allocator.free(sub_path); |
| 133 | 145 | |
| 134 | 146 | const file: *File = switch (options.object_format) { |
| 135 | .coff, .pe => try Coff.openPath(allocator, sub_path, options), | |
| 136 | .elf => try Elf.openPath(allocator, sub_path, options), | |
| 137 | .macho => try MachO.openPath(allocator, sub_path, options), | |
| 138 | .wasm => try Wasm.openPath(allocator, sub_path, options), | |
| 139 | .c => try C.openPath(allocator, sub_path, options), | |
| 147 | .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base, | |
| 148 | .elf => &(try Elf.openPath(allocator, sub_path, options)).base, | |
| 149 | .macho => &(try MachO.openPath(allocator, sub_path, options)).base, | |
| 150 | .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, | |
| 151 | .c => &(try C.openPath(allocator, sub_path, options)).base, | |
| 140 | 152 | .hex => return error.HexObjectFormatUnimplemented, |
| 141 | 153 | .raw => return error.RawObjectFormatUnimplemented, |
| 142 | 154 | }; |
| ... | ... | @@ -216,19 +228,9 @@ pub const File = struct { |
| 216 | 228 | } |
| 217 | 229 | } |
| 218 | 230 | |
| 219 | pub fn deinit(base: *File) void { | |
| 220 | switch (base.tag) { | |
| 221 | .coff => @fieldParentPtr(Coff, "base", base).deinit(), | |
| 222 | .elf => @fieldParentPtr(Elf, "base", base).deinit(), | |
| 223 | .macho => @fieldParentPtr(MachO, "base", base).deinit(), | |
| 224 | .c => @fieldParentPtr(C, "base", base).deinit(), | |
| 225 | .wasm => @fieldParentPtr(Wasm, "base", base).deinit(), | |
| 226 | } | |
| 231 | pub fn destroy(base: *File) void { | |
| 227 | 232 | if (base.file) |f| f.close(); |
| 228 | 233 | if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path); |
| 229 | } | |
| 230 | ||
| 231 | pub fn destroy(base: *File) void { | |
| 232 | 234 | switch (base.tag) { |
| 233 | 235 | .coff => { |
| 234 | 236 | const parent = @fieldParentPtr(Coff, "base", base); |
src-self-hosted/link/C.zig+2-2| ... | ... | @@ -23,7 +23,7 @@ need_stddef: bool = false, |
| 23 | 23 | need_stdint: bool = false, |
| 24 | 24 | error_msg: *Compilation.ErrorMsg = undefined, |
| 25 | 25 | |
| 26 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File { | |
| 26 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C { | |
| 27 | 27 | assert(options.object_format == .c); |
| 28 | 28 | |
| 29 | 29 | if (options.use_llvm) return error.LLVMHasNoCBackend; |
| ... | ... | @@ -48,7 +48,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 48 | 48 | .called = std.StringHashMap(void).init(allocator), |
| 49 | 49 | }; |
| 50 | 50 | |
| 51 | return &c_file.base; | |
| 51 | return c_file; | |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
src-self-hosted/link/Coff.zig+26-54| ... | ... | @@ -28,7 +28,7 @@ pub const base_tag: link.File.Tag = .coff; |
| 28 | 28 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 29 | 29 | |
| 30 | 30 | base: link.File, |
| 31 | ptr_width: enum { p32, p64 }, | |
| 31 | ptr_width: PtrWidth, | |
| 32 | 32 | error_flags: link.File.ErrorFlags = .{}, |
| 33 | 33 | |
| 34 | 34 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| ... | ... | @@ -64,6 +64,8 @@ text_section_size_dirty: bool = false, |
| 64 | 64 | /// and needs to be updated in the optional header. |
| 65 | 65 | size_of_image_dirty: bool = false, |
| 66 | 66 | |
| 67 | pub const PtrWidth = enum { p32, p64 }; | |
| 68 | ||
| 67 | 69 | pub const TextBlock = struct { |
| 68 | 70 | /// Offset of the code relative to the start of the text section |
| 69 | 71 | text_offset: u32, |
| ... | ... | @@ -111,7 +113,7 @@ pub const TextBlock = struct { |
| 111 | 113 | |
| 112 | 114 | pub const SrcFn = void; |
| 113 | 115 | |
| 114 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File { | |
| 116 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff { | |
| 115 | 117 | assert(options.object_format == .coff); |
| 116 | 118 | |
| 117 | 119 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO |
| ... | ... | @@ -124,66 +126,17 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 124 | 126 | }); |
| 125 | 127 | errdefer file.close(); |
| 126 | 128 | |
| 127 | var coff_file = try allocator.create(Coff); | |
| 128 | errdefer allocator.destroy(coff_file); | |
| 129 | ||
| 130 | coff_file.* = openFile(allocator, file, options) catch |err| switch (err) { | |
| 131 | error.IncrFailed => try createFile(allocator, file, options), | |
| 132 | else => |e| return e, | |
| 133 | }; | |
| 134 | ||
| 135 | return &coff_file.base; | |
| 136 | } | |
| 137 | ||
| 138 | /// Returns error.IncrFailed if incremental update could not be performed. | |
| 139 | fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { | |
| 140 | switch (options.output_mode) { | |
| 141 | .Exe => {}, | |
| 142 | .Obj => return error.IncrFailed, | |
| 143 | .Lib => return error.IncrFailed, | |
| 144 | } | |
| 145 | var self: Coff = .{ | |
| 146 | .base = .{ | |
| 147 | .file = file, | |
| 148 | .tag = .coff, | |
| 149 | .options = options, | |
| 150 | .allocator = allocator, | |
| 151 | }, | |
| 152 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { | |
| 153 | 32 => .p32, | |
| 154 | 64 => .p64, | |
| 155 | else => return error.UnsupportedELFArchitecture, | |
| 156 | }, | |
| 157 | }; | |
| 158 | errdefer self.deinit(); | |
| 129 | const self = try createEmpty(allocator, options); | |
| 130 | errdefer self.base.destroy(); | |
| 159 | 131 | |
| 160 | // TODO implement reading the PE/COFF file | |
| 161 | return error.IncrFailed; | |
| 162 | } | |
| 132 | self.base.file = file; | |
| 163 | 133 | |
| 164 | /// Truncates the existing file contents and overwrites the contents. | |
| 165 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | |
| 166 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { | |
| 167 | 134 | // TODO Write object specific relocations, COFF symbol table, then enable object file output. |
| 168 | 135 | switch (options.output_mode) { |
| 169 | 136 | .Exe => {}, |
| 170 | 137 | .Obj => return error.TODOImplementWritingObjFiles, |
| 171 | 138 | .Lib => return error.TODOImplementWritingLibFiles, |
| 172 | 139 | } |
| 173 | var self: Coff = .{ | |
| 174 | .base = .{ | |
| 175 | .tag = .coff, | |
| 176 | .options = options, | |
| 177 | .allocator = allocator, | |
| 178 | .file = file, | |
| 179 | }, | |
| 180 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { | |
| 181 | 32 => .p32, | |
| 182 | 64 => .p64, | |
| 183 | else => return error.UnsupportedCOFFArchitecture, | |
| 184 | }, | |
| 185 | }; | |
| 186 | errdefer self.deinit(); | |
| 187 | 140 | |
| 188 | 141 | var coff_file_header_offset: u32 = 0; |
| 189 | 142 | if (options.output_mode == .Exe) { |
| ... | ... | @@ -426,6 +379,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff |
| 426 | 379 | return self; |
| 427 | 380 | } |
| 428 | 381 | |
| 382 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { | |
| 383 | const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { | |
| 384 | 0...32 => .p32, | |
| 385 | 33...64 => .p64, | |
| 386 | else => return error.UnsupportedCOFFArchitecture, | |
| 387 | }; | |
| 388 | const self = try gpa.create(Coff); | |
| 389 | self.* = .{ | |
| 390 | .base = .{ | |
| 391 | .tag = .coff, | |
| 392 | .options = options, | |
| 393 | .allocator = gpa, | |
| 394 | .file = null, | |
| 395 | }, | |
| 396 | .ptr_width = ptr_width, | |
| 397 | }; | |
| 398 | return self; | |
| 399 | } | |
| 400 | ||
| 429 | 401 | pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { |
| 430 | 402 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 431 | 403 |
src-self-hosted/link/Elf.zig+27-26| ... | ... | @@ -31,7 +31,7 @@ pub const base_tag: File.Tag = .elf; |
| 31 | 31 | |
| 32 | 32 | base: File, |
| 33 | 33 | |
| 34 | ptr_width: enum { p32, p64 }, | |
| 34 | ptr_width: PtrWidth, | |
| 35 | 35 | |
| 36 | 36 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 37 | 37 | /// Same order as in the file. |
| ... | ... | @@ -136,6 +136,8 @@ const alloc_den = 3; |
| 136 | 136 | const minimum_text_block_size = 64; |
| 137 | 137 | const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; |
| 138 | 138 | |
| 139 | pub const PtrWidth = enum { p32, p64 }; | |
| 140 | ||
| 139 | 141 | pub const TextBlock = struct { |
| 140 | 142 | /// Each decl always gets a local symbol with the fully qualified name. |
| 141 | 143 | /// The vaddr and size are found here directly. |
| ... | ... | @@ -222,7 +224,7 @@ pub const SrcFn = struct { |
| 222 | 224 | }; |
| 223 | 225 | }; |
| 224 | 226 | |
| 225 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File { | |
| 227 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf { | |
| 226 | 228 | assert(options.object_format == .elf); |
| 227 | 229 | |
| 228 | 230 | if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO |
| ... | ... | @@ -234,31 +236,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 234 | 236 | }); |
| 235 | 237 | errdefer file.close(); |
| 236 | 238 | |
| 237 | var elf_file = try allocator.create(Elf); | |
| 238 | errdefer allocator.destroy(elf_file); | |
| 239 | ||
| 240 | elf_file.* = try createFile(allocator, file, options); | |
| 241 | return &elf_file.base; | |
| 242 | } | |
| 239 | const self = try createEmpty(allocator, options); | |
| 240 | errdefer self.base.destroy(); | |
| 243 | 241 | |
| 244 | /// Truncates the existing file contents and overwrites the contents. | |
| 245 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | |
| 246 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { | |
| 247 | var self: Elf = .{ | |
| 248 | .base = .{ | |
| 249 | .tag = .elf, | |
| 250 | .options = options, | |
| 251 | .allocator = allocator, | |
| 252 | .file = file, | |
| 253 | }, | |
| 254 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { | |
| 255 | 0 ... 32 => .p32, | |
| 256 | 33 ... 64 => .p64, | |
| 257 | else => return error.UnsupportedELFArchitecture, | |
| 258 | }, | |
| 259 | .shdr_table_dirty = true, | |
| 260 | }; | |
| 261 | errdefer self.deinit(); | |
| 242 | self.base.file = file; | |
| 243 | self.shdr_table_dirty = true; | |
| 262 | 244 | |
| 263 | 245 | // Index 0 is always a null symbol. |
| 264 | 246 | try self.local_symbols.append(allocator, .{ |
| ... | ... | @@ -289,6 +271,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf |
| 289 | 271 | return self; |
| 290 | 272 | } |
| 291 | 273 | |
| 274 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { | |
| 275 | const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { | |
| 276 | 0 ... 32 => .p32, | |
| 277 | 33 ... 64 => .p64, | |
| 278 | else => return error.UnsupportedELFArchitecture, | |
| 279 | }; | |
| 280 | const self = try gpa.create(Elf); | |
| 281 | self.* = .{ | |
| 282 | .base = .{ | |
| 283 | .tag = .elf, | |
| 284 | .options = options, | |
| 285 | .allocator = gpa, | |
| 286 | .file = null, | |
| 287 | }, | |
| 288 | .ptr_width = ptr_width, | |
| 289 | }; | |
| 290 | return self; | |
| 291 | } | |
| 292 | ||
| 292 | 293 | pub fn releaseLock(self: *Elf) void { |
| 293 | 294 | if (self.lock) |*lock| { |
| 294 | 295 | lock.release(); |
src-self-hosted/link/MachO.zig+13-42| ... | ... | @@ -135,7 +135,7 @@ pub const SrcFn = struct { |
| 135 | 135 | pub const empty = SrcFn{}; |
| 136 | 136 | }; |
| 137 | 137 | |
| 138 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File { | |
| 138 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO { | |
| 139 | 139 | assert(options.object_format == .macho); |
| 140 | 140 | |
| 141 | 141 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO |
| ... | ... | @@ -148,61 +148,32 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 148 | 148 | }); |
| 149 | 149 | errdefer file.close(); |
| 150 | 150 | |
| 151 | var macho_file = try allocator.create(MachO); | |
| 152 | errdefer allocator.destroy(macho_file); | |
| 151 | const self = try createEmpty(allocator, options); | |
| 152 | errdefer self.base.destroy(); | |
| 153 | 153 | |
| 154 | macho_file.* = openFile(allocator, file, options) catch |err| switch (err) { | |
| 155 | error.IncrFailed => try createFile(allocator, file, options), | |
| 156 | else => |e| return e, | |
| 157 | }; | |
| 158 | ||
| 159 | return &macho_file.base; | |
| 160 | } | |
| 154 | self.base.file = file; | |
| 161 | 155 | |
| 162 | /// Returns error.IncrFailed if incremental update could not be performed. | |
| 163 | fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO { | |
| 164 | 156 | switch (options.output_mode) { |
| 165 | 157 | .Exe => {}, |
| 166 | 158 | .Obj => {}, |
| 167 | .Lib => return error.IncrFailed, | |
| 159 | .Lib => return error.TODOImplementWritingLibFiles, | |
| 168 | 160 | } |
| 169 | var self: MachO = .{ | |
| 170 | .base = .{ | |
| 171 | .file = file, | |
| 172 | .tag = .macho, | |
| 173 | .options = options, | |
| 174 | .allocator = allocator, | |
| 175 | }, | |
| 176 | }; | |
| 177 | errdefer self.deinit(); | |
| 178 | 161 | |
| 179 | // TODO implement reading the macho file | |
| 180 | return error.IncrFailed; | |
| 181 | //try self.populateMissingMetadata(); | |
| 182 | //return self; | |
| 183 | } | |
| 162 | try self.populateMissingMetadata(); | |
| 184 | 163 | |
| 185 | /// Truncates the existing file contents and overwrites the contents. | |
| 186 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | |
| 187 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO { | |
| 188 | switch (options.output_mode) { | |
| 189 | .Exe => {}, | |
| 190 | .Obj => {}, | |
| 191 | .Lib => return error.TODOImplementWritingLibFiles, | |
| 192 | } | |
| 164 | return self; | |
| 165 | } | |
| 193 | 166 | |
| 194 | var self: MachO = .{ | |
| 167 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { | |
| 168 | const self = try gpa.create(MachO); | |
| 169 | self.* = .{ | |
| 195 | 170 | .base = .{ |
| 196 | .file = file, | |
| 197 | 171 | .tag = .macho, |
| 198 | 172 | .options = options, |
| 199 | .allocator = allocator, | |
| 173 | .allocator = gpa, | |
| 174 | .file = null, | |
| 200 | 175 | }, |
| 201 | 176 | }; |
| 202 | errdefer self.deinit(); | |
| 203 | ||
| 204 | try self.populateMissingMetadata(); | |
| 205 | ||
| 206 | 177 | return self; |
| 207 | 178 | } |
| 208 | 179 |
src-self-hosted/link/Wasm.zig+13-7| ... | ... | @@ -50,7 +50,7 @@ base: link.File, |
| 50 | 50 | /// TODO: can/should we access some data structure in Module directly? |
| 51 | 51 | funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 52 | 52 | |
| 53 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File { | |
| 53 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { | |
| 54 | 54 | assert(options.object_format == .wasm); |
| 55 | 55 | |
| 56 | 56 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO |
| ... | ... | @@ -60,21 +60,27 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 60 | 60 | const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); |
| 61 | 61 | errdefer file.close(); |
| 62 | 62 | |
| 63 | const wasm = try allocator.create(Wasm); | |
| 64 | errdefer allocator.destroy(wasm); | |
| 63 | const wasm = try createEmpty(allocator, options); | |
| 64 | errdefer wasm.base.destroy(); | |
| 65 | ||
| 66 | wasm.base.file = file; | |
| 65 | 67 | |
| 66 | 68 | try file.writeAll(&(spec.magic ++ spec.version)); |
| 67 | 69 | |
| 70 | return wasm; | |
| 71 | } | |
| 72 | ||
| 73 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { | |
| 74 | const wasm = try gpa.create(Wasm); | |
| 68 | 75 | wasm.* = .{ |
| 69 | 76 | .base = .{ |
| 70 | 77 | .tag = .wasm, |
| 71 | 78 | .options = options, |
| 72 | .file = file, | |
| 73 | .allocator = allocator, | |
| 79 | .file = null, | |
| 80 | .allocator = gpa, | |
| 74 | 81 | }, |
| 75 | 82 | }; |
| 76 | ||
| 77 | return &wasm.base; | |
| 83 | return wasm; | |
| 78 | 84 | } |
| 79 | 85 | |
| 80 | 86 | pub fn deinit(self: *Wasm) void { |