Commit b37955f273
Changed files (8)
src-self-hosted/link/C.zig
@@ -22,13 +22,13 @@ need_stddef: bool = false,
need_stdint: bool = false,
error_msg: *Module.ErrorMsg = undefined,
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
assert(options.object_format == .c);
- if (options.use_llvm) return error.LLVM_HasNoCBackend;
- if (options.use_lld) return error.LLD_HasNoCBackend;
+ if (options.use_llvm) return error.LLVMHasNoCBackend;
+ if (options.use_lld) return error.LLDHasNoCBackend;
- const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
+ const file = try options.dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
errdefer file.close();
var c_file = try allocator.create(C);
src-self-hosted/link/Coff.zig
@@ -19,7 +19,7 @@ const file_alignment = 512;
const image_base = 0x400_000;
const section_table_size = 2 * 40;
comptime {
- std.debug.assert(std.mem.isAligned(image_base, section_alignment));
+ assert(std.mem.isAligned(image_base, section_alignment));
}
pub const base_tag: link.File.Tag = .coff;
@@ -110,13 +110,17 @@ pub const TextBlock = struct {
pub const SrcFn = void;
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File {
assert(options.object_format == .coff);
if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO
if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO
- const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
+ const file = try options.dir.createFile(sub_path, .{
+ .truncate = false,
+ .read = true,
+ .mode = link.determineMode(options),
+ });
errdefer file.close();
var coff_file = try allocator.create(Coff);
src-self-hosted/link/Elf.zig
@@ -216,65 +216,28 @@ pub const SrcFn = struct {
};
};
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
assert(options.object_format == .elf);
if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
- if (build_options.have_llvm and options.use_lld) {
- std.debug.print("TODO open a temporary object file, not the final output file because we want to link with LLD\n", .{});
- }
-
- const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
+ const file = try options.dir.createFile(sub_path, .{
+ .truncate = false,
+ .read = true,
+ .mode = link.determineMode(options),
+ });
errdefer file.close();
var elf_file = try allocator.create(Elf);
errdefer allocator.destroy(elf_file);
- elf_file.* = openFile(allocator, file, options) catch |err| switch (err) {
- error.IncrFailed => try createFile(allocator, file, options),
- else => |e| return e,
- };
-
+ elf_file.* = try createFile(allocator, file, options);
return &elf_file.base;
}
-/// Returns error.IncrFailed if incremental update could not be performed.
-fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
- switch (options.effectiveOutputMode()) {
- .Exe => {},
- .Obj => {},
- .Lib => return error.IncrFailed,
- }
- var self: Elf = .{
- .base = .{
- .file = file,
- .tag = .elf,
- .options = options,
- .allocator = allocator,
- },
- .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
- 0 ... 32 => .p32,
- 33 ... 64 => .p64,
- else => return error.UnsupportedELFArchitecture,
- },
- };
- errdefer self.deinit();
-
- // TODO implement reading the elf file
- return error.IncrFailed;
- //try self.populateMissingMetadata();
- //return self;
-}
-
/// Truncates the existing file contents and overwrites the contents.
/// Returns an error if `file` is not already open with +read +write +seek abilities.
fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
- switch (options.effectiveOutputMode()) {
- .Exe => {},
- .Obj => {},
- .Lib => return error.TODOImplementWritingLibFiles,
- }
var self: Elf = .{
.base = .{
.tag = .elf,
@@ -753,6 +716,10 @@ pub fn flush(self: *Elf, module: *Module) !void {
}
std.debug.print("TODO create an LLD command line and invoke it\n", .{});
} else {
+ switch (self.base.options.effectiveOutputMode()) {
+ .Exe, .Obj => {},
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
return self.flushInner(module);
}
}
src-self-hosted/link/MachO.zig
@@ -134,13 +134,17 @@ pub const SrcFn = struct {
pub const empty = SrcFn{};
};
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
assert(options.object_format == .macho);
if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO
- const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
+ const file = try options.dir.createFile(sub_path, .{
+ .truncate = false,
+ .read = true,
+ .mode = link.determineMode(options),
+ });
errdefer file.close();
var macho_file = try allocator.create(MachO);
src-self-hosted/link/Wasm.zig
@@ -49,14 +49,14 @@ base: link.File,
/// TODO: can/should we access some data structure in Module directly?
funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File {
assert(options.object_format == .wasm);
if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO
if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO
// TODO: read the file and keep vaild parts instead of truncating
- const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true });
+ const file = try options.dir.createFile(sub_path, .{ .truncate = true, .read = true });
errdefer file.close();
const wasm = try allocator.create(Wasm);
src-self-hosted/link.zig
@@ -10,6 +10,12 @@ const build_options = @import("build_options");
pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
pub const Options = struct {
+ dir: fs.Dir,
+ /// Redundant with dir. Needed when linking with LLD because we have to pass paths rather
+ /// than file descriptors. `null` means cwd. OK to pass `null` when `use_lld` is `false`.
+ dir_path: ?[]const u8,
+ /// Path to the output file, relative to dir.
+ sub_path: []const u8,
target: std.Target,
output_mode: std.builtin.OutputMode,
link_mode: std.builtin.LinkMode,
@@ -56,6 +62,9 @@ pub const File = struct {
options: Options,
file: ?fs.File,
allocator: *Allocator,
+ /// When linking with LLD, this linker code will output an object file only at
+ /// this location, and then this path can be placed on the LLD linker line.
+ intermediary_basename: ?[]const u8 = null,
pub const LinkBlock = union {
elf: Elf.TextBlock,
@@ -90,16 +99,29 @@ pub const File = struct {
/// incremental linking fails, falls back to truncating the file and
/// rewriting it. A malicious file is detected as incremental link failure
/// and does not cause Illegal Behavior. This operation is not atomic.
- pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
- switch (options.object_format) {
- .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options),
- .elf => return Elf.openPath(allocator, dir, sub_path, options),
- .macho => return MachO.openPath(allocator, dir, sub_path, options),
- .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
- .c => return C.openPath(allocator, dir, sub_path, options),
+ pub fn openPath(allocator: *Allocator, options: Options) !*File {
+ const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
+ const sub_path = if (use_lld) blk: {
+ // Open a temporary object file, not the final output file because we want to link with LLD.
+ break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ options.sub_path, options.target.oFileExt() });
+ } else options.sub_path;
+ errdefer if (use_lld) allocator.free(sub_path);
+
+ const file: *File = switch (options.object_format) {
+ .coff, .pe => try Coff.openPath(allocator, sub_path, options),
+ .elf => try Elf.openPath(allocator, sub_path, options),
+ .macho => try MachO.openPath(allocator, sub_path, options),
+ .wasm => try Wasm.openPath(allocator, sub_path, options),
+ .c => try C.openPath(allocator, sub_path, options),
.hex => return error.HexObjectFormatUnimplemented,
.raw => return error.RawObjectFormatUnimplemented,
+ };
+
+ if (use_lld) {
+ file.intermediary_basename = sub_path;
}
+
+ return file;
}
pub fn cast(base: *File, comptime T: type) ?*T {
@@ -109,11 +131,11 @@ pub const File = struct {
return @fieldParentPtr(T, "base", base);
}
- pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
+ pub fn makeWritable(base: *File) !void {
switch (base.tag) {
.coff, .elf, .macho => {
if (base.file != null) return;
- base.file = try dir.createFile(sub_path, .{
+ base.file = try base.options.dir.createFile(base.options.sub_path, .{
.truncate = false,
.read = true,
.mode = determineMode(base.options),
@@ -125,12 +147,16 @@ pub const File = struct {
pub fn makeExecutable(base: *File) !void {
switch (base.tag) {
- .c => unreachable,
- .wasm => {},
- else => if (base.file) |f| {
+ .coff, .elf, .macho => if (base.file) |f| {
+ if (base.intermediary_basename != null) {
+ // The file we have open is not the final file that we want to
+ // make executable, so we don't have to close it.
+ return;
+ }
f.close();
base.file = null;
},
+ .c, .wasm => {},
}
}
@@ -167,7 +193,6 @@ pub const File = struct {
}
pub fn deinit(base: *File) void {
- if (base.file) |f| f.close();
switch (base.tag) {
.coff => @fieldParentPtr(Coff, "base", base).deinit(),
.elf => @fieldParentPtr(Elf, "base", base).deinit(),
@@ -175,6 +200,8 @@ pub const File = struct {
.c => @fieldParentPtr(C, "base", base).deinit(),
.wasm => @fieldParentPtr(Wasm, "base", base).deinit(),
}
+ if (base.file) |f| f.close();
+ if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);
}
pub fn destroy(base: *File) void {
@@ -292,7 +319,7 @@ pub fn determineMode(options: Options) fs.File.Mode {
// more leniently. As another data point, C's fopen seems to open files with the
// 666 mode.
const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
- switch (options.output_mode) {
+ switch (options.effectiveOutputMode()) {
.Lib => return switch (options.link_mode) {
.Dynamic => executable_mode,
.Static => fs.File.default_mode,
src-self-hosted/main.zig
@@ -1000,6 +1000,7 @@ pub fn buildOutputType(
.target = target_info.target,
.output_mode = output_mode,
.root_pkg = root_pkg,
+ .bin_file_dir_path = null,
.bin_file_dir = fs.cwd(),
.bin_file_path = bin_path,
.link_mode = link_mode,
src-self-hosted/Module.zig
@@ -35,8 +35,6 @@ root_pkg: ?*Package,
/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
root_scope: *Scope,
bin_file: *link.File,
-bin_file_dir: std.fs.Dir,
-bin_file_path: []const u8,
/// It's rare for a decl to be exported, so we save memory by having a sparse map of
/// Decl pointers to details about them being exported.
/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
@@ -934,6 +932,7 @@ pub const InitOptions = struct {
root_pkg: ?*Package,
output_mode: std.builtin.OutputMode,
rand: *std.rand.Random,
+ bin_file_dir_path: ?[]const u8 = null,
bin_file_dir: ?std.fs.Dir = null,
bin_file_path: []const u8,
emit_h: ?[]const u8 = null,
@@ -1018,8 +1017,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
break :blk false;
};
- const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
- const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
+ const bin_file = try link.File.openPath(gpa, .{
+ .dir = options.bin_file_dir orelse std.fs.cwd(),
+ .dir_path = options.bin_file_dir_path,
+ .sub_path = options.bin_file_path,
.root_name = root_name,
.root_pkg = options.root_pkg,
.target = options.target,
@@ -1165,8 +1166,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
.root_name = root_name,
.root_pkg = options.root_pkg,
.root_scope = root_scope,
- .bin_file_dir = bin_file_dir,
- .bin_file_path = options.bin_file_path,
.bin_file = bin_file,
.work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
.keep_source_files_loaded = options.keep_source_files_loaded,
@@ -1350,7 +1349,7 @@ pub fn makeBinFileExecutable(self: *Module) !void {
}
pub fn makeBinFileWritable(self: *Module) !void {
- return self.bin_file.makeWritable(self.bin_file_dir, self.bin_file_path);
+ return self.bin_file.makeWritable();
}
pub fn totalErrorCount(self: *Module) usize {