authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-08 10:45:17+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-07-08 10:45:17+00:00
log6fbb5f0a81946d0392100d0f1c8ecfd43f825651
tree4e466e799e54945ab145a5f7307338ce7b49d88c
parent5667a21b1e7b8aa2dfcefed81d2b594029a116db
parent9aaffe00d3450d2fabd5fbb6a099b3cdc56d04d2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5816 from pixelherodev/cbe

Beginnings of C backend

9 files changed, 1716 insertions(+), 1209 deletions(-)

src-self-hosted/Module.zig+30-25
...@@ -26,7 +26,7 @@ root_pkg: *Package,...@@ -26,7 +26,7 @@ root_pkg: *Package,
26/// Module owns this resource.26/// Module owns this resource.
27/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.27/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
28root_scope: *Scope,28root_scope: *Scope,
29bin_file: link.ElfFile,29bin_file: *link.File,
30bin_file_dir: std.fs.Dir,30bin_file_dir: std.fs.Dir,
31bin_file_path: []const u8,31bin_file_path: []const u8,
32/// It's rare for a decl to be exported, so we save memory by having a sparse map of32/// It's rare for a decl to be exported, so we save memory by having a sparse map of
...@@ -45,7 +45,7 @@ export_owners: std.AutoHashMap(*Decl, []*Export),...@@ -45,7 +45,7 @@ export_owners: std.AutoHashMap(*Decl, []*Export),
45decl_table: DeclTable,45decl_table: DeclTable,
4646
47optimize_mode: std.builtin.Mode,47optimize_mode: std.builtin.Mode,
48link_error_flags: link.ElfFile.ErrorFlags = .{},48link_error_flags: link.File.ErrorFlags = .{},
4949
50work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),50work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
5151
...@@ -91,7 +91,7 @@ pub const Export = struct {...@@ -91,7 +91,7 @@ pub const Export = struct {
91 /// Byte offset into the file that contains the export directive.91 /// Byte offset into the file that contains the export directive.
92 src: usize,92 src: usize,
93 /// Represents the position of the export, if any, in the output file.93 /// Represents the position of the export, if any, in the output file.
94 link: link.ElfFile.Export,94 link: link.File.Elf.Export,
95 /// The Decl that performs the export. Note that this is *not* the Decl being exported.95 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
96 owner_decl: *Decl,96 owner_decl: *Decl,
97 /// The Decl being exported. Note this is *not* the Decl performing the export.97 /// The Decl being exported. Note this is *not* the Decl performing the export.
...@@ -169,7 +169,7 @@ pub const Decl = struct {...@@ -169,7 +169,7 @@ pub const Decl = struct {
169169
170 /// Represents the position of the code in the output file.170 /// Represents the position of the code in the output file.
171 /// This is populated regardless of semantic analysis and code generation.171 /// This is populated regardless of semantic analysis and code generation.
172 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,172 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
173173
174 contents_hash: std.zig.SrcHash,174 contents_hash: std.zig.SrcHash,
175175
...@@ -732,17 +732,19 @@ pub const InitOptions = struct {...@@ -732,17 +732,19 @@ pub const InitOptions = struct {
732 object_format: ?std.builtin.ObjectFormat = null,732 object_format: ?std.builtin.ObjectFormat = null,
733 optimize_mode: std.builtin.Mode = .Debug,733 optimize_mode: std.builtin.Mode = .Debug,
734 keep_source_files_loaded: bool = false,734 keep_source_files_loaded: bool = false,
735 cbe: bool = false,
735};736};
736737
737pub fn init(gpa: *Allocator, options: InitOptions) !Module {738pub fn init(gpa: *Allocator, options: InitOptions) !Module {
738 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();739 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
739 var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{740 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
740 .target = options.target,741 .target = options.target,
741 .output_mode = options.output_mode,742 .output_mode = options.output_mode,
742 .link_mode = options.link_mode orelse .Static,743 .link_mode = options.link_mode orelse .Static,
743 .object_format = options.object_format orelse options.target.getObjectFormat(),744 .object_format = options.object_format orelse options.target.getObjectFormat(),
745 .cbe = options.cbe,
744 });746 });
745 errdefer bin_file.deinit();747 errdefer bin_file.destroy();
746748
747 const root_scope = blk: {749 const root_scope = blk: {
748 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {750 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
...@@ -791,7 +793,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -791,7 +793,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
791}793}
792794
793pub fn deinit(self: *Module) void {795pub fn deinit(self: *Module) void {
794 self.bin_file.deinit();796 self.bin_file.destroy();
795 const allocator = self.allocator;797 const allocator = self.allocator;
796 self.deletion_set.deinit(allocator);798 self.deletion_set.deinit(allocator);
797 self.work_queue.deinit();799 self.work_queue.deinit();
...@@ -840,7 +842,7 @@ fn freeExportList(allocator: *Allocator, export_list: []*Export) void {...@@ -840,7 +842,7 @@ fn freeExportList(allocator: *Allocator, export_list: []*Export) void {
840}842}
841843
842pub fn target(self: Module) std.Target {844pub fn target(self: Module) std.Target {
843 return self.bin_file.options.target;845 return self.bin_file.options().target;
844}846}
845847
846/// Detect changes to source files, perform semantic analysis, and update the output files.848/// Detect changes to source files, perform semantic analysis, and update the output files.
...@@ -882,7 +884,7 @@ pub fn update(self: *Module) !void {...@@ -882,7 +884,7 @@ pub fn update(self: *Module) !void {
882 try self.deleteDecl(decl);884 try self.deleteDecl(decl);
883 }885 }
884886
885 self.link_error_flags = self.bin_file.error_flags;887 self.link_error_flags = self.bin_file.errorFlags();
886888
887 // If there are any errors, we anticipate the source files being loaded889 // If there are any errors, we anticipate the source files being loaded
888 // to report error messages. Otherwise we unload all source files to save memory.890 // to report error messages. Otherwise we unload all source files to save memory.
...@@ -1898,8 +1900,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {...@@ -1898,8 +1900,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
1898 self.decl_exports.removeAssertDiscard(exp.exported_decl);1900 self.decl_exports.removeAssertDiscard(exp.exported_decl);
1899 }1901 }
1900 }1902 }
19011903 if (self.bin_file.cast(link.File.Elf)) |elf| {
1902 self.bin_file.deleteExport(exp.link);1904 elf.deleteExport(exp.link);
1905 }
1903 if (self.failed_exports.remove(exp)) |entry| {1906 if (self.failed_exports.remove(exp)) |entry| {
1904 entry.value.destroy(self.allocator);1907 entry.value.destroy(self.allocator);
1905 }1908 }
...@@ -1961,7 +1964,7 @@ fn allocateNewDecl(...@@ -1961,7 +1964,7 @@ fn allocateNewDecl(
1961 .analysis = .unreferenced,1964 .analysis = .unreferenced,
1962 .deletion_flag = false,1965 .deletion_flag = false,
1963 .contents_hash = contents_hash,1966 .contents_hash = contents_hash,
1964 .link = link.ElfFile.TextBlock.empty,1967 .link = link.File.Elf.TextBlock.empty,
1965 .generation = 0,1968 .generation = 0,
1966 };1969 };
1967 return new_decl;1970 return new_decl;
...@@ -2189,19 +2192,21 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const...@@ -2189,19 +2192,21 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
2189 }2192 }
21902193
2191 try self.symbol_exports.putNoClobber(symbol_name, new_export);2194 try self.symbol_exports.putNoClobber(symbol_name, new_export);
2192 self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {2195 if (self.bin_file.cast(link.File.Elf)) |elf| {
2193 error.OutOfMemory => return error.OutOfMemory,2196 elf.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
2194 else => {2197 error.OutOfMemory => return error.OutOfMemory,
2195 try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);2198 else => {
2196 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(2199 try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);
2197 self.allocator,2200 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
2198 src,2201 self.allocator,
2199 "unable to export: {}",2202 src,
2200 .{@errorName(err)},2203 "unable to export: {}",
2201 ));2204 .{@errorName(err)},
2202 new_export.status = .failed_retryable;2205 ));
2203 },2206 new_export.status = .failed_retryable;
2204 };2207 },
2208 };
2209 }
2205}2210}
22062211
2207fn addNewInstArgs(2212fn addNewInstArgs(
src-self-hosted/cbe.h created+8
...@@ -0,0 +1,8 @@
1#if __STDC_VERSION__ >= 201112L
2#define noreturn _Noreturn
3#elif !__STRICT_ANSI__
4#define noreturn __attribute__ ((noreturn))
5#else
6#define noreturn
7#endif
8
src-self-hosted/cgen.zig created+161
...@@ -0,0 +1,161 @@
1const link = @import("link.zig");
2const Module = @import("Module.zig");
3const ir = @import("ir.zig");
4const Value = @import("value.zig").Value;
5const Type = @import("type.zig").Type;
6const std = @import("std");
7
8const C = link.File.C;
9const Decl = Module.Decl;
10const mem = std.mem;
11
12/// Maps a name from Zig source to C. This will always give the same output for
13/// any given input.
14fn map(name: []const u8) ![]const u8 {
15 return name;
16}
17
18fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void {
19 if (T.tag() == .usize) {
20 file.need_stddef = true;
21 try writer.writeAll("size_t");
22 } else {
23 switch (T.zigTypeTag()) {
24 .NoReturn => {
25 file.need_noreturn = true;
26 try writer.writeAll("noreturn void");
27 },
28 .Void => try writer.writeAll("void"),
29 else => |e| return file.fail(src, "TODO implement type {}", .{e}),
30 }
31 }
32}
33
34fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
35 const tv = decl.typed_value.most_recent.typed_value;
36 try renderType(file, writer, tv.ty.fnReturnType(), decl.src());
37 const name = try map(mem.spanZ(decl.name));
38 try writer.print(" {}(", .{name});
39 if (tv.ty.fnParamLen() == 0) {
40 try writer.writeAll("void)");
41 } else {
42 return file.fail(decl.src(), "TODO implement parameters", .{});
43 }
44}
45
46pub fn generate(file: *C, decl: *Decl) !void {
47 const writer = file.main.writer();
48 const header = file.header.writer();
49 const tv = decl.typed_value.most_recent.typed_value;
50 switch (tv.ty.zigTypeTag()) {
51 .Fn => {
52 try renderFunctionSignature(file, writer, decl);
53
54 try writer.writeAll(" {");
55
56 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
57 const instructions = func.analysis.success.instructions;
58 if (instructions.len > 0) {
59 for (instructions) |inst| {
60 try writer.writeAll("\n\t");
61 switch (inst.tag) {
62 .assembly => {
63 const as = inst.cast(ir.Inst.Assembly).?.args;
64 for (as.inputs) |i, index| {
65 if (i[0] == '{' and i[i.len - 1] == '}') {
66 const reg = i[1 .. i.len - 1];
67 const arg = as.args[index];
68 if (arg.cast(ir.Inst.Constant)) |c| {
69 if (c.val.tag() == .int_u64) {
70 try writer.writeAll("register ");
71 try renderType(file, writer, arg.ty, decl.src());
72 try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
73 } else {
74 return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
75 }
76 } else {
77 return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
78 }
79 } else {
80 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
81 }
82 }
83 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
84 if (as.output) |o| {
85 return file.fail(decl.src(), "TODO inline asm output", .{});
86 }
87 if (as.inputs.len > 0) {
88 if (as.output == null) {
89 try writer.writeAll(" :");
90 }
91 try writer.writeAll(": ");
92 for (as.inputs) |i, index| {
93 if (i[0] == '{' and i[i.len - 1] == '}') {
94 const reg = i[1 .. i.len - 1];
95 const arg = as.args[index];
96 if (index > 0) {
97 try writer.writeAll(", ");
98 }
99 if (arg.cast(ir.Inst.Constant)) |c| {
100 try writer.print("\"\"({}_constant)", .{reg});
101 } else {
102 // This is blocked by the earlier test
103 unreachable;
104 }
105 } else {
106 // This is blocked by the earlier test
107 unreachable;
108 }
109 }
110 }
111 try writer.writeAll(");");
112 },
113 .call => {
114 const call = inst.cast(ir.Inst.Call).?.args;
115 if (call.func.cast(ir.Inst.Constant)) |func_inst| {
116 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
117 const target = func_val.func.owner_decl;
118 const tname = mem.spanZ(target.name);
119 if (file.called.get(tname) == null) {
120 try file.called.put(tname, void{});
121 try renderFunctionSignature(file, header, target);
122 try header.writeAll(";\n");
123 }
124 try writer.print("{}();", .{tname});
125 } else {
126 return file.fail(decl.src(), "TODO non-function call target?", .{});
127 }
128 if (call.args.len != 0) {
129 return file.fail(decl.src(), "TODO function arguments", .{});
130 }
131 } else {
132 return file.fail(decl.src(), "TODO non-constant call inst?", .{});
133 }
134 },
135 else => |e| {
136 return file.fail(decl.src(), "TODO {}", .{e});
137 },
138 }
139 }
140 try writer.writeAll("\n");
141 }
142
143 try writer.writeAll("}\n\n");
144 },
145 .Array => {
146 if (mem.indexOf(u8, mem.span(decl.name), "$") == null) {
147 // TODO: prevent inline asm constants from being emitted
148 if (tv.val.cast(Value.Payload.Bytes)) |payload| {
149 try writer.print("const char *const {} = \"{}\";\n", .{ decl.name, payload.data });
150 std.debug.warn("\n\nARRAYTRANS\n", .{});
151 if (tv.ty.arraySentinel()) |sentinel| {}
152 } else {
153 return file.fail(decl.src(), "TODO non-byte arrays", .{});
154 }
155 }
156 },
157 else => |e| {
158 return file.fail(decl.src(), "TODO {}", .{e});
159 },
160 }
161}
src-self-hosted/codegen.zig+2-2
...@@ -21,7 +21,7 @@ pub const Result = union(enum) {...@@ -21,7 +21,7 @@ pub const Result = union(enum) {
21};21};
2222
23pub fn generateSymbol(23pub fn generateSymbol(
24 bin_file: *link.ElfFile,24 bin_file: *link.File.Elf,
25 src: usize,25 src: usize,
26 typed_value: TypedValue,26 typed_value: TypedValue,
27 code: *std.ArrayList(u8),27 code: *std.ArrayList(u8),
...@@ -211,7 +211,7 @@ pub fn generateSymbol(...@@ -211,7 +211,7 @@ pub fn generateSymbol(
211}211}
212212
213const Function = struct {213const Function = struct {
214 bin_file: *link.ElfFile,214 bin_file: *link.File.Elf,
215 target: *const std.Target,215 target: *const std.Target,
216 mod_fn: *const Module.Fn,216 mod_fn: *const Module.Fn,
217 code: *std.ArrayList(u8),217 code: *std.ArrayList(u8),
src-self-hosted/link.zig+1297-1107
...@@ -7,6 +7,7 @@ const Module = @import("Module.zig");...@@ -7,6 +7,7 @@ const Module = @import("Module.zig");
7const fs = std.fs;7const fs = std.fs;
8const elf = std.elf;8const elf = std.elf;
9const codegen = @import("codegen.zig");9const codegen = @import("codegen.zig");
10const cgen = @import("cgen.zig");
1011
11const default_entry_addr = 0x8000000;12const default_entry_addr = 0x8000000;
1213
...@@ -21,6 +22,7 @@ pub const Options = struct {...@@ -21,6 +22,7 @@ pub const Options = struct {
21 /// Used for calculating how much space to reserve for executable program code in case22 /// Used for calculating how much space to reserve for executable program code in case
22 /// the binary file deos not already have such a section.23 /// the binary file deos not already have such a section.
23 program_code_size_hint: u64 = 256 * 1024,24 program_code_size_hint: u64 = 256 * 1024,
25 cbe: bool = false,
24};26};
2527
26/// Attempts incremental linking, if the file already exists.28/// Attempts incremental linking, if the file already exists.
...@@ -32,13 +34,22 @@ pub fn openBinFilePath(...@@ -32,13 +34,22 @@ pub fn openBinFilePath(
32 dir: fs.Dir,34 dir: fs.Dir,
33 sub_path: []const u8,35 sub_path: []const u8,
34 options: Options,36 options: Options,
35) !ElfFile {37) !*File {
36 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });38 const file = try dir.createFile(sub_path, .{ .truncate = options.cbe, .read = true, .mode = determineMode(options) });
37 errdefer file.close();39 errdefer file.close();
3840
39 var bin_file = try openBinFile(allocator, file, options);41 if (options.cbe) {
40 bin_file.owns_file_handle = true;42 var bin_file = try allocator.create(File.C);
41 return bin_file;43 errdefer allocator.destroy(bin_file);
44 bin_file.* = try openCFile(allocator, file, options);
45 return &bin_file.base;
46 } else {
47 var bin_file = try allocator.create(File.Elf);
48 errdefer allocator.destroy(bin_file);
49 bin_file.* = try openBinFile(allocator, file, options);
50 bin_file.owns_file_handle = true;
51 return &bin_file.base;
52 }
42}53}
4354
44/// Atomically overwrites the old file, if present.55/// Atomically overwrites the old file, if present.
...@@ -75,12 +86,23 @@ pub fn writeFilePath(...@@ -75,12 +86,23 @@ pub fn writeFilePath(
75 return result;86 return result;
76}87}
7788
89pub fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {
90 return File.C{
91 .allocator = allocator,
92 .file = file,
93 .options = options,
94 .main = std.ArrayList(u8).init(allocator),
95 .header = std.ArrayList(u8).init(allocator),
96 .called = std.StringHashMap(void).init(allocator),
97 };
98}
99
78/// Attempts incremental linking, if the file already exists.100/// Attempts incremental linking, if the file already exists.
79/// If incremental linking fails, falls back to truncating the file and rewriting it.101/// If incremental linking fails, falls back to truncating the file and rewriting it.
80/// Returns an error if `file` is not already open with +read +write +seek abilities.102/// Returns an error if `file` is not already open with +read +write +seek abilities.
81/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.103/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
82/// This operation is not atomic.104/// This operation is not atomic.
83pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {105pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
84 return openBinFileInner(allocator, file, options) catch |err| switch (err) {106 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
85 error.IncrFailed => {107 error.IncrFailed => {
86 return createElfFile(allocator, file, options);108 return createElfFile(allocator, file, options);
...@@ -89,447 +111,584 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF...@@ -89,447 +111,584 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF
89 };111 };
90}112}
91113
92pub const ElfFile = struct {114pub const File = struct {
93 allocator: *Allocator,115 tag: Tag,
94 file: ?fs.File,116 pub fn cast(base: *File, comptime T: type) ?*T {
95 owns_file_handle: bool,117 if (base.tag != T.base_tag)
96 options: Options,118 return null;
97 ptr_width: enum { p32, p64 },
98
99 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
100 /// Same order as in the file.
101 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
102 shdr_table_offset: ?u64 = null,
103
104 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
105 /// Same order as in the file.
106 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
107 phdr_table_offset: ?u64 = null,
108 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
109 phdr_load_re_index: ?u16 = null,
110 /// The index into the program headers of the global offset table.
111 /// It needs PT_LOAD and Read flags.
112 phdr_got_index: ?u16 = null,
113 entry_addr: ?u64 = null,
114
115 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
116 shstrtab_index: ?u16 = null,
117
118 text_section_index: ?u16 = null,
119 symtab_section_index: ?u16 = null,
120 got_section_index: ?u16 = null,
121
122 /// The same order as in the file. ELF requires global symbols to all be after the
123 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
124 /// write them at the end. These are only the local symbols. The length of this array
125 /// is the value used for sh_info in the .symtab section.
126 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
127 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
128
129 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
130 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
131 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
132
133 /// Same order as in the file. The value is the absolute vaddr value.
134 /// If the vaddr of the executable program header changes, the entire
135 /// offset table needs to be rewritten.
136 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
137
138 phdr_table_dirty: bool = false,
139 shdr_table_dirty: bool = false,
140 shstrtab_dirty: bool = false,
141 offset_table_count_dirty: bool = false,
142
143 error_flags: ErrorFlags = ErrorFlags{},
144
145 /// A list of text blocks that have surplus capacity. This list can have false
146 /// positives, as functions grow and shrink over time, only sometimes being added
147 /// or removed from the freelist.
148 ///
149 /// A text block has surplus capacity when its overcapacity value is greater than
150 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
151 /// much extra capacity, that we could fit a small new symbol in it, itself with
152 /// ideal_capacity or more.
153 ///
154 /// Ideal capacity is defined by size * alloc_num / alloc_den.
155 ///
156 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
157 /// overcapacity can be negative. A simple way to have negative overcapacity is to
158 /// allocate a fresh text block, which will have ideal capacity, and then grow it
159 /// by 1 byte. It will then have -1 overcapacity.
160 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
161 last_text_block: ?*TextBlock = null,
162
163 /// `alloc_num / alloc_den` is the factor of padding when allocating.
164 const alloc_num = 4;
165 const alloc_den = 3;
166
167 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
168 /// it as a possible place to put new symbols, it must have enough room for this many bytes
169 /// (plus extra for reserved capacity).
170 const minimum_text_block_size = 64;
171 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
172119
173 pub const ErrorFlags = struct {120 return @fieldParentPtr(T, "base", base);
174 no_entry_point_found: bool = false,121 }
175 };
176122
177 pub const TextBlock = struct {123 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
178 /// Each decl always gets a local symbol with the fully qualified name.124 switch (base.tag) {
179 /// The vaddr and size are found here directly.125 .Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
180 /// The file offset is found by computing the vaddr offset from the section vaddr126 .C => {},
181 /// the symbol references, and adding that to the file offset of the section.127 else => unreachable,
182 /// If this field is 0, it means the codegen size = 0 and there is no symbol or128 }
183 /// offset table entry.129 }
184 local_sym_index: u32,
185 /// This field is undefined for symbols with size = 0.
186 offset_table_index: u32,
187 /// Points to the previous and next neighbors, based on the `text_offset`.
188 /// This can be used to find, for example, the capacity of this `TextBlock`.
189 prev: ?*TextBlock,
190 next: ?*TextBlock,
191
192 pub const empty = TextBlock{
193 .local_sym_index = 0,
194 .offset_table_index = undefined,
195 .prev = null,
196 .next = null,
197 };
198130
199 /// Returns how much room there is to grow in virtual address space.131 pub fn makeExecutable(base: *File) !void {
200 /// File offset relocation happens transparently, so it is not included in132 switch (base.tag) {
201 /// this calculation.133 .Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
202 fn capacity(self: TextBlock, elf_file: ElfFile) u64 {134 else => unreachable,
203 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
204 if (self.next) |next| {
205 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
206 return next_sym.st_value - self_sym.st_value;
207 } else {
208 // We are the last block. The capacity is limited only by virtual address space.
209 return std.math.maxInt(u32) - self_sym.st_value;
210 }
211 }135 }
136 }
212137
213 fn freeListEligible(self: TextBlock, elf_file: ElfFile) bool {138 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
214 // No need to keep a free list node for the last block.139 switch (base.tag) {
215 const next = self.next orelse return false;140 .Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
216 const self_sym = elf_file.local_symbols.items[self.local_sym_index];141 .C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
217 const next_sym = elf_file.local_symbols.items[next.local_sym_index];142 else => unreachable,
218 const cap = next_sym.st_value - self_sym.st_value;
219 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
220 if (cap <= ideal_cap) return false;
221 const surplus = cap - ideal_cap;
222 return surplus >= min_text_capacity;
223 }143 }
224 };144 }
225145
226 pub const Export = struct {146 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
227 sym_index: ?u32 = null,147 switch (base.tag) {
228 };148 .Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
149 .C => {},
150 else => unreachable,
151 }
152 }
229153
230 pub fn deinit(self: *ElfFile) void {154 pub fn deinit(base: *File) void {
231 self.sections.deinit(self.allocator);155 switch (base.tag) {
232 self.program_headers.deinit(self.allocator);156 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),
233 self.shstrtab.deinit(self.allocator);157 .C => @fieldParentPtr(C, "base", base).deinit(),
234 self.local_symbols.deinit(self.allocator);158 else => unreachable,
235 self.global_symbols.deinit(self.allocator);
236 self.global_symbol_free_list.deinit(self.allocator);
237 self.local_symbol_free_list.deinit(self.allocator);
238 self.offset_table_free_list.deinit(self.allocator);
239 self.text_block_free_list.deinit(self.allocator);
240 self.offset_table.deinit(self.allocator);
241 if (self.owns_file_handle) {
242 if (self.file) |f| f.close();
243 }159 }
244 }160 }
245161
246 pub fn makeExecutable(self: *ElfFile) !void {162 pub fn destroy(base: *File) void {
247 assert(self.owns_file_handle);163 switch (base.tag) {
248 if (self.file) |f| {164 .Elf => {
249 f.close();165 const parent = @fieldParentPtr(Elf, "base", base);
250 self.file = null;166 parent.deinit();
167 parent.allocator.destroy(parent);
168 },
169 .C => {
170 const parent = @fieldParentPtr(C, "base", base);
171 parent.deinit();
172 parent.allocator.destroy(parent);
173 },
174 else => unreachable,
251 }175 }
252 }176 }
253177
254 pub fn makeWritable(self: *ElfFile, dir: fs.Dir, sub_path: []const u8) !void {178 pub fn flush(base: *File) !void {
255 assert(self.owns_file_handle);179 try switch (base.tag) {
256 if (self.file != null) return;180 .Elf => @fieldParentPtr(Elf, "base", base).flush(),
257 self.file = try dir.createFile(sub_path, .{181 .C => @fieldParentPtr(C, "base", base).flush(),
258 .truncate = false,182 else => unreachable,
259 .read = true,183 };
260 .mode = determineMode(self.options),
261 });
262 }184 }
263185
264 /// Returns end pos of collision, if any.186 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
265 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {187 switch (base.tag) {
266 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;188 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
267 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);189 else => unreachable,
268 if (start < ehdr_size)
269 return ehdr_size;
270
271 const end = start + satMul(size, alloc_num) / alloc_den;
272
273 if (self.shdr_table_offset) |off| {
274 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
275 const tight_size = self.sections.items.len * shdr_size;
276 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
277 const test_end = off + increased_size;
278 if (end > off and start < test_end) {
279 return test_end;
280 }
281 }190 }
191 }
282192
283 if (self.phdr_table_offset) |off| {193 pub fn errorFlags(base: *File) ErrorFlags {
284 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);194 return switch (base.tag) {
285 const tight_size = self.sections.items.len * phdr_size;195 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,
286 const increased_size = satMul(tight_size, alloc_num) / alloc_den;196 .C => return .{ .no_entry_point_found = false },
287 const test_end = off + increased_size;197 else => unreachable,
288 if (end > off and start < test_end) {198 };
289 return test_end;199 }
290 }
291 }
292200
293 for (self.sections.items) |section| {201 pub fn options(base: *File) Options {
294 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;202 return switch (base.tag) {
295 const test_end = section.sh_offset + increased_size;203 .Elf => @fieldParentPtr(Elf, "base", base).options,
296 if (end > section.sh_offset and start < test_end) {204 .C => @fieldParentPtr(C, "base", base).options,
297 return test_end;205 };
298 }
299 }
300 for (self.program_headers.items) |program_header| {
301 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
302 const test_end = program_header.p_offset + increased_size;
303 if (end > program_header.p_offset and start < test_end) {
304 return test_end;
305 }
306 }
307 return null;
308 }206 }
309207
310 fn allocatedSize(self: *ElfFile, start: u64) u64 {208 pub const Tag = enum {
311 var min_pos: u64 = std.math.maxInt(u64);209 Elf,
312 if (self.shdr_table_offset) |off| {210 C,
313 if (off > start and off < min_pos) min_pos = off;211 };
314 }212
315 if (self.phdr_table_offset) |off| {213 pub const ErrorFlags = struct {
316 if (off > start and off < min_pos) min_pos = off;214 no_entry_point_found: bool = false,
317 }215 };
318 for (self.sections.items) |section| {216
319 if (section.sh_offset <= start) continue;217 pub const C = struct {
320 if (section.sh_offset < min_pos) min_pos = section.sh_offset;218 pub const base_tag: Tag = .C;
219 base: File = File{ .tag = base_tag },
220
221 allocator: *Allocator,
222 header: std.ArrayList(u8),
223 main: std.ArrayList(u8),
224 file: ?fs.File,
225 options: Options,
226 called: std.StringHashMap(void),
227 need_stddef: bool = false,
228 need_stdint: bool = false,
229 need_noreturn: bool = false,
230 error_msg: *Module.ErrorMsg = undefined,
231
232 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: var) !void {
233 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
234 return error.CGenFailure;
321 }235 }
322 for (self.program_headers.items) |program_header| {236
323 if (program_header.p_offset <= start) continue;237 pub fn deinit(self: *File.C) void {
324 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;238 self.main.deinit();
239 self.header.deinit();
240 self.called.deinit();
241 if (self.file) |f|
242 f.close();
325 }243 }
326 return min_pos - start;
327 }
328244
329 fn findFreeSpace(self: *ElfFile, object_size: u64, min_alignment: u16) u64 {245 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
330 var start: u64 = 0;246 cgen.generate(self, decl) catch |err| {
331 while (self.detectAllocCollision(start, object_size)) |item_end| {247 if (err == error.CGenFailure) {
332 start = mem.alignForwardGeneric(u64, item_end, min_alignment);248 try module.failed_decls.put(decl, self.error_msg);
249 }
250 return err;
251 };
333 }252 }
334 return start;
335 }
336253
337 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {254 pub fn flush(self: *File.C) !void {
338 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);255 const writer = self.file.?.writer();
339 const result = self.shstrtab.items.len;256 try writer.writeAll(@embedFile("cbe.h"));
340 self.shstrtab.appendSliceAssumeCapacity(bytes);257 var includes = false;
341 self.shstrtab.appendAssumeCapacity(0);258 if (self.need_stddef) {
342 return @intCast(u32, result);259 try writer.writeAll("#include <stddef.h>\n");
343 }260 includes = true;
261 }
262 if (self.need_stdint) {
263 try writer.writeAll("#include <stdint.h>\n");
264 includes = true;
265 }
266 if (includes) {
267 try writer.writeByte('\n');
268 }
269 if (self.header.items.len > 0) {
270 try writer.print("{}\n", .{self.header.items});
271 }
272 if (self.main.items.len > 1) {
273 const last_two = self.main.items[self.main.items.len - 2 ..];
274 if (std.mem.eql(u8, last_two, "\n\n")) {
275 self.main.items.len -= 1;
276 }
277 }
278 try writer.writeAll(self.main.items);
279 }
280 };
344281
345 fn getString(self: *ElfFile, str_off: u32) []const u8 {282 pub const Elf = struct {
346 assert(str_off < self.shstrtab.items.len);283 pub const base_tag: Tag = .Elf;
347 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));284 base: File = File{ .tag = base_tag },
348 }285
286 allocator: *Allocator,
287 file: ?fs.File,
288 owns_file_handle: bool,
289 options: Options,
290 ptr_width: enum { p32, p64 },
291
292 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
293 /// Same order as in the file.
294 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
295 shdr_table_offset: ?u64 = null,
296
297 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
298 /// Same order as in the file.
299 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
300 phdr_table_offset: ?u64 = null,
301 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
302 phdr_load_re_index: ?u16 = null,
303 /// The index into the program headers of the global offset table.
304 /// It needs PT_LOAD and Read flags.
305 phdr_got_index: ?u16 = null,
306 entry_addr: ?u64 = null,
307
308 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
309 shstrtab_index: ?u16 = null,
310
311 text_section_index: ?u16 = null,
312 symtab_section_index: ?u16 = null,
313 got_section_index: ?u16 = null,
314
315 /// The same order as in the file. ELF requires global symbols to all be after the
316 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
317 /// write them at the end. These are only the local symbols. The length of this array
318 /// is the value used for sh_info in the .symtab section.
319 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
320 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
321
322 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
323 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
324 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
325
326 /// Same order as in the file. The value is the absolute vaddr value.
327 /// If the vaddr of the executable program header changes, the entire
328 /// offset table needs to be rewritten.
329 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
330
331 phdr_table_dirty: bool = false,
332 shdr_table_dirty: bool = false,
333 shstrtab_dirty: bool = false,
334 offset_table_count_dirty: bool = false,
335
336 error_flags: ErrorFlags = ErrorFlags{},
337
338 /// A list of text blocks that have surplus capacity. This list can have false
339 /// positives, as functions grow and shrink over time, only sometimes being added
340 /// or removed from the freelist.
341 ///
342 /// A text block has surplus capacity when its overcapacity value is greater than
343 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
344 /// much extra capacity, that we could fit a small new symbol in it, itself with
345 /// ideal_capacity or more.
346 ///
347 /// Ideal capacity is defined by size * alloc_num / alloc_den.
348 ///
349 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
350 /// overcapacity can be negative. A simple way to have negative overcapacity is to
351 /// allocate a fresh text block, which will have ideal capacity, and then grow it
352 /// by 1 byte. It will then have -1 overcapacity.
353 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
354 last_text_block: ?*TextBlock = null,
355
356 /// `alloc_num / alloc_den` is the factor of padding when allocating.
357 const alloc_num = 4;
358 const alloc_den = 3;
359
360 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
361 /// it as a possible place to put new symbols, it must have enough room for this many bytes
362 /// (plus extra for reserved capacity).
363 const minimum_text_block_size = 64;
364 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
365
366 pub const TextBlock = struct {
367 /// Each decl always gets a local symbol with the fully qualified name.
368 /// The vaddr and size are found here directly.
369 /// The file offset is found by computing the vaddr offset from the section vaddr
370 /// the symbol references, and adding that to the file offset of the section.
371 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
372 /// offset table entry.
373 local_sym_index: u32,
374 /// This field is undefined for symbols with size = 0.
375 offset_table_index: u32,
376 /// Points to the previous and next neighbors, based on the `text_offset`.
377 /// This can be used to find, for example, the capacity of this `TextBlock`.
378 prev: ?*TextBlock,
379 next: ?*TextBlock,
380
381 pub const empty = TextBlock{
382 .local_sym_index = 0,
383 .offset_table_index = undefined,
384 .prev = null,
385 .next = null,
386 };
349387
350 fn updateString(self: *ElfFile, old_str_off: u32, new_name: []const u8) !u32 {388 /// Returns how much room there is to grow in virtual address space.
351 const existing_name = self.getString(old_str_off);389 /// File offset relocation happens transparently, so it is not included in
352 if (mem.eql(u8, existing_name, new_name)) {390 /// this calculation.
353 return old_str_off;391 fn capacity(self: TextBlock, elf_file: Elf) u64 {
354 }392 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
355 return self.makeString(new_name);393 if (self.next) |next| {
356 }394 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
395 return next_sym.st_value - self_sym.st_value;
396 } else {
397 // We are the last block. The capacity is limited only by virtual address space.
398 return std.math.maxInt(u32) - self_sym.st_value;
399 }
400 }
357401
358 pub fn populateMissingMetadata(self: *ElfFile) !void {402 fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
359 const small_ptr = switch (self.ptr_width) {403 // No need to keep a free list node for the last block.
360 .p32 => true,404 const next = self.next orelse return false;
361 .p64 => false,405 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
406 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
407 const cap = next_sym.st_value - self_sym.st_value;
408 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
409 if (cap <= ideal_cap) return false;
410 const surplus = cap - ideal_cap;
411 return surplus >= min_text_capacity;
412 }
362 };413 };
363 const ptr_size: u8 = switch (self.ptr_width) {414
364 .p32 => 4,415 pub const Export = struct {
365 .p64 => 8,416 sym_index: ?u32 = null,
366 };417 };
367 if (self.phdr_load_re_index == null) {418
368 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);419 pub fn deinit(self: *Elf) void {
369 const file_size = self.options.program_code_size_hint;420 self.sections.deinit(self.allocator);
370 const p_align = 0x1000;421 self.program_headers.deinit(self.allocator);
371 const off = self.findFreeSpace(file_size, p_align);422 self.shstrtab.deinit(self.allocator);
372 //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });423 self.local_symbols.deinit(self.allocator);
373 try self.program_headers.append(self.allocator, .{424 self.global_symbols.deinit(self.allocator);
374 .p_type = elf.PT_LOAD,425 self.global_symbol_free_list.deinit(self.allocator);
375 .p_offset = off,426 self.local_symbol_free_list.deinit(self.allocator);
376 .p_filesz = file_size,427 self.offset_table_free_list.deinit(self.allocator);
377 .p_vaddr = default_entry_addr,428 self.text_block_free_list.deinit(self.allocator);
378 .p_paddr = default_entry_addr,429 self.offset_table.deinit(self.allocator);
379 .p_memsz = file_size,430 if (self.owns_file_handle) {
380 .p_align = p_align,431 if (self.file) |f| f.close();
381 .p_flags = elf.PF_X | elf.PF_R,432 }
382 });
383 self.entry_addr = null;
384 self.phdr_table_dirty = true;
385 }433 }
386 if (self.phdr_got_index == null) {434
387 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);435 pub fn makeExecutable(self: *Elf) !void {
388 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;436 assert(self.owns_file_handle);
389 // We really only need ptr alignment but since we are using PROGBITS, linux requires437 if (self.file) |f| {
390 // page align.438 f.close();
391 const p_align = 0x1000;439 self.file = null;
392 const off = self.findFreeSpace(file_size, p_align);440 }
393 //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
394 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
395 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
396 // else in virtual memory.
397 const default_got_addr = 0x4000000;
398 try self.program_headers.append(self.allocator, .{
399 .p_type = elf.PT_LOAD,
400 .p_offset = off,
401 .p_filesz = file_size,
402 .p_vaddr = default_got_addr,
403 .p_paddr = default_got_addr,
404 .p_memsz = file_size,
405 .p_align = p_align,
406 .p_flags = elf.PF_R,
407 });
408 self.phdr_table_dirty = true;
409 }441 }
410 if (self.shstrtab_index == null) {442
411 self.shstrtab_index = @intCast(u16, self.sections.items.len);443 pub fn makeWritable(self: *Elf, dir: fs.Dir, sub_path: []const u8) !void {
412 assert(self.shstrtab.items.len == 0);444 assert(self.owns_file_handle);
413 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0445 if (self.file != null) return;
414 const off = self.findFreeSpace(self.shstrtab.items.len, 1);446 self.file = try dir.createFile(sub_path, .{
415 //std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });447 .truncate = false,
416 try self.sections.append(self.allocator, .{448 .read = true,
417 .sh_name = try self.makeString(".shstrtab"),449 .mode = determineMode(self.options),
418 .sh_type = elf.SHT_STRTAB,
419 .sh_flags = 0,
420 .sh_addr = 0,
421 .sh_offset = off,
422 .sh_size = self.shstrtab.items.len,
423 .sh_link = 0,
424 .sh_info = 0,
425 .sh_addralign = 1,
426 .sh_entsize = 0,
427 });450 });
428 self.shstrtab_dirty = true;
429 self.shdr_table_dirty = true;
430 }451 }
431 if (self.text_section_index == null) {
432 self.text_section_index = @intCast(u16, self.sections.items.len);
433 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
434452
435 try self.sections.append(self.allocator, .{453 /// Returns end pos of collision, if any.
436 .sh_name = try self.makeString(".text"),454 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
437 .sh_type = elf.SHT_PROGBITS,455 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
438 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,456 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
439 .sh_addr = phdr.p_vaddr,457 if (start < ehdr_size)
440 .sh_offset = phdr.p_offset,458 return ehdr_size;
441 .sh_size = phdr.p_filesz,459
442 .sh_link = 0,460 const end = start + satMul(size, alloc_num) / alloc_den;
443 .sh_info = 0,461
444 .sh_addralign = phdr.p_align,462 if (self.shdr_table_offset) |off| {
445 .sh_entsize = 0,463 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
446 });464 const tight_size = self.sections.items.len * shdr_size;
447 self.shdr_table_dirty = true;465 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
466 const test_end = off + increased_size;
467 if (end > off and start < test_end) {
468 return test_end;
469 }
470 }
471
472 if (self.phdr_table_offset) |off| {
473 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
474 const tight_size = self.sections.items.len * phdr_size;
475 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
476 const test_end = off + increased_size;
477 if (end > off and start < test_end) {
478 return test_end;
479 }
480 }
481
482 for (self.sections.items) |section| {
483 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
484 const test_end = section.sh_offset + increased_size;
485 if (end > section.sh_offset and start < test_end) {
486 return test_end;
487 }
488 }
489 for (self.program_headers.items) |program_header| {
490 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
491 const test_end = program_header.p_offset + increased_size;
492 if (end > program_header.p_offset and start < test_end) {
493 return test_end;
494 }
495 }
496 return null;
448 }497 }
449 if (self.got_section_index == null) {
450 self.got_section_index = @intCast(u16, self.sections.items.len);
451 const phdr = &self.program_headers.items[self.phdr_got_index.?];
452498
453 try self.sections.append(self.allocator, .{499 fn allocatedSize(self: *Elf, start: u64) u64 {
454 .sh_name = try self.makeString(".got"),500 var min_pos: u64 = std.math.maxInt(u64);
455 .sh_type = elf.SHT_PROGBITS,501 if (self.shdr_table_offset) |off| {
456 .sh_flags = elf.SHF_ALLOC,502 if (off > start and off < min_pos) min_pos = off;
457 .sh_addr = phdr.p_vaddr,503 }
458 .sh_offset = phdr.p_offset,504 if (self.phdr_table_offset) |off| {
459 .sh_size = phdr.p_filesz,505 if (off > start and off < min_pos) min_pos = off;
460 .sh_link = 0,506 }
461 .sh_info = 0,507 for (self.sections.items) |section| {
462 .sh_addralign = phdr.p_align,508 if (section.sh_offset <= start) continue;
463 .sh_entsize = 0,509 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
464 });510 }
465 self.shdr_table_dirty = true;511 for (self.program_headers.items) |program_header| {
512 if (program_header.p_offset <= start) continue;
513 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
514 }
515 return min_pos - start;
466 }516 }
467 if (self.symtab_section_index == null) {517
468 self.symtab_section_index = @intCast(u16, self.sections.items.len);518 fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
469 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);519 var start: u64 = 0;
470 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);520 while (self.detectAllocCollision(start, object_size)) |item_end| {
471 const file_size = self.options.symbol_count_hint * each_size;521 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
472 const off = self.findFreeSpace(file_size, min_align);522 }
473 //std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });523 return start;
474
475 try self.sections.append(self.allocator, .{
476 .sh_name = try self.makeString(".symtab"),
477 .sh_type = elf.SHT_SYMTAB,
478 .sh_flags = 0,
479 .sh_addr = 0,
480 .sh_offset = off,
481 .sh_size = file_size,
482 // The section header index of the associated string table.
483 .sh_link = self.shstrtab_index.?,
484 .sh_info = @intCast(u32, self.local_symbols.items.len),
485 .sh_addralign = min_align,
486 .sh_entsize = each_size,
487 });
488 self.shdr_table_dirty = true;
489 try self.writeSymbol(0);
490 }524 }
491 const shsize: u64 = switch (self.ptr_width) {525
492 .p32 => @sizeOf(elf.Elf32_Shdr),526 fn makeString(self: *Elf, bytes: []const u8) !u32 {
493 .p64 => @sizeOf(elf.Elf64_Shdr),527 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
494 };528 const result = self.shstrtab.items.len;
495 const shalign: u16 = switch (self.ptr_width) {529 self.shstrtab.appendSliceAssumeCapacity(bytes);
496 .p32 => @alignOf(elf.Elf32_Shdr),530 self.shstrtab.appendAssumeCapacity(0);
497 .p64 => @alignOf(elf.Elf64_Shdr),531 return @intCast(u32, result);
498 };
499 if (self.shdr_table_offset == null) {
500 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
501 self.shdr_table_dirty = true;
502 }532 }
503 const phsize: u64 = switch (self.ptr_width) {533
504 .p32 => @sizeOf(elf.Elf32_Phdr),534 fn getString(self: *Elf, str_off: u32) []const u8 {
505 .p64 => @sizeOf(elf.Elf64_Phdr),535 assert(str_off < self.shstrtab.items.len);
506 };536 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
507 const phalign: u16 = switch (self.ptr_width) {
508 .p32 => @alignOf(elf.Elf32_Phdr),
509 .p64 => @alignOf(elf.Elf64_Phdr),
510 };
511 if (self.phdr_table_offset == null) {
512 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
513 self.phdr_table_dirty = true;
514 }537 }
515 {538
516 // Iterate over symbols, populating free_list and last_text_block.539 fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
517 if (self.local_symbols.items.len != 1) {540 const existing_name = self.getString(old_str_off);
518 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");541 if (mem.eql(u8, existing_name, new_name)) {
542 return old_str_off;
519 }543 }
520 // We are starting with an empty file. The default values are correct, null and empty list.544 return self.makeString(new_name);
521 }545 }
522 }
523546
524 /// Commit pending changes and write headers.547 pub fn populateMissingMetadata(self: *Elf) !void {
525 pub fn flush(self: *ElfFile) !void {548 const small_ptr = switch (self.ptr_width) {
526 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();549 .p32 => true,
527550 .p64 => false,
528 // Unfortunately these have to be buffered and done at the end because ELF does not allow551 };
529 // mixing local and global symbols within a symbol table.552 const ptr_size: u8 = switch (self.ptr_width) {
530 try self.writeAllGlobalSymbols();553 .p32 => 4,
531554 .p64 => 8,
532 if (self.phdr_table_dirty) {555 };
556 if (self.phdr_load_re_index == null) {
557 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
558 const file_size = self.options.program_code_size_hint;
559 const p_align = 0x1000;
560 const off = self.findFreeSpace(file_size, p_align);
561 //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
562 try self.program_headers.append(self.allocator, .{
563 .p_type = elf.PT_LOAD,
564 .p_offset = off,
565 .p_filesz = file_size,
566 .p_vaddr = default_entry_addr,
567 .p_paddr = default_entry_addr,
568 .p_memsz = file_size,
569 .p_align = p_align,
570 .p_flags = elf.PF_X | elf.PF_R,
571 });
572 self.entry_addr = null;
573 self.phdr_table_dirty = true;
574 }
575 if (self.phdr_got_index == null) {
576 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
577 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
578 // We really only need ptr alignment but since we are using PROGBITS, linux requires
579 // page align.
580 const p_align = 0x1000;
581 const off = self.findFreeSpace(file_size, p_align);
582 //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
583 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
584 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
585 // else in virtual memory.
586 const default_got_addr = 0x4000000;
587 try self.program_headers.append(self.allocator, .{
588 .p_type = elf.PT_LOAD,
589 .p_offset = off,
590 .p_filesz = file_size,
591 .p_vaddr = default_got_addr,
592 .p_paddr = default_got_addr,
593 .p_memsz = file_size,
594 .p_align = p_align,
595 .p_flags = elf.PF_R,
596 });
597 self.phdr_table_dirty = true;
598 }
599 if (self.shstrtab_index == null) {
600 self.shstrtab_index = @intCast(u16, self.sections.items.len);
601 assert(self.shstrtab.items.len == 0);
602 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
603 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
604 //std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
605 try self.sections.append(self.allocator, .{
606 .sh_name = try self.makeString(".shstrtab"),
607 .sh_type = elf.SHT_STRTAB,
608 .sh_flags = 0,
609 .sh_addr = 0,
610 .sh_offset = off,
611 .sh_size = self.shstrtab.items.len,
612 .sh_link = 0,
613 .sh_info = 0,
614 .sh_addralign = 1,
615 .sh_entsize = 0,
616 });
617 self.shstrtab_dirty = true;
618 self.shdr_table_dirty = true;
619 }
620 if (self.text_section_index == null) {
621 self.text_section_index = @intCast(u16, self.sections.items.len);
622 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
623
624 try self.sections.append(self.allocator, .{
625 .sh_name = try self.makeString(".text"),
626 .sh_type = elf.SHT_PROGBITS,
627 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
628 .sh_addr = phdr.p_vaddr,
629 .sh_offset = phdr.p_offset,
630 .sh_size = phdr.p_filesz,
631 .sh_link = 0,
632 .sh_info = 0,
633 .sh_addralign = phdr.p_align,
634 .sh_entsize = 0,
635 });
636 self.shdr_table_dirty = true;
637 }
638 if (self.got_section_index == null) {
639 self.got_section_index = @intCast(u16, self.sections.items.len);
640 const phdr = &self.program_headers.items[self.phdr_got_index.?];
641
642 try self.sections.append(self.allocator, .{
643 .sh_name = try self.makeString(".got"),
644 .sh_type = elf.SHT_PROGBITS,
645 .sh_flags = elf.SHF_ALLOC,
646 .sh_addr = phdr.p_vaddr,
647 .sh_offset = phdr.p_offset,
648 .sh_size = phdr.p_filesz,
649 .sh_link = 0,
650 .sh_info = 0,
651 .sh_addralign = phdr.p_align,
652 .sh_entsize = 0,
653 });
654 self.shdr_table_dirty = true;
655 }
656 if (self.symtab_section_index == null) {
657 self.symtab_section_index = @intCast(u16, self.sections.items.len);
658 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
659 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
660 const file_size = self.options.symbol_count_hint * each_size;
661 const off = self.findFreeSpace(file_size, min_align);
662 //std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
663
664 try self.sections.append(self.allocator, .{
665 .sh_name = try self.makeString(".symtab"),
666 .sh_type = elf.SHT_SYMTAB,
667 .sh_flags = 0,
668 .sh_addr = 0,
669 .sh_offset = off,
670 .sh_size = file_size,
671 // The section header index of the associated string table.
672 .sh_link = self.shstrtab_index.?,
673 .sh_info = @intCast(u32, self.local_symbols.items.len),
674 .sh_addralign = min_align,
675 .sh_entsize = each_size,
676 });
677 self.shdr_table_dirty = true;
678 try self.writeSymbol(0);
679 }
680 const shsize: u64 = switch (self.ptr_width) {
681 .p32 => @sizeOf(elf.Elf32_Shdr),
682 .p64 => @sizeOf(elf.Elf64_Shdr),
683 };
684 const shalign: u16 = switch (self.ptr_width) {
685 .p32 => @alignOf(elf.Elf32_Shdr),
686 .p64 => @alignOf(elf.Elf64_Shdr),
687 };
688 if (self.shdr_table_offset == null) {
689 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
690 self.shdr_table_dirty = true;
691 }
533 const phsize: u64 = switch (self.ptr_width) {692 const phsize: u64 = switch (self.ptr_width) {
534 .p32 => @sizeOf(elf.Elf32_Phdr),693 .p32 => @sizeOf(elf.Elf32_Phdr),
535 .p64 => @sizeOf(elf.Elf64_Phdr),694 .p64 => @sizeOf(elf.Elf64_Phdr),
...@@ -538,823 +697,854 @@ pub const ElfFile = struct {...@@ -538,823 +697,854 @@ pub const ElfFile = struct {
538 .p32 => @alignOf(elf.Elf32_Phdr),697 .p32 => @alignOf(elf.Elf32_Phdr),
539 .p64 => @alignOf(elf.Elf64_Phdr),698 .p64 => @alignOf(elf.Elf64_Phdr),
540 };699 };
541 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);700 if (self.phdr_table_offset == null) {
542 const needed_size = self.program_headers.items.len * phsize;701 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
543702 self.phdr_table_dirty = true;
544 if (needed_size > allocated_size) {
545 self.phdr_table_offset = null; // free the space
546 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
547 }703 }
548704 {
549 switch (self.ptr_width) {705 // Iterate over symbols, populating free_list and last_text_block.
550 .p32 => {706 if (self.local_symbols.items.len != 1) {
551 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);707 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
552 defer self.allocator.free(buf);708 }
553709 // We are starting with an empty file. The default values are correct, null and empty list.
554 for (buf) |*phdr, i| {
555 phdr.* = progHeaderTo32(self.program_headers.items[i]);
556 if (foreign_endian) {
557 bswapAllFields(elf.Elf32_Phdr, phdr);
558 }
559 }
560 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
561 },
562 .p64 => {
563 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
564 defer self.allocator.free(buf);
565
566 for (buf) |*phdr, i| {
567 phdr.* = self.program_headers.items[i];
568 if (foreign_endian) {
569 bswapAllFields(elf.Elf64_Phdr, phdr);
570 }
571 }
572 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
573 },
574 }710 }
575 self.phdr_table_dirty = false;
576 }711 }
577712
578 {713 /// Commit pending changes and write headers.
579 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];714 pub fn flush(self: *Elf) !void {
580 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {715 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
581 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);716
582 const needed_size = self.shstrtab.items.len;717 // Unfortunately these have to be buffered and done at the end because ELF does not allow
718 // mixing local and global symbols within a symbol table.
719 try self.writeAllGlobalSymbols();
720
721 if (self.phdr_table_dirty) {
722 const phsize: u64 = switch (self.ptr_width) {
723 .p32 => @sizeOf(elf.Elf32_Phdr),
724 .p64 => @sizeOf(elf.Elf64_Phdr),
725 };
726 const phalign: u16 = switch (self.ptr_width) {
727 .p32 => @alignOf(elf.Elf32_Phdr),
728 .p64 => @alignOf(elf.Elf64_Phdr),
729 };
730 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
731 const needed_size = self.program_headers.items.len * phsize;
583732
584 if (needed_size > allocated_size) {733 if (needed_size > allocated_size) {
585 shstrtab_sect.sh_size = 0; // free the space734 self.phdr_table_offset = null; // free the space
586 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);735 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
587 }736 }
588 shstrtab_sect.sh_size = needed_size;
589 //std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
590737
591 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);738 switch (self.ptr_width) {
592 if (!self.shdr_table_dirty) {739 .p32 => {
593 // Then it won't get written with the others and we need to do it.740 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
594 try self.writeSectHeader(self.shstrtab_index.?);741 defer self.allocator.free(buf);
595 }
596 self.shstrtab_dirty = false;
597 }
598 }
599 if (self.shdr_table_dirty) {
600 const shsize: u64 = switch (self.ptr_width) {
601 .p32 => @sizeOf(elf.Elf32_Shdr),
602 .p64 => @sizeOf(elf.Elf64_Shdr),
603 };
604 const shalign: u16 = switch (self.ptr_width) {
605 .p32 => @alignOf(elf.Elf32_Shdr),
606 .p64 => @alignOf(elf.Elf64_Shdr),
607 };
608 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
609 const needed_size = self.sections.items.len * shsize;
610742
611 if (needed_size > allocated_size) {743 for (buf) |*phdr, i| {
612 self.shdr_table_offset = null; // free the space744 phdr.* = progHeaderTo32(self.program_headers.items[i]);
613 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);745 if (foreign_endian) {
746 bswapAllFields(elf.Elf32_Phdr, phdr);
747 }
748 }
749 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
750 },
751 .p64 => {
752 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
753 defer self.allocator.free(buf);
754
755 for (buf) |*phdr, i| {
756 phdr.* = self.program_headers.items[i];
757 if (foreign_endian) {
758 bswapAllFields(elf.Elf64_Phdr, phdr);
759 }
760 }
761 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
762 },
763 }
764 self.phdr_table_dirty = false;
614 }765 }
615766
616 switch (self.ptr_width) {767 {
617 .p32 => {768 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
618 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);769 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
619 defer self.allocator.free(buf);770 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
771 const needed_size = self.shstrtab.items.len;
620772
621 for (buf) |*shdr, i| {773 if (needed_size > allocated_size) {
622 shdr.* = sectHeaderTo32(self.sections.items[i]);774 shstrtab_sect.sh_size = 0; // free the space
623 if (foreign_endian) {775 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
624 bswapAllFields(elf.Elf32_Shdr, shdr);
625 }
626 }776 }
627 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);777 shstrtab_sect.sh_size = needed_size;
628 },778 //std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
629 .p64 => {
630 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
631 defer self.allocator.free(buf);
632779
633 for (buf) |*shdr, i| {780 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
634 shdr.* = self.sections.items[i];781 if (!self.shdr_table_dirty) {
635 //std.log.debug(.link, "writing section {}\n", .{shdr.*});782 // Then it won't get written with the others and we need to do it.
636 if (foreign_endian) {783 try self.writeSectHeader(self.shstrtab_index.?);
637 bswapAllFields(elf.Elf64_Shdr, shdr);
638 }
639 }784 }
640 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);785 self.shstrtab_dirty = false;
641 },786 }
642 }787 }
643 self.shdr_table_dirty = false;788 if (self.shdr_table_dirty) {
644 }789 const shsize: u64 = switch (self.ptr_width) {
645 if (self.entry_addr == null and self.options.output_mode == .Exe) {790 .p32 => @sizeOf(elf.Elf32_Shdr),
646 self.error_flags.no_entry_point_found = true;791 .p64 => @sizeOf(elf.Elf64_Shdr),
647 } else {792 };
648 self.error_flags.no_entry_point_found = false;793 const shalign: u16 = switch (self.ptr_width) {
649 try self.writeElfHeader();794 .p32 => @alignOf(elf.Elf32_Shdr),
650 }795 .p64 => @alignOf(elf.Elf64_Shdr),
796 };
797 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
798 const needed_size = self.sections.items.len * shsize;
651799
652 // The point of flush() is to commit changes, so nothing should be dirty after this.800 if (needed_size > allocated_size) {
653 assert(!self.phdr_table_dirty);801 self.shdr_table_offset = null; // free the space
654 assert(!self.shdr_table_dirty);802 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
655 assert(!self.shstrtab_dirty);803 }
656 assert(!self.offset_table_count_dirty);
657 const syms_sect = &self.sections.items[self.symtab_section_index.?];
658 assert(syms_sect.sh_info == self.local_symbols.items.len);
659 }
660804
661 fn writeElfHeader(self: *ElfFile) !void {805 switch (self.ptr_width) {
662 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;806 .p32 => {
807 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
808 defer self.allocator.free(buf);
663809
664 var index: usize = 0;810 for (buf) |*shdr, i| {
665 hdr_buf[0..4].* = "\x7fELF".*;811 shdr.* = sectHeaderTo32(self.sections.items[i]);
666 index += 4;812 if (foreign_endian) {
813 bswapAllFields(elf.Elf32_Shdr, shdr);
814 }
815 }
816 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
817 },
818 .p64 => {
819 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
820 defer self.allocator.free(buf);
821
822 for (buf) |*shdr, i| {
823 shdr.* = self.sections.items[i];
824 //std.log.debug(.link, "writing section {}\n", .{shdr.*});
825 if (foreign_endian) {
826 bswapAllFields(elf.Elf64_Shdr, shdr);
827 }
828 }
829 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
830 },
831 }
832 self.shdr_table_dirty = false;
833 }
834 if (self.entry_addr == null and self.options.output_mode == .Exe) {
835 self.error_flags.no_entry_point_found = true;
836 } else {
837 self.error_flags.no_entry_point_found = false;
838 try self.writeElfHeader();
839 }
667840
668 hdr_buf[index] = switch (self.ptr_width) {841 // The point of flush() is to commit changes, so nothing should be dirty after this.
669 .p32 => elf.ELFCLASS32,842 assert(!self.phdr_table_dirty);
670 .p64 => elf.ELFCLASS64,843 assert(!self.shdr_table_dirty);
671 };844 assert(!self.shstrtab_dirty);
672 index += 1;845 assert(!self.offset_table_count_dirty);
846 const syms_sect = &self.sections.items[self.symtab_section_index.?];
847 assert(syms_sect.sh_info == self.local_symbols.items.len);
848 }
673849
674 const endian = self.options.target.cpu.arch.endian();850 fn writeElfHeader(self: *Elf) !void {
675 hdr_buf[index] = switch (endian) {851 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
676 .Little => elf.ELFDATA2LSB,
677 .Big => elf.ELFDATA2MSB,
678 };
679 index += 1;
680852
681 hdr_buf[index] = 1; // ELF version853 var index: usize = 0;
682 index += 1;854 hdr_buf[0..4].* = "\x7fELF".*;
855 index += 4;
683856
684 // OS ABI, often set to 0 regardless of target platform857 hdr_buf[index] = switch (self.ptr_width) {
685 // ABI Version, possibly used by glibc but not by static executables858 .p32 => elf.ELFCLASS32,
686 // padding859 .p64 => elf.ELFCLASS64,
687 mem.set(u8, hdr_buf[index..][0..9], 0);860 };
688 index += 9;861 index += 1;
689862
690 assert(index == 16);863 const endian = self.options.target.cpu.arch.endian();
864 hdr_buf[index] = switch (endian) {
865 .Little => elf.ELFDATA2LSB,
866 .Big => elf.ELFDATA2MSB,
867 };
868 index += 1;
691869
692 const elf_type = switch (self.options.output_mode) {870 hdr_buf[index] = 1; // ELF version
693 .Exe => elf.ET.EXEC,871 index += 1;
694 .Obj => elf.ET.REL,
695 .Lib => switch (self.options.link_mode) {
696 .Static => elf.ET.REL,
697 .Dynamic => elf.ET.DYN,
698 },
699 };
700 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
701 index += 2;
702872
703 const machine = self.options.target.cpu.arch.toElfMachine();873 // OS ABI, often set to 0 regardless of target platform
704 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);874 // ABI Version, possibly used by glibc but not by static executables
705 index += 2;875 // padding
876 mem.set(u8, hdr_buf[index..][0..9], 0);
877 index += 9;
706878
707 // ELF Version, again879 assert(index == 16);
708 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
709 index += 4;
710880
711 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;881 const elf_type = switch (self.options.output_mode) {
882 .Exe => elf.ET.EXEC,
883 .Obj => elf.ET.REL,
884 .Lib => switch (self.options.link_mode) {
885 .Static => elf.ET.REL,
886 .Dynamic => elf.ET.DYN,
887 },
888 };
889 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
890 index += 2;
712891
713 switch (self.ptr_width) {892 const machine = self.options.target.cpu.arch.toElfMachine();
714 .p32 => {893 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
715 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);894 index += 2;
716 index += 4;
717895
718 // e_phoff896 // ELF Version, again
719 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);897 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
720 index += 4;898 index += 4;
721899
722 // e_shoff900 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
723 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
724 index += 4;
725 },
726 .p64 => {
727 // e_entry
728 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
729 index += 8;
730
731 // e_phoff
732 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
733 index += 8;
734
735 // e_shoff
736 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
737 index += 8;
738 },
739 }
740901
741 const e_flags = 0;902 switch (self.ptr_width) {
742 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);903 .p32 => {
743 index += 4;904 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
905 index += 4;
744906
745 const e_ehsize: u16 = switch (self.ptr_width) {907 // e_phoff
746 .p32 => @sizeOf(elf.Elf32_Ehdr),908 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
747 .p64 => @sizeOf(elf.Elf64_Ehdr),909 index += 4;
748 };
749 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
750 index += 2;
751910
752 const e_phentsize: u16 = switch (self.ptr_width) {911 // e_shoff
753 .p32 => @sizeOf(elf.Elf32_Phdr),912 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
754 .p64 => @sizeOf(elf.Elf64_Phdr),913 index += 4;
755 };914 },
756 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);915 .p64 => {
757 index += 2;916 // e_entry
917 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
918 index += 8;
758919
759 const e_phnum = @intCast(u16, self.program_headers.items.len);920 // e_phoff
760 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);921 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
761 index += 2;922 index += 8;
762923
763 const e_shentsize: u16 = switch (self.ptr_width) {924 // e_shoff
764 .p32 => @sizeOf(elf.Elf32_Shdr),925 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
765 .p64 => @sizeOf(elf.Elf64_Shdr),926 index += 8;
766 };927 },
767 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);928 }
768 index += 2;
769929
770 const e_shnum = @intCast(u16, self.sections.items.len);930 const e_flags = 0;
771 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);931 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
772 index += 2;932 index += 4;
773933
774 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);934 const e_ehsize: u16 = switch (self.ptr_width) {
775 index += 2;935 .p32 => @sizeOf(elf.Elf32_Ehdr),
936 .p64 => @sizeOf(elf.Elf64_Ehdr),
937 };
938 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
939 index += 2;
776940
777 assert(index == e_ehsize);941 const e_phentsize: u16 = switch (self.ptr_width) {
942 .p32 => @sizeOf(elf.Elf32_Phdr),
943 .p64 => @sizeOf(elf.Elf64_Phdr),
944 };
945 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
946 index += 2;
778947
779 try self.file.?.pwriteAll(hdr_buf[0..index], 0);948 const e_phnum = @intCast(u16, self.program_headers.items.len);
780 }949 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
950 index += 2;
781951
782 fn freeTextBlock(self: *ElfFile, text_block: *TextBlock) void {952 const e_shentsize: u16 = switch (self.ptr_width) {
783 var already_have_free_list_node = false;953 .p32 => @sizeOf(elf.Elf32_Shdr),
784 {954 .p64 => @sizeOf(elf.Elf64_Shdr),
785 var i: usize = 0;955 };
786 while (i < self.text_block_free_list.items.len) {956 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
787 if (self.text_block_free_list.items[i] == text_block) {957 index += 2;
788 _ = self.text_block_free_list.swapRemove(i);
789 continue;
790 }
791 if (self.text_block_free_list.items[i] == text_block.prev) {
792 already_have_free_list_node = true;
793 }
794 i += 1;
795 }
796 }
797958
798 if (self.last_text_block == text_block) {959 const e_shnum = @intCast(u16, self.sections.items.len);
799 // TODO shrink the .text section size here960 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
800 self.last_text_block = text_block.prev;961 index += 2;
801 }
802962
803 if (text_block.prev) |prev| {963 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
804 prev.next = text_block.next;964 index += 2;
805965
806 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {966 assert(index == e_ehsize);
807 // The free list is heuristics, it doesn't have to be perfect, so we can
808 // ignore the OOM here.
809 self.text_block_free_list.append(self.allocator, prev) catch {};
810 }
811 } else {
812 text_block.prev = null;
813 }
814967
815 if (text_block.next) |next| {968 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
816 next.prev = text_block.prev;
817 } else {
818 text_block.next = null;
819 }969 }
820 }
821
822 fn shrinkTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64) void {
823 // TODO check the new capacity, and if it crosses the size threshold into a big enough
824 // capacity, insert a free list node for it.
825 }
826
827 fn growTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
828 const sym = self.local_symbols.items[text_block.local_sym_index];
829 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
830 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
831 if (!need_realloc) return sym.st_value;
832 return self.allocateTextBlock(text_block, new_block_size, alignment);
833 }
834970
835 fn allocateTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {971 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
836 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];972 var already_have_free_list_node = false;
837 const shdr = &self.sections.items[self.text_section_index.?];973 {
838 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;974 var i: usize = 0;
839975 while (i < self.text_block_free_list.items.len) {
840 // We use these to indicate our intention to update metadata, placing the new block,976 if (self.text_block_free_list.items[i] == text_block) {
841 // and possibly removing a free list node.
842 // It would be simpler to do it inside the for loop below, but that would cause a
843 // problem if an error was returned later in the function. So this action
844 // is actually carried out at the end of the function, when errors are no longer possible.
845 var block_placement: ?*TextBlock = null;
846 var free_list_removal: ?usize = null;
847
848 // First we look for an appropriately sized free list node.
849 // The list is unordered. We'll just take the first thing that works.
850 const vaddr = blk: {
851 var i: usize = 0;
852 while (i < self.text_block_free_list.items.len) {
853 const big_block = self.text_block_free_list.items[i];
854 // We now have a pointer to a live text block that has too much capacity.
855 // Is it enough that we could fit this new text block?
856 const sym = self.local_symbols.items[big_block.local_sym_index];
857 const capacity = big_block.capacity(self.*);
858 const ideal_capacity = capacity * alloc_num / alloc_den;
859 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
860 const capacity_end_vaddr = sym.st_value + capacity;
861 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
862 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
863 if (new_start_vaddr < ideal_capacity_end_vaddr) {
864 // Additional bookkeeping here to notice if this free list node
865 // should be deleted because the block that it points to has grown to take up
866 // more of the extra capacity.
867 if (!big_block.freeListEligible(self.*)) {
868 _ = self.text_block_free_list.swapRemove(i);977 _ = self.text_block_free_list.swapRemove(i);
869 } else {978 continue;
870 i += 1;
871 }979 }
872 continue;980 if (self.text_block_free_list.items[i] == text_block.prev) {
873 }981 already_have_free_list_node = true;
874 // At this point we know that we will place the new block here. But the982 }
875 // remaining question is whether there is still yet enough capacity left983 i += 1;
876 // over for there to still be a free list node.
877 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
878 const keep_free_list_node = remaining_capacity >= min_text_capacity;
879
880 // Set up the metadata to be updated, after errors are no longer possible.
881 block_placement = big_block;
882 if (!keep_free_list_node) {
883 free_list_removal = i;
884 }984 }
885 break :blk new_start_vaddr;
886 } else if (self.last_text_block) |last| {
887 const sym = self.local_symbols.items[last.local_sym_index];
888 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
889 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
890 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
891 // Set up the metadata to be updated, after errors are no longer possible.
892 block_placement = last;
893 break :blk new_start_vaddr;
894 } else {
895 break :blk phdr.p_vaddr;
896 }985 }
897 };
898986
899 const expand_text_section = block_placement == null or block_placement.?.next == null;987 if (self.last_text_block == text_block) {
900 if (expand_text_section) {988 // TODO shrink the .text section size here
901 const text_capacity = self.allocatedSize(shdr.sh_offset);989 self.last_text_block = text_block.prev;
902 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
903 if (needed_size > text_capacity) {
904 // Must move the entire text section.
905 const new_offset = self.findFreeSpace(needed_size, 0x1000);
906 const text_size = if (self.last_text_block) |last| blk: {
907 const sym = self.local_symbols.items[last.local_sym_index];
908 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
909 } else 0;
910 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
911 if (amt != text_size) return error.InputOutput;
912 shdr.sh_offset = new_offset;
913 phdr.p_offset = new_offset;
914 }990 }
915 self.last_text_block = text_block;
916991
917 shdr.sh_size = needed_size;992 if (text_block.prev) |prev| {
918 phdr.p_memsz = needed_size;993 prev.next = text_block.next;
919 phdr.p_filesz = needed_size;
920994
921 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty995 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
922 self.shdr_table_dirty = true; // TODO look into making only the one section dirty996 // The free list is heuristics, it doesn't have to be perfect, so we can
923 }997 // ignore the OOM here.
998 self.text_block_free_list.append(self.allocator, prev) catch {};
999 }
1000 } else {
1001 text_block.prev = null;
1002 }
9241003
925 // This function can also reallocate a text block.1004 if (text_block.next) |next| {
926 // In this case we need to "unplug" it from its previous location before1005 next.prev = text_block.prev;
927 // plugging it in to its new location.1006 } else {
928 if (text_block.prev) |prev| {1007 text_block.next = null;
929 prev.next = text_block.next;1008 }
930 }
931 if (text_block.next) |next| {
932 next.prev = text_block.prev;
933 }1009 }
9341010
935 if (block_placement) |big_block| {1011 fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
936 text_block.prev = big_block;1012 // TODO check the new capacity, and if it crosses the size threshold into a big enough
937 text_block.next = big_block.next;1013 // capacity, insert a free list node for it.
938 big_block.next = text_block;
939 } else {
940 text_block.prev = null;
941 text_block.next = null;
942 }1014 }
943 if (free_list_removal) |i| {
944 _ = self.text_block_free_list.swapRemove(i);
945 }
946 return vaddr;
947 }
9481015
949 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {1016 fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
950 if (decl.link.local_sym_index != 0) return;1017 const sym = self.local_symbols.items[text_block.local_sym_index];
9511018 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
952 // Here we also ensure capacity for the free lists so that they can be appended to without fail.1019 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
953 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);1020 if (!need_realloc) return sym.st_value;
954 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);1021 return self.allocateTextBlock(text_block, new_block_size, alignment);
955 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
956 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
957
958 if (self.local_symbol_free_list.popOrNull()) |i| {
959 //std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
960 decl.link.local_sym_index = i;
961 } else {
962 //std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
963 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
964 _ = self.local_symbols.addOneAssumeCapacity();
965 }1022 }
9661023
967 if (self.offset_table_free_list.popOrNull()) |i| {1024 fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
968 decl.link.offset_table_index = i;1025 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
969 } else {1026 const shdr = &self.sections.items[self.text_section_index.?];
970 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);1027 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
971 _ = self.offset_table.addOneAssumeCapacity();1028
972 self.offset_table_count_dirty = true;1029 // We use these to indicate our intention to update metadata, placing the new block,
973 }1030 // and possibly removing a free list node.
1031 // It would be simpler to do it inside the for loop below, but that would cause a
1032 // problem if an error was returned later in the function. So this action
1033 // is actually carried out at the end of the function, when errors are no longer possible.
1034 var block_placement: ?*TextBlock = null;
1035 var free_list_removal: ?usize = null;
1036
1037 // First we look for an appropriately sized free list node.
1038 // The list is unordered. We'll just take the first thing that works.
1039 const vaddr = blk: {
1040 var i: usize = 0;
1041 while (i < self.text_block_free_list.items.len) {
1042 const big_block = self.text_block_free_list.items[i];
1043 // We now have a pointer to a live text block that has too much capacity.
1044 // Is it enough that we could fit this new text block?
1045 const sym = self.local_symbols.items[big_block.local_sym_index];
1046 const capacity = big_block.capacity(self.*);
1047 const ideal_capacity = capacity * alloc_num / alloc_den;
1048 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1049 const capacity_end_vaddr = sym.st_value + capacity;
1050 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
1051 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
1052 if (new_start_vaddr < ideal_capacity_end_vaddr) {
1053 // Additional bookkeeping here to notice if this free list node
1054 // should be deleted because the block that it points to has grown to take up
1055 // more of the extra capacity.
1056 if (!big_block.freeListEligible(self.*)) {
1057 _ = self.text_block_free_list.swapRemove(i);
1058 } else {
1059 i += 1;
1060 }
1061 continue;
1062 }
1063 // At this point we know that we will place the new block here. But the
1064 // remaining question is whether there is still yet enough capacity left
1065 // over for there to still be a free list node.
1066 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
1067 const keep_free_list_node = remaining_capacity >= min_text_capacity;
1068
1069 // Set up the metadata to be updated, after errors are no longer possible.
1070 block_placement = big_block;
1071 if (!keep_free_list_node) {
1072 free_list_removal = i;
1073 }
1074 break :blk new_start_vaddr;
1075 } else if (self.last_text_block) |last| {
1076 const sym = self.local_symbols.items[last.local_sym_index];
1077 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
1078 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1079 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
1080 // Set up the metadata to be updated, after errors are no longer possible.
1081 block_placement = last;
1082 break :blk new_start_vaddr;
1083 } else {
1084 break :blk phdr.p_vaddr;
1085 }
1086 };
9741087
975 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];1088 const expand_text_section = block_placement == null or block_placement.?.next == null;
1089 if (expand_text_section) {
1090 const text_capacity = self.allocatedSize(shdr.sh_offset);
1091 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
1092 if (needed_size > text_capacity) {
1093 // Must move the entire text section.
1094 const new_offset = self.findFreeSpace(needed_size, 0x1000);
1095 const text_size = if (self.last_text_block) |last| blk: {
1096 const sym = self.local_symbols.items[last.local_sym_index];
1097 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
1098 } else 0;
1099 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
1100 if (amt != text_size) return error.InputOutput;
1101 shdr.sh_offset = new_offset;
1102 phdr.p_offset = new_offset;
1103 }
1104 self.last_text_block = text_block;
9761105
977 self.local_symbols.items[decl.link.local_sym_index] = .{1106 shdr.sh_size = needed_size;
978 .st_name = 0,1107 phdr.p_memsz = needed_size;
979 .st_info = 0,1108 phdr.p_filesz = needed_size;
980 .st_other = 0,
981 .st_shndx = 0,
982 .st_value = phdr.p_vaddr,
983 .st_size = 0,
984 };
985 self.offset_table.items[decl.link.offset_table_index] = 0;
986 }
9871109
988 pub fn freeDecl(self: *ElfFile, decl: *Module.Decl) void {1110 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
989 self.freeTextBlock(&decl.link);1111 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
990 if (decl.link.local_sym_index != 0) {1112 }
991 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
992 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
9931113
994 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;1114 // This function can also reallocate a text block.
1115 // In this case we need to "unplug" it from its previous location before
1116 // plugging it in to its new location.
1117 if (text_block.prev) |prev| {
1118 prev.next = text_block.next;
1119 }
1120 if (text_block.next) |next| {
1121 next.prev = text_block.prev;
1122 }
9951123
996 decl.link.local_sym_index = 0;1124 if (block_placement) |big_block| {
1125 text_block.prev = big_block;
1126 text_block.next = big_block.next;
1127 big_block.next = text_block;
1128 } else {
1129 text_block.prev = null;
1130 text_block.next = null;
1131 }
1132 if (free_list_removal) |i| {
1133 _ = self.text_block_free_list.swapRemove(i);
1134 }
1135 return vaddr;
997 }1136 }
998 }
9991137
1000 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {1138 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1001 var code_buffer = std.ArrayList(u8).init(self.allocator);1139 if (decl.link.local_sym_index != 0) return;
1002 defer code_buffer.deinit();
1003
1004 const typed_value = decl.typed_value.most_recent.typed_value;
1005 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1006 .externally_managed => |x| x,
1007 .appended => code_buffer.items,
1008 .fail => |em| {
1009 decl.analysis = .codegen_failure;
1010 _ = try module.failed_decls.put(decl, em);
1011 return;
1012 },
1013 };
10141140
1015 const required_alignment = typed_value.ty.abiAlignment(self.options.target);1141 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
1142 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
1143 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
1144 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
1145 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
10161146
1017 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {1147 if (self.local_symbol_free_list.popOrNull()) |i| {
1018 .Fn => elf.STT_FUNC,1148 //std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
1019 else => elf.STT_OBJECT,1149 decl.link.local_sym_index = i;
1020 };1150 } else {
1151 //std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
1152 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1153 _ = self.local_symbols.addOneAssumeCapacity();
1154 }
10211155
1022 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()1156 if (self.offset_table_free_list.popOrNull()) |i| {
1023 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];1157 decl.link.offset_table_index = i;
1024 if (local_sym.st_size != 0) {1158 } else {
1025 const capacity = decl.link.capacity(self.*);1159 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
1026 const need_realloc = code.len > capacity or1160 _ = self.offset_table.addOneAssumeCapacity();
1027 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);1161 self.offset_table_count_dirty = true;
1028 if (need_realloc) {
1029 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1030 //std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1031 if (vaddr != local_sym.st_value) {
1032 local_sym.st_value = vaddr;
1033
1034 //std.log.debug(.link, " (writing new offset table entry)\n", .{});
1035 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1036 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1037 }
1038 } else if (code.len < local_sym.st_size) {
1039 self.shrinkTextBlock(&decl.link, code.len);
1040 }1162 }
1041 local_sym.st_size = code.len;1163
1042 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));1164 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1043 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;1165
1044 local_sym.st_other = 0;1166 self.local_symbols.items[decl.link.local_sym_index] = .{
1045 local_sym.st_shndx = self.text_section_index.?;1167 .st_name = 0,
1046 // TODO this write could be avoided if no fields of the symbol were changed.1168 .st_info = 0,
1047 try self.writeSymbol(decl.link.local_sym_index);
1048 } else {
1049 const decl_name = mem.spanZ(decl.name);
1050 const name_str_index = try self.makeString(decl_name);
1051 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1052 //std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1053 errdefer self.freeTextBlock(&decl.link);
1054
1055 local_sym.* = .{
1056 .st_name = name_str_index,
1057 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1058 .st_other = 0,1169 .st_other = 0,
1059 .st_shndx = self.text_section_index.?,1170 .st_shndx = 0,
1060 .st_value = vaddr,1171 .st_value = phdr.p_vaddr,
1061 .st_size = code.len,1172 .st_size = 0,
1062 };1173 };
1063 self.offset_table.items[decl.link.offset_table_index] = vaddr;1174 self.offset_table.items[decl.link.offset_table_index] = 0;
1064
1065 try self.writeSymbol(decl.link.local_sym_index);
1066 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1067 }1175 }
10681176
1069 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;1177 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1070 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;1178 self.freeTextBlock(&decl.link);
1071 try self.file.?.pwriteAll(code, file_offset);1179 if (decl.link.local_sym_index != 0) {
1180 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
1181 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
10721182
1073 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.1183 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
1074 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1075 return self.updateDeclExports(module, decl, decl_exports);
1076 }
10771184
1078 /// Must be called only after a successful call to `updateDecl`.1185 decl.link.local_sym_index = 0;
1079 pub fn updateDeclExports(
1080 self: *ElfFile,
1081 module: *Module,
1082 decl: *const Module.Decl,
1083 exports: []const *Module.Export,
1084 ) !void {
1085 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1086 // them, so that deleting exports is guaranteed to succeed.
1087 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1088 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1089 const typed_value = decl.typed_value.most_recent.typed_value;
1090 if (decl.link.local_sym_index == 0) return;
1091 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1092
1093 for (exports) |exp| {
1094 if (exp.options.section) |section_name| {
1095 if (!mem.eql(u8, section_name, ".text")) {
1096 try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1);
1097 module.failed_exports.putAssumeCapacityNoClobber(
1098 exp,
1099 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1100 );
1101 continue;
1102 }
1103 }1186 }
1104 const stb_bits: u8 = switch (exp.options.linkage) {1187 }
1105 .Internal => elf.STB_LOCAL,1188
1106 .Strong => blk: {1189 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1107 if (mem.eql(u8, exp.options.name, "_start")) {1190 var code_buffer = std.ArrayList(u8).init(self.allocator);
1108 self.entry_addr = decl_sym.st_value;1191 defer code_buffer.deinit();
1109 }1192
1110 break :blk elf.STB_GLOBAL;1193 const typed_value = decl.typed_value.most_recent.typed_value;
1111 },1194 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1112 .Weak => elf.STB_WEAK,1195 .externally_managed => |x| x,
1113 .LinkOnce => {1196 .appended => code_buffer.items,
1114 try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1);1197 .fail => |em| {
1115 module.failed_exports.putAssumeCapacityNoClobber(1198 decl.analysis = .codegen_failure;
1116 exp,1199 try module.failed_decls.put(decl, em);
1117 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),1200 return;
1118 );
1119 continue;
1120 },1201 },
1121 };1202 };
1122 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);1203
1123 if (exp.link.sym_index) |i| {1204 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1124 const sym = &self.global_symbols.items[i];1205
1125 sym.* = .{1206 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1126 .st_name = try self.updateString(sym.st_name, exp.options.name),1207 .Fn => elf.STT_FUNC,
1127 .st_info = (stb_bits << 4) | stt_bits,1208 else => elf.STT_OBJECT,
1128 .st_other = 0,1209 };
1129 .st_shndx = self.text_section_index.?,1210
1130 .st_value = decl_sym.st_value,1211 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1131 .st_size = decl_sym.st_size,1212 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1132 };1213 if (local_sym.st_size != 0) {
1214 const capacity = decl.link.capacity(self.*);
1215 const need_realloc = code.len > capacity or
1216 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1217 if (need_realloc) {
1218 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1219 //std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1220 if (vaddr != local_sym.st_value) {
1221 local_sym.st_value = vaddr;
1222
1223 //std.log.debug(.link, " (writing new offset table entry)\n", .{});
1224 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1225 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1226 }
1227 } else if (code.len < local_sym.st_size) {
1228 self.shrinkTextBlock(&decl.link, code.len);
1229 }
1230 local_sym.st_size = code.len;
1231 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1232 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1233 local_sym.st_other = 0;
1234 local_sym.st_shndx = self.text_section_index.?;
1235 // TODO this write could be avoided if no fields of the symbol were changed.
1236 try self.writeSymbol(decl.link.local_sym_index);
1133 } else {1237 } else {
1134 const name = try self.makeString(exp.options.name);1238 const decl_name = mem.spanZ(decl.name);
1135 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {1239 const name_str_index = try self.makeString(decl_name);
1136 _ = self.global_symbols.addOneAssumeCapacity();1240 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1137 break :blk self.global_symbols.items.len - 1;1241 //std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1138 };1242 errdefer self.freeTextBlock(&decl.link);
1139 self.global_symbols.items[i] = .{1243
1140 .st_name = name,1244 local_sym.* = .{
1141 .st_info = (stb_bits << 4) | stt_bits,1245 .st_name = name_str_index,
1246 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1142 .st_other = 0,1247 .st_other = 0,
1143 .st_shndx = self.text_section_index.?,1248 .st_shndx = self.text_section_index.?,
1144 .st_value = decl_sym.st_value,1249 .st_value = vaddr,
1145 .st_size = decl_sym.st_size,1250 .st_size = code.len,
1146 };1251 };
1252 self.offset_table.items[decl.link.offset_table_index] = vaddr;
11471253
1148 exp.link.sym_index = @intCast(u32, i);1254 try self.writeSymbol(decl.link.local_sym_index);
1255 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1149 }1256 }
1150 }
1151 }
11521257
1153 pub fn deleteExport(self: *ElfFile, exp: Export) void {1258 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1154 const sym_index = exp.sym_index orelse return;1259 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1155 self.global_symbol_free_list.appendAssumeCapacity(sym_index);1260 try self.file.?.pwriteAll(code, file_offset);
1156 self.global_symbols.items[sym_index].st_info = 0;
1157 }
11581261
1159 fn writeProgHeader(self: *ElfFile, index: usize) !void {1262 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1160 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1263 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1161 const offset = self.program_headers.items[index].p_offset;1264 return self.updateDeclExports(module, decl, decl_exports);
1162 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1163 32 => {
1164 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1165 if (foreign_endian) {
1166 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1167 }
1168 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1169 },
1170 64 => {
1171 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1172 if (foreign_endian) {
1173 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1174 }
1175 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1176 },
1177 else => return error.UnsupportedArchitecture,
1178 }1265 }
1179 }
11801266
1181 fn writeSectHeader(self: *ElfFile, index: usize) !void {1267 /// Must be called only after a successful call to `updateDecl`.
1182 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1268 pub fn updateDeclExports(
1183 const offset = self.sections.items[index].sh_offset;1269 self: *Elf,
1184 switch (self.options.target.cpu.arch.ptrBitWidth()) {1270 module: *Module,
1185 32 => {1271 decl: *const Module.Decl,
1186 var shdr: [1]elf.Elf32_Shdr = undefined;1272 exports: []const *Module.Export,
1187 shdr[0] = sectHeaderTo32(self.sections.items[index]);1273 ) !void {
1188 if (foreign_endian) {1274 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1189 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);1275 // them, so that deleting exports is guaranteed to succeed.
1190 }1276 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1191 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);1277 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1192 },1278 const typed_value = decl.typed_value.most_recent.typed_value;
1193 64 => {1279 if (decl.link.local_sym_index == 0) return;
1194 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};1280 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1195 if (foreign_endian) {1281
1196 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);1282 for (exports) |exp| {
1283 if (exp.options.section) |section_name| {
1284 if (!mem.eql(u8, section_name, ".text")) {
1285 try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1);
1286 module.failed_exports.putAssumeCapacityNoClobber(
1287 exp,
1288 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1289 );
1290 continue;
1291 }
1197 }1292 }
1198 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);1293 const stb_bits: u8 = switch (exp.options.linkage) {
1199 },1294 .Internal => elf.STB_LOCAL,
1200 else => return error.UnsupportedArchitecture,1295 .Strong => blk: {
1201 }1296 if (mem.eql(u8, exp.options.name, "_start")) {
1202 }1297 self.entry_addr = decl_sym.st_value;
1298 }
1299 break :blk elf.STB_GLOBAL;
1300 },
1301 .Weak => elf.STB_WEAK,
1302 .LinkOnce => {
1303 try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1);
1304 module.failed_exports.putAssumeCapacityNoClobber(
1305 exp,
1306 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
1307 );
1308 continue;
1309 },
1310 };
1311 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
1312 if (exp.link.sym_index) |i| {
1313 const sym = &self.global_symbols.items[i];
1314 sym.* = .{
1315 .st_name = try self.updateString(sym.st_name, exp.options.name),
1316 .st_info = (stb_bits << 4) | stt_bits,
1317 .st_other = 0,
1318 .st_shndx = self.text_section_index.?,
1319 .st_value = decl_sym.st_value,
1320 .st_size = decl_sym.st_size,
1321 };
1322 } else {
1323 const name = try self.makeString(exp.options.name);
1324 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1325 _ = self.global_symbols.addOneAssumeCapacity();
1326 break :blk self.global_symbols.items.len - 1;
1327 };
1328 self.global_symbols.items[i] = .{
1329 .st_name = name,
1330 .st_info = (stb_bits << 4) | stt_bits,
1331 .st_other = 0,
1332 .st_shndx = self.text_section_index.?,
1333 .st_value = decl_sym.st_value,
1334 .st_size = decl_sym.st_size,
1335 };
12031336
1204 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {1337 exp.link.sym_index = @intCast(u32, i);
1205 const shdr = &self.sections.items[self.got_section_index.?];1338 }
1206 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1207 const entry_size: u16 = switch (self.ptr_width) {
1208 .p32 => 4,
1209 .p64 => 8,
1210 };
1211 if (self.offset_table_count_dirty) {
1212 // TODO Also detect virtual address collisions.
1213 const allocated_size = self.allocatedSize(shdr.sh_offset);
1214 const needed_size = self.local_symbols.items.len * entry_size;
1215 if (needed_size > allocated_size) {
1216 // Must move the entire got section.
1217 const new_offset = self.findFreeSpace(needed_size, entry_size);
1218 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1219 if (amt != shdr.sh_size) return error.InputOutput;
1220 shdr.sh_offset = new_offset;
1221 phdr.p_offset = new_offset;
1222 }1339 }
1223 shdr.sh_size = needed_size;1340 }
1224 phdr.p_memsz = needed_size;
1225 phdr.p_filesz = needed_size;
12261341
1227 self.shdr_table_dirty = true; // TODO look into making only the one section dirty1342 pub fn deleteExport(self: *Elf, exp: Export) void {
1228 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty1343 const sym_index = exp.sym_index orelse return;
1344 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1345 self.global_symbols.items[sym_index].st_info = 0;
1346 }
12291347
1230 self.offset_table_count_dirty = false;1348 fn writeProgHeader(self: *Elf, index: usize) !void {
1349 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1350 const offset = self.program_headers.items[index].p_offset;
1351 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1352 32 => {
1353 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1354 if (foreign_endian) {
1355 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1356 }
1357 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1358 },
1359 64 => {
1360 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1361 if (foreign_endian) {
1362 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1363 }
1364 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1365 },
1366 else => return error.UnsupportedArchitecture,
1367 }
1231 }1368 }
1232 const endian = self.options.target.cpu.arch.endian();1369
1233 const off = shdr.sh_offset + @as(u64, entry_size) * index;1370 fn writeSectHeader(self: *Elf, index: usize) !void {
1234 switch (self.ptr_width) {1371 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1235 .p32 => {1372 const offset = self.sections.items[index].sh_offset;
1236 var buf: [4]u8 = undefined;1373 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1237 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);1374 32 => {
1238 try self.file.?.pwriteAll(&buf, off);1375 var shdr: [1]elf.Elf32_Shdr = undefined;
1239 },1376 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1240 .p64 => {1377 if (foreign_endian) {
1241 var buf: [8]u8 = undefined;1378 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1242 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);1379 }
1243 try self.file.?.pwriteAll(&buf, off);1380 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1244 },1381 },
1382 64 => {
1383 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
1384 if (foreign_endian) {
1385 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1386 }
1387 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1388 },
1389 else => return error.UnsupportedArchitecture,
1390 }
1245 }1391 }
1246 }
12471392
1248 fn writeSymbol(self: *ElfFile, index: usize) !void {1393 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
1249 const syms_sect = &self.sections.items[self.symtab_section_index.?];1394 const shdr = &self.sections.items[self.got_section_index.?];
1250 // Make sure we are not pointlessly writing symbol data that will have to get relocated1395 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1251 // due to running out of space.1396 const entry_size: u16 = switch (self.ptr_width) {
1252 if (self.local_symbols.items.len != syms_sect.sh_info) {1397 .p32 => 4,
1253 const sym_size: u64 = switch (self.ptr_width) {1398 .p64 => 8,
1254 .p32 => @sizeOf(elf.Elf32_Sym),
1255 .p64 => @sizeOf(elf.Elf64_Sym),
1256 };
1257 const sym_align: u16 = switch (self.ptr_width) {
1258 .p32 => @alignOf(elf.Elf32_Sym),
1259 .p64 => @alignOf(elf.Elf64_Sym),
1260 };1399 };
1261 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;1400 if (self.offset_table_count_dirty) {
1262 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {1401 // TODO Also detect virtual address collisions.
1263 // Move all the symbols to a new file location.1402 const allocated_size = self.allocatedSize(shdr.sh_offset);
1264 const new_offset = self.findFreeSpace(needed_size, sym_align);1403 const needed_size = self.local_symbols.items.len * entry_size;
1265 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;1404 if (needed_size > allocated_size) {
1266 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);1405 // Must move the entire got section.
1267 if (amt != existing_size) return error.InputOutput;1406 const new_offset = self.findFreeSpace(needed_size, entry_size);
1268 syms_sect.sh_offset = new_offset;1407 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1408 if (amt != shdr.sh_size) return error.InputOutput;
1409 shdr.sh_offset = new_offset;
1410 phdr.p_offset = new_offset;
1411 }
1412 shdr.sh_size = needed_size;
1413 phdr.p_memsz = needed_size;
1414 phdr.p_filesz = needed_size;
1415
1416 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1417 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1418
1419 self.offset_table_count_dirty = false;
1420 }
1421 const endian = self.options.target.cpu.arch.endian();
1422 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1423 switch (self.ptr_width) {
1424 .p32 => {
1425 var buf: [4]u8 = undefined;
1426 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1427 try self.file.?.pwriteAll(&buf, off);
1428 },
1429 .p64 => {
1430 var buf: [8]u8 = undefined;
1431 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1432 try self.file.?.pwriteAll(&buf, off);
1433 },
1269 }1434 }
1270 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1271 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1272 self.shdr_table_dirty = true; // TODO look into only writing one section
1273 }1435 }
1274 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1436
1275 switch (self.ptr_width) {1437 fn writeSymbol(self: *Elf, index: usize) !void {
1276 .p32 => {1438 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1277 var sym = [1]elf.Elf32_Sym{1439 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1278 .{1440 // due to running out of space.
1279 .st_name = self.local_symbols.items[index].st_name,1441 if (self.local_symbols.items.len != syms_sect.sh_info) {
1280 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),1442 const sym_size: u64 = switch (self.ptr_width) {
1281 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),1443 .p32 => @sizeOf(elf.Elf32_Sym),
1282 .st_info = self.local_symbols.items[index].st_info,1444 .p64 => @sizeOf(elf.Elf64_Sym),
1283 .st_other = self.local_symbols.items[index].st_other,
1284 .st_shndx = self.local_symbols.items[index].st_shndx,
1285 },
1286 };1445 };
1287 if (foreign_endian) {1446 const sym_align: u16 = switch (self.ptr_width) {
1288 bswapAllFields(elf.Elf32_Sym, &sym[0]);1447 .p32 => @alignOf(elf.Elf32_Sym),
1289 }1448 .p64 => @alignOf(elf.Elf64_Sym),
1290 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;1449 };
1291 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1450 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1292 },1451 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1293 .p64 => {1452 // Move all the symbols to a new file location.
1294 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};1453 const new_offset = self.findFreeSpace(needed_size, sym_align);
1295 if (foreign_endian) {1454 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1296 bswapAllFields(elf.Elf64_Sym, &sym[0]);1455 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1456 if (amt != existing_size) return error.InputOutput;
1457 syms_sect.sh_offset = new_offset;
1297 }1458 }
1298 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;1459 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1299 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1460 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1300 },1461 self.shdr_table_dirty = true; // TODO look into only writing one section
1301 }1462 }
1302 }1463 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
13031464 switch (self.ptr_width) {
1304 fn writeAllGlobalSymbols(self: *ElfFile) !void {1465 .p32 => {
1305 const syms_sect = &self.sections.items[self.symtab_section_index.?];1466 var sym = [1]elf.Elf32_Sym{
1306 const sym_size: u64 = switch (self.ptr_width) {1467 .{
1307 .p32 => @sizeOf(elf.Elf32_Sym),1468 .st_name = self.local_symbols.items[index].st_name,
1308 .p64 => @sizeOf(elf.Elf64_Sym),1469 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1309 };1470 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1310 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1471 .st_info = self.local_symbols.items[index].st_info,
1311 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;1472 .st_other = self.local_symbols.items[index].st_other,
1312 switch (self.ptr_width) {1473 .st_shndx = self.local_symbols.items[index].st_shndx,
1313 .p32 => {1474 },
1314 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1315 defer self.allocator.free(buf);
1316
1317 for (buf) |*sym, i| {
1318 sym.* = .{
1319 .st_name = self.global_symbols.items[i].st_name,
1320 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1321 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1322 .st_info = self.global_symbols.items[i].st_info,
1323 .st_other = self.global_symbols.items[i].st_other,
1324 .st_shndx = self.global_symbols.items[i].st_shndx,
1325 };1475 };
1326 if (foreign_endian) {1476 if (foreign_endian) {
1327 bswapAllFields(elf.Elf32_Sym, sym);1477 bswapAllFields(elf.Elf32_Sym, &sym[0]);
1328 }1478 }
1329 }1479 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1330 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);1480 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1331 },1481 },
1332 .p64 => {1482 .p64 => {
1333 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);1483 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
1334 defer self.allocator.free(buf);
1335
1336 for (buf) |*sym, i| {
1337 sym.* = .{
1338 .st_name = self.global_symbols.items[i].st_name,
1339 .st_value = self.global_symbols.items[i].st_value,
1340 .st_size = self.global_symbols.items[i].st_size,
1341 .st_info = self.global_symbols.items[i].st_info,
1342 .st_other = self.global_symbols.items[i].st_other,
1343 .st_shndx = self.global_symbols.items[i].st_shndx,
1344 };
1345 if (foreign_endian) {1484 if (foreign_endian) {
1346 bswapAllFields(elf.Elf64_Sym, sym);1485 bswapAllFields(elf.Elf64_Sym, &sym[0]);
1347 }1486 }
1348 }1487 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1349 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);1488 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1350 },1489 },
1490 }
1351 }1491 }
1352 }1492
1493 fn writeAllGlobalSymbols(self: *Elf) !void {
1494 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1495 const sym_size: u64 = switch (self.ptr_width) {
1496 .p32 => @sizeOf(elf.Elf32_Sym),
1497 .p64 => @sizeOf(elf.Elf64_Sym),
1498 };
1499 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1500 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1501 switch (self.ptr_width) {
1502 .p32 => {
1503 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1504 defer self.allocator.free(buf);
1505
1506 for (buf) |*sym, i| {
1507 sym.* = .{
1508 .st_name = self.global_symbols.items[i].st_name,
1509 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1510 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1511 .st_info = self.global_symbols.items[i].st_info,
1512 .st_other = self.global_symbols.items[i].st_other,
1513 .st_shndx = self.global_symbols.items[i].st_shndx,
1514 };
1515 if (foreign_endian) {
1516 bswapAllFields(elf.Elf32_Sym, sym);
1517 }
1518 }
1519 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1520 },
1521 .p64 => {
1522 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1523 defer self.allocator.free(buf);
1524
1525 for (buf) |*sym, i| {
1526 sym.* = .{
1527 .st_name = self.global_symbols.items[i].st_name,
1528 .st_value = self.global_symbols.items[i].st_value,
1529 .st_size = self.global_symbols.items[i].st_size,
1530 .st_info = self.global_symbols.items[i].st_info,
1531 .st_other = self.global_symbols.items[i].st_other,
1532 .st_shndx = self.global_symbols.items[i].st_shndx,
1533 };
1534 if (foreign_endian) {
1535 bswapAllFields(elf.Elf64_Sym, sym);
1536 }
1537 }
1538 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1539 },
1540 }
1541 }
1542 };
1353};1543};
13541544
1355/// Truncates the existing file contents and overwrites the contents.1545/// Truncates the existing file contents and overwrites the contents.
1356/// Returns an error if `file` is not already open with +read +write +seek abilities.1546/// Returns an error if `file` is not already open with +read +write +seek abilities.
1357pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {1547pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
1358 switch (options.output_mode) {1548 switch (options.output_mode) {
1359 .Exe => {},1549 .Exe => {},
1360 .Obj => {},1550 .Obj => {},
...@@ -1368,7 +1558,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El...@@ -1368,7 +1558,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
1368 .wasm => return error.TODOImplementWritingWasmObjects,1558 .wasm => return error.TODOImplementWritingWasmObjects,
1369 }1559 }
13701560
1371 var self: ElfFile = .{1561 var self: File.Elf = .{
1372 .allocator = allocator,1562 .allocator = allocator,
1373 .file = file,1563 .file = file,
1374 .options = options,1564 .options = options,
...@@ -1412,7 +1602,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El...@@ -1412,7 +1602,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
1412}1602}
14131603
1414/// Returns error.IncrFailed if incremental update could not be performed.1604/// Returns error.IncrFailed if incremental update could not be performed.
1415fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {1605fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
1416 switch (options.output_mode) {1606 switch (options.output_mode) {
1417 .Exe => {},1607 .Exe => {},
1418 .Obj => {},1608 .Obj => {},
...@@ -1425,7 +1615,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf...@@ -1425,7 +1615,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
1425 .macho => return error.IncrFailed,1615 .macho => return error.IncrFailed,
1426 .wasm => return error.IncrFailed,1616 .wasm => return error.IncrFailed,
1427 }1617 }
1428 var self: ElfFile = .{1618 var self: File.Elf = .{
1429 .allocator = allocator,1619 .allocator = allocator,
1430 .file = file,1620 .file = file,
1431 .owns_file_handle = false,1621 .owns_file_handle = false,
src-self-hosted/main.zig+48-43
...@@ -71,7 +71,7 @@ pub fn main() !void {...@@ -71,7 +71,7 @@ pub fn main() !void {
71 const args = try process.argsAlloc(arena);71 const args = try process.argsAlloc(arena);
7272
73 if (args.len <= 1) {73 if (args.len <= 1) {
74 std.debug.warn("expected command argument\n\n{}", .{usage});74 std.debug.print("expected command argument\n\n{}", .{usage});
75 process.exit(1);75 process.exit(1);
76 }76 }
7777
...@@ -91,14 +91,14 @@ pub fn main() !void {...@@ -91,14 +91,14 @@ pub fn main() !void {
91 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);91 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
92 } else if (mem.eql(u8, cmd, "version")) {92 } else if (mem.eql(u8, cmd, "version")) {
93 // Need to set up the build script to give the version as a comptime value.93 // Need to set up the build script to give the version as a comptime value.
94 std.debug.warn("TODO version command not implemented yet\n", .{});94 std.debug.print("TODO version command not implemented yet\n", .{});
95 return error.Unimplemented;95 return error.Unimplemented;
96 } else if (mem.eql(u8, cmd, "zen")) {96 } else if (mem.eql(u8, cmd, "zen")) {
97 try io.getStdOut().writeAll(info_zen);97 try io.getStdOut().writeAll(info_zen);
98 } else if (mem.eql(u8, cmd, "help")) {98 } else if (mem.eql(u8, cmd, "help")) {
99 try io.getStdOut().writeAll(usage);99 try io.getStdOut().writeAll(usage);
100 } else {100 } else {
101 std.debug.warn("unknown command: {}\n\n{}", .{ args[1], usage });101 std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage });
102 process.exit(1);102 process.exit(1);
103 }103 }
104}104}
...@@ -191,6 +191,7 @@ fn buildOutputType(...@@ -191,6 +191,7 @@ fn buildOutputType(
191 var emit_zir: Emit = .no;191 var emit_zir: Emit = .no;
192 var target_arch_os_abi: []const u8 = "native";192 var target_arch_os_abi: []const u8 = "native";
193 var target_mcpu: ?[]const u8 = null;193 var target_mcpu: ?[]const u8 = null;
194 var cbe: bool = false;
194 var target_dynamic_linker: ?[]const u8 = null;195 var target_dynamic_linker: ?[]const u8 = null;
195196
196 var system_libs = std.ArrayList([]const u8).init(gpa);197 var system_libs = std.ArrayList([]const u8).init(gpa);
...@@ -206,7 +207,7 @@ fn buildOutputType(...@@ -206,7 +207,7 @@ fn buildOutputType(
206 process.exit(0);207 process.exit(0);
207 } else if (mem.eql(u8, arg, "--color")) {208 } else if (mem.eql(u8, arg, "--color")) {
208 if (i + 1 >= args.len) {209 if (i + 1 >= args.len) {
209 std.debug.warn("expected [auto|on|off] after --color\n", .{});210 std.debug.print("expected [auto|on|off] after --color\n", .{});
210 process.exit(1);211 process.exit(1);
211 }212 }
212 i += 1;213 i += 1;
...@@ -218,12 +219,12 @@ fn buildOutputType(...@@ -218,12 +219,12 @@ fn buildOutputType(
218 } else if (mem.eql(u8, next_arg, "off")) {219 } else if (mem.eql(u8, next_arg, "off")) {
219 color = .Off;220 color = .Off;
220 } else {221 } else {
221 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});222 std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
222 process.exit(1);223 process.exit(1);
223 }224 }
224 } else if (mem.eql(u8, arg, "--mode")) {225 } else if (mem.eql(u8, arg, "--mode")) {
225 if (i + 1 >= args.len) {226 if (i + 1 >= args.len) {
226 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});227 std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
227 process.exit(1);228 process.exit(1);
228 }229 }
229 i += 1;230 i += 1;
...@@ -237,52 +238,54 @@ fn buildOutputType(...@@ -237,52 +238,54 @@ fn buildOutputType(
237 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {238 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
238 build_mode = .ReleaseSmall;239 build_mode = .ReleaseSmall;
239 } else {240 } else {
240 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});241 std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
241 process.exit(1);242 process.exit(1);
242 }243 }
243 } else if (mem.eql(u8, arg, "--name")) {244 } else if (mem.eql(u8, arg, "--name")) {
244 if (i + 1 >= args.len) {245 if (i + 1 >= args.len) {
245 std.debug.warn("expected parameter after --name\n", .{});246 std.debug.print("expected parameter after --name\n", .{});
246 process.exit(1);247 process.exit(1);
247 }248 }
248 i += 1;249 i += 1;
249 provided_name = args[i];250 provided_name = args[i];
250 } else if (mem.eql(u8, arg, "--library")) {251 } else if (mem.eql(u8, arg, "--library")) {
251 if (i + 1 >= args.len) {252 if (i + 1 >= args.len) {
252 std.debug.warn("expected parameter after --library\n", .{});253 std.debug.print("expected parameter after --library\n", .{});
253 process.exit(1);254 process.exit(1);
254 }255 }
255 i += 1;256 i += 1;
256 try system_libs.append(args[i]);257 try system_libs.append(args[i]);
257 } else if (mem.eql(u8, arg, "--version")) {258 } else if (mem.eql(u8, arg, "--version")) {
258 if (i + 1 >= args.len) {259 if (i + 1 >= args.len) {
259 std.debug.warn("expected parameter after --version\n", .{});260 std.debug.print("expected parameter after --version\n", .{});
260 process.exit(1);261 process.exit(1);
261 }262 }
262 i += 1;263 i += 1;
263 version = std.builtin.Version.parse(args[i]) catch |err| {264 version = std.builtin.Version.parse(args[i]) catch |err| {
264 std.debug.warn("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });265 std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
265 process.exit(1);266 process.exit(1);
266 };267 };
267 } else if (mem.eql(u8, arg, "-target")) {268 } else if (mem.eql(u8, arg, "-target")) {
268 if (i + 1 >= args.len) {269 if (i + 1 >= args.len) {
269 std.debug.warn("expected parameter after -target\n", .{});270 std.debug.print("expected parameter after -target\n", .{});
270 process.exit(1);271 process.exit(1);
271 }272 }
272 i += 1;273 i += 1;
273 target_arch_os_abi = args[i];274 target_arch_os_abi = args[i];
274 } else if (mem.eql(u8, arg, "-mcpu")) {275 } else if (mem.eql(u8, arg, "-mcpu")) {
275 if (i + 1 >= args.len) {276 if (i + 1 >= args.len) {
276 std.debug.warn("expected parameter after -mcpu\n", .{});277 std.debug.print("expected parameter after -mcpu\n", .{});
277 process.exit(1);278 process.exit(1);
278 }279 }
279 i += 1;280 i += 1;
280 target_mcpu = args[i];281 target_mcpu = args[i];
282 } else if (mem.eql(u8, arg, "--c")) {
283 cbe = true;
281 } else if (mem.startsWith(u8, arg, "-mcpu=")) {284 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
282 target_mcpu = arg["-mcpu=".len..];285 target_mcpu = arg["-mcpu=".len..];
283 } else if (mem.eql(u8, arg, "--dynamic-linker")) {286 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
284 if (i + 1 >= args.len) {287 if (i + 1 >= args.len) {
285 std.debug.warn("expected parameter after --dynamic-linker\n", .{});288 std.debug.print("expected parameter after --dynamic-linker\n", .{});
286 process.exit(1);289 process.exit(1);
287 }290 }
288 i += 1;291 i += 1;
...@@ -324,39 +327,39 @@ fn buildOutputType(...@@ -324,39 +327,39 @@ fn buildOutputType(
324 } else if (mem.startsWith(u8, arg, "-l")) {327 } else if (mem.startsWith(u8, arg, "-l")) {
325 try system_libs.append(arg[2..]);328 try system_libs.append(arg[2..]);
326 } else {329 } else {
327 std.debug.warn("unrecognized parameter: '{}'", .{arg});330 std.debug.print("unrecognized parameter: '{}'", .{arg});
328 process.exit(1);331 process.exit(1);
329 }332 }
330 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {333 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
331 std.debug.warn("assembly files not supported yet", .{});334 std.debug.print("assembly files not supported yet", .{});
332 process.exit(1);335 process.exit(1);
333 } else if (mem.endsWith(u8, arg, ".o") or336 } else if (mem.endsWith(u8, arg, ".o") or
334 mem.endsWith(u8, arg, ".obj") or337 mem.endsWith(u8, arg, ".obj") or
335 mem.endsWith(u8, arg, ".a") or338 mem.endsWith(u8, arg, ".a") or
336 mem.endsWith(u8, arg, ".lib"))339 mem.endsWith(u8, arg, ".lib"))
337 {340 {
338 std.debug.warn("object files and static libraries not supported yet", .{});341 std.debug.print("object files and static libraries not supported yet", .{});
339 process.exit(1);342 process.exit(1);
340 } else if (mem.endsWith(u8, arg, ".c") or343 } else if (mem.endsWith(u8, arg, ".c") or
341 mem.endsWith(u8, arg, ".cpp"))344 mem.endsWith(u8, arg, ".cpp"))
342 {345 {
343 std.debug.warn("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});346 std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
344 process.exit(1);347 process.exit(1);
345 } else if (mem.endsWith(u8, arg, ".so") or348 } else if (mem.endsWith(u8, arg, ".so") or
346 mem.endsWith(u8, arg, ".dylib") or349 mem.endsWith(u8, arg, ".dylib") or
347 mem.endsWith(u8, arg, ".dll"))350 mem.endsWith(u8, arg, ".dll"))
348 {351 {
349 std.debug.warn("linking against dynamic libraries not yet supported", .{});352 std.debug.print("linking against dynamic libraries not yet supported", .{});
350 process.exit(1);353 process.exit(1);
351 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {354 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
352 if (root_src_file) |other| {355 if (root_src_file) |other| {
353 std.debug.warn("found another zig file '{}' after root source file '{}'", .{ arg, other });356 std.debug.print("found another zig file '{}' after root source file '{}'", .{ arg, other });
354 process.exit(1);357 process.exit(1);
355 } else {358 } else {
356 root_src_file = arg;359 root_src_file = arg;
357 }360 }
358 } else {361 } else {
359 std.debug.warn("unrecognized file extension of parameter '{}'", .{arg});362 std.debug.print("unrecognized file extension of parameter '{}'", .{arg});
360 }363 }
361 }364 }
362 }365 }
...@@ -367,13 +370,13 @@ fn buildOutputType(...@@ -367,13 +370,13 @@ fn buildOutputType(
367 var it = mem.split(basename, ".");370 var it = mem.split(basename, ".");
368 break :blk it.next() orelse basename;371 break :blk it.next() orelse basename;
369 } else {372 } else {
370 std.debug.warn("--name [name] not provided and unable to infer\n", .{});373 std.debug.print("--name [name] not provided and unable to infer\n", .{});
371 process.exit(1);374 process.exit(1);
372 }375 }
373 };376 };
374377
375 if (system_libs.items.len != 0) {378 if (system_libs.items.len != 0) {
376 std.debug.warn("linking against system libraries not yet supported", .{});379 std.debug.print("linking against system libraries not yet supported", .{});
377 process.exit(1);380 process.exit(1);
378 }381 }
379382
...@@ -385,17 +388,17 @@ fn buildOutputType(...@@ -385,17 +388,17 @@ fn buildOutputType(
385 .diagnostics = &diags,388 .diagnostics = &diags,
386 }) catch |err| switch (err) {389 }) catch |err| switch (err) {
387 error.UnknownCpuModel => {390 error.UnknownCpuModel => {
388 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{391 std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
389 diags.cpu_name.?,392 diags.cpu_name.?,
390 @tagName(diags.arch.?),393 @tagName(diags.arch.?),
391 });394 });
392 for (diags.arch.?.allCpuModels()) |cpu| {395 for (diags.arch.?.allCpuModels()) |cpu| {
393 std.debug.warn(" {}\n", .{cpu.name});396 std.debug.print(" {}\n", .{cpu.name});
394 }397 }
395 process.exit(1);398 process.exit(1);
396 },399 },
397 error.UnknownCpuFeature => {400 error.UnknownCpuFeature => {
398 std.debug.warn(401 std.debug.print(
399 \\Unknown CPU feature: '{}'402 \\Unknown CPU feature: '{}'
400 \\Available CPU features for architecture '{}':403 \\Available CPU features for architecture '{}':
401 \\404 \\
...@@ -404,7 +407,7 @@ fn buildOutputType(...@@ -404,7 +407,7 @@ fn buildOutputType(
404 @tagName(diags.arch.?),407 @tagName(diags.arch.?),
405 });408 });
406 for (diags.arch.?.allFeaturesList()) |feature| {409 for (diags.arch.?.allFeaturesList()) |feature| {
407 std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });410 std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
408 }411 }
409 process.exit(1);412 process.exit(1);
410 },413 },
...@@ -416,21 +419,22 @@ fn buildOutputType(...@@ -416,21 +419,22 @@ fn buildOutputType(
416 if (target_info.cpu_detection_unimplemented) {419 if (target_info.cpu_detection_unimplemented) {
417 // TODO We want to just use detected_info.target but implementing420 // TODO We want to just use detected_info.target but implementing
418 // CPU model & feature detection is todo so here we rely on LLVM.421 // CPU model & feature detection is todo so here we rely on LLVM.
419 std.debug.warn("CPU features detection is not yet available for this system without LLVM extensions\n", .{});422 std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
420 process.exit(1);423 process.exit(1);
421 }424 }
422425
423 const src_path = root_src_file orelse {426 const src_path = root_src_file orelse {
424 std.debug.warn("expected at least one file argument", .{});427 std.debug.print("expected at least one file argument", .{});
425 process.exit(1);428 process.exit(1);
426 };429 };
427430
428 const bin_path = switch (emit_bin) {431 const bin_path = switch (emit_bin) {
429 .no => {432 .no => {
430 std.debug.warn("-fno-emit-bin not supported yet", .{});433 std.debug.print("-fno-emit-bin not supported yet", .{});
431 process.exit(1);434 process.exit(1);
432 },435 },
433 .yes_default_path => try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),436 .yes_default_path => try std.fmt.allocPrint(arena, "{}.c", .{root_name}),
437
434 .yes => |p| p,438 .yes => |p| p,
435 };439 };
436440
...@@ -460,6 +464,7 @@ fn buildOutputType(...@@ -460,6 +464,7 @@ fn buildOutputType(
460 .object_format = object_format,464 .object_format = object_format,
461 .optimize_mode = build_mode,465 .optimize_mode = build_mode,
462 .keep_source_files_loaded = zir_out_path != null,466 .keep_source_files_loaded = zir_out_path != null,
467 .cbe = cbe,
463 });468 });
464 defer module.deinit();469 defer module.deinit();
465470
...@@ -506,7 +511,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo...@@ -506,7 +511,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
506511
507 if (errors.list.len != 0) {512 if (errors.list.len != 0) {
508 for (errors.list) |full_err_msg| {513 for (errors.list) |full_err_msg| {
509 std.debug.warn("{}:{}:{}: error: {}\n", .{514 std.debug.print("{}:{}:{}: error: {}\n", .{
510 full_err_msg.src_path,515 full_err_msg.src_path,
511 full_err_msg.line + 1,516 full_err_msg.line + 1,
512 full_err_msg.column + 1,517 full_err_msg.column + 1,
...@@ -583,7 +588,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -583,7 +588,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
583 process.exit(0);588 process.exit(0);
584 } else if (mem.eql(u8, arg, "--color")) {589 } else if (mem.eql(u8, arg, "--color")) {
585 if (i + 1 >= args.len) {590 if (i + 1 >= args.len) {
586 std.debug.warn("expected [auto|on|off] after --color\n", .{});591 std.debug.print("expected [auto|on|off] after --color\n", .{});
587 process.exit(1);592 process.exit(1);
588 }593 }
589 i += 1;594 i += 1;
...@@ -595,7 +600,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -595,7 +600,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
595 } else if (mem.eql(u8, next_arg, "off")) {600 } else if (mem.eql(u8, next_arg, "off")) {
596 color = .Off;601 color = .Off;
597 } else {602 } else {
598 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});603 std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
599 process.exit(1);604 process.exit(1);
600 }605 }
601 } else if (mem.eql(u8, arg, "--stdin")) {606 } else if (mem.eql(u8, arg, "--stdin")) {
...@@ -603,7 +608,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -603,7 +608,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
603 } else if (mem.eql(u8, arg, "--check")) {608 } else if (mem.eql(u8, arg, "--check")) {
604 check_flag = true;609 check_flag = true;
605 } else {610 } else {
606 std.debug.warn("unrecognized parameter: '{}'", .{arg});611 std.debug.print("unrecognized parameter: '{}'", .{arg});
607 process.exit(1);612 process.exit(1);
608 }613 }
609 } else {614 } else {
...@@ -614,7 +619,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -614,7 +619,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
614619
615 if (stdin_flag) {620 if (stdin_flag) {
616 if (input_files.items.len != 0) {621 if (input_files.items.len != 0) {
617 std.debug.warn("cannot use --stdin with positional arguments\n", .{});622 std.debug.print("cannot use --stdin with positional arguments\n", .{});
618 process.exit(1);623 process.exit(1);
619 }624 }
620625
...@@ -624,7 +629,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -624,7 +629,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
624 defer gpa.free(source_code);629 defer gpa.free(source_code);
625630
626 const tree = std.zig.parse(gpa, source_code) catch |err| {631 const tree = std.zig.parse(gpa, source_code) catch |err| {
627 std.debug.warn("error parsing stdin: {}\n", .{err});632 std.debug.print("error parsing stdin: {}\n", .{err});
628 process.exit(1);633 process.exit(1);
629 };634 };
630 defer tree.deinit();635 defer tree.deinit();
...@@ -647,7 +652,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -647,7 +652,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
647 }652 }
648653
649 if (input_files.items.len == 0) {654 if (input_files.items.len == 0) {
650 std.debug.warn("expected at least one source file argument\n", .{});655 std.debug.print("expected at least one source file argument\n", .{});
651 process.exit(1);656 process.exit(1);
652 }657 }
653658
...@@ -664,7 +669,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -664,7 +669,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
664 for (input_files.span()) |file_path| {669 for (input_files.span()) |file_path| {
665 // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.670 // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
666 const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {671 const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
667 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });672 std.debug.print("unable to open '{}': {}\n", .{ file_path, err });
668 process.exit(1);673 process.exit(1);
669 };674 };
670 defer gpa.free(real_path);675 defer gpa.free(real_path);
...@@ -702,7 +707,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_...@@ -702,7 +707,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_
702 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {707 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
703 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),708 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
704 else => {709 else => {
705 std.debug.warn("unable to format '{}': {}\n", .{ file_path, err });710 std.debug.print("unable to format '{}': {}\n", .{ file_path, err });
706 fmt.any_error = true;711 fmt.any_error = true;
707 return;712 return;
708 },713 },
...@@ -733,7 +738,7 @@ fn fmtPathDir(...@@ -733,7 +738,7 @@ fn fmtPathDir(
733 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);738 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
734 } else {739 } else {
735 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {740 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
736 std.debug.warn("unable to format '{}': {}\n", .{ full_path, err });741 std.debug.print("unable to format '{}': {}\n", .{ full_path, err });
737 fmt.any_error = true;742 fmt.any_error = true;
738 return;743 return;
739 };744 };
...@@ -784,7 +789,7 @@ fn fmtPathFile(...@@ -784,7 +789,7 @@ fn fmtPathFile(
784 if (check_mode) {789 if (check_mode) {
785 const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);790 const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
786 if (anything_changed) {791 if (anything_changed) {
787 std.debug.warn("{}\n", .{file_path});792 std.debug.print("{}\n", .{file_path});
788 fmt.any_error = true;793 fmt.any_error = true;
789 }794 }
790 } else {795 } else {
...@@ -800,7 +805,7 @@ fn fmtPathFile(...@@ -800,7 +805,7 @@ fn fmtPathFile(
800805
801 try af.file.writeAll(fmt.out_buffer.items);806 try af.file.writeAll(fmt.out_buffer.items);
802 try af.finish();807 try af.finish();
803 std.debug.warn("{}\n", .{file_path});808 std.debug.print("{}\n", .{file_path});
804 }809 }
805}810}
806811
src-self-hosted/test.zig+84-32
...@@ -5,6 +5,8 @@ const Allocator = std.mem.Allocator;...@@ -5,6 +5,8 @@ const Allocator = std.mem.Allocator;
5const zir = @import("zir.zig");5const zir = @import("zir.zig");
6const Package = @import("Package.zig");6const Package = @import("Package.zig");
77
8const cheader = @embedFile("cbe.h");
9
8test "self-hosted" {10test "self-hosted" {
9 var ctx = TestContext.init();11 var ctx = TestContext.init();
10 defer ctx.deinit();12 defer ctx.deinit();
...@@ -68,6 +70,7 @@ pub const TestContext = struct {...@@ -68,6 +70,7 @@ pub const TestContext = struct {
68 output_mode: std.builtin.OutputMode,70 output_mode: std.builtin.OutputMode,
69 updates: std.ArrayList(Update),71 updates: std.ArrayList(Update),
70 extension: TestType,72 extension: TestType,
73 cbe: bool = false,
7174
72 /// Adds a subcase in which the module is updated with `src`, and the75 /// Adds a subcase in which the module is updated with `src`, and the
73 /// resulting ZIR is validated against `result`.76 /// resulting ZIR is validated against `result`.
...@@ -187,6 +190,22 @@ pub const TestContext = struct {...@@ -187,6 +190,22 @@ pub const TestContext = struct {
187 return ctx.addObj(name, target, .ZIR);190 return ctx.addObj(name, target, .ZIR);
188 }191 }
189192
193 pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case {
194 ctx.cases.append(Case{
195 .name = name,
196 .target = target,
197 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
198 .output_mode = .Obj,
199 .extension = T,
200 .cbe = true,
201 }) catch unreachable;
202 return &ctx.cases.items[ctx.cases.items.len - 1];
203 }
204
205 pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
206 ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out);
207 }
208
190 pub fn addCompareOutput(209 pub fn addCompareOutput(
191 ctx: *TestContext,210 ctx: *TestContext,
192 name: []const u8,211 name: []const u8,
...@@ -365,13 +384,13 @@ pub const TestContext = struct {...@@ -365,13 +384,13 @@ pub const TestContext = struct {
365 }384 }
366385
367 fn deinit(self: *TestContext) void {386 fn deinit(self: *TestContext) void {
368 for (self.cases.items) |c| {387 for (self.cases.items) |case| {
369 for (c.updates.items) |u| {388 for (case.updates.items) |u| {
370 if (u.case == .Error) {389 if (u.case == .Error) {
371 c.updates.allocator.free(u.case.Error);390 case.updates.allocator.free(u.case.Error);
372 }391 }
373 }392 }
374 c.updates.deinit();393 case.updates.deinit();
375 }394 }
376 self.cases.deinit();395 self.cases.deinit();
377 self.* = undefined;396 self.* = undefined;
...@@ -415,9 +434,6 @@ pub const TestContext = struct {...@@ -415,9 +434,6 @@ pub const TestContext = struct {
415434
416 var module = try Module.init(allocator, .{435 var module = try Module.init(allocator, .{
417 .target = target,436 .target = target,
418 // This is an Executable, as opposed to e.g. a *library*. This does
419 // not mean no ZIR is generated.
420 //
421 // TODO: support tests for object file building, and library builds437 // TODO: support tests for object file building, and library builds
422 // and linking. This will require a rework to support multi-file438 // and linking. This will require a rework to support multi-file
423 // tests.439 // tests.
...@@ -428,6 +444,7 @@ pub const TestContext = struct {...@@ -428,6 +444,7 @@ pub const TestContext = struct {
428 .bin_file_path = bin_name,444 .bin_file_path = bin_name,
429 .root_pkg = root_pkg,445 .root_pkg = root_pkg,
430 .keep_source_files_loaded = true,446 .keep_source_files_loaded = true,
447 .cbe = case.cbe,
431 });448 });
432 defer module.deinit();449 defer module.deinit();
433450
...@@ -447,33 +464,66 @@ pub const TestContext = struct {...@@ -447,33 +464,66 @@ pub const TestContext = struct {
447 try module.update();464 try module.update();
448 module_node.end();465 module_node.end();
449466
467 if (update.case != .Error) {
468 var all_errors = try module.getAllErrorsAlloc();
469 defer all_errors.deinit(allocator);
470 if (all_errors.list.len != 0) {
471 std.debug.warn("\nErrors occurred updating the module:\n================\n", .{});
472 for (all_errors.list) |err| {
473 std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
474 }
475 std.debug.warn("Test failed.\n", .{});
476 std.process.exit(1);
477 }
478 }
479
450 switch (update.case) {480 switch (update.case) {
451 .Transformation => |expected_output| {481 .Transformation => |expected_output| {
452 update_node.estimated_total_items = 5;482 if (case.cbe) {
453 var emit_node = update_node.start("emit", null);483 var cfile: *link.File.C = module.bin_file.cast(link.File.C).?;
454 emit_node.activate();484 cfile.file.?.close();
455 var new_zir_module = try zir.emit(allocator, module);485 cfile.file = null;
456 defer new_zir_module.deinit(allocator);486 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
457 emit_node.end();487 defer file.close();
458488 var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");
459 var write_node = update_node.start("write", null);489 defer allocator.free(out);
460 write_node.activate();490
461 var out_zir = std.ArrayList(u8).init(allocator);491 if (expected_output.len != out.len) {
462 defer out_zir.deinit();492 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
463 try new_zir_module.writeToStream(allocator, out_zir.outStream());493 std.process.exit(1);
464 write_node.end();494 }
465495 for (expected_output) |e, i| {
466 var test_node = update_node.start("assert", null);496 if (out[i] != e) {
467 test_node.activate();497 std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
468 defer test_node.end();498 std.process.exit(1);
469 if (expected_output.len != out_zir.items.len) {499 }
470 std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });500 }
471 std.process.exit(1);501 } else {
472 }502 update_node.estimated_total_items = 5;
473 for (expected_output) |e, i| {503 var emit_node = update_node.start("emit", null);
474 if (out_zir.items[i] != e) {504 emit_node.activate();
475 if (expected_output.len != out_zir.items.len) {505 var new_zir_module = try zir.emit(allocator, module);
476 std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });506 defer new_zir_module.deinit(allocator);
507 emit_node.end();
508
509 var write_node = update_node.start("write", null);
510 write_node.activate();
511 var out_zir = std.ArrayList(u8).init(allocator);
512 defer out_zir.deinit();
513 try new_zir_module.writeToStream(allocator, out_zir.outStream());
514 write_node.end();
515
516 var test_node = update_node.start("assert", null);
517 test_node.activate();
518 defer test_node.end();
519
520 if (expected_output.len != out_zir.items.len) {
521 std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
522 std.process.exit(1);
523 }
524 for (expected_output) |e, i| {
525 if (out_zir.items[i] != e) {
526 std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
477 std.process.exit(1);527 std.process.exit(1);
478 }528 }
479 }529 }
...@@ -511,6 +561,8 @@ pub const TestContext = struct {...@@ -511,6 +561,8 @@ pub const TestContext = struct {
511 }561 }
512 },562 },
513 .Execution => |expected_stdout| {563 .Execution => |expected_stdout| {
564 std.debug.assert(!case.cbe);
565
514 update_node.estimated_total_items = 4;566 update_node.estimated_total_items = 4;
515 var exec_result = x: {567 var exec_result = x: {
516 var exec_node = update_node.start("execute", null);568 var exec_node = update_node.start("execute", null);
test/stage2/cbe.zig created+85
...@@ -0,0 +1,85 @@
1const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3
4// These tests should work with all platforms, but we're using linux_x64 for
5// now for consistency. Will be expanded eventually.
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
10
11pub fn addCases(ctx: *TestContext) !void {
12 ctx.c("empty start function", linux_x64,
13 \\export fn _start() noreturn {}
14 ,
15 \\noreturn void _start(void) {}
16 \\
17 );
18 ctx.c("less empty start function", linux_x64,
19 \\fn main() noreturn {}
20 \\
21 \\export fn _start() noreturn {
22 \\ main();
23 \\}
24 ,
25 \\noreturn void main(void);
26 \\
27 \\noreturn void _start(void) {
28 \\ main();
29 \\}
30 \\
31 \\noreturn void main(void) {}
32 \\
33 );
34 // TODO: implement return values
35 ctx.c("inline asm", linux_x64,
36 \\fn exitGood() void {
37 \\ asm volatile ("syscall"
38 \\ :
39 \\ : [number] "{rax}" (231),
40 \\ [arg1] "{rdi}" (0)
41 \\ );
42 \\}
43 \\
44 \\export fn _start() noreturn {
45 \\ exitGood();
46 \\}
47 ,
48 \\#include <stddef.h>
49 \\
50 \\void exitGood(void);
51 \\
52 \\noreturn void _start(void) {
53 \\ exitGood();
54 \\}
55 \\
56 \\void exitGood(void) {
57 \\ register size_t rax_constant __asm__("rax") = 231;
58 \\ register size_t rdi_constant __asm__("rdi") = 0;
59 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
60 \\}
61 \\
62 );
63 //ctx.c("basic return", linux_x64,
64 // \\fn main() u8 {
65 // \\ return 103;
66 // \\}
67 // \\
68 // \\export fn _start() noreturn {
69 // \\ _ = main();
70 // \\}
71 //,
72 // \\#include <stdint.h>
73 // \\
74 // \\uint8_t main(void);
75 // \\
76 // \\noreturn void _start(void) {
77 // \\ (void)main();
78 // \\}
79 // \\
80 // \\uint8_t main(void) {
81 // \\ return 103;
82 // \\}
83 // \\
84 //);
85}
test/stage2/test.zig+1
...@@ -4,4 +4,5 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -4,4 +4,5 @@ pub fn addCases(ctx: *TestContext) !void {
4 try @import("compile_errors.zig").addCases(ctx);4 try @import("compile_errors.zig").addCases(ctx);
5 try @import("compare_output.zig").addCases(ctx);5 try @import("compare_output.zig").addCases(ctx);
6 try @import("zir.zig").addCases(ctx);6 try @import("zir.zig").addCases(ctx);
7 try @import("cbe.zig").addCases(ctx);
7}8}