authorgravatar for noam@pixelhero.devNoam Preil <noam@pixelhero.dev> 2020-07-07 14:55:44-04:00
committergravatar for noam@pixelhero.devNoam Preil <noam@pixelhero.dev> 2020-07-07 14:55:44-04:00
logb4c571301be7dd2174b2d067d643a2a093797e7e
treeb7dcb2e7e0913dffc22d7b30c1eccc7e190561cb
parent0db0258fb2fed196791d3579fb3b893736d28d0a
signaturelock-open Commit is signed but in an unrecognized format.

Stage2: Refactor in preparation for C backend


6 files changed, 1284 insertions(+), 1147 deletions(-)

src-self-hosted/Module.zig+36-24
......@@ -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
......@@ -722,6 +722,12 @@ pub const AllErrors = struct {
722722 }
723723};
724724
725pub const CStandard = enum {
726 C99,
727 GNU99,
728 C11,
729};
730
725731pub const InitOptions = struct {
726732 target: std.Target,
727733 root_pkg: *Package,
......@@ -732,17 +738,19 @@ pub const InitOptions = struct {
732738 object_format: ?std.builtin.ObjectFormat = null,
733739 optimize_mode: std.builtin.Mode = .Debug,
734740 keep_source_files_loaded: bool = false,
741 c_standard: ?CStandard = null,
735742};
736743
737744pub fn init(gpa: *Allocator, options: InitOptions) !Module {
738745 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, .{
746 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
740747 .target = options.target,
741748 .output_mode = options.output_mode,
742749 .link_mode = options.link_mode orelse .Static,
743750 .object_format = options.object_format orelse options.target.getObjectFormat(),
751 .c_standard = options.c_standard,
744752 });
745 errdefer bin_file.deinit();
753 errdefer bin_file.*.deinit();
746754
747755 const root_scope = blk: {
748756 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
......@@ -793,6 +801,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
793801pub fn deinit(self: *Module) void {
794802 self.bin_file.deinit();
795803 const allocator = self.allocator;
804 allocator.destroy(self.bin_file);
796805 self.deletion_set.deinit(allocator);
797806 self.work_queue.deinit();
798807
......@@ -840,7 +849,7 @@ fn freeExportList(allocator: *Allocator, export_list: []*Export) void {
840849}
841850
842851pub fn target(self: Module) std.Target {
843 return self.bin_file.options.target;
852 return self.bin_file.options().target;
844853}
845854
846855/// Detect changes to source files, perform semantic analysis, and update the output files.
......@@ -882,7 +891,7 @@ pub fn update(self: *Module) !void {
882891 try self.deleteDecl(decl);
883892 }
884893
885 self.link_error_flags = self.bin_file.error_flags;
894 self.link_error_flags = self.bin_file.errorFlags();
886895
887896 // If there are any errors, we anticipate the source files being loaded
888897 // to report error messages. Otherwise we unload all source files to save memory.
......@@ -1898,8 +1907,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
18981907 self.decl_exports.removeAssertDiscard(exp.exported_decl);
18991908 }
19001909 }
1901
1902 self.bin_file.deleteExport(exp.link);
1910 if (self.bin_file.cast(link.File.Elf)) |elf| {
1911 elf.deleteExport(exp.link);
1912 }
19031913 if (self.failed_exports.remove(exp)) |entry| {
19041914 entry.value.destroy(self.allocator);
19051915 }
......@@ -1961,7 +1971,7 @@ fn allocateNewDecl(
19611971 .analysis = .unreferenced,
19621972 .deletion_flag = false,
19631973 .contents_hash = contents_hash,
1964 .link = link.ElfFile.TextBlock.empty,
1974 .link = link.File.Elf.TextBlock.empty,
19651975 .generation = 0,
19661976 };
19671977 return new_decl;
......@@ -2189,19 +2199,21 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
21892199 }
21902200
21912201 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 };
2202 if (self.bin_file.cast(link.File.Elf)) |elf| {
2203 elf.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
2204 error.OutOfMemory => return error.OutOfMemory,
2205 else => {
2206 try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);
2207 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
2208 self.allocator,
2209 src,
2210 "unable to export: {}",
2211 .{@errorName(err)},
2212 ));
2213 new_export.status = .failed_retryable;
2214 },
2215 };
2216 }
22052217}
22062218
22072219fn addNewInstArgs(
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+1205-1118
......@@ -21,6 +21,7 @@ pub const Options = struct {
2121 /// Used for calculating how much space to reserve for executable program code in case
2222 /// the binary file deos not already have such a section.
2323 program_code_size_hint: u64 = 256 * 1024,
24 c_standard: ?Module.CStandard = null,
2425};
2526
2627/// Attempts incremental linking, if the file already exists.
......@@ -32,13 +33,19 @@ pub fn openBinFilePath(
3233 dir: fs.Dir,
3334 sub_path: []const u8,
3435 options: Options,
35) !ElfFile {
36) !*File {
3637 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });
3738 errdefer file.close();
3839
39 var bin_file = try openBinFile(allocator, file, options);
40 bin_file.owns_file_handle = true;
41 return bin_file;
40 if (options.c_standard) |cstd| {
41 return error.Unimplemented;
42 } else {
43 var bin_file = try allocator.create(File.Elf);
44 errdefer allocator.destroy(bin_file);
45 bin_file.* = try openBinFile(allocator, file, options);
46 bin_file.owns_file_handle = true;
47 return &bin_file.base;
48 }
4249}
4350
4451/// Atomically overwrites the old file, if present.
......@@ -80,7 +87,7 @@ pub fn writeFilePath(
8087/// Returns an error if `file` is not already open with +read +write +seek abilities.
8188/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
8289/// This operation is not atomic.
83pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
90pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
8491 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
8592 error.IncrFailed => {
8693 return createElfFile(allocator, file, options);
......@@ -89,447 +96,496 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF
8996 };
9097}
9198
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;
99pub const File = struct {
100 tag: Tag,
101 pub fn cast(base: *File, comptime T: type) ?*T {
102 if (base.tag != T.base_tag)
103 return null;
172104
173 pub const ErrorFlags = struct {
174 no_entry_point_found: bool = false,
175 };
105 return @fieldParentPtr(T, "base", base);
106 }
176107
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,
108 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
109 try switch (base.tag) {
110 .Elf => @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
111 else => unreachable,
197112 };
198
199 /// Returns how much room there is to grow in virtual address space.
200 /// File offset relocation happens transparently, so it is not included in
201 /// this calculation.
202 fn capacity(self: TextBlock, elf_file: ElfFile) u64 {
203 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
204 if (self.next) |next| {
205 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
206 return next_sym.st_value - self_sym.st_value;
207 } else {
208 // We are the last block. The capacity is limited only by virtual address space.
209 return std.math.maxInt(u32) - self_sym.st_value;
210 }
211 }
212
213 fn freeListEligible(self: TextBlock, elf_file: ElfFile) bool {
214 // No need to keep a free list node for the last block.
215 const next = self.next orelse return false;
216 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
217 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
218 const cap = next_sym.st_value - self_sym.st_value;
219 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
220 if (cap <= ideal_cap) return false;
221 const surplus = cap - ideal_cap;
222 return surplus >= min_text_capacity;
223 }
224 };
225
226 pub const Export = struct {
227 sym_index: ?u32 = null,
228 };
229
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();
243 }
244113 }
245114
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;
251 }
115 pub fn makeExecutable(base: *File) !void {
116 try switch (base.tag) {
117 .Elf => @fieldParentPtr(Elf, "base", base).makeExecutable(),
118 else => unreachable,
119 };
252120 }
253121
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 });
122 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
123 try switch (base.tag) {
124 .Elf => @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
125 else => unreachable,
126 };
262127 }
263128
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 }
281 }
282
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 }
129 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
130 try switch (base.tag) {
131 .Elf => @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
132 else => unreachable,
133 };
134 }
292135
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 }
136 pub fn deinit(base: *File) void {
137 switch (base.tag) {
138 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),
139 else => unreachable,
306140 }
307 return null;
308141 }
309142
310 fn allocatedSize(self: *ElfFile, start: u64) u64 {
311 var min_pos: u64 = std.math.maxInt(u64);
312 if (self.shdr_table_offset) |off| {
313 if (off > start and off < min_pos) min_pos = off;
314 }
315 if (self.phdr_table_offset) |off| {
316 if (off > start and off < min_pos) min_pos = off;
317 }
318 for (self.sections.items) |section| {
319 if (section.sh_offset <= start) continue;
320 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
321 }
322 for (self.program_headers.items) |program_header| {
323 if (program_header.p_offset <= start) continue;
324 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
325 }
326 return min_pos - start;
143 pub fn flush(base: *File) !void {
144 try switch (base.tag) {
145 .Elf => @fieldParentPtr(Elf, "base", base).flush(),
146 else => unreachable,
147 };
327148 }
328149
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);
150 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
151 switch (base.tag) {
152 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
153 else => unreachable,
333154 }
334 return start;
335155 }
336156
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);
157 pub fn errorFlags(base: *File) ErrorFlags {
158 return switch (base.tag) {
159 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,
160 else => unreachable,
161 };
343162 }
344163
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));
164 pub fn options(base: *File) Options {
165 return switch (base.tag) {
166 .Elf => @fieldParentPtr(Elf, "base", base).options,
167 else => unreachable,
168 };
348169 }
349170
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 }
171 pub const Tag = enum {
172 Elf,
173 C,
174 };
357175
358 pub fn populateMissingMetadata(self: *ElfFile) !void {
359 const small_ptr = switch (self.ptr_width) {
360 .p32 => true,
361 .p64 => false,
176 pub const ErrorFlags = struct {
177 no_entry_point_found: bool = false,
178 };
179 pub const Elf = struct {
180 pub const base_tag: Tag = .Elf;
181 base: File = File{ .tag = base_tag },
182
183 allocator: *Allocator,
184 file: ?fs.File,
185 owns_file_handle: bool,
186 options: Options,
187 ptr_width: enum { p32, p64 },
188
189 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
190 /// Same order as in the file.
191 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
192 shdr_table_offset: ?u64 = null,
193
194 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
195 /// Same order as in the file.
196 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
197 phdr_table_offset: ?u64 = null,
198 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
199 phdr_load_re_index: ?u16 = null,
200 /// The index into the program headers of the global offset table.
201 /// It needs PT_LOAD and Read flags.
202 phdr_got_index: ?u16 = null,
203 entry_addr: ?u64 = null,
204
205 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
206 shstrtab_index: ?u16 = null,
207
208 text_section_index: ?u16 = null,
209 symtab_section_index: ?u16 = null,
210 got_section_index: ?u16 = null,
211
212 /// The same order as in the file. ELF requires global symbols to all be after the
213 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
214 /// write them at the end. These are only the local symbols. The length of this array
215 /// is the value used for sh_info in the .symtab section.
216 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
217 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
218
219 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
220 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
221 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
222
223 /// Same order as in the file. The value is the absolute vaddr value.
224 /// If the vaddr of the executable program header changes, the entire
225 /// offset table needs to be rewritten.
226 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
227
228 phdr_table_dirty: bool = false,
229 shdr_table_dirty: bool = false,
230 shstrtab_dirty: bool = false,
231 offset_table_count_dirty: bool = false,
232
233 error_flags: ErrorFlags = ErrorFlags{},
234
235 /// A list of text blocks that have surplus capacity. This list can have false
236 /// positives, as functions grow and shrink over time, only sometimes being added
237 /// or removed from the freelist.
238 ///
239 /// A text block has surplus capacity when its overcapacity value is greater than
240 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
241 /// much extra capacity, that we could fit a small new symbol in it, itself with
242 /// ideal_capacity or more.
243 ///
244 /// Ideal capacity is defined by size * alloc_num / alloc_den.
245 ///
246 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
247 /// overcapacity can be negative. A simple way to have negative overcapacity is to
248 /// allocate a fresh text block, which will have ideal capacity, and then grow it
249 /// by 1 byte. It will then have -1 overcapacity.
250 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
251 last_text_block: ?*TextBlock = null,
252
253 /// `alloc_num / alloc_den` is the factor of padding when allocating.
254 const alloc_num = 4;
255 const alloc_den = 3;
256
257 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
258 /// it as a possible place to put new symbols, it must have enough room for this many bytes
259 /// (plus extra for reserved capacity).
260 const minimum_text_block_size = 64;
261 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
262
263 pub const TextBlock = struct {
264 /// Each decl always gets a local symbol with the fully qualified name.
265 /// The vaddr and size are found here directly.
266 /// The file offset is found by computing the vaddr offset from the section vaddr
267 /// the symbol references, and adding that to the file offset of the section.
268 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
269 /// offset table entry.
270 local_sym_index: u32,
271 /// This field is undefined for symbols with size = 0.
272 offset_table_index: u32,
273 /// Points to the previous and next neighbors, based on the `text_offset`.
274 /// This can be used to find, for example, the capacity of this `TextBlock`.
275 prev: ?*TextBlock,
276 next: ?*TextBlock,
277
278 pub const empty = TextBlock{
279 .local_sym_index = 0,
280 .offset_table_index = undefined,
281 .prev = null,
282 .next = null,
283 };
284
285 /// Returns how much room there is to grow in virtual address space.
286 /// File offset relocation happens transparently, so it is not included in
287 /// this calculation.
288 fn capacity(self: TextBlock, elf_file: File.Elf) u64 {
289 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
290 if (self.next) |next| {
291 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
292 return next_sym.st_value - self_sym.st_value;
293 } else {
294 // We are the last block. The capacity is limited only by virtual address space.
295 return std.math.maxInt(u32) - self_sym.st_value;
296 }
297 }
298
299 fn freeListEligible(self: TextBlock, elf_file: File.Elf) bool {
300 // No need to keep a free list node for the last block.
301 const next = self.next orelse return false;
302 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
303 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
304 const cap = next_sym.st_value - self_sym.st_value;
305 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
306 if (cap <= ideal_cap) return false;
307 const surplus = cap - ideal_cap;
308 return surplus >= min_text_capacity;
309 }
362310 };
363 const ptr_size: u8 = switch (self.ptr_width) {
364 .p32 => 4,
365 .p64 => 8,
311
312 pub const Export = struct {
313 sym_index: ?u32 = null,
366314 };
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;
315
316 pub fn deinit(self: *File.Elf) void {
317 self.sections.deinit(self.allocator);
318 self.program_headers.deinit(self.allocator);
319 self.shstrtab.deinit(self.allocator);
320 self.local_symbols.deinit(self.allocator);
321 self.global_symbols.deinit(self.allocator);
322 self.global_symbol_free_list.deinit(self.allocator);
323 self.local_symbol_free_list.deinit(self.allocator);
324 self.offset_table_free_list.deinit(self.allocator);
325 self.text_block_free_list.deinit(self.allocator);
326 self.offset_table.deinit(self.allocator);
327 if (self.owns_file_handle) {
328 if (self.file) |f| f.close();
329 }
385330 }
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;
331
332 pub fn makeExecutable(self: *File.Elf) !void {
333 assert(self.owns_file_handle);
334 if (self.file) |f| {
335 f.close();
336 self.file = null;
337 }
409338 }
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,
339
340 pub fn makeWritable(self: *File.Elf, dir: fs.Dir, sub_path: []const u8) !void {
341 assert(self.owns_file_handle);
342 if (self.file != null) return;
343 self.file = try dir.createFile(sub_path, .{
344 .truncate = false,
345 .read = true,
346 .mode = determineMode(self.options),
427347 });
428 self.shstrtab_dirty = true;
429 self.shdr_table_dirty = true;
430348 }
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.?];
434349
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;
350 /// Returns end pos of collision, if any.
351 fn detectAllocCollision(self: *File.Elf, start: u64, size: u64) ?u64 {
352 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
353 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
354 if (start < ehdr_size)
355 return ehdr_size;
356
357 const end = start + satMul(size, alloc_num) / alloc_den;
358
359 if (self.shdr_table_offset) |off| {
360 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
361 const tight_size = self.sections.items.len * shdr_size;
362 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
363 const test_end = off + increased_size;
364 if (end > off and start < test_end) {
365 return test_end;
366 }
367 }
368
369 if (self.phdr_table_offset) |off| {
370 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
371 const tight_size = self.sections.items.len * phdr_size;
372 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
373 const test_end = off + increased_size;
374 if (end > off and start < test_end) {
375 return test_end;
376 }
377 }
378
379 for (self.sections.items) |section| {
380 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
381 const test_end = section.sh_offset + increased_size;
382 if (end > section.sh_offset and start < test_end) {
383 return test_end;
384 }
385 }
386 for (self.program_headers.items) |program_header| {
387 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
388 const test_end = program_header.p_offset + increased_size;
389 if (end > program_header.p_offset and start < test_end) {
390 return test_end;
391 }
392 }
393 return null;
448394 }
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.?];
452395
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;
396 fn allocatedSize(self: *File.Elf, start: u64) u64 {
397 var min_pos: u64 = std.math.maxInt(u64);
398 if (self.shdr_table_offset) |off| {
399 if (off > start and off < min_pos) min_pos = off;
400 }
401 if (self.phdr_table_offset) |off| {
402 if (off > start and off < min_pos) min_pos = off;
403 }
404 for (self.sections.items) |section| {
405 if (section.sh_offset <= start) continue;
406 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
407 }
408 for (self.program_headers.items) |program_header| {
409 if (program_header.p_offset <= start) continue;
410 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
411 }
412 return min_pos - start;
466413 }
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);
414
415 fn findFreeSpace(self: *File.Elf, object_size: u64, min_alignment: u16) u64 {
416 var start: u64 = 0;
417 while (self.detectAllocCollision(start, object_size)) |item_end| {
418 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
419 }
420 return start;
490421 }
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;
422
423 fn makeString(self: *File.Elf, bytes: []const u8) !u32 {
424 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
425 const result = self.shstrtab.items.len;
426 self.shstrtab.appendSliceAssumeCapacity(bytes);
427 self.shstrtab.appendAssumeCapacity(0);
428 return @intCast(u32, result);
502429 }
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;
430
431 fn getString(self: *File.Elf, str_off: u32) []const u8 {
432 assert(str_off < self.shstrtab.items.len);
433 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
514434 }
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");
435
436 fn updateString(self: *File.Elf, old_str_off: u32, new_name: []const u8) !u32 {
437 const existing_name = self.getString(old_str_off);
438 if (mem.eql(u8, existing_name, new_name)) {
439 return old_str_off;
519440 }
520 // We are starting with an empty file. The default values are correct, null and empty list.
441 return self.makeString(new_name);
521442 }
522 }
523
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();
527443
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) {
444 pub fn populateMissingMetadata(self: *File.Elf) !void {
445 const small_ptr = switch (self.ptr_width) {
446 .p32 => true,
447 .p64 => false,
448 };
449 const ptr_size: u8 = switch (self.ptr_width) {
450 .p32 => 4,
451 .p64 => 8,
452 };
453 if (self.phdr_load_re_index == null) {
454 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
455 const file_size = self.options.program_code_size_hint;
456 const p_align = 0x1000;
457 const off = self.findFreeSpace(file_size, p_align);
458 //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
459 try self.program_headers.append(self.allocator, .{
460 .p_type = elf.PT_LOAD,
461 .p_offset = off,
462 .p_filesz = file_size,
463 .p_vaddr = default_entry_addr,
464 .p_paddr = default_entry_addr,
465 .p_memsz = file_size,
466 .p_align = p_align,
467 .p_flags = elf.PF_X | elf.PF_R,
468 });
469 self.entry_addr = null;
470 self.phdr_table_dirty = true;
471 }
472 if (self.phdr_got_index == null) {
473 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
474 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
475 // We really only need ptr alignment but since we are using PROGBITS, linux requires
476 // page align.
477 const p_align = 0x1000;
478 const off = self.findFreeSpace(file_size, p_align);
479 //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
480 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
481 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
482 // else in virtual memory.
483 const default_got_addr = 0x4000000;
484 try self.program_headers.append(self.allocator, .{
485 .p_type = elf.PT_LOAD,
486 .p_offset = off,
487 .p_filesz = file_size,
488 .p_vaddr = default_got_addr,
489 .p_paddr = default_got_addr,
490 .p_memsz = file_size,
491 .p_align = p_align,
492 .p_flags = elf.PF_R,
493 });
494 self.phdr_table_dirty = true;
495 }
496 if (self.shstrtab_index == null) {
497 self.shstrtab_index = @intCast(u16, self.sections.items.len);
498 assert(self.shstrtab.items.len == 0);
499 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
500 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
501 //std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
502 try self.sections.append(self.allocator, .{
503 .sh_name = try self.makeString(".shstrtab"),
504 .sh_type = elf.SHT_STRTAB,
505 .sh_flags = 0,
506 .sh_addr = 0,
507 .sh_offset = off,
508 .sh_size = self.shstrtab.items.len,
509 .sh_link = 0,
510 .sh_info = 0,
511 .sh_addralign = 1,
512 .sh_entsize = 0,
513 });
514 self.shstrtab_dirty = true;
515 self.shdr_table_dirty = true;
516 }
517 if (self.text_section_index == null) {
518 self.text_section_index = @intCast(u16, self.sections.items.len);
519 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
520
521 try self.sections.append(self.allocator, .{
522 .sh_name = try self.makeString(".text"),
523 .sh_type = elf.SHT_PROGBITS,
524 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
525 .sh_addr = phdr.p_vaddr,
526 .sh_offset = phdr.p_offset,
527 .sh_size = phdr.p_filesz,
528 .sh_link = 0,
529 .sh_info = 0,
530 .sh_addralign = phdr.p_align,
531 .sh_entsize = 0,
532 });
533 self.shdr_table_dirty = true;
534 }
535 if (self.got_section_index == null) {
536 self.got_section_index = @intCast(u16, self.sections.items.len);
537 const phdr = &self.program_headers.items[self.phdr_got_index.?];
538
539 try self.sections.append(self.allocator, .{
540 .sh_name = try self.makeString(".got"),
541 .sh_type = elf.SHT_PROGBITS,
542 .sh_flags = elf.SHF_ALLOC,
543 .sh_addr = phdr.p_vaddr,
544 .sh_offset = phdr.p_offset,
545 .sh_size = phdr.p_filesz,
546 .sh_link = 0,
547 .sh_info = 0,
548 .sh_addralign = phdr.p_align,
549 .sh_entsize = 0,
550 });
551 self.shdr_table_dirty = true;
552 }
553 if (self.symtab_section_index == null) {
554 self.symtab_section_index = @intCast(u16, self.sections.items.len);
555 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
556 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
557 const file_size = self.options.symbol_count_hint * each_size;
558 const off = self.findFreeSpace(file_size, min_align);
559 //std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
560
561 try self.sections.append(self.allocator, .{
562 .sh_name = try self.makeString(".symtab"),
563 .sh_type = elf.SHT_SYMTAB,
564 .sh_flags = 0,
565 .sh_addr = 0,
566 .sh_offset = off,
567 .sh_size = file_size,
568 // The section header index of the associated string table.
569 .sh_link = self.shstrtab_index.?,
570 .sh_info = @intCast(u32, self.local_symbols.items.len),
571 .sh_addralign = min_align,
572 .sh_entsize = each_size,
573 });
574 self.shdr_table_dirty = true;
575 try self.writeSymbol(0);
576 }
577 const shsize: u64 = switch (self.ptr_width) {
578 .p32 => @sizeOf(elf.Elf32_Shdr),
579 .p64 => @sizeOf(elf.Elf64_Shdr),
580 };
581 const shalign: u16 = switch (self.ptr_width) {
582 .p32 => @alignOf(elf.Elf32_Shdr),
583 .p64 => @alignOf(elf.Elf64_Shdr),
584 };
585 if (self.shdr_table_offset == null) {
586 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
587 self.shdr_table_dirty = true;
588 }
533589 const phsize: u64 = switch (self.ptr_width) {
534590 .p32 => @sizeOf(elf.Elf32_Phdr),
535591 .p64 => @sizeOf(elf.Elf64_Phdr),
......@@ -538,823 +594,854 @@ pub const ElfFile = struct {
538594 .p32 => @alignOf(elf.Elf32_Phdr),
539595 .p64 => @alignOf(elf.Elf64_Phdr),
540596 };
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);
597 if (self.phdr_table_offset == null) {
598 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
599 self.phdr_table_dirty = true;
547600 }
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 },
601 {
602 // Iterate over symbols, populating free_list and last_text_block.
603 if (self.local_symbols.items.len != 1) {
604 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
605 }
606 // We are starting with an empty file. The default values are correct, null and empty list.
574607 }
575 self.phdr_table_dirty = false;
576608 }
577609
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;
610 /// Commit pending changes and write headers.
611 pub fn flush(self: *File.Elf) !void {
612 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
613
614 // Unfortunately these have to be buffered and done at the end because ELF does not allow
615 // mixing local and global symbols within a symbol table.
616 try self.writeAllGlobalSymbols();
617
618 if (self.phdr_table_dirty) {
619 const phsize: u64 = switch (self.ptr_width) {
620 .p32 => @sizeOf(elf.Elf32_Phdr),
621 .p64 => @sizeOf(elf.Elf64_Phdr),
622 };
623 const phalign: u16 = switch (self.ptr_width) {
624 .p32 => @alignOf(elf.Elf32_Phdr),
625 .p64 => @alignOf(elf.Elf64_Phdr),
626 };
627 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
628 const needed_size = self.program_headers.items.len * phsize;
583629
584630 if (needed_size > allocated_size) {
585 shstrtab_sect.sh_size = 0; // free the space
586 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
631 self.phdr_table_offset = null; // free the space
632 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
587633 }
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 });
590634
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.?);
635 switch (self.ptr_width) {
636 .p32 => {
637 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
638 defer self.allocator.free(buf);
639
640 for (buf) |*phdr, i| {
641 phdr.* = progHeaderTo32(self.program_headers.items[i]);
642 if (foreign_endian) {
643 bswapAllFields(elf.Elf32_Phdr, phdr);
644 }
645 }
646 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
647 },
648 .p64 => {
649 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
650 defer self.allocator.free(buf);
651
652 for (buf) |*phdr, i| {
653 phdr.* = self.program_headers.items[i];
654 if (foreign_endian) {
655 bswapAllFields(elf.Elf64_Phdr, phdr);
656 }
657 }
658 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
659 },
595660 }
596 self.shstrtab_dirty = false;
661 self.phdr_table_dirty = false;
597662 }
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;
610663
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);
614 }
664 {
665 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
666 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
667 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
668 const needed_size = self.shstrtab.items.len;
615669
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);
670 if (needed_size > allocated_size) {
671 shstrtab_sect.sh_size = 0; // free the space
672 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
673 }
674 shstrtab_sect.sh_size = needed_size;
675 //std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
620676
621 for (buf) |*shdr, i| {
622 shdr.* = sectHeaderTo32(self.sections.items[i]);
623 if (foreign_endian) {
624 bswapAllFields(elf.Elf32_Shdr, shdr);
625 }
677 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
678 if (!self.shdr_table_dirty) {
679 // Then it won't get written with the others and we need to do it.
680 try self.writeSectHeader(self.shstrtab_index.?);
626681 }
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);
682 self.shstrtab_dirty = false;
683 }
684 }
685 if (self.shdr_table_dirty) {
686 const shsize: u64 = switch (self.ptr_width) {
687 .p32 => @sizeOf(elf.Elf32_Shdr),
688 .p64 => @sizeOf(elf.Elf64_Shdr),
689 };
690 const shalign: u16 = switch (self.ptr_width) {
691 .p32 => @alignOf(elf.Elf32_Shdr),
692 .p64 => @alignOf(elf.Elf64_Shdr),
693 };
694 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
695 const needed_size = self.sections.items.len * shsize;
632696
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);
697 if (needed_size > allocated_size) {
698 self.shdr_table_offset = null; // free the space
699 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
700 }
701
702 switch (self.ptr_width) {
703 .p32 => {
704 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
705 defer self.allocator.free(buf);
706
707 for (buf) |*shdr, i| {
708 shdr.* = sectHeaderTo32(self.sections.items[i]);
709 if (foreign_endian) {
710 bswapAllFields(elf.Elf32_Shdr, shdr);
711 }
638712 }
639 }
640 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
641 },
713 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
714 },
715 .p64 => {
716 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
717 defer self.allocator.free(buf);
718
719 for (buf) |*shdr, i| {
720 shdr.* = self.sections.items[i];
721 //std.log.debug(.link, "writing section {}\n", .{shdr.*});
722 if (foreign_endian) {
723 bswapAllFields(elf.Elf64_Shdr, shdr);
724 }
725 }
726 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
727 },
728 }
729 self.shdr_table_dirty = false;
642730 }
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();
731 if (self.entry_addr == null and self.options.output_mode == .Exe) {
732 self.error_flags.no_entry_point_found = true;
733 } else {
734 self.error_flags.no_entry_point_found = false;
735 try self.writeElfHeader();
736 }
737
738 // The point of flush() is to commit changes, so nothing should be dirty after this.
739 assert(!self.phdr_table_dirty);
740 assert(!self.shdr_table_dirty);
741 assert(!self.shstrtab_dirty);
742 assert(!self.offset_table_count_dirty);
743 const syms_sect = &self.sections.items[self.symtab_section_index.?];
744 assert(syms_sect.sh_info == self.local_symbols.items.len);
650745 }
651746
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 }
747 fn writeElfHeader(self: *File.Elf) !void {
748 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
660749
661 fn writeElfHeader(self: *ElfFile) !void {
662 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
750 var index: usize = 0;
751 hdr_buf[0..4].* = "\x7fELF".*;
752 index += 4;
663753
664 var index: usize = 0;
665 hdr_buf[0..4].* = "\x7fELF".*;
666 index += 4;
754 hdr_buf[index] = switch (self.ptr_width) {
755 .p32 => elf.ELFCLASS32,
756 .p64 => elf.ELFCLASS64,
757 };
758 index += 1;
667759
668 hdr_buf[index] = switch (self.ptr_width) {
669 .p32 => elf.ELFCLASS32,
670 .p64 => elf.ELFCLASS64,
671 };
672 index += 1;
760 const endian = self.options.target.cpu.arch.endian();
761 hdr_buf[index] = switch (endian) {
762 .Little => elf.ELFDATA2LSB,
763 .Big => elf.ELFDATA2MSB,
764 };
765 index += 1;
673766
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;
680
681 hdr_buf[index] = 1; // ELF version
682 index += 1;
683
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;
689
690 assert(index == 16);
691
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;
702
703 const machine = self.options.target.cpu.arch.toElfMachine();
704 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
705 index += 2;
706
707 // ELF Version, again
708 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
709 index += 4;
710
711 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
712
713 switch (self.ptr_width) {
714 .p32 => {
715 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
716 index += 4;
717
718 // e_phoff
719 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
720 index += 4;
721
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 }
767 hdr_buf[index] = 1; // ELF version
768 index += 1;
740769
741 const e_flags = 0;
742 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
743 index += 4;
770 // OS ABI, often set to 0 regardless of target platform
771 // ABI Version, possibly used by glibc but not by static executables
772 // padding
773 mem.set(u8, hdr_buf[index..][0..9], 0);
774 index += 9;
744775
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;
776 assert(index == 16);
751777
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;
778 const elf_type = switch (self.options.output_mode) {
779 .Exe => elf.ET.EXEC,
780 .Obj => elf.ET.REL,
781 .Lib => switch (self.options.link_mode) {
782 .Static => elf.ET.REL,
783 .Dynamic => elf.ET.DYN,
784 },
785 };
786 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
787 index += 2;
758788
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;
789 const machine = self.options.target.cpu.arch.toElfMachine();
790 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
791 index += 2;
762792
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;
793 // ELF Version, again
794 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
795 index += 4;
769796
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;
797 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
773798
774 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
775 index += 2;
799 switch (self.ptr_width) {
800 .p32 => {
801 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
802 index += 4;
776803
777 assert(index == e_ehsize);
804 // e_phoff
805 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
806 index += 4;
778807
779 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
780 }
808 // e_shoff
809 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
810 index += 4;
811 },
812 .p64 => {
813 // e_entry
814 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
815 index += 8;
781816
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;
817 // e_phoff
818 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
819 index += 8;
820
821 // e_shoff
822 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
823 index += 8;
824 },
795825 }
796 }
797826
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 }
827 const e_flags = 0;
828 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
829 index += 4;
802830
803 if (text_block.prev) |prev| {
804 prev.next = text_block.next;
831 const e_ehsize: u16 = switch (self.ptr_width) {
832 .p32 => @sizeOf(elf.Elf32_Ehdr),
833 .p64 => @sizeOf(elf.Elf64_Ehdr),
834 };
835 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
836 index += 2;
805837
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 }
838 const e_phentsize: u16 = switch (self.ptr_width) {
839 .p32 => @sizeOf(elf.Elf32_Phdr),
840 .p64 => @sizeOf(elf.Elf64_Phdr),
841 };
842 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
843 index += 2;
814844
815 if (text_block.next) |next| {
816 next.prev = text_block.prev;
817 } else {
818 text_block.next = null;
819 }
820 }
845 const e_phnum = @intCast(u16, self.program_headers.items.len);
846 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
847 index += 2;
821848
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 }
849 const e_shentsize: u16 = switch (self.ptr_width) {
850 .p32 => @sizeOf(elf.Elf32_Shdr),
851 .p64 => @sizeOf(elf.Elf64_Shdr),
852 };
853 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
854 index += 2;
826855
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 }
856 const e_shnum = @intCast(u16, self.sections.items.len);
857 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
858 index += 2;
859
860 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
861 index += 2;
862
863 assert(index == e_ehsize);
834864
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.*)) {
865 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
866 }
867
868 fn freeTextBlock(self: *File.Elf, text_block: *TextBlock) void {
869 var already_have_free_list_node = false;
870 {
871 var i: usize = 0;
872 while (i < self.text_block_free_list.items.len) {
873 if (self.text_block_free_list.items[i] == text_block) {
868874 _ = self.text_block_free_list.swapRemove(i);
869 } else {
870 i += 1;
875 continue;
871876 }
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;
877 if (self.text_block_free_list.items[i] == text_block.prev) {
878 already_have_free_list_node = true;
879 }
880 i += 1;
884881 }
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;
896882 }
897 };
898883
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;
884 if (self.last_text_block == text_block) {
885 // TODO shrink the .text section size here
886 self.last_text_block = text_block.prev;
914887 }
915 self.last_text_block = text_block;
916888
917 shdr.sh_size = needed_size;
918 phdr.p_memsz = needed_size;
919 phdr.p_filesz = needed_size;
889 if (text_block.prev) |prev| {
890 prev.next = text_block.next;
920891
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 }
892 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
893 // The free list is heuristics, it doesn't have to be perfect, so we can
894 // ignore the OOM here.
895 self.text_block_free_list.append(self.allocator, prev) catch {};
896 }
897 } else {
898 text_block.prev = null;
899 }
924900
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;
901 if (text_block.next) |next| {
902 next.prev = text_block.prev;
903 } else {
904 text_block.next = null;
905 }
933906 }
934907
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;
942 }
943 if (free_list_removal) |i| {
944 _ = self.text_block_free_list.swapRemove(i);
908 fn shrinkTextBlock(self: *File.Elf, text_block: *TextBlock, new_block_size: u64) void {
909 // TODO check the new capacity, and if it crosses the size threshold into a big enough
910 // capacity, insert a free list node for it.
945911 }
946 return vaddr;
947 }
948912
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();
913 fn growTextBlock(self: *File.Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
914 const sym = self.local_symbols.items[text_block.local_sym_index];
915 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
916 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
917 if (!need_realloc) return sym.st_value;
918 return self.allocateTextBlock(text_block, new_block_size, alignment);
965919 }
966920
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 }
921 fn allocateTextBlock(self: *File.Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
922 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
923 const shdr = &self.sections.items[self.text_section_index.?];
924 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
925
926 // We use these to indicate our intention to update metadata, placing the new block,
927 // and possibly removing a free list node.
928 // It would be simpler to do it inside the for loop below, but that would cause a
929 // problem if an error was returned later in the function. So this action
930 // is actually carried out at the end of the function, when errors are no longer possible.
931 var block_placement: ?*TextBlock = null;
932 var free_list_removal: ?usize = null;
933
934 // First we look for an appropriately sized free list node.
935 // The list is unordered. We'll just take the first thing that works.
936 const vaddr = blk: {
937 var i: usize = 0;
938 while (i < self.text_block_free_list.items.len) {
939 const big_block = self.text_block_free_list.items[i];
940 // We now have a pointer to a live text block that has too much capacity.
941 // Is it enough that we could fit this new text block?
942 const sym = self.local_symbols.items[big_block.local_sym_index];
943 const capacity = big_block.capacity(self.*);
944 const ideal_capacity = capacity * alloc_num / alloc_den;
945 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
946 const capacity_end_vaddr = sym.st_value + capacity;
947 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
948 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
949 if (new_start_vaddr < ideal_capacity_end_vaddr) {
950 // Additional bookkeeping here to notice if this free list node
951 // should be deleted because the block that it points to has grown to take up
952 // more of the extra capacity.
953 if (!big_block.freeListEligible(self.*)) {
954 _ = self.text_block_free_list.swapRemove(i);
955 } else {
956 i += 1;
957 }
958 continue;
959 }
960 // At this point we know that we will place the new block here. But the
961 // remaining question is whether there is still yet enough capacity left
962 // over for there to still be a free list node.
963 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
964 const keep_free_list_node = remaining_capacity >= min_text_capacity;
965
966 // Set up the metadata to be updated, after errors are no longer possible.
967 block_placement = big_block;
968 if (!keep_free_list_node) {
969 free_list_removal = i;
970 }
971 break :blk new_start_vaddr;
972 } else if (self.last_text_block) |last| {
973 const sym = self.local_symbols.items[last.local_sym_index];
974 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
975 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
976 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
977 // Set up the metadata to be updated, after errors are no longer possible.
978 block_placement = last;
979 break :blk new_start_vaddr;
980 } else {
981 break :blk phdr.p_vaddr;
982 }
983 };
974984
975 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
985 const expand_text_section = block_placement == null or block_placement.?.next == null;
986 if (expand_text_section) {
987 const text_capacity = self.allocatedSize(shdr.sh_offset);
988 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
989 if (needed_size > text_capacity) {
990 // Must move the entire text section.
991 const new_offset = self.findFreeSpace(needed_size, 0x1000);
992 const text_size = if (self.last_text_block) |last| blk: {
993 const sym = self.local_symbols.items[last.local_sym_index];
994 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
995 } else 0;
996 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
997 if (amt != text_size) return error.InputOutput;
998 shdr.sh_offset = new_offset;
999 phdr.p_offset = new_offset;
1000 }
1001 self.last_text_block = text_block;
9761002
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 }
1003 shdr.sh_size = needed_size;
1004 phdr.p_memsz = needed_size;
1005 phdr.p_filesz = needed_size;
9871006
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);
1007 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1008 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1009 }
9931010
994 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
1011 // This function can also reallocate a text block.
1012 // In this case we need to "unplug" it from its previous location before
1013 // plugging it in to its new location.
1014 if (text_block.prev) |prev| {
1015 prev.next = text_block.next;
1016 }
1017 if (text_block.next) |next| {
1018 next.prev = text_block.prev;
1019 }
9951020
996 decl.link.local_sym_index = 0;
1021 if (block_placement) |big_block| {
1022 text_block.prev = big_block;
1023 text_block.next = big_block.next;
1024 big_block.next = text_block;
1025 } else {
1026 text_block.prev = null;
1027 text_block.next = null;
1028 }
1029 if (free_list_removal) |i| {
1030 _ = self.text_block_free_list.swapRemove(i);
1031 }
1032 return vaddr;
9971033 }
998 }
9991034
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 };
1035 pub fn allocateDeclIndexes(self: *File.Elf, decl: *Module.Decl) !void {
1036 if (decl.link.local_sym_index != 0) return;
10141037
1015 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1038 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
1039 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
1040 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
1041 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
1042 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
10161043
1017 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1018 .Fn => elf.STT_FUNC,
1019 else => elf.STT_OBJECT,
1020 };
1044 if (self.local_symbol_free_list.popOrNull()) |i| {
1045 //std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
1046 decl.link.local_sym_index = i;
1047 } else {
1048 //std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
1049 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1050 _ = self.local_symbols.addOneAssumeCapacity();
1051 }
10211052
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);
1053 if (self.offset_table_free_list.popOrNull()) |i| {
1054 decl.link.offset_table_index = i;
1055 } else {
1056 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
1057 _ = self.offset_table.addOneAssumeCapacity();
1058 self.offset_table_count_dirty = true;
10401059 }
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,
1060
1061 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1062
1063 self.local_symbols.items[decl.link.local_sym_index] = .{
1064 .st_name = 0,
1065 .st_info = 0,
10581066 .st_other = 0,
1059 .st_shndx = self.text_section_index.?,
1060 .st_value = vaddr,
1061 .st_size = code.len,
1067 .st_shndx = 0,
1068 .st_value = phdr.p_vaddr,
1069 .st_size = 0,
10621070 };
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);
1071 self.offset_table.items[decl.link.offset_table_index] = 0;
10671072 }
10681073
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);
1074 pub fn freeDecl(self: *File.Elf, decl: *Module.Decl) void {
1075 self.freeTextBlock(&decl.link);
1076 if (decl.link.local_sym_index != 0) {
1077 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
1078 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
10721079
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 }
1080 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
10771081
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 }
1082 decl.link.local_sym_index = 0;
11031083 }
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;
1084 }
1085
1086 pub fn updateDecl(self: *File.Elf, module: *Module, decl: *Module.Decl) !void {
1087 var code_buffer = std.ArrayList(u8).init(self.allocator);
1088 defer code_buffer.deinit();
1089
1090 const typed_value = decl.typed_value.most_recent.typed_value;
1091 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1092 .externally_managed => |x| x,
1093 .appended => code_buffer.items,
1094 .fail => |em| {
1095 decl.analysis = .codegen_failure;
1096 _ = try module.failed_decls.put(decl, em);
1097 return;
11201098 },
11211099 };
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 };
1100
1101 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1102
1103 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1104 .Fn => elf.STT_FUNC,
1105 else => elf.STT_OBJECT,
1106 };
1107
1108 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1109 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1110 if (local_sym.st_size != 0) {
1111 const capacity = decl.link.capacity(self.*);
1112 const need_realloc = code.len > capacity or
1113 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1114 if (need_realloc) {
1115 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1116 //std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1117 if (vaddr != local_sym.st_value) {
1118 local_sym.st_value = vaddr;
1119
1120 //std.log.debug(.link, " (writing new offset table entry)\n", .{});
1121 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1122 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1123 }
1124 } else if (code.len < local_sym.st_size) {
1125 self.shrinkTextBlock(&decl.link, code.len);
1126 }
1127 local_sym.st_size = code.len;
1128 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1129 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1130 local_sym.st_other = 0;
1131 local_sym.st_shndx = self.text_section_index.?;
1132 // TODO this write could be avoided if no fields of the symbol were changed.
1133 try self.writeSymbol(decl.link.local_sym_index);
11331134 } 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,
1135 const decl_name = mem.spanZ(decl.name);
1136 const name_str_index = try self.makeString(decl_name);
1137 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1138 //std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1139 errdefer self.freeTextBlock(&decl.link);
1140
1141 local_sym.* = .{
1142 .st_name = name_str_index,
1143 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
11421144 .st_other = 0,
11431145 .st_shndx = self.text_section_index.?,
1144 .st_value = decl_sym.st_value,
1145 .st_size = decl_sym.st_size,
1146 .st_value = vaddr,
1147 .st_size = code.len,
11461148 };
1149 self.offset_table.items[decl.link.offset_table_index] = vaddr;
11471150
1148 exp.link.sym_index = @intCast(u32, i);
1151 try self.writeSymbol(decl.link.local_sym_index);
1152 try self.writeOffsetTableEntry(decl.link.offset_table_index);
11491153 }
1150 }
1151 }
11521154
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 }
1155 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1156 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1157 try self.file.?.pwriteAll(code, file_offset);
11581158
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,
1159 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1160 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1161 return self.updateDeclExports(module, decl, decl_exports);
11781162 }
1179 }
11801163
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]);
1164 /// Must be called only after a successful call to `updateDecl`.
1165 pub fn updateDeclExports(
1166 self: *File.Elf,
1167 module: *Module,
1168 decl: *const Module.Decl,
1169 exports: []const *Module.Export,
1170 ) !void {
1171 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1172 // them, so that deleting exports is guaranteed to succeed.
1173 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1174 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1175 const typed_value = decl.typed_value.most_recent.typed_value;
1176 if (decl.link.local_sym_index == 0) return;
1177 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1178
1179 for (exports) |exp| {
1180 if (exp.options.section) |section_name| {
1181 if (!mem.eql(u8, section_name, ".text")) {
1182 try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1);
1183 module.failed_exports.putAssumeCapacityNoClobber(
1184 exp,
1185 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1186 );
1187 continue;
1188 }
11971189 }
1198 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1199 },
1200 else => return error.UnsupportedArchitecture,
1201 }
1202 }
1190 const stb_bits: u8 = switch (exp.options.linkage) {
1191 .Internal => elf.STB_LOCAL,
1192 .Strong => blk: {
1193 if (mem.eql(u8, exp.options.name, "_start")) {
1194 self.entry_addr = decl_sym.st_value;
1195 }
1196 break :blk elf.STB_GLOBAL;
1197 },
1198 .Weak => elf.STB_WEAK,
1199 .LinkOnce => {
1200 try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1);
1201 module.failed_exports.putAssumeCapacityNoClobber(
1202 exp,
1203 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
1204 );
1205 continue;
1206 },
1207 };
1208 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
1209 if (exp.link.sym_index) |i| {
1210 const sym = &self.global_symbols.items[i];
1211 sym.* = .{
1212 .st_name = try self.updateString(sym.st_name, exp.options.name),
1213 .st_info = (stb_bits << 4) | stt_bits,
1214 .st_other = 0,
1215 .st_shndx = self.text_section_index.?,
1216 .st_value = decl_sym.st_value,
1217 .st_size = decl_sym.st_size,
1218 };
1219 } else {
1220 const name = try self.makeString(exp.options.name);
1221 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1222 _ = self.global_symbols.addOneAssumeCapacity();
1223 break :blk self.global_symbols.items.len - 1;
1224 };
1225 self.global_symbols.items[i] = .{
1226 .st_name = name,
1227 .st_info = (stb_bits << 4) | stt_bits,
1228 .st_other = 0,
1229 .st_shndx = self.text_section_index.?,
1230 .st_value = decl_sym.st_value,
1231 .st_size = decl_sym.st_size,
1232 };
12031233
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;
1234 exp.link.sym_index = @intCast(u32, i);
1235 }
12221236 }
1223 shdr.sh_size = needed_size;
1224 phdr.p_memsz = needed_size;
1225 phdr.p_filesz = needed_size;
1237 }
12261238
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
1239 pub fn deleteExport(self: *File.Elf, exp: Export) void {
1240 const sym_index = exp.sym_index orelse return;
1241 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1242 self.global_symbols.items[sym_index].st_info = 0;
1243 }
12291244
1230 self.offset_table_count_dirty = false;
1245 fn writeProgHeader(self: *File.Elf, index: usize) !void {
1246 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1247 const offset = self.program_headers.items[index].p_offset;
1248 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1249 32 => {
1250 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1251 if (foreign_endian) {
1252 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1253 }
1254 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1255 },
1256 64 => {
1257 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1258 if (foreign_endian) {
1259 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1260 }
1261 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1262 },
1263 else => return error.UnsupportedArchitecture,
1264 }
12311265 }
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 },
1266
1267 fn writeSectHeader(self: *File.Elf, index: usize) !void {
1268 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1269 const offset = self.sections.items[index].sh_offset;
1270 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1271 32 => {
1272 var shdr: [1]elf.Elf32_Shdr = undefined;
1273 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1274 if (foreign_endian) {
1275 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1276 }
1277 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1278 },
1279 64 => {
1280 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
1281 if (foreign_endian) {
1282 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1283 }
1284 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1285 },
1286 else => return error.UnsupportedArchitecture,
1287 }
12451288 }
1246 }
12471289
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),
1290 fn writeOffsetTableEntry(self: *File.Elf, index: usize) !void {
1291 const shdr = &self.sections.items[self.got_section_index.?];
1292 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1293 const entry_size: u16 = switch (self.ptr_width) {
1294 .p32 => 4,
1295 .p64 => 8,
12601296 };
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;
1297 if (self.offset_table_count_dirty) {
1298 // TODO Also detect virtual address collisions.
1299 const allocated_size = self.allocatedSize(shdr.sh_offset);
1300 const needed_size = self.local_symbols.items.len * entry_size;
1301 if (needed_size > allocated_size) {
1302 // Must move the entire got section.
1303 const new_offset = self.findFreeSpace(needed_size, entry_size);
1304 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1305 if (amt != shdr.sh_size) return error.InputOutput;
1306 shdr.sh_offset = new_offset;
1307 phdr.p_offset = new_offset;
1308 }
1309 shdr.sh_size = needed_size;
1310 phdr.p_memsz = needed_size;
1311 phdr.p_filesz = needed_size;
1312
1313 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1314 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1315
1316 self.offset_table_count_dirty = false;
1317 }
1318 const endian = self.options.target.cpu.arch.endian();
1319 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1320 switch (self.ptr_width) {
1321 .p32 => {
1322 var buf: [4]u8 = undefined;
1323 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1324 try self.file.?.pwriteAll(&buf, off);
1325 },
1326 .p64 => {
1327 var buf: [8]u8 = undefined;
1328 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1329 try self.file.?.pwriteAll(&buf, off);
1330 },
12691331 }
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
12731332 }
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 },
1333
1334 fn writeSymbol(self: *File.Elf, index: usize) !void {
1335 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1336 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1337 // due to running out of space.
1338 if (self.local_symbols.items.len != syms_sect.sh_info) {
1339 const sym_size: u64 = switch (self.ptr_width) {
1340 .p32 => @sizeOf(elf.Elf32_Sym),
1341 .p64 => @sizeOf(elf.Elf64_Sym),
12861342 };
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]);
1343 const sym_align: u16 = switch (self.ptr_width) {
1344 .p32 => @alignOf(elf.Elf32_Sym),
1345 .p64 => @alignOf(elf.Elf64_Sym),
1346 };
1347 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1348 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1349 // Move all the symbols to a new file location.
1350 const new_offset = self.findFreeSpace(needed_size, sym_align);
1351 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1352 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1353 if (amt != existing_size) return error.InputOutput;
1354 syms_sect.sh_offset = new_offset;
12971355 }
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,
1356 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1357 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1358 self.shdr_table_dirty = true; // TODO look into only writing one section
1359 }
1360 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1361 switch (self.ptr_width) {
1362 .p32 => {
1363 var sym = [1]elf.Elf32_Sym{
1364 .{
1365 .st_name = self.local_symbols.items[index].st_name,
1366 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1367 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1368 .st_info = self.local_symbols.items[index].st_info,
1369 .st_other = self.local_symbols.items[index].st_other,
1370 .st_shndx = self.local_symbols.items[index].st_shndx,
1371 },
13251372 };
13261373 if (foreign_endian) {
1327 bswapAllFields(elf.Elf32_Sym, sym);
1374 bswapAllFields(elf.Elf32_Sym, &sym[0]);
13281375 }
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 };
1376 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1377 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1378 },
1379 .p64 => {
1380 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
13451381 if (foreign_endian) {
1346 bswapAllFields(elf.Elf64_Sym, sym);
1382 bswapAllFields(elf.Elf64_Sym, &sym[0]);
13471383 }
1348 }
1349 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1350 },
1384 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1385 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1386 },
1387 }
13511388 }
1352 }
1389
1390 fn writeAllGlobalSymbols(self: *File.Elf) !void {
1391 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1392 const sym_size: u64 = switch (self.ptr_width) {
1393 .p32 => @sizeOf(elf.Elf32_Sym),
1394 .p64 => @sizeOf(elf.Elf64_Sym),
1395 };
1396 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1397 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1398 switch (self.ptr_width) {
1399 .p32 => {
1400 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1401 defer self.allocator.free(buf);
1402
1403 for (buf) |*sym, i| {
1404 sym.* = .{
1405 .st_name = self.global_symbols.items[i].st_name,
1406 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1407 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1408 .st_info = self.global_symbols.items[i].st_info,
1409 .st_other = self.global_symbols.items[i].st_other,
1410 .st_shndx = self.global_symbols.items[i].st_shndx,
1411 };
1412 if (foreign_endian) {
1413 bswapAllFields(elf.Elf32_Sym, sym);
1414 }
1415 }
1416 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1417 },
1418 .p64 => {
1419 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1420 defer self.allocator.free(buf);
1421
1422 for (buf) |*sym, i| {
1423 sym.* = .{
1424 .st_name = self.global_symbols.items[i].st_name,
1425 .st_value = self.global_symbols.items[i].st_value,
1426 .st_size = self.global_symbols.items[i].st_size,
1427 .st_info = self.global_symbols.items[i].st_info,
1428 .st_other = self.global_symbols.items[i].st_other,
1429 .st_shndx = self.global_symbols.items[i].st_shndx,
1430 };
1431 if (foreign_endian) {
1432 bswapAllFields(elf.Elf64_Sym, sym);
1433 }
1434 }
1435 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1436 },
1437 }
1438 }
1439 };
13531440};
13541441
13551442/// Truncates the existing file contents and overwrites the contents.
13561443/// 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 {
1444pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
13581445 switch (options.output_mode) {
13591446 .Exe => {},
13601447 .Obj => {},
......@@ -1368,7 +1455,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
13681455 .wasm => return error.TODOImplementWritingWasmObjects,
13691456 }
13701457
1371 var self: ElfFile = .{
1458 var self: File.Elf = .{
13721459 .allocator = allocator,
13731460 .file = file,
13741461 .options = options,
......@@ -1412,7 +1499,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
14121499}
14131500
14141501/// Returns error.IncrFailed if incremental update could not be performed.
1415fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
1502fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
14161503 switch (options.output_mode) {
14171504 .Exe => {},
14181505 .Obj => {},
......@@ -1425,7 +1512,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
14251512 .macho => return error.IncrFailed,
14261513 .wasm => return error.IncrFailed,
14271514 }
1428 var self: ElfFile = .{
1515 var self: File.Elf = .{
14291516 .allocator = allocator,
14301517 .file = file,
14311518 .owns_file_handle = false,
src-self-hosted/test.zig+22-3
......@@ -64,10 +64,11 @@ pub const TestContext = struct {
6464 /// such as QEMU is required for tests to complete.
6565 target: std.zig.CrossTarget,
6666 /// In order to be able to run e.g. Execution updates, this must be set
67 /// to Executable.
67 /// to Executable. This is ignored when generating C output.
6868 output_mode: std.builtin.OutputMode,
6969 updates: std.ArrayList(Update),
7070 extension: TestType,
71 c_standard: ?Module.CStandard = null,
7172
7273 /// Adds a subcase in which the module is updated with `src`, and the
7374 /// resulting ZIR is validated against `result`.
......@@ -187,6 +188,22 @@ pub const TestContext = struct {
187188 return ctx.addObj(name, target, .ZIR);
188189 }
189190
191 pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType, standard: Module.CStandard) *Case {
192 ctx.cases.append(Case{
193 .name = name,
194 .target = target,
195 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
196 .output_mode = .Obj,
197 .extension = T,
198 .c_standard = standard,
199 }) catch unreachable;
200 return &ctx.cases.items[ctx.cases.items.len - 1];
201 }
202
203 pub fn c11(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, c: [:0]const u8) void {
204 ctx.addC(name, target, .Zig, .C11).addTransform(src, c);
205 }
206
190207 pub fn addCompareOutput(
191208 ctx: *TestContext,
192209 name: []const u8,
......@@ -425,6 +442,7 @@ pub const TestContext = struct {
425442 .bin_file_path = bin_name,
426443 .root_pkg = root_pkg,
427444 .keep_source_files_loaded = true,
445 .c_standard = case.c_standard,
428446 });
429447 defer module.deinit();
430448
......@@ -463,14 +481,15 @@ pub const TestContext = struct {
463481 var test_node = update_node.start("assert", null);
464482 test_node.activate();
465483 defer test_node.end();
484 const label = if (case.c_standard) |_| "C" else "ZIR";
466485 if (expected_output.len != out_zir.items.len) {
467 std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
486 std.debug.warn("{}\nTransformed {} length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, label, expected_output, out_zir.items });
468487 std.process.exit(1);
469488 }
470489 for (expected_output) |e, i| {
471490 if (out_zir.items[i] != e) {
472491 if (expected_output.len != out_zir.items.len) {
473 std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
492 std.debug.warn("{}\nTransformed {} differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, label, expected_output, out_zir.items });
474493 std.process.exit(1);
475494 }
476495 }
test/stage2/cbe.zig created+18
......@@ -0,0 +1,18 @@
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 // // These tests should work on every platform
13 // ctx.c11("empty start function", linux_x64,
14 // \\export fn start() void {}
15 // ,
16 // \\void start(void) {}
17 // );
18}
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}