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

Merge pull request #5816 from pixelherodev/cbe

Beginnings of C backend

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

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