authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2020-08-02 12:16:03+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-02 18:07:10+00:00
log6123201f06e0bfb138d6dfba0b3ba9ee105062f2
treeefb8b123e412c687291f11b81ad42d3c622e5dba
parent5ae88e919b2e1e0a519fda1e49c8e83e139cba40

stage2: move format-specific code to link.File.X

This makes the code outside of link.File.Elf less elf-specific and will allow for easier implementation of other formats such as wasm.

2 files changed, 144 insertions(+), 197 deletions(-)

src-self-hosted/Module.zig+1-1
......@@ -790,7 +790,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
790790 errdefer gpa.free(root_name);
791791
792792 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
793 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
793 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
794794 .root_name = root_name,
795795 .root_src_dir_path = options.root_pkg.root_src_dir_path,
796796 .target = options.target,
src-self-hosted/link.zig+143-196
......@@ -33,102 +33,27 @@ pub const Options = struct {
3333 program_code_size_hint: u64 = 256 * 1024,
3434};
3535
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.
40pub 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.
65pub 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
99fn 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.
119pub 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
12836pub const File = struct {
12937 tag: Tag,
13038 options: Options,
13139
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
13257 pub fn cast(base: *File, comptime T: type) ?*T {
13358 if (base.tag != T.base_tag)
13459 return null;
......@@ -248,6 +173,31 @@ pub const File = struct {
248173 need_noreturn: bool = false,
249174 error_msg: *Module.ErrorMsg = undefined,
250175
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
251201 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
252202 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
253203 return error.CGenFailure;
......@@ -452,6 +402,107 @@ pub const File = struct {
452402 sym_index: ?u32 = null,
453403 };
454404
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
455506 pub fn deinit(self: *Elf) void {
456507 self.sections.deinit(self.allocator);
457508 self.program_headers.deinit(self.allocator);
......@@ -1939,110 +1990,6 @@ pub const File = struct {
19391990 };
19401991};
19411992
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.
1944pub 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.
2008fn 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
20461993/// Saturating multiplication
20471994fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
20481995 const T = @TypeOf(a, b);