mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
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.
This commit is contained in:
parent
5ae88e919b
commit
6123201f06
@ -790,7 +790,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
errdefer gpa.free(root_name);
|
||||
|
||||
const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
|
||||
const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
|
||||
const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
|
||||
.root_name = root_name,
|
||||
.root_src_dir_path = options.root_pkg.root_src_dir_path,
|
||||
.target = options.target,
|
||||
|
||||
@ -33,102 +33,27 @@ pub const Options = struct {
|
||||
program_code_size_hint: u64 = 256 * 1024,
|
||||
};
|
||||
|
||||
/// Attempts incremental linking, if the file already exists.
|
||||
/// If 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 openBinFilePath(
|
||||
allocator: *Allocator,
|
||||
dir: fs.Dir,
|
||||
sub_path: []const u8,
|
||||
options: Options,
|
||||
) !*File {
|
||||
const cbe = options.object_format == .c;
|
||||
const file = try dir.createFile(sub_path, .{ .truncate = cbe, .read = true, .mode = determineMode(options) });
|
||||
errdefer file.close();
|
||||
|
||||
if (cbe) {
|
||||
var bin_file = try allocator.create(File.C);
|
||||
errdefer allocator.destroy(bin_file);
|
||||
bin_file.* = try openCFile(allocator, file, options);
|
||||
return &bin_file.base;
|
||||
} else {
|
||||
var bin_file = try allocator.create(File.Elf);
|
||||
errdefer allocator.destroy(bin_file);
|
||||
bin_file.* = try openBinFile(allocator, file, options);
|
||||
bin_file.owns_file_handle = true;
|
||||
return &bin_file.base;
|
||||
}
|
||||
}
|
||||
|
||||
/// Atomically overwrites the old file, if present.
|
||||
pub fn writeFilePath(
|
||||
allocator: *Allocator,
|
||||
dir: fs.Dir,
|
||||
sub_path: []const u8,
|
||||
module: Module,
|
||||
errors: *std.ArrayList(Module.ErrorMsg),
|
||||
) !void {
|
||||
const options: Options = .{
|
||||
.target = module.target,
|
||||
.output_mode = module.output_mode,
|
||||
.link_mode = module.link_mode,
|
||||
.object_format = module.object_format,
|
||||
.symbol_count_hint = module.decls.items.len,
|
||||
.optimize_mode = module.optimize_mode,
|
||||
};
|
||||
const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) });
|
||||
defer af.deinit();
|
||||
|
||||
const elf_file = try createElfFile(allocator, af.file, options);
|
||||
for (module.decls.items) |decl| {
|
||||
try elf_file.updateDecl(module, decl, errors);
|
||||
}
|
||||
try elf_file.flush();
|
||||
if (elf_file.error_flags.no_entry_point_found) {
|
||||
try errors.ensureCapacity(errors.items.len + 1);
|
||||
errors.appendAssumeCapacity(.{
|
||||
.byte_offset = 0,
|
||||
.msg = try std.fmt.allocPrint(errors.allocator, "no entry point found", .{}),
|
||||
});
|
||||
}
|
||||
try af.finish();
|
||||
return result;
|
||||
}
|
||||
|
||||
fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {
|
||||
return File.C{
|
||||
.base = .{
|
||||
.tag = .c,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.main = std.ArrayList(u8).init(allocator),
|
||||
.header = std.ArrayList(u8).init(allocator),
|
||||
.constants = std.ArrayList(u8).init(allocator),
|
||||
.called = std.StringHashMap(void).init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
/// Attempts incremental linking, if the file already exists.
|
||||
/// If incremental linking fails, falls back to truncating the file and rewriting it.
|
||||
/// Returns an error if `file` is not already open with +read +write +seek abilities.
|
||||
/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
|
||||
/// This operation is not atomic.
|
||||
pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
|
||||
return openBinFileInner(allocator, file, options) catch |err| switch (err) {
|
||||
error.IncrFailed => {
|
||||
return createElfFile(allocator, file, options);
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
pub const File = struct {
|
||||
tag: Tag,
|
||||
options: Options,
|
||||
|
||||
/// Attempts incremental linking, if the file already exists. If
|
||||
/// 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) {
|
||||
.unknown => unreachable,
|
||||
.coff => return error.TODOImplementCoff,
|
||||
.elf => return Elf.openPath(allocator, dir, sub_path, options),
|
||||
.macho => return error.TODOImplementMacho,
|
||||
.wasm => return error.TODOImplementWasm,
|
||||
.c => return C.openPath(allocator, dir, sub_path, options),
|
||||
.hex => return error.TODOImplementHex,
|
||||
.raw => return error.TODOImplementRaw,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cast(base: *File, comptime T: type) ?*T {
|
||||
if (base.tag != T.base_tag)
|
||||
return null;
|
||||
@ -248,6 +173,31 @@ pub const File = struct {
|
||||
need_noreturn: bool = false,
|
||||
error_msg: *Module.ErrorMsg = undefined,
|
||||
|
||||
pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
|
||||
assert(options.object_format == .c);
|
||||
|
||||
const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = determineMode(options) });
|
||||
errdefer file.close();
|
||||
|
||||
var c_file = try allocator.create(C);
|
||||
errdefer allocator.destroy(c_file);
|
||||
|
||||
c_file.* = File.C{
|
||||
.base = .{
|
||||
.tag = .c,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.main = std.ArrayList(u8).init(allocator),
|
||||
.header = std.ArrayList(u8).init(allocator),
|
||||
.constants = std.ArrayList(u8).init(allocator),
|
||||
.called = std.StringHashMap(void).init(allocator),
|
||||
};
|
||||
|
||||
return &c_file.base;
|
||||
}
|
||||
|
||||
pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
|
||||
self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
|
||||
return error.CGenFailure;
|
||||
@ -452,6 +402,107 @@ pub const File = struct {
|
||||
sym_index: ?u32 = null,
|
||||
};
|
||||
|
||||
pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
|
||||
assert(options.object_format == .elf);
|
||||
|
||||
const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = 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.owns_file_handle = true;
|
||||
return &elf_file.base;
|
||||
}
|
||||
|
||||
/// Returns error.IncrFailed if incremental update could not be performed.
|
||||
fn openFile(allocator: *Allocator, file: fs.File, options: Options) !Elf {
|
||||
switch (options.output_mode) {
|
||||
.Exe => {},
|
||||
.Obj => {},
|
||||
.Lib => return error.IncrFailed,
|
||||
}
|
||||
var self: Elf = .{
|
||||
.base = .{
|
||||
.tag = .elf,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.owns_file_handle = false,
|
||||
.ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
|
||||
32 => .p32,
|
||||
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: Options) !Elf {
|
||||
switch (options.output_mode) {
|
||||
.Exe => {},
|
||||
.Obj => {},
|
||||
.Lib => return error.TODOImplementWritingLibFiles,
|
||||
}
|
||||
var self: Elf = .{
|
||||
.base = .{
|
||||
.tag = .elf,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
|
||||
32 => .p32,
|
||||
64 => .p64,
|
||||
else => return error.UnsupportedELFArchitecture,
|
||||
},
|
||||
.shdr_table_dirty = true,
|
||||
.owns_file_handle = false,
|
||||
};
|
||||
errdefer self.deinit();
|
||||
|
||||
// Index 0 is always a null symbol.
|
||||
try self.local_symbols.append(allocator, .{
|
||||
.st_name = 0,
|
||||
.st_info = 0,
|
||||
.st_other = 0,
|
||||
.st_shndx = 0,
|
||||
.st_value = 0,
|
||||
.st_size = 0,
|
||||
});
|
||||
|
||||
// There must always be a null section in index 0
|
||||
try self.sections.append(allocator, .{
|
||||
.sh_name = 0,
|
||||
.sh_type = elf.SHT_NULL,
|
||||
.sh_flags = 0,
|
||||
.sh_addr = 0,
|
||||
.sh_offset = 0,
|
||||
.sh_size = 0,
|
||||
.sh_link = 0,
|
||||
.sh_info = 0,
|
||||
.sh_addralign = 0,
|
||||
.sh_entsize = 0,
|
||||
});
|
||||
|
||||
try self.populateMissingMetadata();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Elf) void {
|
||||
self.sections.deinit(self.allocator);
|
||||
self.program_headers.deinit(self.allocator);
|
||||
@ -1939,110 +1990,6 @@ pub const File = struct {
|
||||
};
|
||||
};
|
||||
|
||||
/// Truncates the existing file contents and overwrites the contents.
|
||||
/// Returns an error if `file` is not already open with +read +write +seek abilities.
|
||||
pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
|
||||
switch (options.output_mode) {
|
||||
.Exe => {},
|
||||
.Obj => {},
|
||||
.Lib => return error.TODOImplementWritingLibFiles,
|
||||
}
|
||||
switch (options.object_format) {
|
||||
.c => unreachable,
|
||||
.unknown => unreachable, // TODO remove this tag from the enum
|
||||
.coff => return error.TODOImplementWritingCOFF,
|
||||
.elf => {},
|
||||
.macho => return error.TODOImplementWritingMachO,
|
||||
.wasm => return error.TODOImplementWritingWasmObjects,
|
||||
.hex => return error.TODOImplementWritingHex,
|
||||
.raw => return error.TODOImplementWritingRaw,
|
||||
}
|
||||
|
||||
var self: File.Elf = .{
|
||||
.base = .{
|
||||
.tag = .elf,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
|
||||
32 => .p32,
|
||||
64 => .p64,
|
||||
else => return error.UnsupportedELFArchitecture,
|
||||
},
|
||||
.shdr_table_dirty = true,
|
||||
.owns_file_handle = false,
|
||||
};
|
||||
errdefer self.deinit();
|
||||
|
||||
// Index 0 is always a null symbol.
|
||||
try self.local_symbols.append(allocator, .{
|
||||
.st_name = 0,
|
||||
.st_info = 0,
|
||||
.st_other = 0,
|
||||
.st_shndx = 0,
|
||||
.st_value = 0,
|
||||
.st_size = 0,
|
||||
});
|
||||
|
||||
// There must always be a null section in index 0
|
||||
try self.sections.append(allocator, .{
|
||||
.sh_name = 0,
|
||||
.sh_type = elf.SHT_NULL,
|
||||
.sh_flags = 0,
|
||||
.sh_addr = 0,
|
||||
.sh_offset = 0,
|
||||
.sh_size = 0,
|
||||
.sh_link = 0,
|
||||
.sh_info = 0,
|
||||
.sh_addralign = 0,
|
||||
.sh_entsize = 0,
|
||||
});
|
||||
|
||||
try self.populateMissingMetadata();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Returns error.IncrFailed if incremental update could not be performed.
|
||||
fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
|
||||
switch (options.output_mode) {
|
||||
.Exe => {},
|
||||
.Obj => {},
|
||||
.Lib => return error.IncrFailed,
|
||||
}
|
||||
switch (options.object_format) {
|
||||
.unknown => unreachable, // TODO remove this tag from the enum
|
||||
.c => unreachable,
|
||||
.coff => return error.IncrFailed,
|
||||
.elf => {},
|
||||
.macho => return error.IncrFailed,
|
||||
.wasm => return error.IncrFailed,
|
||||
.hex => return error.IncrFailed,
|
||||
.raw => return error.IncrFailed,
|
||||
}
|
||||
var self: File.Elf = .{
|
||||
.base = .{
|
||||
.tag = .elf,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.owns_file_handle = false,
|
||||
.ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
|
||||
32 => .p32,
|
||||
64 => .p64,
|
||||
else => return error.UnsupportedELFArchitecture,
|
||||
},
|
||||
};
|
||||
errdefer self.deinit();
|
||||
|
||||
// TODO implement reading the elf file
|
||||
return error.IncrFailed;
|
||||
//try self.populateMissingMetadata();
|
||||
//return self;
|
||||
}
|
||||
|
||||
/// Saturating multiplication
|
||||
fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
|
||||
const T = @TypeOf(a, b);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user