authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-08 20:46:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-08 20:46:06-07:00
log7bd0500589f02a6f2ba75525d803b2c1d7409ebe
tree63c26680b4ce918a718192ac540772e3152b9fe0
parent8e425c0c8d78acc64a4223a35010df478d5b7e16
parent0e1c7209e8632ebf398e60de9053e2e0fe8b5661

Merge remote-tracking branch 'origin/master' into register-allocation


17 files changed, 1883 insertions(+), 1218 deletions(-)

ci/azure/linux_script+5-1
......@@ -12,7 +12,7 @@ sudo apt-get update -q
1212
1313sudo apt-get remove -y llvm-*
1414sudo rm -rf /usr/local/*
15sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build
15sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build tidy
1616
1717QEMUBASE="qemu-linux-x86_64-5.0.0-49ee115552"
1818wget https://ziglang.org/deps/$QEMUBASE.tar.xz
......@@ -51,6 +51,10 @@ cd build
5151cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja
5252ninja install
5353./zig build test -Denable-qemu -Denable-wasmtime
54
55# look for HTML errors
56tidy -qe ../zig-cache/langref.html
57
5458VERSION="$(./zig version)"
5559
5660if [ "${BUILD_REASON}" != "PullRequest" ]; then
doc/langref.html.in+47-4
......@@ -97,7 +97,7 @@
9797 margin: auto;
9898 }
9999
100 #index {
100 #toc {
101101 padding: 0 1em;
102102 }
103103
......@@ -105,7 +105,7 @@
105105 #main-wrapper {
106106 flex-direction: row;
107107 }
108 #contents-wrapper, #index {
108 #contents-wrapper, #toc {
109109 overflow: auto;
110110 }
111111 }
......@@ -181,7 +181,7 @@
181181 </head>
182182 <body>
183183 <div id="main-wrapper">
184 <div id="index">
184 <div id="toc">
185185 <a href="https://ziglang.org/documentation/0.1.1/">0.1.1</a> |
186186 <a href="https://ziglang.org/documentation/0.2.0/">0.2.0</a> |
187187 <a href="https://ziglang.org/documentation/0.3.0/">0.3.0</a> |
......@@ -189,7 +189,7 @@
189189 <a href="https://ziglang.org/documentation/0.5.0/">0.5.0</a> |
190190 <a href="https://ziglang.org/documentation/0.6.0/">0.6.0</a> |
191191 master
192 <h1>Index</h1>
192 <h1>Contents</h1>
193193 {#nav#}
194194 </div>
195195 <div id="contents-wrapper"><div id="contents">
......@@ -3861,6 +3861,48 @@ test "if error union" {
38613861 unreachable;
38623862 }
38633863}
3864
3865test "if error union with optional" {
3866 // If expressions test for errors before unwrapping optionals.
3867 // The |optional_value| capture's type is ?u32.
3868
3869 const a: anyerror!?u32 = 0;
3870 if (a) |optional_value| {
3871 assert(optional_value.? == 0);
3872 } else |err| {
3873 unreachable;
3874 }
3875
3876 const b: anyerror!?u32 = null;
3877 if (b) |optional_value| {
3878 assert(optional_value == null);
3879 } else |err| {
3880 unreachable;
3881 }
3882
3883 const c: anyerror!?u32 = error.BadValue;
3884 if (c) |optional_value| {
3885 unreachable;
3886 } else |err| {
3887 assert(err == error.BadValue);
3888 }
3889
3890 // Access the value by reference by using a pointer capture each time.
3891 var d: anyerror!?u32 = 3;
3892 if (d) |*optional_value| {
3893 if (optional_value.*) |*value| {
3894 value.* = 9;
3895 }
3896 } else |err| {
3897 unreachable;
3898 }
3899
3900 if (d) |optional_value| {
3901 assert(optional_value.? == 9);
3902 } else |err| {
3903 unreachable;
3904 }
3905}
38643906 {#code_end#}
38653907 {#see_also|Optionals|Errors#}
38663908 {#header_close#}
......@@ -8393,6 +8435,7 @@ fn foo(comptime T: type, ptr: *T) T {
83938435 {#header_close#}
83948436
83958437 {#header_open|Opaque Types#}
8438 <p>
83968439 {#syntax#}@Type(.Opaque){#endsyntax#} creates a new type with an unknown (but non-zero) size and alignment.
83978440 </p>
83988441 <p>
lib/std/fs.zig+2
......@@ -453,6 +453,8 @@ pub const Dir = struct {
453453
454454 pub const Error = IteratorError;
455455
456 /// Memory such as file names referenced in this returned entry becomes invalid
457 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
456458 pub fn next(self: *Self) Error!?Entry {
457459 start_over: while (true) {
458460 const w = os.windows;
lib/std/fs/test.zig+44-1
......@@ -3,10 +3,53 @@ const testing = std.testing;
33const builtin = std.builtin;
44const fs = std.fs;
55const mem = std.mem;
6const wasi = std.os.wasi;
67
8const ArenaAllocator = std.heap.ArenaAllocator;
9const Dir = std.fs.Dir;
710const File = std.fs.File;
811const tmpDir = testing.tmpDir;
912
13test "Dir.Iterator" {
14 var tmp_dir = tmpDir(.{ .iterate = true });
15 defer tmp_dir.cleanup();
16
17 // First, create a couple of entries to iterate over.
18 const file = try tmp_dir.dir.createFile("some_file", .{});
19 file.close();
20
21 try tmp_dir.dir.makeDir("some_dir");
22
23 var arena = ArenaAllocator.init(testing.allocator);
24 defer arena.deinit();
25
26 var entries = std.ArrayList(Dir.Entry).init(&arena.allocator);
27
28 // Create iterator.
29 var iter = tmp_dir.dir.iterate();
30 while (try iter.next()) |entry| {
31 // We cannot just store `entry` as on Windows, we're re-using the name buffer
32 // which means we'll actually share the `name` pointer between entries!
33 const name = try arena.allocator.dupe(u8, entry.name);
34 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
35 }
36
37 testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
38 testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));
39 testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
40}
41
42fn entry_eql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
43 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
44}
45
46fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
47 for (entries.items) |entry| {
48 if (entry_eql(entry, el)) return true;
49 }
50 return false;
51}
52
1053test "readAllAlloc" {
1154 var tmp_dir = tmpDir(.{});
1255 defer tmp_dir.cleanup();
......@@ -237,7 +280,7 @@ test "fs.copyFile" {
237280 try expectFileContents(tmp.dir, dest_file2, data);
238281}
239282
240fn expectFileContents(dir: fs.Dir, file_path: []const u8, data: []const u8) !void {
283fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void {
241284 const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000);
242285 defer testing.allocator.free(contents);
243286
lib/std/hash_map.zig+26-6
......@@ -533,12 +533,13 @@ pub fn HashMapUnmanaged(
533533 }
534534
535535 pub fn clone(self: Self, allocator: *Allocator) !Self {
536 // TODO this can be made more efficient by directly allocating
537 // the memory slices and memcpying the elements.
538 var other = Self.init();
539 try other.initCapacity(allocator, self.entries.len);
540 for (self.entries.items) |entry| {
541 other.putAssumeCapacityNoClobber(entry.key, entry.value);
536 var other: Self = .{};
537 try other.entries.appendSlice(allocator, self.entries.items);
538
539 if (self.index_header) |header| {
540 const new_header = try IndexHeader.alloc(allocator, header.indexes_len);
541 other.insertAllEntriesIntoNewHeader(new_header);
542 other.index_header = new_header;
542543 }
543544 return other;
544545 }
......@@ -980,6 +981,25 @@ test "ensure capacity" {
980981 testing.expect(initial_capacity == map.capacity());
981982}
982983
984test "clone" {
985 var original = AutoHashMap(i32, i32).init(std.testing.allocator);
986 defer original.deinit();
987
988 // put more than `linear_scan_max` so we can test that the index header is properly cloned
989 var i: u8 = 0;
990 while (i < 10) : (i += 1) {
991 try original.putNoClobber(i, i * 10);
992 }
993
994 var copy = try original.clone();
995 defer copy.deinit();
996
997 i = 0;
998 while (i < 10) : (i += 1) {
999 testing.expect(copy.get(i).? == i * 10);
1000 }
1001}
1002
9831003pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
9841004 return struct {
9851005 fn hash(key: K) u32 {
src-self-hosted/Module.zig+16-13
......@@ -27,7 +27,7 @@ root_pkg: *Package,
2727/// Module owns this resource.
2828/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
2929root_scope: *Scope,
30bin_file: link.ElfFile,
30bin_file: *link.File,
3131bin_file_dir: std.fs.Dir,
3232bin_file_path: []const u8,
3333/// It's rare for a decl to be exported, so we save memory by having a sparse map of
......@@ -46,7 +46,7 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
4646decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
4747
4848optimize_mode: std.builtin.Mode,
49link_error_flags: link.ElfFile.ErrorFlags = .{},
49link_error_flags: link.File.ErrorFlags = .{},
5050
5151work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
5252
......@@ -90,7 +90,7 @@ pub const Export = struct {
9090 /// Byte offset into the file that contains the export directive.
9191 src: usize,
9292 /// Represents the position of the export, if any, in the output file.
93 link: link.ElfFile.Export,
93 link: link.File.Elf.Export,
9494 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
9595 owner_decl: *Decl,
9696 /// The Decl being exported. Note this is *not* the Decl performing the export.
......@@ -168,7 +168,7 @@ pub const Decl = struct {
168168
169169 /// Represents the position of the code in the output file.
170170 /// This is populated regardless of semantic analysis and code generation.
171 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,
171 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
172172
173173 contents_hash: std.zig.SrcHash,
174174
......@@ -723,17 +723,19 @@ pub const InitOptions = struct {
723723 object_format: ?std.builtin.ObjectFormat = null,
724724 optimize_mode: std.builtin.Mode = .Debug,
725725 keep_source_files_loaded: bool = false,
726 cbe: bool = false,
726727};
727728
728729pub fn init(gpa: *Allocator, options: InitOptions) !Module {
729730 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
730 var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
731 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
731732 .target = options.target,
732733 .output_mode = options.output_mode,
733734 .link_mode = options.link_mode orelse .Static,
734735 .object_format = options.object_format orelse options.target.getObjectFormat(),
736 .cbe = options.cbe,
735737 });
736 errdefer bin_file.deinit();
738 errdefer bin_file.destroy();
737739
738740 const root_scope = blk: {
739741 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
......@@ -776,7 +778,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
776778}
777779
778780pub fn deinit(self: *Module) void {
779 self.bin_file.deinit();
781 self.bin_file.destroy();
780782 const gpa = self.gpa;
781783 self.deletion_set.deinit(gpa);
782784 self.work_queue.deinit();
......@@ -825,7 +827,7 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
825827}
826828
827829pub fn target(self: Module) std.Target {
828 return self.bin_file.options.target;
830 return self.bin_file.options().target;
829831}
830832
831833/// Detect changes to source files, perform semantic analysis, and update the output files.
......@@ -872,7 +874,7 @@ pub fn update(self: *Module) !void {
872874 try self.bin_file.flush();
873875 }
874876
875 self.link_error_flags = self.bin_file.error_flags;
877 self.link_error_flags = self.bin_file.errorFlags();
876878 std.log.debug(.module, "link_error_flags: {}\n", .{self.link_error_flags});
877879
878880 // If there are any errors, we anticipate the source files being loaded
......@@ -1985,8 +1987,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
19851987 self.decl_exports.removeAssertDiscard(exp.exported_decl);
19861988 }
19871989 }
1988
1989 self.bin_file.deleteExport(exp.link);
1990 if (self.bin_file.cast(link.File.Elf)) |elf| {
1991 elf.deleteExport(exp.link);
1992 }
19901993 if (self.failed_exports.remove(exp)) |entry| {
19911994 entry.value.destroy(self.gpa);
19921995 }
......@@ -2048,7 +2051,7 @@ fn allocateNewDecl(
20482051 .analysis = .unreferenced,
20492052 .deletion_flag = false,
20502053 .contents_hash = contents_hash,
2051 .link = link.ElfFile.TextBlock.empty,
2054 .link = link.File.Elf.TextBlock.empty,
20522055 .generation = 0,
20532056 };
20542057 return new_decl;
......@@ -2559,7 +2562,7 @@ fn createAnonymousDecl(
25592562) !*Decl {
25602563 const name_index = self.getNextAnonNameIndex();
25612564 const scope_decl = scope.decl().?;
2562 const name = try std.fmt.allocPrint(self.gpa, "{}${}", .{ scope_decl.name, name_index });
2565 const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
25632566 defer self.gpa.free(name);
25642567 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
25652568 const src_hash: std.zig.SrcHash = undefined;
src-self-hosted/cbe.h created+8
......@@ -0,0 +1,8 @@
1#if __STDC_VERSION__ >= 201112L
2#define noreturn _Noreturn
3#elif __GNUC__ && !__STRICT_ANSI__
4#define noreturn __attribute__ ((noreturn))
5#else
6#define noreturn
7#endif
8
src-self-hosted/cgen.zig created+168
......@@ -0,0 +1,168 @@
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(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
15 return allocator.dupe(u8, 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(file.allocator, mem.spanZ(decl.name));
38 defer file.allocator.free(name);
39 try writer.print(" {}(", .{name});
40 if (tv.ty.fnParamLen() == 0) {
41 try writer.writeAll("void)");
42 } else {
43 return file.fail(decl.src(), "TODO implement parameters", .{});
44 }
45}
46
47pub fn generate(file: *C, decl: *Decl) !void {
48 const writer = file.main.writer();
49 const header = file.header.writer();
50 const tv = decl.typed_value.most_recent.typed_value;
51 switch (tv.ty.zigTypeTag()) {
52 .Fn => {
53 try renderFunctionSignature(file, writer, decl);
54
55 try writer.writeAll(" {");
56
57 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
58 const instructions = func.analysis.success.instructions;
59 if (instructions.len > 0) {
60 for (instructions) |inst| {
61 try writer.writeAll("\n\t");
62 switch (inst.tag) {
63 .assembly => {
64 const as = inst.cast(ir.Inst.Assembly).?.args;
65 for (as.inputs) |i, index| {
66 if (i[0] == '{' and i[i.len - 1] == '}') {
67 const reg = i[1 .. i.len - 1];
68 const arg = as.args[index];
69 if (arg.cast(ir.Inst.Constant)) |c| {
70 if (c.val.tag() == .int_u64) {
71 try writer.writeAll("register ");
72 try renderType(file, writer, arg.ty, decl.src());
73 try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
74 } else {
75 return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
76 }
77 } else {
78 return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
79 }
80 } else {
81 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
82 }
83 }
84 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
85 if (as.output) |o| {
86 return file.fail(decl.src(), "TODO inline asm output", .{});
87 }
88 if (as.inputs.len > 0) {
89 if (as.output == null) {
90 try writer.writeAll(" :");
91 }
92 try writer.writeAll(": ");
93 for (as.inputs) |i, index| {
94 if (i[0] == '{' and i[i.len - 1] == '}') {
95 const reg = i[1 .. i.len - 1];
96 const arg = as.args[index];
97 if (index > 0) {
98 try writer.writeAll(", ");
99 }
100 if (arg.cast(ir.Inst.Constant)) |c| {
101 try writer.print("\"\"({}_constant)", .{reg});
102 } else {
103 // This is blocked by the earlier test
104 unreachable;
105 }
106 } else {
107 // This is blocked by the earlier test
108 unreachable;
109 }
110 }
111 }
112 try writer.writeAll(");");
113 },
114 .call => {
115 const call = inst.cast(ir.Inst.Call).?.args;
116 if (call.func.cast(ir.Inst.Constant)) |func_inst| {
117 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
118 const target = func_val.func.owner_decl;
119 const tname = mem.spanZ(target.name);
120 if (file.called.get(tname) == null) {
121 try file.called.put(tname, void{});
122 try renderFunctionSignature(file, header, target);
123 try header.writeAll(";\n");
124 }
125 try writer.print("{}();", .{tname});
126 } else {
127 return file.fail(decl.src(), "TODO non-function call target?", .{});
128 }
129 if (call.args.len != 0) {
130 return file.fail(decl.src(), "TODO function arguments", .{});
131 }
132 } else {
133 return file.fail(decl.src(), "TODO non-constant call inst?", .{});
134 }
135 },
136 else => |e| {
137 return file.fail(decl.src(), "TODO {}", .{e});
138 },
139 }
140 }
141 try writer.writeAll("\n");
142 }
143
144 try writer.writeAll("}\n\n");
145 },
146 .Array => {
147 // TODO: prevent inline asm constants from being emitted
148 const name = try map(file.allocator, mem.span(decl.name));
149 defer file.allocator.free(name);
150 if (tv.val.cast(Value.Payload.Bytes)) |payload| {
151 if (tv.ty.arraySentinel()) |sentinel| {
152 if (sentinel.toUnsignedInt() == 0) {
153 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data });
154 } else {
155 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{});
156 }
157 } else {
158 return file.fail(decl.src(), "TODO byte arrays without sentinels", .{});
159 }
160 } else {
161 return file.fail(decl.src(), "TODO non-byte arrays", .{});
162 }
163 },
164 else => |e| {
165 return file.fail(decl.src(), "TODO {}", .{e});
166 },
167 }
168}
src-self-hosted/codegen.zig+2-2
......@@ -33,7 +33,7 @@ pub const Result = union(enum) {
3333};
3434
3535pub fn generateSymbol(
36 bin_file: *link.ElfFile,
36 bin_file: *link.File.Elf,
3737 src: usize,
3838 typed_value: TypedValue,
3939 code: *std.ArrayList(u8),
......@@ -237,7 +237,7 @@ const InnerError = error {
237237
238238const Function = struct {
239239 gpa: *Allocator,
240 bin_file: *link.ElfFile,
240 bin_file: *link.File.Elf,
241241 target: *const std.Target,
242242 mod_fn: *const Module.Fn,
243243 code: *std.ArrayList(u8),
src-self-hosted/link.zig+1318-1107
......@@ -7,6 +7,7 @@ const Module = @import("Module.zig");
77const fs = std.fs;
88const elf = std.elf;
99const codegen = @import("codegen.zig");
10const cgen = @import("cgen.zig");
1011
1112const default_entry_addr = 0x8000000;
1213
......@@ -21,6 +22,7 @@ pub const Options = struct {
2122 /// Used for calculating how much space to reserve for executable program code in case
2223 /// the binary file deos not already have such a section.
2324 program_code_size_hint: u64 = 256 * 1024,
25 cbe: bool = false,
2426};
2527
2628/// Attempts incremental linking, if the file already exists.
......@@ -32,13 +34,22 @@ pub fn openBinFilePath(
3234 dir: fs.Dir,
3335 sub_path: []const u8,
3436 options: Options,
35) !ElfFile {
36 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });
37) !*File {
38 const file = try dir.createFile(sub_path, .{ .truncate = options.cbe, .read = true, .mode = determineMode(options) });
3739 errdefer file.close();
3840
39 var bin_file = try openBinFile(allocator, file, options);
40 bin_file.owns_file_handle = true;
41 return bin_file;
41 if (options.cbe) {
42 var bin_file = try allocator.create(File.C);
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 }
4253}
4354
4455/// Atomically overwrites the old file, if present.
......@@ -75,12 +86,24 @@ pub fn writeFilePath(
7586 return result;
7687}
7788
89fn 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 .constants = std.ArrayList(u8).init(allocator),
97 .called = std.StringHashMap(void).init(allocator),
98 };
99}
100
78101/// Attempts incremental linking, if the file already exists.
79102/// If incremental linking fails, falls back to truncating the file and rewriting it.
80103/// Returns an error if `file` is not already open with +read +write +seek abilities.
81104/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
82105/// This operation is not atomic.
83pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
106pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
84107 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
85108 error.IncrFailed => {
86109 return createElfFile(allocator, file, options);
......@@ -89,514 +112,592 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF
89112 };
90113}
91114
92pub const ElfFile = struct {
93 allocator: *Allocator,
94 file: ?fs.File,
95 owns_file_handle: bool,
96 options: Options,
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;
172
173 pub const ErrorFlags = struct {
174 no_entry_point_found: bool = false,
175 };
176
177 pub const TextBlock = struct {
178 /// Each decl always gets a local symbol with the fully qualified name.
179 /// The vaddr and size are found here directly.
180 /// The file offset is found by computing the vaddr offset from the section vaddr
181 /// the symbol references, and adding that to the file offset of the section.
182 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
183 /// offset table entry.
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 };
198
199 /// Returns how much room there is to grow in virtual address space.
200 /// File offset relocation happens transparently, so it is not included in
201 /// this calculation.
202 fn capacity(self: TextBlock, elf_file: ElfFile) u64 {
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 }
115pub const File = struct {
116 tag: Tag,
117 pub fn cast(base: *File, comptime T: type) ?*T {
118 if (base.tag != T.base_tag)
119 return null;
212120
213 fn freeListEligible(self: TextBlock, elf_file: ElfFile) bool {
214 // No need to keep a free list node for the last block.
215 const next = self.next orelse return false;
216 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
217 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
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 }
224 };
225
226 pub const Export = struct {
227 sym_index: ?u32 = null,
228 };
121 return @fieldParentPtr(T, "base", base);
122 }
229123
230 pub fn deinit(self: *ElfFile) void {
231 self.sections.deinit(self.allocator);
232 self.program_headers.deinit(self.allocator);
233 self.shstrtab.deinit(self.allocator);
234 self.local_symbols.deinit(self.allocator);
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();
124 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
125 switch (base.tag) {
126 .Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
127 .C => {},
128 else => unreachable,
243129 }
244130 }
245131
246 pub fn makeExecutable(self: *ElfFile) !void {
247 assert(self.owns_file_handle);
248 if (self.file) |f| {
249 f.close();
250 self.file = null;
132 pub fn makeExecutable(base: *File) !void {
133 switch (base.tag) {
134 .Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
135 else => unreachable,
251136 }
252137 }
253138
254 pub fn makeWritable(self: *ElfFile, dir: fs.Dir, sub_path: []const u8) !void {
255 assert(self.owns_file_handle);
256 if (self.file != null) return;
257 self.file = try dir.createFile(sub_path, .{
258 .truncate = false,
259 .read = true,
260 .mode = determineMode(self.options),
261 });
139 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
140 switch (base.tag) {
141 .Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
142 .C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
143 else => unreachable,
144 }
262145 }
263146
264 /// Returns end pos of collision, if any.
265 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {
266 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
267 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
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 }
147 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
148 switch (base.tag) {
149 .Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
150 .C => {},
151 else => unreachable,
281152 }
153 }
282154
283 if (self.phdr_table_offset) |off| {
284 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
285 const tight_size = self.sections.items.len * phdr_size;
286 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
287 const test_end = off + increased_size;
288 if (end > off and start < test_end) {
289 return test_end;
290 }
155 pub fn deinit(base: *File) void {
156 switch (base.tag) {
157 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),
158 .C => @fieldParentPtr(C, "base", base).deinit(),
159 else => unreachable,
291160 }
161 }
292162
293 for (self.sections.items) |section| {
294 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
295 const test_end = section.sh_offset + increased_size;
296 if (end > section.sh_offset and start < test_end) {
297 return test_end;
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 }
163 pub fn destroy(base: *File) void {
164 switch (base.tag) {
165 .Elf => {
166 const parent = @fieldParentPtr(Elf, "base", base);
167 parent.deinit();
168 parent.allocator.destroy(parent);
169 },
170 .C => {
171 const parent = @fieldParentPtr(C, "base", base);
172 parent.deinit();
173 parent.allocator.destroy(parent);
174 },
175 else => unreachable,
306176 }
307 return null;
308177 }
309178
310 fn allocatedSize(self: *ElfFile, start: u64) u64 {
311 var min_pos: u64 = std.math.maxInt(u64);
312 if (self.shdr_table_offset) |off| {
313 if (off > start and off < min_pos) min_pos = off;
314 }
315 if (self.phdr_table_offset) |off| {
316 if (off > start and off < min_pos) min_pos = off;
317 }
318 for (self.sections.items) |section| {
319 if (section.sh_offset <= start) continue;
320 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
321 }
322 for (self.program_headers.items) |program_header| {
323 if (program_header.p_offset <= start) continue;
324 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
325 }
326 return min_pos - start;
179 pub fn flush(base: *File) !void {
180 try switch (base.tag) {
181 .Elf => @fieldParentPtr(Elf, "base", base).flush(),
182 .C => @fieldParentPtr(C, "base", base).flush(),
183 else => unreachable,
184 };
327185 }
328186
329 fn findFreeSpace(self: *ElfFile, object_size: u64, min_alignment: u16) u64 {
330 var start: u64 = 0;
331 while (self.detectAllocCollision(start, object_size)) |item_end| {
332 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
187 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188 switch (base.tag) {
189 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190 else => unreachable,
333191 }
334 return start;
335192 }
336193
337 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {
338 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
339 const result = self.shstrtab.items.len;
340 self.shstrtab.appendSliceAssumeCapacity(bytes);
341 self.shstrtab.appendAssumeCapacity(0);
342 return @intCast(u32, result);
194 pub fn errorFlags(base: *File) ErrorFlags {
195 return switch (base.tag) {
196 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,
197 .C => return .{ .no_entry_point_found = false },
198 else => unreachable,
199 };
343200 }
344201
345 fn getString(self: *ElfFile, str_off: u32) []const u8 {
346 assert(str_off < self.shstrtab.items.len);
347 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
202 pub fn options(base: *File) Options {
203 return switch (base.tag) {
204 .Elf => @fieldParentPtr(Elf, "base", base).options,
205 .C => @fieldParentPtr(C, "base", base).options,
206 };
348207 }
349208
350 fn updateString(self: *ElfFile, old_str_off: u32, new_name: []const u8) !u32 {
351 const existing_name = self.getString(old_str_off);
352 if (mem.eql(u8, existing_name, new_name)) {
353 return old_str_off;
209 /// Must be called only after a successful call to `updateDecl`.
210 pub fn updateDeclExports(
211 base: *File,
212 module: *Module,
213 decl: *const Module.Decl,
214 exports: []const *Module.Export,
215 ) !void {
216 switch (base.tag) {
217 .Elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
218 .C => return {},
354219 }
355 return self.makeString(new_name);
356220 }
357221
358 pub fn populateMissingMetadata(self: *ElfFile) !void {
359 const small_ptr = switch (self.ptr_width) {
360 .p32 => true,
361 .p64 => false,
362 };
363 const ptr_size: u8 = switch (self.ptr_width) {
364 .p32 => 4,
365 .p64 => 8,
366 };
367 if (self.phdr_load_re_index == null) {
368 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
369 const file_size = self.options.program_code_size_hint;
370 const p_align = 0x1000;
371 const off = self.findFreeSpace(file_size, p_align);
372 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
373 try self.program_headers.append(self.allocator, .{
374 .p_type = elf.PT_LOAD,
375 .p_offset = off,
376 .p_filesz = file_size,
377 .p_vaddr = default_entry_addr,
378 .p_paddr = default_entry_addr,
379 .p_memsz = file_size,
380 .p_align = p_align,
381 .p_flags = elf.PF_X | elf.PF_R,
382 });
383 self.entry_addr = null;
384 self.phdr_table_dirty = true;
385 }
386 if (self.phdr_got_index == null) {
387 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
388 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
389 // We really only need ptr alignment but since we are using PROGBITS, linux requires
390 // page align.
391 const p_align = 0x1000;
392 const off = self.findFreeSpace(file_size, p_align);
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 }
410 if (self.shstrtab_index == null) {
411 self.shstrtab_index = @intCast(u16, self.sections.items.len);
412 assert(self.shstrtab.items.len == 0);
413 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
414 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
415 std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
416 try self.sections.append(self.allocator, .{
417 .sh_name = try self.makeString(".shstrtab"),
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 });
428 self.shstrtab_dirty = true;
429 self.shdr_table_dirty = true;
430 }
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.?];
222 pub const Tag = enum {
223 Elf,
224 C,
225 };
434226
435 try self.sections.append(self.allocator, .{
436 .sh_name = try self.makeString(".text"),
437 .sh_type = elf.SHT_PROGBITS,
438 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
439 .sh_addr = phdr.p_vaddr,
440 .sh_offset = phdr.p_offset,
441 .sh_size = phdr.p_filesz,
442 .sh_link = 0,
443 .sh_info = 0,
444 .sh_addralign = phdr.p_align,
445 .sh_entsize = 0,
446 });
447 self.shdr_table_dirty = true;
227 pub const ErrorFlags = struct {
228 no_entry_point_found: bool = false,
229 };
230
231 pub const C = struct {
232 pub const base_tag: Tag = .C;
233 base: File = File{ .tag = base_tag },
234
235 allocator: *Allocator,
236 header: std.ArrayList(u8),
237 constants: std.ArrayList(u8),
238 main: std.ArrayList(u8),
239 file: ?fs.File,
240 options: Options,
241 called: std.StringHashMap(void),
242 need_stddef: bool = false,
243 need_stdint: bool = false,
244 need_noreturn: bool = false,
245 error_msg: *Module.ErrorMsg = undefined,
246
247 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: var) !void {
248 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
249 return error.CGenFailure;
448250 }
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.?];
452251
453 try self.sections.append(self.allocator, .{
454 .sh_name = try self.makeString(".got"),
455 .sh_type = elf.SHT_PROGBITS,
456 .sh_flags = elf.SHF_ALLOC,
457 .sh_addr = phdr.p_vaddr,
458 .sh_offset = phdr.p_offset,
459 .sh_size = phdr.p_filesz,
460 .sh_link = 0,
461 .sh_info = 0,
462 .sh_addralign = phdr.p_align,
463 .sh_entsize = 0,
464 });
465 self.shdr_table_dirty = true;
252 pub fn deinit(self: *File.C) void {
253 self.main.deinit();
254 self.header.deinit();
255 self.constants.deinit();
256 self.called.deinit();
257 if (self.file) |f|
258 f.close();
466259 }
467 if (self.symtab_section_index == null) {
468 self.symtab_section_index = @intCast(u16, self.sections.items.len);
469 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
470 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
471 const file_size = self.options.symbol_count_hint * each_size;
472 const off = self.findFreeSpace(file_size, min_align);
473 std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
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);
260
261 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
262 cgen.generate(self, decl) catch |err| {
263 if (err == error.CGenFailure) {
264 try module.failed_decls.put(module.gpa, decl, self.error_msg);
265 }
266 return err;
267 };
490268 }
491 const shsize: u64 = switch (self.ptr_width) {
492 .p32 => @sizeOf(elf.Elf32_Shdr),
493 .p64 => @sizeOf(elf.Elf64_Shdr),
494 };
495 const shalign: u16 = switch (self.ptr_width) {
496 .p32 => @alignOf(elf.Elf32_Shdr),
497 .p64 => @alignOf(elf.Elf64_Shdr),
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;
269
270 pub fn flush(self: *File.C) !void {
271 const writer = self.file.?.writer();
272 try writer.writeAll(@embedFile("cbe.h"));
273 var includes = false;
274 if (self.need_stddef) {
275 try writer.writeAll("#include <stddef.h>\n");
276 includes = true;
277 }
278 if (self.need_stdint) {
279 try writer.writeAll("#include <stdint.h>\n");
280 includes = true;
281 }
282 if (includes) {
283 try writer.writeByte('\n');
284 }
285 if (self.header.items.len > 0) {
286 try writer.print("{}\n", .{self.header.items});
287 }
288 if (self.constants.items.len > 0) {
289 try writer.print("{}\n", .{self.constants.items});
290 }
291 if (self.main.items.len > 1) {
292 const last_two = self.main.items[self.main.items.len - 2 ..];
293 if (std.mem.eql(u8, last_two, "\n\n")) {
294 self.main.items.len -= 1;
295 }
296 }
297 try writer.writeAll(self.main.items);
298 self.file.?.close();
299 self.file = null;
502300 }
503 const phsize: u64 = switch (self.ptr_width) {
504 .p32 => @sizeOf(elf.Elf32_Phdr),
505 .p64 => @sizeOf(elf.Elf64_Phdr),
301 };
302
303 pub const Elf = struct {
304 pub const base_tag: Tag = .Elf;
305 base: File = File{ .tag = base_tag },
306
307 allocator: *Allocator,
308 file: ?fs.File,
309 owns_file_handle: bool,
310 options: Options,
311 ptr_width: enum { p32, p64 },
312
313 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
314 /// Same order as in the file.
315 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
316 shdr_table_offset: ?u64 = null,
317
318 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
319 /// Same order as in the file.
320 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
321 phdr_table_offset: ?u64 = null,
322 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
323 phdr_load_re_index: ?u16 = null,
324 /// The index into the program headers of the global offset table.
325 /// It needs PT_LOAD and Read flags.
326 phdr_got_index: ?u16 = null,
327 entry_addr: ?u64 = null,
328
329 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
330 shstrtab_index: ?u16 = null,
331
332 text_section_index: ?u16 = null,
333 symtab_section_index: ?u16 = null,
334 got_section_index: ?u16 = null,
335
336 /// The same order as in the file. ELF requires global symbols to all be after the
337 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
338 /// write them at the end. These are only the local symbols. The length of this array
339 /// is the value used for sh_info in the .symtab section.
340 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
341 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
342
343 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
344 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
345 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
346
347 /// Same order as in the file. The value is the absolute vaddr value.
348 /// If the vaddr of the executable program header changes, the entire
349 /// offset table needs to be rewritten.
350 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
351
352 phdr_table_dirty: bool = false,
353 shdr_table_dirty: bool = false,
354 shstrtab_dirty: bool = false,
355 offset_table_count_dirty: bool = false,
356
357 error_flags: ErrorFlags = ErrorFlags{},
358
359 /// A list of text blocks that have surplus capacity. This list can have false
360 /// positives, as functions grow and shrink over time, only sometimes being added
361 /// or removed from the freelist.
362 ///
363 /// A text block has surplus capacity when its overcapacity value is greater than
364 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
365 /// much extra capacity, that we could fit a small new symbol in it, itself with
366 /// ideal_capacity or more.
367 ///
368 /// Ideal capacity is defined by size * alloc_num / alloc_den.
369 ///
370 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
371 /// overcapacity can be negative. A simple way to have negative overcapacity is to
372 /// allocate a fresh text block, which will have ideal capacity, and then grow it
373 /// by 1 byte. It will then have -1 overcapacity.
374 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
375 last_text_block: ?*TextBlock = null,
376
377 /// `alloc_num / alloc_den` is the factor of padding when allocating.
378 const alloc_num = 4;
379 const alloc_den = 3;
380
381 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
382 /// it as a possible place to put new symbols, it must have enough room for this many bytes
383 /// (plus extra for reserved capacity).
384 const minimum_text_block_size = 64;
385 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
386
387 pub const TextBlock = struct {
388 /// Each decl always gets a local symbol with the fully qualified name.
389 /// The vaddr and size are found here directly.
390 /// The file offset is found by computing the vaddr offset from the section vaddr
391 /// the symbol references, and adding that to the file offset of the section.
392 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
393 /// offset table entry.
394 local_sym_index: u32,
395 /// This field is undefined for symbols with size = 0.
396 offset_table_index: u32,
397 /// Points to the previous and next neighbors, based on the `text_offset`.
398 /// This can be used to find, for example, the capacity of this `TextBlock`.
399 prev: ?*TextBlock,
400 next: ?*TextBlock,
401
402 pub const empty = TextBlock{
403 .local_sym_index = 0,
404 .offset_table_index = undefined,
405 .prev = null,
406 .next = null,
407 };
408
409 /// Returns how much room there is to grow in virtual address space.
410 /// File offset relocation happens transparently, so it is not included in
411 /// this calculation.
412 fn capacity(self: TextBlock, elf_file: Elf) u64 {
413 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
414 if (self.next) |next| {
415 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
416 return next_sym.st_value - self_sym.st_value;
417 } else {
418 // We are the last block. The capacity is limited only by virtual address space.
419 return std.math.maxInt(u32) - self_sym.st_value;
420 }
421 }
422
423 fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
424 // No need to keep a free list node for the last block.
425 const next = self.next orelse return false;
426 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
427 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
428 const cap = next_sym.st_value - self_sym.st_value;
429 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
430 if (cap <= ideal_cap) return false;
431 const surplus = cap - ideal_cap;
432 return surplus >= min_text_capacity;
433 }
506434 };
507 const phalign: u16 = switch (self.ptr_width) {
508 .p32 => @alignOf(elf.Elf32_Phdr),
509 .p64 => @alignOf(elf.Elf64_Phdr),
435
436 pub const Export = struct {
437 sym_index: ?u32 = null,
510438 };
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 }
515 {
516 // Iterate over symbols, populating free_list and last_text_block.
517 if (self.local_symbols.items.len != 1) {
518 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
439
440 pub fn deinit(self: *Elf) void {
441 self.sections.deinit(self.allocator);
442 self.program_headers.deinit(self.allocator);
443 self.shstrtab.deinit(self.allocator);
444 self.local_symbols.deinit(self.allocator);
445 self.global_symbols.deinit(self.allocator);
446 self.global_symbol_free_list.deinit(self.allocator);
447 self.local_symbol_free_list.deinit(self.allocator);
448 self.offset_table_free_list.deinit(self.allocator);
449 self.text_block_free_list.deinit(self.allocator);
450 self.offset_table.deinit(self.allocator);
451 if (self.owns_file_handle) {
452 if (self.file) |f| f.close();
519453 }
520 // We are starting with an empty file. The default values are correct, null and empty list.
521454 }
522 }
523455
524 /// Commit pending changes and write headers.
525 pub fn flush(self: *ElfFile) !void {
526 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
456 pub fn makeExecutable(self: *Elf) !void {
457 assert(self.owns_file_handle);
458 if (self.file) |f| {
459 f.close();
460 self.file = null;
461 }
462 }
527463
528 // Unfortunately these have to be buffered and done at the end because ELF does not allow
529 // mixing local and global symbols within a symbol table.
530 try self.writeAllGlobalSymbols();
464 pub fn makeWritable(self: *Elf, dir: fs.Dir, sub_path: []const u8) !void {
465 assert(self.owns_file_handle);
466 if (self.file != null) return;
467 self.file = try dir.createFile(sub_path, .{
468 .truncate = false,
469 .read = true,
470 .mode = determineMode(self.options),
471 });
472 }
531473
532 if (self.phdr_table_dirty) {
533 const phsize: u64 = switch (self.ptr_width) {
534 .p32 => @sizeOf(elf.Elf32_Phdr),
535 .p64 => @sizeOf(elf.Elf64_Phdr),
536 };
537 const phalign: u16 = switch (self.ptr_width) {
538 .p32 => @alignOf(elf.Elf32_Phdr),
539 .p64 => @alignOf(elf.Elf64_Phdr),
540 };
541 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
542 const needed_size = self.program_headers.items.len * phsize;
474 /// Returns end pos of collision, if any.
475 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
476 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
477 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
478 if (start < ehdr_size)
479 return ehdr_size;
480
481 const end = start + satMul(size, alloc_num) / alloc_den;
482
483 if (self.shdr_table_offset) |off| {
484 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
485 const tight_size = self.sections.items.len * shdr_size;
486 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
487 const test_end = off + increased_size;
488 if (end > off and start < test_end) {
489 return test_end;
490 }
491 }
543492
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);
493 if (self.phdr_table_offset) |off| {
494 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
495 const tight_size = self.sections.items.len * phdr_size;
496 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
497 const test_end = off + increased_size;
498 if (end > off and start < test_end) {
499 return test_end;
500 }
547501 }
548502
549 switch (self.ptr_width) {
550 .p32 => {
551 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
552 defer self.allocator.free(buf);
503 for (self.sections.items) |section| {
504 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
505 const test_end = section.sh_offset + increased_size;
506 if (end > section.sh_offset and start < test_end) {
507 return test_end;
508 }
509 }
510 for (self.program_headers.items) |program_header| {
511 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
512 const test_end = program_header.p_offset + increased_size;
513 if (end > program_header.p_offset and start < test_end) {
514 return test_end;
515 }
516 }
517 return null;
518 }
553519
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);
520 fn allocatedSize(self: *Elf, start: u64) u64 {
521 var min_pos: u64 = std.math.maxInt(u64);
522 if (self.shdr_table_offset) |off| {
523 if (off > start and off < min_pos) min_pos = off;
524 }
525 if (self.phdr_table_offset) |off| {
526 if (off > start and off < min_pos) min_pos = off;
527 }
528 for (self.sections.items) |section| {
529 if (section.sh_offset <= start) continue;
530 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
531 }
532 for (self.program_headers.items) |program_header| {
533 if (program_header.p_offset <= start) continue;
534 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
535 }
536 return min_pos - start;
537 }
565538
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 },
539 fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
540 var start: u64 = 0;
541 while (self.detectAllocCollision(start, object_size)) |item_end| {
542 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
574543 }
575 self.phdr_table_dirty = false;
544 return start;
576545 }
577546
578 {
579 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
580 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
581 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
582 const needed_size = self.shstrtab.items.len;
547 fn makeString(self: *Elf, bytes: []const u8) !u32 {
548 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
549 const result = self.shstrtab.items.len;
550 self.shstrtab.appendSliceAssumeCapacity(bytes);
551 self.shstrtab.appendAssumeCapacity(0);
552 return @intCast(u32, result);
553 }
583554
584 if (needed_size > allocated_size) {
585 shstrtab_sect.sh_size = 0; // free the space
586 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
587 }
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 });
555 fn getString(self: *Elf, str_off: u32) []const u8 {
556 assert(str_off < self.shstrtab.items.len);
557 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
558 }
590559
591 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
592 if (!self.shdr_table_dirty) {
593 // Then it won't get written with the others and we need to do it.
594 try self.writeSectHeader(self.shstrtab_index.?);
595 }
596 self.shstrtab_dirty = false;
560 fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
561 const existing_name = self.getString(old_str_off);
562 if (mem.eql(u8, existing_name, new_name)) {
563 return old_str_off;
597564 }
565 return self.makeString(new_name);
598566 }
599 if (self.shdr_table_dirty) {
567
568 pub fn populateMissingMetadata(self: *Elf) !void {
569 const small_ptr = switch (self.ptr_width) {
570 .p32 => true,
571 .p64 => false,
572 };
573 const ptr_size: u8 = switch (self.ptr_width) {
574 .p32 => 4,
575 .p64 => 8,
576 };
577 if (self.phdr_load_re_index == null) {
578 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
579 const file_size = self.options.program_code_size_hint;
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 try self.program_headers.append(self.allocator, .{
584 .p_type = elf.PT_LOAD,
585 .p_offset = off,
586 .p_filesz = file_size,
587 .p_vaddr = default_entry_addr,
588 .p_paddr = default_entry_addr,
589 .p_memsz = file_size,
590 .p_align = p_align,
591 .p_flags = elf.PF_X | elf.PF_R,
592 });
593 self.entry_addr = null;
594 self.phdr_table_dirty = true;
595 }
596 if (self.phdr_got_index == null) {
597 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
598 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
599 // We really only need ptr alignment but since we are using PROGBITS, linux requires
600 // page align.
601 const p_align = 0x1000;
602 const off = self.findFreeSpace(file_size, p_align);
603 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
604 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
605 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
606 // else in virtual memory.
607 const default_got_addr = 0x4000000;
608 try self.program_headers.append(self.allocator, .{
609 .p_type = elf.PT_LOAD,
610 .p_offset = off,
611 .p_filesz = file_size,
612 .p_vaddr = default_got_addr,
613 .p_paddr = default_got_addr,
614 .p_memsz = file_size,
615 .p_align = p_align,
616 .p_flags = elf.PF_R,
617 });
618 self.phdr_table_dirty = true;
619 }
620 if (self.shstrtab_index == null) {
621 self.shstrtab_index = @intCast(u16, self.sections.items.len);
622 assert(self.shstrtab.items.len == 0);
623 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
624 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
625 std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
626 try self.sections.append(self.allocator, .{
627 .sh_name = try self.makeString(".shstrtab"),
628 .sh_type = elf.SHT_STRTAB,
629 .sh_flags = 0,
630 .sh_addr = 0,
631 .sh_offset = off,
632 .sh_size = self.shstrtab.items.len,
633 .sh_link = 0,
634 .sh_info = 0,
635 .sh_addralign = 1,
636 .sh_entsize = 0,
637 });
638 self.shstrtab_dirty = true;
639 self.shdr_table_dirty = true;
640 }
641 if (self.text_section_index == null) {
642 self.text_section_index = @intCast(u16, self.sections.items.len);
643 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
644
645 try self.sections.append(self.allocator, .{
646 .sh_name = try self.makeString(".text"),
647 .sh_type = elf.SHT_PROGBITS,
648 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
649 .sh_addr = phdr.p_vaddr,
650 .sh_offset = phdr.p_offset,
651 .sh_size = phdr.p_filesz,
652 .sh_link = 0,
653 .sh_info = 0,
654 .sh_addralign = phdr.p_align,
655 .sh_entsize = 0,
656 });
657 self.shdr_table_dirty = true;
658 }
659 if (self.got_section_index == null) {
660 self.got_section_index = @intCast(u16, self.sections.items.len);
661 const phdr = &self.program_headers.items[self.phdr_got_index.?];
662
663 try self.sections.append(self.allocator, .{
664 .sh_name = try self.makeString(".got"),
665 .sh_type = elf.SHT_PROGBITS,
666 .sh_flags = elf.SHF_ALLOC,
667 .sh_addr = phdr.p_vaddr,
668 .sh_offset = phdr.p_offset,
669 .sh_size = phdr.p_filesz,
670 .sh_link = 0,
671 .sh_info = 0,
672 .sh_addralign = phdr.p_align,
673 .sh_entsize = 0,
674 });
675 self.shdr_table_dirty = true;
676 }
677 if (self.symtab_section_index == null) {
678 self.symtab_section_index = @intCast(u16, self.sections.items.len);
679 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
680 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
681 const file_size = self.options.symbol_count_hint * each_size;
682 const off = self.findFreeSpace(file_size, min_align);
683 std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
684
685 try self.sections.append(self.allocator, .{
686 .sh_name = try self.makeString(".symtab"),
687 .sh_type = elf.SHT_SYMTAB,
688 .sh_flags = 0,
689 .sh_addr = 0,
690 .sh_offset = off,
691 .sh_size = file_size,
692 // The section header index of the associated string table.
693 .sh_link = self.shstrtab_index.?,
694 .sh_info = @intCast(u32, self.local_symbols.items.len),
695 .sh_addralign = min_align,
696 .sh_entsize = each_size,
697 });
698 self.shdr_table_dirty = true;
699 try self.writeSymbol(0);
700 }
600701 const shsize: u64 = switch (self.ptr_width) {
601702 .p32 => @sizeOf(elf.Elf32_Shdr),
602703 .p64 => @sizeOf(elf.Elf64_Shdr),
......@@ -605,757 +706,867 @@ pub const ElfFile = struct {
605706 .p32 => @alignOf(elf.Elf32_Shdr),
606707 .p64 => @alignOf(elf.Elf64_Shdr),
607708 };
608 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
609 const needed_size = self.sections.items.len * shsize;
610
611 if (needed_size > allocated_size) {
612 self.shdr_table_offset = null; // free the space
613 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
709 if (self.shdr_table_offset == null) {
710 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
711 self.shdr_table_dirty = true;
712 }
713 const phsize: u64 = switch (self.ptr_width) {
714 .p32 => @sizeOf(elf.Elf32_Phdr),
715 .p64 => @sizeOf(elf.Elf64_Phdr),
716 };
717 const phalign: u16 = switch (self.ptr_width) {
718 .p32 => @alignOf(elf.Elf32_Phdr),
719 .p64 => @alignOf(elf.Elf64_Phdr),
720 };
721 if (self.phdr_table_offset == null) {
722 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
723 self.phdr_table_dirty = true;
724 }
725 {
726 // Iterate over symbols, populating free_list and last_text_block.
727 if (self.local_symbols.items.len != 1) {
728 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
729 }
730 // We are starting with an empty file. The default values are correct, null and empty list.
614731 }
732 }
615733
616 switch (self.ptr_width) {
617 .p32 => {
618 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
619 defer self.allocator.free(buf);
734 /// Commit pending changes and write headers.
735 pub fn flush(self: *Elf) !void {
736 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
620737
621 for (buf) |*shdr, i| {
622 shdr.* = sectHeaderTo32(self.sections.items[i]);
623 if (foreign_endian) {
624 bswapAllFields(elf.Elf32_Shdr, shdr);
738 // Unfortunately these have to be buffered and done at the end because ELF does not allow
739 // mixing local and global symbols within a symbol table.
740 try self.writeAllGlobalSymbols();
741
742 if (self.phdr_table_dirty) {
743 const phsize: u64 = switch (self.ptr_width) {
744 .p32 => @sizeOf(elf.Elf32_Phdr),
745 .p64 => @sizeOf(elf.Elf64_Phdr),
746 };
747 const phalign: u16 = switch (self.ptr_width) {
748 .p32 => @alignOf(elf.Elf32_Phdr),
749 .p64 => @alignOf(elf.Elf64_Phdr),
750 };
751 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
752 const needed_size = self.program_headers.items.len * phsize;
753
754 if (needed_size > allocated_size) {
755 self.phdr_table_offset = null; // free the space
756 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
757 }
758
759 switch (self.ptr_width) {
760 .p32 => {
761 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
762 defer self.allocator.free(buf);
763
764 for (buf) |*phdr, i| {
765 phdr.* = progHeaderTo32(self.program_headers.items[i]);
766 if (foreign_endian) {
767 bswapAllFields(elf.Elf32_Phdr, phdr);
768 }
625769 }
770 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
771 },
772 .p64 => {
773 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
774 defer self.allocator.free(buf);
775
776 for (buf) |*phdr, i| {
777 phdr.* = self.program_headers.items[i];
778 if (foreign_endian) {
779 bswapAllFields(elf.Elf64_Phdr, phdr);
780 }
781 }
782 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
783 },
784 }
785 self.phdr_table_dirty = false;
786 }
787
788 {
789 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
790 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
791 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
792 const needed_size = self.shstrtab.items.len;
793
794 if (needed_size > allocated_size) {
795 shstrtab_sect.sh_size = 0; // free the space
796 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
626797 }
627 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
628 },
629 .p64 => {
630 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
631 defer self.allocator.free(buf);
798 shstrtab_sect.sh_size = needed_size;
799 std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
632800
633 for (buf) |*shdr, i| {
634 shdr.* = self.sections.items[i];
635 std.log.debug(.link, "writing section {}\n", .{shdr.*});
636 if (foreign_endian) {
637 bswapAllFields(elf.Elf64_Shdr, shdr);
638 }
801 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
802 if (!self.shdr_table_dirty) {
803 // Then it won't get written with the others and we need to do it.
804 try self.writeSectHeader(self.shstrtab_index.?);
639805 }
640 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
641 },
806 self.shstrtab_dirty = false;
807 }
642808 }
643 self.shdr_table_dirty = false;
644 }
645 if (self.entry_addr == null and self.options.output_mode == .Exe) {
646 std.log.debug(.link, "no_entry_point_found = true\n", .{});
647 self.error_flags.no_entry_point_found = true;
648 } else {
649 self.error_flags.no_entry_point_found = false;
650 try self.writeElfHeader();
651 }
809 if (self.shdr_table_dirty) {
810 const shsize: u64 = switch (self.ptr_width) {
811 .p32 => @sizeOf(elf.Elf32_Shdr),
812 .p64 => @sizeOf(elf.Elf64_Shdr),
813 };
814 const shalign: u16 = switch (self.ptr_width) {
815 .p32 => @alignOf(elf.Elf32_Shdr),
816 .p64 => @alignOf(elf.Elf64_Shdr),
817 };
818 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
819 const needed_size = self.sections.items.len * shsize;
652820
653 // The point of flush() is to commit changes, so nothing should be dirty after this.
654 assert(!self.phdr_table_dirty);
655 assert(!self.shdr_table_dirty);
656 assert(!self.shstrtab_dirty);
657 assert(!self.offset_table_count_dirty);
658 const syms_sect = &self.sections.items[self.symtab_section_index.?];
659 assert(syms_sect.sh_info == self.local_symbols.items.len);
660 }
821 if (needed_size > allocated_size) {
822 self.shdr_table_offset = null; // free the space
823 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
824 }
661825
662 fn writeElfHeader(self: *ElfFile) !void {
663 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
826 switch (self.ptr_width) {
827 .p32 => {
828 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
829 defer self.allocator.free(buf);
664830
665 var index: usize = 0;
666 hdr_buf[0..4].* = "\x7fELF".*;
667 index += 4;
831 for (buf) |*shdr, i| {
832 shdr.* = sectHeaderTo32(self.sections.items[i]);
833 if (foreign_endian) {
834 bswapAllFields(elf.Elf32_Shdr, shdr);
835 }
836 }
837 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
838 },
839 .p64 => {
840 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
841 defer self.allocator.free(buf);
842
843 for (buf) |*shdr, i| {
844 shdr.* = self.sections.items[i];
845 std.log.debug(.link, "writing section {}\n", .{shdr.*});
846 if (foreign_endian) {
847 bswapAllFields(elf.Elf64_Shdr, shdr);
848 }
849 }
850 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
851 },
852 }
853 self.shdr_table_dirty = false;
854 }
855 if (self.entry_addr == null and self.options.output_mode == .Exe) {
856 std.log.debug(.link, "no_entry_point_found = true\n", .{});
857 self.error_flags.no_entry_point_found = true;
858 } else {
859 self.error_flags.no_entry_point_found = false;
860 try self.writeElfHeader();
861 }
668862
669 hdr_buf[index] = switch (self.ptr_width) {
670 .p32 => elf.ELFCLASS32,
671 .p64 => elf.ELFCLASS64,
672 };
673 index += 1;
863 // The point of flush() is to commit changes, so nothing should be dirty after this.
864 assert(!self.phdr_table_dirty);
865 assert(!self.shdr_table_dirty);
866 assert(!self.shstrtab_dirty);
867 assert(!self.offset_table_count_dirty);
868 const syms_sect = &self.sections.items[self.symtab_section_index.?];
869 assert(syms_sect.sh_info == self.local_symbols.items.len);
870 }
674871
675 const endian = self.options.target.cpu.arch.endian();
676 hdr_buf[index] = switch (endian) {
677 .Little => elf.ELFDATA2LSB,
678 .Big => elf.ELFDATA2MSB,
679 };
680 index += 1;
872 fn writeElfHeader(self: *Elf) !void {
873 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
681874
682 hdr_buf[index] = 1; // ELF version
683 index += 1;
875 var index: usize = 0;
876 hdr_buf[0..4].* = "\x7fELF".*;
877 index += 4;
684878
685 // OS ABI, often set to 0 regardless of target platform
686 // ABI Version, possibly used by glibc but not by static executables
687 // padding
688 mem.set(u8, hdr_buf[index..][0..9], 0);
689 index += 9;
879 hdr_buf[index] = switch (self.ptr_width) {
880 .p32 => elf.ELFCLASS32,
881 .p64 => elf.ELFCLASS64,
882 };
883 index += 1;
690884
691 assert(index == 16);
885 const endian = self.options.target.cpu.arch.endian();
886 hdr_buf[index] = switch (endian) {
887 .Little => elf.ELFDATA2LSB,
888 .Big => elf.ELFDATA2MSB,
889 };
890 index += 1;
692891
693 const elf_type = switch (self.options.output_mode) {
694 .Exe => elf.ET.EXEC,
695 .Obj => elf.ET.REL,
696 .Lib => switch (self.options.link_mode) {
697 .Static => elf.ET.REL,
698 .Dynamic => elf.ET.DYN,
699 },
700 };
701 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
702 index += 2;
892 hdr_buf[index] = 1; // ELF version
893 index += 1;
703894
704 const machine = self.options.target.cpu.arch.toElfMachine();
705 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
706 index += 2;
895 // OS ABI, often set to 0 regardless of target platform
896 // ABI Version, possibly used by glibc but not by static executables
897 // padding
898 mem.set(u8, hdr_buf[index..][0..9], 0);
899 index += 9;
707900
708 // ELF Version, again
709 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
710 index += 4;
901 assert(index == 16);
711902
712 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
903 const elf_type = switch (self.options.output_mode) {
904 .Exe => elf.ET.EXEC,
905 .Obj => elf.ET.REL,
906 .Lib => switch (self.options.link_mode) {
907 .Static => elf.ET.REL,
908 .Dynamic => elf.ET.DYN,
909 },
910 };
911 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
912 index += 2;
713913
714 switch (self.ptr_width) {
715 .p32 => {
716 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
717 index += 4;
914 const machine = self.options.target.cpu.arch.toElfMachine();
915 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
916 index += 2;
718917
719 // e_phoff
720 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
721 index += 4;
918 // ELF Version, again
919 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
920 index += 4;
722921
723 // e_shoff
724 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
725 index += 4;
726 },
727 .p64 => {
728 // e_entry
729 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
730 index += 8;
731
732 // e_phoff
733 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
734 index += 8;
735
736 // e_shoff
737 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
738 index += 8;
739 },
740 }
922 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
741923
742 const e_flags = 0;
743 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
744 index += 4;
924 switch (self.ptr_width) {
925 .p32 => {
926 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
927 index += 4;
745928
746 const e_ehsize: u16 = switch (self.ptr_width) {
747 .p32 => @sizeOf(elf.Elf32_Ehdr),
748 .p64 => @sizeOf(elf.Elf64_Ehdr),
749 };
750 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
751 index += 2;
929 // e_phoff
930 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
931 index += 4;
752932
753 const e_phentsize: u16 = switch (self.ptr_width) {
754 .p32 => @sizeOf(elf.Elf32_Phdr),
755 .p64 => @sizeOf(elf.Elf64_Phdr),
756 };
757 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
758 index += 2;
933 // e_shoff
934 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
935 index += 4;
936 },
937 .p64 => {
938 // e_entry
939 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
940 index += 8;
759941
760 const e_phnum = @intCast(u16, self.program_headers.items.len);
761 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
762 index += 2;
942 // e_phoff
943 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
944 index += 8;
763945
764 const e_shentsize: u16 = switch (self.ptr_width) {
765 .p32 => @sizeOf(elf.Elf32_Shdr),
766 .p64 => @sizeOf(elf.Elf64_Shdr),
767 };
768 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
769 index += 2;
946 // e_shoff
947 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
948 index += 8;
949 },
950 }
770951
771 const e_shnum = @intCast(u16, self.sections.items.len);
772 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
773 index += 2;
952 const e_flags = 0;
953 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
954 index += 4;
774955
775 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
776 index += 2;
956 const e_ehsize: u16 = switch (self.ptr_width) {
957 .p32 => @sizeOf(elf.Elf32_Ehdr),
958 .p64 => @sizeOf(elf.Elf64_Ehdr),
959 };
960 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
961 index += 2;
777962
778 assert(index == e_ehsize);
963 const e_phentsize: u16 = switch (self.ptr_width) {
964 .p32 => @sizeOf(elf.Elf32_Phdr),
965 .p64 => @sizeOf(elf.Elf64_Phdr),
966 };
967 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
968 index += 2;
779969
780 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
781 }
970 const e_phnum = @intCast(u16, self.program_headers.items.len);
971 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
972 index += 2;
782973
783 fn freeTextBlock(self: *ElfFile, text_block: *TextBlock) void {
784 var already_have_free_list_node = false;
785 {
786 var i: usize = 0;
787 while (i < self.text_block_free_list.items.len) {
788 if (self.text_block_free_list.items[i] == text_block) {
789 _ = self.text_block_free_list.swapRemove(i);
790 continue;
791 }
792 if (self.text_block_free_list.items[i] == text_block.prev) {
793 already_have_free_list_node = true;
794 }
795 i += 1;
796 }
797 }
974 const e_shentsize: u16 = switch (self.ptr_width) {
975 .p32 => @sizeOf(elf.Elf32_Shdr),
976 .p64 => @sizeOf(elf.Elf64_Shdr),
977 };
978 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
979 index += 2;
798980
799 if (self.last_text_block == text_block) {
800 // TODO shrink the .text section size here
801 self.last_text_block = text_block.prev;
802 }
981 const e_shnum = @intCast(u16, self.sections.items.len);
982 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
983 index += 2;
803984
804 if (text_block.prev) |prev| {
805 prev.next = text_block.next;
985 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
986 index += 2;
806987
807 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
808 // The free list is heuristics, it doesn't have to be perfect, so we can
809 // ignore the OOM here.
810 self.text_block_free_list.append(self.allocator, prev) catch {};
811 }
812 } else {
813 text_block.prev = null;
814 }
988 assert(index == e_ehsize);
815989
816 if (text_block.next) |next| {
817 next.prev = text_block.prev;
818 } else {
819 text_block.next = null;
990 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
820991 }
821 }
822992
823 fn shrinkTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64) void {
824 // TODO check the new capacity, and if it crosses the size threshold into a big enough
825 // capacity, insert a free list node for it.
826 }
827
828 fn growTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
829 const sym = self.local_symbols.items[text_block.local_sym_index];
830 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
831 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
832 if (!need_realloc) return sym.st_value;
833 return self.allocateTextBlock(text_block, new_block_size, alignment);
834 }
835
836 fn allocateTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
837 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
838 const shdr = &self.sections.items[self.text_section_index.?];
839 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
840
841 // We use these to indicate our intention to update metadata, placing the new block,
842 // and possibly removing a free list node.
843 // It would be simpler to do it inside the for loop below, but that would cause a
844 // problem if an error was returned later in the function. So this action
845 // is actually carried out at the end of the function, when errors are no longer possible.
846 var block_placement: ?*TextBlock = null;
847 var free_list_removal: ?usize = null;
848
849 // First we look for an appropriately sized free list node.
850 // The list is unordered. We'll just take the first thing that works.
851 const vaddr = blk: {
852 var i: usize = 0;
853 while (i < self.text_block_free_list.items.len) {
854 const big_block = self.text_block_free_list.items[i];
855 // We now have a pointer to a live text block that has too much capacity.
856 // Is it enough that we could fit this new text block?
857 const sym = self.local_symbols.items[big_block.local_sym_index];
858 const capacity = big_block.capacity(self.*);
859 const ideal_capacity = capacity * alloc_num / alloc_den;
860 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
861 const capacity_end_vaddr = sym.st_value + capacity;
862 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
863 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
864 if (new_start_vaddr < ideal_capacity_end_vaddr) {
865 // Additional bookkeeping here to notice if this free list node
866 // should be deleted because the block that it points to has grown to take up
867 // more of the extra capacity.
868 if (!big_block.freeListEligible(self.*)) {
993 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
994 var already_have_free_list_node = false;
995 {
996 var i: usize = 0;
997 while (i < self.text_block_free_list.items.len) {
998 if (self.text_block_free_list.items[i] == text_block) {
869999 _ = self.text_block_free_list.swapRemove(i);
870 } else {
871 i += 1;
1000 continue;
8721001 }
873 continue;
874 }
875 // At this point we know that we will place the new block here. But the
876 // remaining question is whether there is still yet enough capacity left
877 // over for there to still be a free list node.
878 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
879 const keep_free_list_node = remaining_capacity >= min_text_capacity;
880
881 // Set up the metadata to be updated, after errors are no longer possible.
882 block_placement = big_block;
883 if (!keep_free_list_node) {
884 free_list_removal = i;
1002 if (self.text_block_free_list.items[i] == text_block.prev) {
1003 already_have_free_list_node = true;
1004 }
1005 i += 1;
8851006 }
886 break :blk new_start_vaddr;
887 } else if (self.last_text_block) |last| {
888 const sym = self.local_symbols.items[last.local_sym_index];
889 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
890 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
891 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
892 // Set up the metadata to be updated, after errors are no longer possible.
893 block_placement = last;
894 break :blk new_start_vaddr;
895 } else {
896 break :blk phdr.p_vaddr;
8971007 }
898 };
8991008
900 const expand_text_section = block_placement == null or block_placement.?.next == null;
901 if (expand_text_section) {
902 const text_capacity = self.allocatedSize(shdr.sh_offset);
903 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
904 if (needed_size > text_capacity) {
905 // Must move the entire text section.
906 const new_offset = self.findFreeSpace(needed_size, 0x1000);
907 const text_size = if (self.last_text_block) |last| blk: {
908 const sym = self.local_symbols.items[last.local_sym_index];
909 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
910 } else 0;
911 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
912 if (amt != text_size) return error.InputOutput;
913 shdr.sh_offset = new_offset;
914 phdr.p_offset = new_offset;
1009 if (self.last_text_block == text_block) {
1010 // TODO shrink the .text section size here
1011 self.last_text_block = text_block.prev;
9151012 }
916 self.last_text_block = text_block;
9171013
918 shdr.sh_size = needed_size;
919 phdr.p_memsz = needed_size;
920 phdr.p_filesz = needed_size;
1014 if (text_block.prev) |prev| {
1015 prev.next = text_block.next;
9211016
922 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
923 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
924 }
1017 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
1018 // The free list is heuristics, it doesn't have to be perfect, so we can
1019 // ignore the OOM here.
1020 self.text_block_free_list.append(self.allocator, prev) catch {};
1021 }
1022 } else {
1023 text_block.prev = null;
1024 }
9251025
926 // This function can also reallocate a text block.
927 // In this case we need to "unplug" it from its previous location before
928 // plugging it in to its new location.
929 if (text_block.prev) |prev| {
930 prev.next = text_block.next;
931 }
932 if (text_block.next) |next| {
933 next.prev = text_block.prev;
1026 if (text_block.next) |next| {
1027 next.prev = text_block.prev;
1028 } else {
1029 text_block.next = null;
1030 }
9341031 }
9351032
936 if (block_placement) |big_block| {
937 text_block.prev = big_block;
938 text_block.next = big_block.next;
939 big_block.next = text_block;
940 } else {
941 text_block.prev = null;
942 text_block.next = null;
1033 fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
1034 // TODO check the new capacity, and if it crosses the size threshold into a big enough
1035 // capacity, insert a free list node for it.
9431036 }
944 if (free_list_removal) |i| {
945 _ = self.text_block_free_list.swapRemove(i);
946 }
947 return vaddr;
948 }
9491037
950 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
951 if (decl.link.local_sym_index != 0) return;
952
953 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
954 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
955 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
956 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
957 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
958
959 if (self.local_symbol_free_list.popOrNull()) |i| {
960 std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
961 decl.link.local_sym_index = i;
962 } else {
963 std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
964 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
965 _ = self.local_symbols.addOneAssumeCapacity();
1038 fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1039 const sym = self.local_symbols.items[text_block.local_sym_index];
1040 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
1041 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
1042 if (!need_realloc) return sym.st_value;
1043 return self.allocateTextBlock(text_block, new_block_size, alignment);
9661044 }
9671045
968 if (self.offset_table_free_list.popOrNull()) |i| {
969 decl.link.offset_table_index = i;
970 } else {
971 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
972 _ = self.offset_table.addOneAssumeCapacity();
973 self.offset_table_count_dirty = true;
974 }
1046 fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1047 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1048 const shdr = &self.sections.items[self.text_section_index.?];
1049 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
1050
1051 // We use these to indicate our intention to update metadata, placing the new block,
1052 // and possibly removing a free list node.
1053 // It would be simpler to do it inside the for loop below, but that would cause a
1054 // problem if an error was returned later in the function. So this action
1055 // is actually carried out at the end of the function, when errors are no longer possible.
1056 var block_placement: ?*TextBlock = null;
1057 var free_list_removal: ?usize = null;
1058
1059 // First we look for an appropriately sized free list node.
1060 // The list is unordered. We'll just take the first thing that works.
1061 const vaddr = blk: {
1062 var i: usize = 0;
1063 while (i < self.text_block_free_list.items.len) {
1064 const big_block = self.text_block_free_list.items[i];
1065 // We now have a pointer to a live text block that has too much capacity.
1066 // Is it enough that we could fit this new text block?
1067 const sym = self.local_symbols.items[big_block.local_sym_index];
1068 const capacity = big_block.capacity(self.*);
1069 const ideal_capacity = capacity * alloc_num / alloc_den;
1070 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1071 const capacity_end_vaddr = sym.st_value + capacity;
1072 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
1073 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
1074 if (new_start_vaddr < ideal_capacity_end_vaddr) {
1075 // Additional bookkeeping here to notice if this free list node
1076 // should be deleted because the block that it points to has grown to take up
1077 // more of the extra capacity.
1078 if (!big_block.freeListEligible(self.*)) {
1079 _ = self.text_block_free_list.swapRemove(i);
1080 } else {
1081 i += 1;
1082 }
1083 continue;
1084 }
1085 // At this point we know that we will place the new block here. But the
1086 // remaining question is whether there is still yet enough capacity left
1087 // over for there to still be a free list node.
1088 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
1089 const keep_free_list_node = remaining_capacity >= min_text_capacity;
1090
1091 // Set up the metadata to be updated, after errors are no longer possible.
1092 block_placement = big_block;
1093 if (!keep_free_list_node) {
1094 free_list_removal = i;
1095 }
1096 break :blk new_start_vaddr;
1097 } else if (self.last_text_block) |last| {
1098 const sym = self.local_symbols.items[last.local_sym_index];
1099 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
1100 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1101 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
1102 // Set up the metadata to be updated, after errors are no longer possible.
1103 block_placement = last;
1104 break :blk new_start_vaddr;
1105 } else {
1106 break :blk phdr.p_vaddr;
1107 }
1108 };
9751109
976 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1110 const expand_text_section = block_placement == null or block_placement.?.next == null;
1111 if (expand_text_section) {
1112 const text_capacity = self.allocatedSize(shdr.sh_offset);
1113 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
1114 if (needed_size > text_capacity) {
1115 // Must move the entire text section.
1116 const new_offset = self.findFreeSpace(needed_size, 0x1000);
1117 const text_size = if (self.last_text_block) |last| blk: {
1118 const sym = self.local_symbols.items[last.local_sym_index];
1119 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
1120 } else 0;
1121 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
1122 if (amt != text_size) return error.InputOutput;
1123 shdr.sh_offset = new_offset;
1124 phdr.p_offset = new_offset;
1125 }
1126 self.last_text_block = text_block;
9771127
978 self.local_symbols.items[decl.link.local_sym_index] = .{
979 .st_name = 0,
980 .st_info = 0,
981 .st_other = 0,
982 .st_shndx = 0,
983 .st_value = phdr.p_vaddr,
984 .st_size = 0,
985 };
986 self.offset_table.items[decl.link.offset_table_index] = 0;
987 }
1128 shdr.sh_size = needed_size;
1129 phdr.p_memsz = needed_size;
1130 phdr.p_filesz = needed_size;
9881131
989 pub fn freeDecl(self: *ElfFile, decl: *Module.Decl) void {
990 self.freeTextBlock(&decl.link);
991 if (decl.link.local_sym_index != 0) {
992 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
993 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
1132 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1133 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1134 }
9941135
995 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
1136 // This function can also reallocate a text block.
1137 // In this case we need to "unplug" it from its previous location before
1138 // plugging it in to its new location.
1139 if (text_block.prev) |prev| {
1140 prev.next = text_block.next;
1141 }
1142 if (text_block.next) |next| {
1143 next.prev = text_block.prev;
1144 }
9961145
997 decl.link.local_sym_index = 0;
1146 if (block_placement) |big_block| {
1147 text_block.prev = big_block;
1148 text_block.next = big_block.next;
1149 big_block.next = text_block;
1150 } else {
1151 text_block.prev = null;
1152 text_block.next = null;
1153 }
1154 if (free_list_removal) |i| {
1155 _ = self.text_block_free_list.swapRemove(i);
1156 }
1157 return vaddr;
9981158 }
999 }
10001159
1001 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {
1002 var code_buffer = std.ArrayList(u8).init(self.allocator);
1003 defer code_buffer.deinit();
1004
1005 const typed_value = decl.typed_value.most_recent.typed_value;
1006 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1007 .externally_managed => |x| x,
1008 .appended => code_buffer.items,
1009 .fail => |em| {
1010 decl.analysis = .codegen_failure;
1011 _ = try module.failed_decls.put(module.gpa, decl, em);
1012 return;
1013 },
1014 };
1160 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1161 if (decl.link.local_sym_index != 0) return;
10151162
1016 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1163 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
1164 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
1165 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
1166 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
1167 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
10171168
1018 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1019 .Fn => elf.STT_FUNC,
1020 else => elf.STT_OBJECT,
1021 };
1169 if (self.local_symbol_free_list.popOrNull()) |i| {
1170 std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
1171 decl.link.local_sym_index = i;
1172 } else {
1173 std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
1174 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1175 _ = self.local_symbols.addOneAssumeCapacity();
1176 }
10221177
1023 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1024 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1025 if (local_sym.st_size != 0) {
1026 const capacity = decl.link.capacity(self.*);
1027 const need_realloc = code.len > capacity or
1028 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1029 if (need_realloc) {
1030 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1031 std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1032 if (vaddr != local_sym.st_value) {
1033 local_sym.st_value = vaddr;
1034
1035 std.log.debug(.link, " (writing new offset table entry)\n", .{});
1036 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1037 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1038 }
1039 } else if (code.len < local_sym.st_size) {
1040 self.shrinkTextBlock(&decl.link, code.len);
1178 if (self.offset_table_free_list.popOrNull()) |i| {
1179 decl.link.offset_table_index = i;
1180 } else {
1181 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
1182 _ = self.offset_table.addOneAssumeCapacity();
1183 self.offset_table_count_dirty = true;
10411184 }
1042 local_sym.st_size = code.len;
1043 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1044 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1045 local_sym.st_other = 0;
1046 local_sym.st_shndx = self.text_section_index.?;
1047 // TODO this write could be avoided if no fields of the symbol were changed.
1048 try self.writeSymbol(decl.link.local_sym_index);
1049 } else {
1050 const decl_name = mem.spanZ(decl.name);
1051 const name_str_index = try self.makeString(decl_name);
1052 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1053 std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1054 errdefer self.freeTextBlock(&decl.link);
1055
1056 local_sym.* = .{
1057 .st_name = name_str_index,
1058 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1185
1186 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1187
1188 self.local_symbols.items[decl.link.local_sym_index] = .{
1189 .st_name = 0,
1190 .st_info = 0,
10591191 .st_other = 0,
1060 .st_shndx = self.text_section_index.?,
1061 .st_value = vaddr,
1062 .st_size = code.len,
1192 .st_shndx = 0,
1193 .st_value = phdr.p_vaddr,
1194 .st_size = 0,
10631195 };
1064 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1065
1066 try self.writeSymbol(decl.link.local_sym_index);
1067 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1196 self.offset_table.items[decl.link.offset_table_index] = 0;
10681197 }
10691198
1070 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1071 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1072 try self.file.?.pwriteAll(code, file_offset);
1199 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1200 self.freeTextBlock(&decl.link);
1201 if (decl.link.local_sym_index != 0) {
1202 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
1203 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
10731204
1074 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1075 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1076 return self.updateDeclExports(module, decl, decl_exports);
1077 }
1205 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
10781206
1079 /// Must be called only after a successful call to `updateDecl`.
1080 pub fn updateDeclExports(
1081 self: *ElfFile,
1082 module: *Module,
1083 decl: *const Module.Decl,
1084 exports: []const *Module.Export,
1085 ) !void {
1086 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1087 // them, so that deleting exports is guaranteed to succeed.
1088 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1089 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1090 const typed_value = decl.typed_value.most_recent.typed_value;
1091 if (decl.link.local_sym_index == 0) return;
1092 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1093
1094 for (exports) |exp| {
1095 if (exp.options.section) |section_name| {
1096 if (!mem.eql(u8, section_name, ".text")) {
1097 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
1098 module.failed_exports.putAssumeCapacityNoClobber(
1099 exp,
1100 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1101 );
1102 continue;
1103 }
1207 decl.link.local_sym_index = 0;
11041208 }
1105 const stb_bits: u8 = switch (exp.options.linkage) {
1106 .Internal => elf.STB_LOCAL,
1107 .Strong => blk: {
1108 if (mem.eql(u8, exp.options.name, "_start")) {
1109 self.entry_addr = decl_sym.st_value;
1110 }
1111 break :blk elf.STB_GLOBAL;
1112 },
1113 .Weak => elf.STB_WEAK,
1114 .LinkOnce => {
1115 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
1116 module.failed_exports.putAssumeCapacityNoClobber(
1117 exp,
1118 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
1119 );
1120 continue;
1209 }
1210
1211 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1212 var code_buffer = std.ArrayList(u8).init(self.allocator);
1213 defer code_buffer.deinit();
1214
1215 const typed_value = decl.typed_value.most_recent.typed_value;
1216 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1217 .externally_managed => |x| x,
1218 .appended => code_buffer.items,
1219 .fail => |em| {
1220 decl.analysis = .codegen_failure;
1221 try module.failed_decls.put(module.gpa, decl, em);
1222 return;
11211223 },
11221224 };
1123 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
1124 if (exp.link.sym_index) |i| {
1125 const sym = &self.global_symbols.items[i];
1126 sym.* = .{
1127 .st_name = try self.updateString(sym.st_name, exp.options.name),
1128 .st_info = (stb_bits << 4) | stt_bits,
1129 .st_other = 0,
1130 .st_shndx = self.text_section_index.?,
1131 .st_value = decl_sym.st_value,
1132 .st_size = decl_sym.st_size,
1133 };
1225
1226 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1227
1228 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1229 .Fn => elf.STT_FUNC,
1230 else => elf.STT_OBJECT,
1231 };
1232
1233 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1234 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1235 if (local_sym.st_size != 0) {
1236 const capacity = decl.link.capacity(self.*);
1237 const need_realloc = code.len > capacity or
1238 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1239 if (need_realloc) {
1240 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1241 std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1242 if (vaddr != local_sym.st_value) {
1243 local_sym.st_value = vaddr;
1244
1245 std.log.debug(.link, " (writing new offset table entry)\n", .{});
1246 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1247 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1248 }
1249 } else if (code.len < local_sym.st_size) {
1250 self.shrinkTextBlock(&decl.link, code.len);
1251 }
1252 local_sym.st_size = code.len;
1253 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1254 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1255 local_sym.st_other = 0;
1256 local_sym.st_shndx = self.text_section_index.?;
1257 // TODO this write could be avoided if no fields of the symbol were changed.
1258 try self.writeSymbol(decl.link.local_sym_index);
11341259 } else {
1135 const name = try self.makeString(exp.options.name);
1136 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1137 _ = self.global_symbols.addOneAssumeCapacity();
1138 break :blk self.global_symbols.items.len - 1;
1139 };
1140 self.global_symbols.items[i] = .{
1141 .st_name = name,
1142 .st_info = (stb_bits << 4) | stt_bits,
1260 const decl_name = mem.spanZ(decl.name);
1261 const name_str_index = try self.makeString(decl_name);
1262 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1263 std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1264 errdefer self.freeTextBlock(&decl.link);
1265
1266 local_sym.* = .{
1267 .st_name = name_str_index,
1268 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
11431269 .st_other = 0,
11441270 .st_shndx = self.text_section_index.?,
1145 .st_value = decl_sym.st_value,
1146 .st_size = decl_sym.st_size,
1271 .st_value = vaddr,
1272 .st_size = code.len,
11471273 };
1274 self.offset_table.items[decl.link.offset_table_index] = vaddr;
11481275
1149 exp.link.sym_index = @intCast(u32, i);
1276 try self.writeSymbol(decl.link.local_sym_index);
1277 try self.writeOffsetTableEntry(decl.link.offset_table_index);
11501278 }
1151 }
1152 }
11531279
1154 pub fn deleteExport(self: *ElfFile, exp: Export) void {
1155 const sym_index = exp.sym_index orelse return;
1156 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1157 self.global_symbols.items[sym_index].st_info = 0;
1158 }
1280 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1281 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1282 try self.file.?.pwriteAll(code, file_offset);
11591283
1160 fn writeProgHeader(self: *ElfFile, index: usize) !void {
1161 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1162 const offset = self.program_headers.items[index].p_offset;
1163 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1164 32 => {
1165 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1166 if (foreign_endian) {
1167 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1168 }
1169 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1170 },
1171 64 => {
1172 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1173 if (foreign_endian) {
1174 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1175 }
1176 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1177 },
1178 else => return error.UnsupportedArchitecture,
1284 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1285 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1286 return self.updateDeclExports(module, decl, decl_exports);
11791287 }
1180 }
11811288
1182 fn writeSectHeader(self: *ElfFile, index: usize) !void {
1183 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1184 const offset = self.sections.items[index].sh_offset;
1185 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1186 32 => {
1187 var shdr: [1]elf.Elf32_Shdr = undefined;
1188 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1189 if (foreign_endian) {
1190 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1191 }
1192 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1193 },
1194 64 => {
1195 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
1196 if (foreign_endian) {
1197 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1289 /// Must be called only after a successful call to `updateDecl`.
1290 pub fn updateDeclExports(
1291 self: *Elf,
1292 module: *Module,
1293 decl: *const Module.Decl,
1294 exports: []const *Module.Export,
1295 ) !void {
1296 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1297 // them, so that deleting exports is guaranteed to succeed.
1298 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1299 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1300 const typed_value = decl.typed_value.most_recent.typed_value;
1301 if (decl.link.local_sym_index == 0) return;
1302 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1303
1304 for (exports) |exp| {
1305 if (exp.options.section) |section_name| {
1306 if (!mem.eql(u8, section_name, ".text")) {
1307 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
1308 module.failed_exports.putAssumeCapacityNoClobber(
1309 exp,
1310 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1311 );
1312 continue;
1313 }
11981314 }
1199 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1200 },
1201 else => return error.UnsupportedArchitecture,
1202 }
1203 }
1315 const stb_bits: u8 = switch (exp.options.linkage) {
1316 .Internal => elf.STB_LOCAL,
1317 .Strong => blk: {
1318 if (mem.eql(u8, exp.options.name, "_start")) {
1319 self.entry_addr = decl_sym.st_value;
1320 }
1321 break :blk elf.STB_GLOBAL;
1322 },
1323 .Weak => elf.STB_WEAK,
1324 .LinkOnce => {
1325 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
1326 module.failed_exports.putAssumeCapacityNoClobber(
1327 exp,
1328 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
1329 );
1330 continue;
1331 },
1332 };
1333 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
1334 if (exp.link.sym_index) |i| {
1335 const sym = &self.global_symbols.items[i];
1336 sym.* = .{
1337 .st_name = try self.updateString(sym.st_name, exp.options.name),
1338 .st_info = (stb_bits << 4) | stt_bits,
1339 .st_other = 0,
1340 .st_shndx = self.text_section_index.?,
1341 .st_value = decl_sym.st_value,
1342 .st_size = decl_sym.st_size,
1343 };
1344 } else {
1345 const name = try self.makeString(exp.options.name);
1346 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1347 _ = self.global_symbols.addOneAssumeCapacity();
1348 break :blk self.global_symbols.items.len - 1;
1349 };
1350 self.global_symbols.items[i] = .{
1351 .st_name = name,
1352 .st_info = (stb_bits << 4) | stt_bits,
1353 .st_other = 0,
1354 .st_shndx = self.text_section_index.?,
1355 .st_value = decl_sym.st_value,
1356 .st_size = decl_sym.st_size,
1357 };
12041358
1205 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
1206 const shdr = &self.sections.items[self.got_section_index.?];
1207 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1208 const entry_size: u16 = switch (self.ptr_width) {
1209 .p32 => 4,
1210 .p64 => 8,
1211 };
1212 if (self.offset_table_count_dirty) {
1213 // TODO Also detect virtual address collisions.
1214 const allocated_size = self.allocatedSize(shdr.sh_offset);
1215 const needed_size = self.local_symbols.items.len * entry_size;
1216 if (needed_size > allocated_size) {
1217 // Must move the entire got section.
1218 const new_offset = self.findFreeSpace(needed_size, entry_size);
1219 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1220 if (amt != shdr.sh_size) return error.InputOutput;
1221 shdr.sh_offset = new_offset;
1222 phdr.p_offset = new_offset;
1359 exp.link.sym_index = @intCast(u32, i);
1360 }
12231361 }
1224 shdr.sh_size = needed_size;
1225 phdr.p_memsz = needed_size;
1226 phdr.p_filesz = needed_size;
1362 }
12271363
1228 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1229 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1364 pub fn deleteExport(self: *Elf, exp: Export) void {
1365 const sym_index = exp.sym_index orelse return;
1366 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1367 self.global_symbols.items[sym_index].st_info = 0;
1368 }
12301369
1231 self.offset_table_count_dirty = false;
1370 fn writeProgHeader(self: *Elf, index: usize) !void {
1371 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1372 const offset = self.program_headers.items[index].p_offset;
1373 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1374 32 => {
1375 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1376 if (foreign_endian) {
1377 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1378 }
1379 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1380 },
1381 64 => {
1382 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1383 if (foreign_endian) {
1384 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1385 }
1386 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1387 },
1388 else => return error.UnsupportedArchitecture,
1389 }
12321390 }
1233 const endian = self.options.target.cpu.arch.endian();
1234 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1235 switch (self.ptr_width) {
1236 .p32 => {
1237 var buf: [4]u8 = undefined;
1238 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1239 try self.file.?.pwriteAll(&buf, off);
1240 },
1241 .p64 => {
1242 var buf: [8]u8 = undefined;
1243 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1244 try self.file.?.pwriteAll(&buf, off);
1245 },
1391
1392 fn writeSectHeader(self: *Elf, index: usize) !void {
1393 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1394 const offset = self.sections.items[index].sh_offset;
1395 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1396 32 => {
1397 var shdr: [1]elf.Elf32_Shdr = undefined;
1398 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1399 if (foreign_endian) {
1400 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1401 }
1402 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1403 },
1404 64 => {
1405 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
1406 if (foreign_endian) {
1407 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1408 }
1409 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1410 },
1411 else => return error.UnsupportedArchitecture,
1412 }
12461413 }
1247 }
12481414
1249 fn writeSymbol(self: *ElfFile, index: usize) !void {
1250 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1251 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1252 // due to running out of space.
1253 if (self.local_symbols.items.len != syms_sect.sh_info) {
1254 const sym_size: u64 = switch (self.ptr_width) {
1255 .p32 => @sizeOf(elf.Elf32_Sym),
1256 .p64 => @sizeOf(elf.Elf64_Sym),
1257 };
1258 const sym_align: u16 = switch (self.ptr_width) {
1259 .p32 => @alignOf(elf.Elf32_Sym),
1260 .p64 => @alignOf(elf.Elf64_Sym),
1415 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
1416 const shdr = &self.sections.items[self.got_section_index.?];
1417 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1418 const entry_size: u16 = switch (self.ptr_width) {
1419 .p32 => 4,
1420 .p64 => 8,
12611421 };
1262 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1263 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1264 // Move all the symbols to a new file location.
1265 const new_offset = self.findFreeSpace(needed_size, sym_align);
1266 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1267 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1268 if (amt != existing_size) return error.InputOutput;
1269 syms_sect.sh_offset = new_offset;
1422 if (self.offset_table_count_dirty) {
1423 // TODO Also detect virtual address collisions.
1424 const allocated_size = self.allocatedSize(shdr.sh_offset);
1425 const needed_size = self.local_symbols.items.len * entry_size;
1426 if (needed_size > allocated_size) {
1427 // Must move the entire got section.
1428 const new_offset = self.findFreeSpace(needed_size, entry_size);
1429 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1430 if (amt != shdr.sh_size) return error.InputOutput;
1431 shdr.sh_offset = new_offset;
1432 phdr.p_offset = new_offset;
1433 }
1434 shdr.sh_size = needed_size;
1435 phdr.p_memsz = needed_size;
1436 phdr.p_filesz = needed_size;
1437
1438 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1439 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1440
1441 self.offset_table_count_dirty = false;
1442 }
1443 const endian = self.options.target.cpu.arch.endian();
1444 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1445 switch (self.ptr_width) {
1446 .p32 => {
1447 var buf: [4]u8 = undefined;
1448 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1449 try self.file.?.pwriteAll(&buf, off);
1450 },
1451 .p64 => {
1452 var buf: [8]u8 = undefined;
1453 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1454 try self.file.?.pwriteAll(&buf, off);
1455 },
12701456 }
1271 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1272 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1273 self.shdr_table_dirty = true; // TODO look into only writing one section
12741457 }
1275 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1276 switch (self.ptr_width) {
1277 .p32 => {
1278 var sym = [1]elf.Elf32_Sym{
1279 .{
1280 .st_name = self.local_symbols.items[index].st_name,
1281 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1282 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1283 .st_info = self.local_symbols.items[index].st_info,
1284 .st_other = self.local_symbols.items[index].st_other,
1285 .st_shndx = self.local_symbols.items[index].st_shndx,
1286 },
1458
1459 fn writeSymbol(self: *Elf, index: usize) !void {
1460 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1461 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1462 // due to running out of space.
1463 if (self.local_symbols.items.len != syms_sect.sh_info) {
1464 const sym_size: u64 = switch (self.ptr_width) {
1465 .p32 => @sizeOf(elf.Elf32_Sym),
1466 .p64 => @sizeOf(elf.Elf64_Sym),
12871467 };
1288 if (foreign_endian) {
1289 bswapAllFields(elf.Elf32_Sym, &sym[0]);
1290 }
1291 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1292 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1293 },
1294 .p64 => {
1295 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
1296 if (foreign_endian) {
1297 bswapAllFields(elf.Elf64_Sym, &sym[0]);
1468 const sym_align: u16 = switch (self.ptr_width) {
1469 .p32 => @alignOf(elf.Elf32_Sym),
1470 .p64 => @alignOf(elf.Elf64_Sym),
1471 };
1472 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1473 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1474 // Move all the symbols to a new file location.
1475 const new_offset = self.findFreeSpace(needed_size, sym_align);
1476 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1477 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1478 if (amt != existing_size) return error.InputOutput;
1479 syms_sect.sh_offset = new_offset;
12981480 }
1299 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1300 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1301 },
1302 }
1303 }
1304
1305 fn writeAllGlobalSymbols(self: *ElfFile) !void {
1306 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1307 const sym_size: u64 = switch (self.ptr_width) {
1308 .p32 => @sizeOf(elf.Elf32_Sym),
1309 .p64 => @sizeOf(elf.Elf64_Sym),
1310 };
1311 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1312 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1313 switch (self.ptr_width) {
1314 .p32 => {
1315 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1316 defer self.allocator.free(buf);
1317
1318 for (buf) |*sym, i| {
1319 sym.* = .{
1320 .st_name = self.global_symbols.items[i].st_name,
1321 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1322 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1323 .st_info = self.global_symbols.items[i].st_info,
1324 .st_other = self.global_symbols.items[i].st_other,
1325 .st_shndx = self.global_symbols.items[i].st_shndx,
1481 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1482 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1483 self.shdr_table_dirty = true; // TODO look into only writing one section
1484 }
1485 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1486 switch (self.ptr_width) {
1487 .p32 => {
1488 var sym = [1]elf.Elf32_Sym{
1489 .{
1490 .st_name = self.local_symbols.items[index].st_name,
1491 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1492 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1493 .st_info = self.local_symbols.items[index].st_info,
1494 .st_other = self.local_symbols.items[index].st_other,
1495 .st_shndx = self.local_symbols.items[index].st_shndx,
1496 },
13261497 };
13271498 if (foreign_endian) {
1328 bswapAllFields(elf.Elf32_Sym, sym);
1499 bswapAllFields(elf.Elf32_Sym, &sym[0]);
13291500 }
1330 }
1331 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1332 },
1333 .p64 => {
1334 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1335 defer self.allocator.free(buf);
1336
1337 for (buf) |*sym, i| {
1338 sym.* = .{
1339 .st_name = self.global_symbols.items[i].st_name,
1340 .st_value = self.global_symbols.items[i].st_value,
1341 .st_size = self.global_symbols.items[i].st_size,
1342 .st_info = self.global_symbols.items[i].st_info,
1343 .st_other = self.global_symbols.items[i].st_other,
1344 .st_shndx = self.global_symbols.items[i].st_shndx,
1345 };
1501 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1502 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1503 },
1504 .p64 => {
1505 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
13461506 if (foreign_endian) {
1347 bswapAllFields(elf.Elf64_Sym, sym);
1507 bswapAllFields(elf.Elf64_Sym, &sym[0]);
13481508 }
1349 }
1350 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1351 },
1509 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1510 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1511 },
1512 }
13521513 }
1353 }
1514
1515 fn writeAllGlobalSymbols(self: *Elf) !void {
1516 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1517 const sym_size: u64 = switch (self.ptr_width) {
1518 .p32 => @sizeOf(elf.Elf32_Sym),
1519 .p64 => @sizeOf(elf.Elf64_Sym),
1520 };
1521 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1522 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1523 switch (self.ptr_width) {
1524 .p32 => {
1525 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1526 defer self.allocator.free(buf);
1527
1528 for (buf) |*sym, i| {
1529 sym.* = .{
1530 .st_name = self.global_symbols.items[i].st_name,
1531 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1532 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1533 .st_info = self.global_symbols.items[i].st_info,
1534 .st_other = self.global_symbols.items[i].st_other,
1535 .st_shndx = self.global_symbols.items[i].st_shndx,
1536 };
1537 if (foreign_endian) {
1538 bswapAllFields(elf.Elf32_Sym, sym);
1539 }
1540 }
1541 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1542 },
1543 .p64 => {
1544 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1545 defer self.allocator.free(buf);
1546
1547 for (buf) |*sym, i| {
1548 sym.* = .{
1549 .st_name = self.global_symbols.items[i].st_name,
1550 .st_value = self.global_symbols.items[i].st_value,
1551 .st_size = self.global_symbols.items[i].st_size,
1552 .st_info = self.global_symbols.items[i].st_info,
1553 .st_other = self.global_symbols.items[i].st_other,
1554 .st_shndx = self.global_symbols.items[i].st_shndx,
1555 };
1556 if (foreign_endian) {
1557 bswapAllFields(elf.Elf64_Sym, sym);
1558 }
1559 }
1560 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1561 },
1562 }
1563 }
1564 };
13541565};
13551566
13561567/// Truncates the existing file contents and overwrites the contents.
13571568/// Returns an error if `file` is not already open with +read +write +seek abilities.
1358pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
1569pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
13591570 switch (options.output_mode) {
13601571 .Exe => {},
13611572 .Obj => {},
......@@ -1369,7 +1580,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
13691580 .wasm => return error.TODOImplementWritingWasmObjects,
13701581 }
13711582
1372 var self: ElfFile = .{
1583 var self: File.Elf = .{
13731584 .allocator = allocator,
13741585 .file = file,
13751586 .options = options,
......@@ -1413,7 +1624,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
14131624}
14141625
14151626/// Returns error.IncrFailed if incremental update could not be performed.
1416fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
1627fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
14171628 switch (options.output_mode) {
14181629 .Exe => {},
14191630 .Obj => {},
......@@ -1426,7 +1637,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
14261637 .macho => return error.IncrFailed,
14271638 .wasm => return error.IncrFailed,
14281639 }
1429 var self: ElfFile = .{
1640 var self: File.Elf = .{
14301641 .allocator = allocator,
14311642 .file = file,
14321643 .owns_file_handle = false,
src-self-hosted/main.zig+51-43
......@@ -74,7 +74,7 @@ pub fn main() !void {
7474 const args = try process.argsAlloc(arena);
7575
7676 if (args.len <= 1) {
77 std.debug.warn("expected command argument\n\n{}", .{usage});
77 std.debug.print("expected command argument\n\n{}", .{usage});
7878 process.exit(1);
7979 }
8080
......@@ -94,14 +94,14 @@ pub fn main() !void {
9494 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
9595 } else if (mem.eql(u8, cmd, "version")) {
9696 // Need to set up the build script to give the version as a comptime value.
97 std.debug.warn("TODO version command not implemented yet\n", .{});
97 std.debug.print("TODO version command not implemented yet\n", .{});
9898 return error.Unimplemented;
9999 } else if (mem.eql(u8, cmd, "zen")) {
100100 try io.getStdOut().writeAll(info_zen);
101101 } else if (mem.eql(u8, cmd, "help")) {
102102 try io.getStdOut().writeAll(usage);
103103 } else {
104 std.debug.warn("unknown command: {}\n\n{}", .{ args[1], usage });
104 std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage });
105105 process.exit(1);
106106 }
107107}
......@@ -194,6 +194,7 @@ fn buildOutputType(
194194 var emit_zir: Emit = .no;
195195 var target_arch_os_abi: []const u8 = "native";
196196 var target_mcpu: ?[]const u8 = null;
197 var cbe: bool = false;
197198 var target_dynamic_linker: ?[]const u8 = null;
198199
199200 var system_libs = std.ArrayList([]const u8).init(gpa);
......@@ -209,7 +210,7 @@ fn buildOutputType(
209210 process.exit(0);
210211 } else if (mem.eql(u8, arg, "--color")) {
211212 if (i + 1 >= args.len) {
212 std.debug.warn("expected [auto|on|off] after --color\n", .{});
213 std.debug.print("expected [auto|on|off] after --color\n", .{});
213214 process.exit(1);
214215 }
215216 i += 1;
......@@ -221,12 +222,12 @@ fn buildOutputType(
221222 } else if (mem.eql(u8, next_arg, "off")) {
222223 color = .Off;
223224 } else {
224 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
225 std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
225226 process.exit(1);
226227 }
227228 } else if (mem.eql(u8, arg, "--mode")) {
228229 if (i + 1 >= args.len) {
229 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
230 std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
230231 process.exit(1);
231232 }
232233 i += 1;
......@@ -240,52 +241,54 @@ fn buildOutputType(
240241 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
241242 build_mode = .ReleaseSmall;
242243 } else {
243 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
244 std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
244245 process.exit(1);
245246 }
246247 } else if (mem.eql(u8, arg, "--name")) {
247248 if (i + 1 >= args.len) {
248 std.debug.warn("expected parameter after --name\n", .{});
249 std.debug.print("expected parameter after --name\n", .{});
249250 process.exit(1);
250251 }
251252 i += 1;
252253 provided_name = args[i];
253254 } else if (mem.eql(u8, arg, "--library")) {
254255 if (i + 1 >= args.len) {
255 std.debug.warn("expected parameter after --library\n", .{});
256 std.debug.print("expected parameter after --library\n", .{});
256257 process.exit(1);
257258 }
258259 i += 1;
259260 try system_libs.append(args[i]);
260261 } else if (mem.eql(u8, arg, "--version")) {
261262 if (i + 1 >= args.len) {
262 std.debug.warn("expected parameter after --version\n", .{});
263 std.debug.print("expected parameter after --version\n", .{});
263264 process.exit(1);
264265 }
265266 i += 1;
266267 version = std.builtin.Version.parse(args[i]) catch |err| {
267 std.debug.warn("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
268 std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
268269 process.exit(1);
269270 };
270271 } else if (mem.eql(u8, arg, "-target")) {
271272 if (i + 1 >= args.len) {
272 std.debug.warn("expected parameter after -target\n", .{});
273 std.debug.print("expected parameter after -target\n", .{});
273274 process.exit(1);
274275 }
275276 i += 1;
276277 target_arch_os_abi = args[i];
277278 } else if (mem.eql(u8, arg, "-mcpu")) {
278279 if (i + 1 >= args.len) {
279 std.debug.warn("expected parameter after -mcpu\n", .{});
280 std.debug.print("expected parameter after -mcpu\n", .{});
280281 process.exit(1);
281282 }
282283 i += 1;
283284 target_mcpu = args[i];
285 } else if (mem.eql(u8, arg, "--c")) {
286 cbe = true;
284287 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
285288 target_mcpu = arg["-mcpu=".len..];
286289 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
287290 if (i + 1 >= args.len) {
288 std.debug.warn("expected parameter after --dynamic-linker\n", .{});
291 std.debug.print("expected parameter after --dynamic-linker\n", .{});
289292 process.exit(1);
290293 }
291294 i += 1;
......@@ -327,39 +330,39 @@ fn buildOutputType(
327330 } else if (mem.startsWith(u8, arg, "-l")) {
328331 try system_libs.append(arg[2..]);
329332 } else {
330 std.debug.warn("unrecognized parameter: '{}'", .{arg});
333 std.debug.print("unrecognized parameter: '{}'", .{arg});
331334 process.exit(1);
332335 }
333336 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
334 std.debug.warn("assembly files not supported yet", .{});
337 std.debug.print("assembly files not supported yet", .{});
335338 process.exit(1);
336339 } else if (mem.endsWith(u8, arg, ".o") or
337340 mem.endsWith(u8, arg, ".obj") or
338341 mem.endsWith(u8, arg, ".a") or
339342 mem.endsWith(u8, arg, ".lib"))
340343 {
341 std.debug.warn("object files and static libraries not supported yet", .{});
344 std.debug.print("object files and static libraries not supported yet", .{});
342345 process.exit(1);
343346 } else if (mem.endsWith(u8, arg, ".c") or
344347 mem.endsWith(u8, arg, ".cpp"))
345348 {
346 std.debug.warn("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
349 std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
347350 process.exit(1);
348351 } else if (mem.endsWith(u8, arg, ".so") or
349352 mem.endsWith(u8, arg, ".dylib") or
350353 mem.endsWith(u8, arg, ".dll"))
351354 {
352 std.debug.warn("linking against dynamic libraries not yet supported", .{});
355 std.debug.print("linking against dynamic libraries not yet supported", .{});
353356 process.exit(1);
354357 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
355358 if (root_src_file) |other| {
356 std.debug.warn("found another zig file '{}' after root source file '{}'", .{ arg, other });
359 std.debug.print("found another zig file '{}' after root source file '{}'", .{ arg, other });
357360 process.exit(1);
358361 } else {
359362 root_src_file = arg;
360363 }
361364 } else {
362 std.debug.warn("unrecognized file extension of parameter '{}'", .{arg});
365 std.debug.print("unrecognized file extension of parameter '{}'", .{arg});
363366 }
364367 }
365368 }
......@@ -370,13 +373,13 @@ fn buildOutputType(
370373 var it = mem.split(basename, ".");
371374 break :blk it.next() orelse basename;
372375 } else {
373 std.debug.warn("--name [name] not provided and unable to infer\n", .{});
376 std.debug.print("--name [name] not provided and unable to infer\n", .{});
374377 process.exit(1);
375378 }
376379 };
377380
378381 if (system_libs.items.len != 0) {
379 std.debug.warn("linking against system libraries not yet supported", .{});
382 std.debug.print("linking against system libraries not yet supported", .{});
380383 process.exit(1);
381384 }
382385
......@@ -388,17 +391,17 @@ fn buildOutputType(
388391 .diagnostics = &diags,
389392 }) catch |err| switch (err) {
390393 error.UnknownCpuModel => {
391 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
394 std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
392395 diags.cpu_name.?,
393396 @tagName(diags.arch.?),
394397 });
395398 for (diags.arch.?.allCpuModels()) |cpu| {
396 std.debug.warn(" {}\n", .{cpu.name});
399 std.debug.print(" {}\n", .{cpu.name});
397400 }
398401 process.exit(1);
399402 },
400403 error.UnknownCpuFeature => {
401 std.debug.warn(
404 std.debug.print(
402405 \\Unknown CPU feature: '{}'
403406 \\Available CPU features for architecture '{}':
404407 \\
......@@ -407,7 +410,7 @@ fn buildOutputType(
407410 @tagName(diags.arch.?),
408411 });
409412 for (diags.arch.?.allFeaturesList()) |feature| {
410 std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
413 std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
411414 }
412415 process.exit(1);
413416 },
......@@ -419,21 +422,25 @@ fn buildOutputType(
419422 if (target_info.cpu_detection_unimplemented) {
420423 // TODO We want to just use detected_info.target but implementing
421424 // CPU model & feature detection is todo so here we rely on LLVM.
422 std.debug.warn("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
425 std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
423426 process.exit(1);
424427 }
425428
426429 const src_path = root_src_file orelse {
427 std.debug.warn("expected at least one file argument", .{});
430 std.debug.print("expected at least one file argument", .{});
428431 process.exit(1);
429432 };
430433
431434 const bin_path = switch (emit_bin) {
432435 .no => {
433 std.debug.warn("-fno-emit-bin not supported yet", .{});
436 std.debug.print("-fno-emit-bin not supported yet", .{});
434437 process.exit(1);
435438 },
436 .yes_default_path => try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
439 .yes_default_path => if (cbe)
440 try std.fmt.allocPrint(arena, "{}.c", .{root_name})
441 else
442 try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
443
437444 .yes => |p| p,
438445 };
439446
......@@ -463,6 +470,7 @@ fn buildOutputType(
463470 .object_format = object_format,
464471 .optimize_mode = build_mode,
465472 .keep_source_files_loaded = zir_out_path != null,
473 .cbe = cbe,
466474 });
467475 defer module.deinit();
468476
......@@ -509,7 +517,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
509517
510518 if (errors.list.len != 0) {
511519 for (errors.list) |full_err_msg| {
512 std.debug.warn("{}:{}:{}: error: {}\n", .{
520 std.debug.print("{}:{}:{}: error: {}\n", .{
513521 full_err_msg.src_path,
514522 full_err_msg.line + 1,
515523 full_err_msg.column + 1,
......@@ -586,7 +594,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
586594 process.exit(0);
587595 } else if (mem.eql(u8, arg, "--color")) {
588596 if (i + 1 >= args.len) {
589 std.debug.warn("expected [auto|on|off] after --color\n", .{});
597 std.debug.print("expected [auto|on|off] after --color\n", .{});
590598 process.exit(1);
591599 }
592600 i += 1;
......@@ -598,7 +606,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
598606 } else if (mem.eql(u8, next_arg, "off")) {
599607 color = .Off;
600608 } else {
601 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
609 std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
602610 process.exit(1);
603611 }
604612 } else if (mem.eql(u8, arg, "--stdin")) {
......@@ -606,7 +614,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
606614 } else if (mem.eql(u8, arg, "--check")) {
607615 check_flag = true;
608616 } else {
609 std.debug.warn("unrecognized parameter: '{}'", .{arg});
617 std.debug.print("unrecognized parameter: '{}'", .{arg});
610618 process.exit(1);
611619 }
612620 } else {
......@@ -617,7 +625,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
617625
618626 if (stdin_flag) {
619627 if (input_files.items.len != 0) {
620 std.debug.warn("cannot use --stdin with positional arguments\n", .{});
628 std.debug.print("cannot use --stdin with positional arguments\n", .{});
621629 process.exit(1);
622630 }
623631
......@@ -627,7 +635,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
627635 defer gpa.free(source_code);
628636
629637 const tree = std.zig.parse(gpa, source_code) catch |err| {
630 std.debug.warn("error parsing stdin: {}\n", .{err});
638 std.debug.print("error parsing stdin: {}\n", .{err});
631639 process.exit(1);
632640 };
633641 defer tree.deinit();
......@@ -650,7 +658,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
650658 }
651659
652660 if (input_files.items.len == 0) {
653 std.debug.warn("expected at least one source file argument\n", .{});
661 std.debug.print("expected at least one source file argument\n", .{});
654662 process.exit(1);
655663 }
656664
......@@ -667,7 +675,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
667675 for (input_files.span()) |file_path| {
668676 // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
669677 const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
670 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
678 std.debug.print("unable to open '{}': {}\n", .{ file_path, err });
671679 process.exit(1);
672680 };
673681 defer gpa.free(real_path);
......@@ -705,7 +713,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_
705713 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
706714 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
707715 else => {
708 std.debug.warn("unable to format '{}': {}\n", .{ file_path, err });
716 std.debug.print("unable to format '{}': {}\n", .{ file_path, err });
709717 fmt.any_error = true;
710718 return;
711719 },
......@@ -736,7 +744,7 @@ fn fmtPathDir(
736744 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
737745 } else {
738746 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
739 std.debug.warn("unable to format '{}': {}\n", .{ full_path, err });
747 std.debug.print("unable to format '{}': {}\n", .{ full_path, err });
740748 fmt.any_error = true;
741749 return;
742750 };
......@@ -787,7 +795,7 @@ fn fmtPathFile(
787795 if (check_mode) {
788796 const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
789797 if (anything_changed) {
790 std.debug.warn("{}\n", .{file_path});
798 std.debug.print("{}\n", .{file_path});
791799 fmt.any_error = true;
792800 }
793801 } else {
......@@ -803,7 +811,7 @@ fn fmtPathFile(
803811
804812 try af.file.writeAll(fmt.out_buffer.items);
805813 try af.finish();
806 std.debug.warn("{}\n", .{file_path});
814 std.debug.print("{}\n", .{file_path});
807815 }
808816}
809817
src-self-hosted/test.zig+83-32
......@@ -5,6 +5,8 @@ const Allocator = std.mem.Allocator;
55const zir = @import("zir.zig");
66const Package = @import("Package.zig");
77
8const cheader = @embedFile("cbe.h");
9
810test "self-hosted" {
911 var ctx = TestContext.init();
1012 defer ctx.deinit();
......@@ -68,6 +70,7 @@ pub const TestContext = struct {
6870 output_mode: std.builtin.OutputMode,
6971 updates: std.ArrayList(Update),
7072 extension: TestType,
73 cbe: bool = false,
7174
7275 /// Adds a subcase in which the module is updated with `src`, and the
7376 /// resulting ZIR is validated against `result`.
......@@ -187,6 +190,22 @@ pub const TestContext = struct {
187190 return ctx.addObj(name, target, .ZIR);
188191 }
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
190209 pub fn addCompareOutput(
191210 ctx: *TestContext,
192211 name: []const u8,
......@@ -365,13 +384,13 @@ pub const TestContext = struct {
365384 }
366385
367386 fn deinit(self: *TestContext) void {
368 for (self.cases.items) |c| {
369 for (c.updates.items) |u| {
387 for (self.cases.items) |case| {
388 for (case.updates.items) |u| {
370389 if (u.case == .Error) {
371 c.updates.allocator.free(u.case.Error);
390 case.updates.allocator.free(u.case.Error);
372391 }
373392 }
374 c.updates.deinit();
393 case.updates.deinit();
375394 }
376395 self.cases.deinit();
377396 self.* = undefined;
......@@ -415,9 +434,6 @@ pub const TestContext = struct {
415434
416435 var module = try Module.init(allocator, .{
417436 .target = target,
418 // This is an Executable, as opposed to e.g. a *library*. This does
419 // not mean no ZIR is generated.
420 //
421437 // TODO: support tests for object file building, and library builds
422438 // and linking. This will require a rework to support multi-file
423439 // tests.
......@@ -428,6 +444,7 @@ pub const TestContext = struct {
428444 .bin_file_path = bin_name,
429445 .root_pkg = root_pkg,
430446 .keep_source_files_loaded = true,
447 .cbe = case.cbe,
431448 });
432449 defer module.deinit();
433450
......@@ -447,33 +464,65 @@ pub const TestContext = struct {
447464 try module.update();
448465 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
450480 switch (update.case) {
451481 .Transformation => |expected_output| {
452 update_node.estimated_total_items = 5;
453 var emit_node = update_node.start("emit", null);
454 emit_node.activate();
455 var new_zir_module = try zir.emit(allocator, module);
456 defer new_zir_module.deinit(allocator);
457 emit_node.end();
458
459 var write_node = update_node.start("write", null);
460 write_node.activate();
461 var out_zir = std.ArrayList(u8).init(allocator);
462 defer out_zir.deinit();
463 try new_zir_module.writeToStream(allocator, out_zir.outStream());
464 write_node.end();
465
466 var test_node = update_node.start("assert", null);
467 test_node.activate();
468 defer test_node.end();
469 if (expected_output.len != out_zir.items.len) {
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 });
471 std.process.exit(1);
472 }
473 for (expected_output) |e, i| {
474 if (out_zir.items[i] != e) {
475 if (expected_output.len != out_zir.items.len) {
476 std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
482 if (case.cbe) {
483 // The C file is always closed after an update, because we don't support
484 // incremental updates
485 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
486 defer file.close();
487 var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");
488 defer allocator.free(out);
489
490 if (expected_output.len != out.len) {
491 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
492 std.process.exit(1);
493 }
494 for (expected_output) |e, i| {
495 if (out[i] != e) {
496 std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
497 std.process.exit(1);
498 }
499 }
500 } else {
501 update_node.estimated_total_items = 5;
502 var emit_node = update_node.start("emit", null);
503 emit_node.activate();
504 var new_zir_module = try zir.emit(allocator, module);
505 defer new_zir_module.deinit(allocator);
506 emit_node.end();
507
508 var write_node = update_node.start("write", null);
509 write_node.activate();
510 var out_zir = std.ArrayList(u8).init(allocator);
511 defer out_zir.deinit();
512 try new_zir_module.writeToStream(allocator, out_zir.outStream());
513 write_node.end();
514
515 var test_node = update_node.start("assert", null);
516 test_node.activate();
517 defer test_node.end();
518
519 if (expected_output.len != out_zir.items.len) {
520 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 });
521 std.process.exit(1);
522 }
523 for (expected_output) |e, i| {
524 if (out_zir.items[i] != e) {
525 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 });
477526 std.process.exit(1);
478527 }
479528 }
......@@ -511,6 +560,8 @@ pub const TestContext = struct {
511560 }
512561 },
513562 .Execution => |expected_stdout| {
563 std.debug.assert(!case.cbe);
564
514565 update_node.estimated_total_items = 4;
515566 var exec_result = x: {
516567 var exec_node = update_node.start("execute", null);
src/analyze.cpp+4
......@@ -4012,6 +4012,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
40124012 } else if (!is_extern) {
40134013 add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
40144014 implicit_type = g->builtin_types.entry_invalid;
4015 } else if (explicit_type == nullptr) {
4016 // extern variable without explicit type
4017 add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
4018 implicit_type = g->builtin_types.entry_invalid;
40154019 }
40164020
40174021 ZigType *type = explicit_type ? explicit_type : implicit_type;
test/compile_errors.zig+9
......@@ -2,6 +2,15 @@ const tests = @import("tests.zig");
22const std = @import("std");
33
44pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add("extern variable has no type",
6 \\extern var foo;
7 \\pub export fn entry() void {
8 \\ foo;
9 \\}
10 , &[_][]const u8{
11 "tmp.zig:1:1: error: unable to infer variable type",
12 });
13
514 cases.add("@src outside function",
615 \\comptime {
716 \\ @src();
test/stage2/cbe.zig created+90
......@@ -0,0 +1,90 @@
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 // TODO: figure out a way to prevent asm constants from being generated
36 ctx.c("inline asm", linux_x64,
37 \\fn exitGood() void {
38 \\ asm volatile ("syscall"
39 \\ :
40 \\ : [number] "{rax}" (231),
41 \\ [arg1] "{rdi}" (0)
42 \\ );
43 \\}
44 \\
45 \\export fn _start() noreturn {
46 \\ exitGood();
47 \\}
48 ,
49 \\#include <stddef.h>
50 \\
51 \\void exitGood(void);
52 \\
53 \\const char *const exitGood__anon_0 = "{rax}";
54 \\const char *const exitGood__anon_1 = "{rdi}";
55 \\const char *const exitGood__anon_2 = "syscall";
56 \\
57 \\noreturn void _start(void) {
58 \\ exitGood();
59 \\}
60 \\
61 \\void exitGood(void) {
62 \\ register size_t rax_constant __asm__("rax") = 231;
63 \\ register size_t rdi_constant __asm__("rdi") = 0;
64 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
65 \\}
66 \\
67 );
68 //ctx.c("basic return", linux_x64,
69 // \\fn main() u8 {
70 // \\ return 103;
71 // \\}
72 // \\
73 // \\export fn _start() noreturn {
74 // \\ _ = main();
75 // \\}
76 //,
77 // \\#include <stdint.h>
78 // \\
79 // \\uint8_t main(void);
80 // \\
81 // \\noreturn void _start(void) {
82 // \\ (void)main();
83 // \\}
84 // \\
85 // \\uint8_t main(void) {
86 // \\ return 103;
87 // \\}
88 // \\
89 //);
90}
test/stage2/test.zig+1
......@@ -4,4 +4,5 @@ pub fn addCases(ctx: *TestContext) !void {
44 try @import("compile_errors.zig").addCases(ctx);
55 try @import("compare_output.zig").addCases(ctx);
66 try @import("zir.zig").addCases(ctx);
7 try @import("cbe.zig").addCases(ctx);
78}
test/stage2/zir.zig+9-9
......@@ -22,8 +22,8 @@ pub fn addCases(ctx: *TestContext) !void {
2222 ,
2323 \\@void = primitive(void)
2424 \\@fnty = fntype([], @void, cc=C)
25 \\@9 = declref("9$0")
26 \\@9$0 = str("entry")
25 \\@9 = declref("9__anon_0")
26 \\@9__anon_0 = str("entry")
2727 \\@unnamed$4 = str("entry")
2828 \\@unnamed$5 = export(@unnamed$4, "entry")
2929 \\@unnamed$6 = fntype([], @void, cc=C)
......@@ -77,9 +77,9 @@ pub fn addCases(ctx: *TestContext) !void {
7777 \\@entry = fn(@unnamed$6, {
7878 \\ %0 = returnvoid()
7979 \\})
80 \\@entry$1 = str("2\x08\x01\n")
81 \\@9 = declref("9$0")
82 \\@9$0 = str("entry")
80 \\@entry__anon_1 = str("2\x08\x01\n")
81 \\@9 = declref("9__anon_0")
82 \\@9__anon_0 = str("entry")
8383 \\@unnamed$11 = str("entry")
8484 \\@unnamed$12 = export(@unnamed$11, "entry")
8585 \\
......@@ -111,8 +111,8 @@ pub fn addCases(ctx: *TestContext) !void {
111111 ,
112112 \\@void = primitive(void)
113113 \\@fnty = fntype([], @void, cc=C)
114 \\@9 = declref("9$0")
115 \\@9$0 = str("entry")
114 \\@9 = declref("9__anon_0")
115 \\@9__anon_0 = str("entry")
116116 \\@unnamed$4 = str("entry")
117117 \\@unnamed$5 = export(@unnamed$4, "entry")
118118 \\@unnamed$6 = fntype([], @void, cc=C)
......@@ -187,8 +187,8 @@ pub fn addCases(ctx: *TestContext) !void {
187187 ,
188188 \\@void = primitive(void)
189189 \\@fnty = fntype([], @void, cc=C)
190 \\@9 = declref("9$2")
191 \\@9$2 = str("entry")
190 \\@9 = declref("9__anon_2")
191 \\@9__anon_2 = str("entry")
192192 \\@unnamed$4 = str("entry")
193193 \\@unnamed$5 = export(@unnamed$4, "entry")
194194 \\@unnamed$6 = fntype([], @void, cc=C)