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,...@@ -26,7 +26,7 @@ root_pkg: *Package,
26/// Module owns this resource.26/// Module owns this resource.
27/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.27/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
28root_scope: *Scope,28root_scope: *Scope,
29bin_file: link.ElfFile,29bin_file: *link.File,
30bin_file_dir: std.fs.Dir,30bin_file_dir: std.fs.Dir,
31bin_file_path: []const u8,31bin_file_path: []const u8,
32/// It's rare for a decl to be exported, so we save memory by having a sparse map of32/// It's rare for a decl to be exported, so we save memory by having a sparse map of
...@@ -45,7 +45,7 @@ export_owners: std.AutoHashMap(*Decl, []*Export),...@@ -45,7 +45,7 @@ export_owners: std.AutoHashMap(*Decl, []*Export),
45decl_table: DeclTable,45decl_table: DeclTable,
4646
47optimize_mode: std.builtin.Mode,47optimize_mode: std.builtin.Mode,
48link_error_flags: link.ElfFile.ErrorFlags = .{},48link_error_flags: link.File.ErrorFlags = .{},
4949
50work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),50work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
5151
...@@ -91,7 +91,7 @@ pub const Export = struct {...@@ -91,7 +91,7 @@ pub const Export = struct {
91 /// Byte offset into the file that contains the export directive.91 /// Byte offset into the file that contains the export directive.
92 src: usize,92 src: usize,
93 /// Represents the position of the export, if any, in the output file.93 /// Represents the position of the export, if any, in the output file.
94 link: link.ElfFile.Export,94 link: link.File.Elf.Export,
95 /// The Decl that performs the export. Note that this is *not* the Decl being exported.95 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
96 owner_decl: *Decl,96 owner_decl: *Decl,
97 /// The Decl being exported. Note this is *not* the Decl performing the export.97 /// The Decl being exported. Note this is *not* the Decl performing the export.
...@@ -169,7 +169,7 @@ pub const Decl = struct {...@@ -169,7 +169,7 @@ pub const Decl = struct {
169169
170 /// Represents the position of the code in the output file.170 /// Represents the position of the code in the output file.
171 /// This is populated regardless of semantic analysis and code generation.171 /// This is populated regardless of semantic analysis and code generation.
172 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,172 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
173173
174 contents_hash: std.zig.SrcHash,174 contents_hash: std.zig.SrcHash,
175175
...@@ -722,6 +722,12 @@ pub const AllErrors = struct {...@@ -722,6 +722,12 @@ pub const AllErrors = struct {
722 }722 }
723};723};
724724
725pub const CStandard = enum {
726 C99,
727 GNU99,
728 C11,
729};
730
725pub const InitOptions = struct {731pub const InitOptions = struct {
726 target: std.Target,732 target: std.Target,
727 root_pkg: *Package,733 root_pkg: *Package,
...@@ -732,17 +738,19 @@ pub const InitOptions = struct {...@@ -732,17 +738,19 @@ pub const InitOptions = struct {
732 object_format: ?std.builtin.ObjectFormat = null,738 object_format: ?std.builtin.ObjectFormat = null,
733 optimize_mode: std.builtin.Mode = .Debug,739 optimize_mode: std.builtin.Mode = .Debug,
734 keep_source_files_loaded: bool = false,740 keep_source_files_loaded: bool = false,
741 c_standard: ?CStandard = null,
735};742};
736743
737pub fn init(gpa: *Allocator, options: InitOptions) !Module {744pub fn init(gpa: *Allocator, options: InitOptions) !Module {
738 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();745 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, .{
740 .target = options.target,747 .target = options.target,
741 .output_mode = options.output_mode,748 .output_mode = options.output_mode,
742 .link_mode = options.link_mode orelse .Static,749 .link_mode = options.link_mode orelse .Static,
743 .object_format = options.object_format orelse options.target.getObjectFormat(),750 .object_format = options.object_format orelse options.target.getObjectFormat(),
751 .c_standard = options.c_standard,
744 });752 });
745 errdefer bin_file.deinit();753 errdefer bin_file.*.deinit();
746754
747 const root_scope = blk: {755 const root_scope = blk: {
748 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {756 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
...@@ -793,6 +801,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -793,6 +801,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
793pub fn deinit(self: *Module) void {801pub fn deinit(self: *Module) void {
794 self.bin_file.deinit();802 self.bin_file.deinit();
795 const allocator = self.allocator;803 const allocator = self.allocator;
804 allocator.destroy(self.bin_file);
796 self.deletion_set.deinit(allocator);805 self.deletion_set.deinit(allocator);
797 self.work_queue.deinit();806 self.work_queue.deinit();
798807
...@@ -840,7 +849,7 @@ fn freeExportList(allocator: *Allocator, export_list: []*Export) void {...@@ -840,7 +849,7 @@ fn freeExportList(allocator: *Allocator, export_list: []*Export) void {
840}849}
841850
842pub fn target(self: Module) std.Target {851pub fn target(self: Module) std.Target {
843 return self.bin_file.options.target;852 return self.bin_file.options().target;
844}853}
845854
846/// Detect changes to source files, perform semantic analysis, and update the output files.855/// Detect changes to source files, perform semantic analysis, and update the output files.
...@@ -882,7 +891,7 @@ pub fn update(self: *Module) !void {...@@ -882,7 +891,7 @@ pub fn update(self: *Module) !void {
882 try self.deleteDecl(decl);891 try self.deleteDecl(decl);
883 }892 }
884893
885 self.link_error_flags = self.bin_file.error_flags;894 self.link_error_flags = self.bin_file.errorFlags();
886895
887 // If there are any errors, we anticipate the source files being loaded896 // If there are any errors, we anticipate the source files being loaded
888 // to report error messages. Otherwise we unload all source files to save memory.897 // to report error messages. Otherwise we unload all source files to save memory.
...@@ -1898,8 +1907,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {...@@ -1898,8 +1907,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
1898 self.decl_exports.removeAssertDiscard(exp.exported_decl);1907 self.decl_exports.removeAssertDiscard(exp.exported_decl);
1899 }1908 }
1900 }1909 }
19011910 if (self.bin_file.cast(link.File.Elf)) |elf| {
1902 self.bin_file.deleteExport(exp.link);1911 elf.deleteExport(exp.link);
1912 }
1903 if (self.failed_exports.remove(exp)) |entry| {1913 if (self.failed_exports.remove(exp)) |entry| {
1904 entry.value.destroy(self.allocator);1914 entry.value.destroy(self.allocator);
1905 }1915 }
...@@ -1961,7 +1971,7 @@ fn allocateNewDecl(...@@ -1961,7 +1971,7 @@ fn allocateNewDecl(
1961 .analysis = .unreferenced,1971 .analysis = .unreferenced,
1962 .deletion_flag = false,1972 .deletion_flag = false,
1963 .contents_hash = contents_hash,1973 .contents_hash = contents_hash,
1964 .link = link.ElfFile.TextBlock.empty,1974 .link = link.File.Elf.TextBlock.empty,
1965 .generation = 0,1975 .generation = 0,
1966 };1976 };
1967 return new_decl;1977 return new_decl;
...@@ -2189,19 +2199,21 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const...@@ -2189,19 +2199,21 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
2189 }2199 }
21902200
2191 try self.symbol_exports.putNoClobber(symbol_name, new_export);2201 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) {2202 if (self.bin_file.cast(link.File.Elf)) |elf| {
2193 error.OutOfMemory => return error.OutOfMemory,2203 elf.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
2194 else => {2204 error.OutOfMemory => return error.OutOfMemory,
2195 try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);2205 else => {
2196 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(2206 try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);
2197 self.allocator,2207 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
2198 src,2208 self.allocator,
2199 "unable to export: {}",2209 src,
2200 .{@errorName(err)},2210 "unable to export: {}",
2201 ));2211 .{@errorName(err)},
2202 new_export.status = .failed_retryable;2212 ));
2203 },2213 new_export.status = .failed_retryable;
2204 };2214 },
2215 };
2216 }
2205}2217}
22062218
2207fn addNewInstArgs(2219fn addNewInstArgs(
src-self-hosted/codegen.zig+2-2
...@@ -21,7 +21,7 @@ pub const Result = union(enum) {...@@ -21,7 +21,7 @@ pub const Result = union(enum) {
21};21};
2222
23pub fn generateSymbol(23pub fn generateSymbol(
24 bin_file: *link.ElfFile,24 bin_file: *link.File.Elf,
25 src: usize,25 src: usize,
26 typed_value: TypedValue,26 typed_value: TypedValue,
27 code: *std.ArrayList(u8),27 code: *std.ArrayList(u8),
...@@ -211,7 +211,7 @@ pub fn generateSymbol(...@@ -211,7 +211,7 @@ pub fn generateSymbol(
211}211}
212212
213const Function = struct {213const Function = struct {
214 bin_file: *link.ElfFile,214 bin_file: *link.File.Elf,
215 target: *const std.Target,215 target: *const std.Target,
216 mod_fn: *const Module.Fn,216 mod_fn: *const Module.Fn,
217 code: *std.ArrayList(u8),217 code: *std.ArrayList(u8),
src-self-hosted/link.zig+1205-1118
...@@ -21,6 +21,7 @@ pub const Options = struct {...@@ -21,6 +21,7 @@ pub const Options = struct {
21 /// Used for calculating how much space to reserve for executable program code in case21 /// Used for calculating how much space to reserve for executable program code in case
22 /// the binary file deos not already have such a section.22 /// the binary file deos not already have such a section.
23 program_code_size_hint: u64 = 256 * 1024,23 program_code_size_hint: u64 = 256 * 1024,
24 c_standard: ?Module.CStandard = null,
24};25};
2526
26/// Attempts incremental linking, if the file already exists.27/// Attempts incremental linking, if the file already exists.
...@@ -32,13 +33,19 @@ pub fn openBinFilePath(...@@ -32,13 +33,19 @@ pub fn openBinFilePath(
32 dir: fs.Dir,33 dir: fs.Dir,
33 sub_path: []const u8,34 sub_path: []const u8,
34 options: Options,35 options: Options,
35) !ElfFile {36) !*File {
36 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });37 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });
37 errdefer file.close();38 errdefer file.close();
3839
39 var bin_file = try openBinFile(allocator, file, options);40 if (options.c_standard) |cstd| {
40 bin_file.owns_file_handle = true;41 return error.Unimplemented;
41 return bin_file;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 }
42}49}
4350
44/// Atomically overwrites the old file, if present.51/// Atomically overwrites the old file, if present.
...@@ -80,7 +87,7 @@ pub fn writeFilePath(...@@ -80,7 +87,7 @@ pub fn writeFilePath(
80/// Returns an error if `file` is not already open with +read +write +seek abilities.87/// Returns an error if `file` is not already open with +read +write +seek abilities.
81/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.88/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
82/// This operation is not atomic.89/// 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 {
84 return openBinFileInner(allocator, file, options) catch |err| switch (err) {91 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
85 error.IncrFailed => {92 error.IncrFailed => {
86 return createElfFile(allocator, file, options);93 return createElfFile(allocator, file, options);
...@@ -89,447 +96,496 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF...@@ -89,447 +96,496 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF
89 };96 };
90}97}
9198
92pub const ElfFile = struct {99pub const File = struct {
93 allocator: *Allocator,100 tag: Tag,
94 file: ?fs.File,101 pub fn cast(base: *File, comptime T: type) ?*T {
95 owns_file_handle: bool,102 if (base.tag != T.base_tag)
96 options: Options,103 return null;
97 ptr_width: enum { p32, p64 },
98
99 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
100 /// Same order as in the file.
101 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
102 shdr_table_offset: ?u64 = null,
103
104 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
105 /// Same order as in the file.
106 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
107 phdr_table_offset: ?u64 = null,
108 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
109 phdr_load_re_index: ?u16 = null,
110 /// The index into the program headers of the global offset table.
111 /// It needs PT_LOAD and Read flags.
112 phdr_got_index: ?u16 = null,
113 entry_addr: ?u64 = null,
114
115 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
116 shstrtab_index: ?u16 = null,
117
118 text_section_index: ?u16 = null,
119 symtab_section_index: ?u16 = null,
120 got_section_index: ?u16 = null,
121
122 /// The same order as in the file. ELF requires global symbols to all be after the
123 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
124 /// write them at the end. These are only the local symbols. The length of this array
125 /// is the value used for sh_info in the .symtab section.
126 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
127 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
128
129 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
130 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
131 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
132
133 /// Same order as in the file. The value is the absolute vaddr value.
134 /// If the vaddr of the executable program header changes, the entire
135 /// offset table needs to be rewritten.
136 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
137
138 phdr_table_dirty: bool = false,
139 shdr_table_dirty: bool = false,
140 shstrtab_dirty: bool = false,
141 offset_table_count_dirty: bool = false,
142
143 error_flags: ErrorFlags = ErrorFlags{},
144
145 /// A list of text blocks that have surplus capacity. This list can have false
146 /// positives, as functions grow and shrink over time, only sometimes being added
147 /// or removed from the freelist.
148 ///
149 /// A text block has surplus capacity when its overcapacity value is greater than
150 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
151 /// much extra capacity, that we could fit a small new symbol in it, itself with
152 /// ideal_capacity or more.
153 ///
154 /// Ideal capacity is defined by size * alloc_num / alloc_den.
155 ///
156 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
157 /// overcapacity can be negative. A simple way to have negative overcapacity is to
158 /// allocate a fresh text block, which will have ideal capacity, and then grow it
159 /// by 1 byte. It will then have -1 overcapacity.
160 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
161 last_text_block: ?*TextBlock = null,
162
163 /// `alloc_num / alloc_den` is the factor of padding when allocating.
164 const alloc_num = 4;
165 const alloc_den = 3;
166
167 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
168 /// it as a possible place to put new symbols, it must have enough room for this many bytes
169 /// (plus extra for reserved capacity).
170 const minimum_text_block_size = 64;
171 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
172104
173 pub const ErrorFlags = struct {105 return @fieldParentPtr(T, "base", base);
174 no_entry_point_found: bool = false,106 }
175 };
176107
177 pub const TextBlock = struct {108 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
178 /// Each decl always gets a local symbol with the fully qualified name.109 try switch (base.tag) {
179 /// The vaddr and size are found here directly.110 .Elf => @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
180 /// The file offset is found by computing the vaddr offset from the section vaddr111 else => unreachable,
181 /// the symbol references, and adding that to the file offset of the section.
182 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
183 /// offset table entry.
184 local_sym_index: u32,
185 /// This field is undefined for symbols with size = 0.
186 offset_table_index: u32,
187 /// Points to the previous and next neighbors, based on the `text_offset`.
188 /// This can be used to find, for example, the capacity of this `TextBlock`.
189 prev: ?*TextBlock,
190 next: ?*TextBlock,
191
192 pub const empty = TextBlock{
193 .local_sym_index = 0,
194 .offset_table_index = undefined,
195 .prev = null,
196 .next = null,
197 };112 };
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 }
244 }113 }
245114
246 pub fn makeExecutable(self: *ElfFile) !void {115 pub fn makeExecutable(base: *File) !void {
247 assert(self.owns_file_handle);116 try switch (base.tag) {
248 if (self.file) |f| {117 .Elf => @fieldParentPtr(Elf, "base", base).makeExecutable(),
249 f.close();118 else => unreachable,
250 self.file = null;119 };
251 }
252 }120 }
253121
254 pub fn makeWritable(self: *ElfFile, dir: fs.Dir, sub_path: []const u8) !void {122 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
255 assert(self.owns_file_handle);123 try switch (base.tag) {
256 if (self.file != null) return;124 .Elf => @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
257 self.file = try dir.createFile(sub_path, .{125 else => unreachable,
258 .truncate = false,126 };
259 .read = true,
260 .mode = determineMode(self.options),
261 });
262 }127 }
263128
264 /// Returns end pos of collision, if any.129 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
265 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {130 try switch (base.tag) {
266 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;131 .Elf => @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
267 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);132 else => unreachable,
268 if (start < ehdr_size)133 };
269 return ehdr_size;134 }
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 }
292135
293 for (self.sections.items) |section| {136 pub fn deinit(base: *File) void {
294 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;137 switch (base.tag) {
295 const test_end = section.sh_offset + increased_size;138 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),
296 if (end > section.sh_offset and start < test_end) {139 else => unreachable,
297 return test_end;
298 }
299 }
300 for (self.program_headers.items) |program_header| {
301 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
302 const test_end = program_header.p_offset + increased_size;
303 if (end > program_header.p_offset and start < test_end) {
304 return test_end;
305 }
306 }140 }
307 return null;
308 }141 }
309142
310 fn allocatedSize(self: *ElfFile, start: u64) u64 {143 pub fn flush(base: *File) !void {
311 var min_pos: u64 = std.math.maxInt(u64);144 try switch (base.tag) {
312 if (self.shdr_table_offset) |off| {145 .Elf => @fieldParentPtr(Elf, "base", base).flush(),
313 if (off > start and off < min_pos) min_pos = off;146 else => unreachable,
314 }147 };
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;
327 }148 }
328149
329 fn findFreeSpace(self: *ElfFile, object_size: u64, min_alignment: u16) u64 {150 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
330 var start: u64 = 0;151 switch (base.tag) {
331 while (self.detectAllocCollision(start, object_size)) |item_end| {152 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
332 start = mem.alignForwardGeneric(u64, item_end, min_alignment);153 else => unreachable,
333 }154 }
334 return start;
335 }155 }
336156
337 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {157 pub fn errorFlags(base: *File) ErrorFlags {
338 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);158 return switch (base.tag) {
339 const result = self.shstrtab.items.len;159 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,
340 self.shstrtab.appendSliceAssumeCapacity(bytes);160 else => unreachable,
341 self.shstrtab.appendAssumeCapacity(0);161 };
342 return @intCast(u32, result);
343 }162 }
344163
345 fn getString(self: *ElfFile, str_off: u32) []const u8 {164 pub fn options(base: *File) Options {
346 assert(str_off < self.shstrtab.items.len);165 return switch (base.tag) {
347 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));166 .Elf => @fieldParentPtr(Elf, "base", base).options,
167 else => unreachable,
168 };
348 }169 }
349170
350 fn updateString(self: *ElfFile, old_str_off: u32, new_name: []const u8) !u32 {171 pub const Tag = enum {
351 const existing_name = self.getString(old_str_off);172 Elf,
352 if (mem.eql(u8, existing_name, new_name)) {173 C,
353 return old_str_off;174 };
354 }
355 return self.makeString(new_name);
356 }
357175
358 pub fn populateMissingMetadata(self: *ElfFile) !void {176 pub const ErrorFlags = struct {
359 const small_ptr = switch (self.ptr_width) {177 no_entry_point_found: bool = false,
360 .p32 => true,178 };
361 .p64 => false,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 }
362 };310 };
363 const ptr_size: u8 = switch (self.ptr_width) {311
364 .p32 => 4,312 pub const Export = struct {
365 .p64 => 8,313 sym_index: ?u32 = null,
366 };314 };
367 if (self.phdr_load_re_index == null) {315
368 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);316 pub fn deinit(self: *File.Elf) void {
369 const file_size = self.options.program_code_size_hint;317 self.sections.deinit(self.allocator);
370 const p_align = 0x1000;318 self.program_headers.deinit(self.allocator);
371 const off = self.findFreeSpace(file_size, p_align);319 self.shstrtab.deinit(self.allocator);
372 //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });320 self.local_symbols.deinit(self.allocator);
373 try self.program_headers.append(self.allocator, .{321 self.global_symbols.deinit(self.allocator);
374 .p_type = elf.PT_LOAD,322 self.global_symbol_free_list.deinit(self.allocator);
375 .p_offset = off,323 self.local_symbol_free_list.deinit(self.allocator);
376 .p_filesz = file_size,324 self.offset_table_free_list.deinit(self.allocator);
377 .p_vaddr = default_entry_addr,325 self.text_block_free_list.deinit(self.allocator);
378 .p_paddr = default_entry_addr,326 self.offset_table.deinit(self.allocator);
379 .p_memsz = file_size,327 if (self.owns_file_handle) {
380 .p_align = p_align,328 if (self.file) |f| f.close();
381 .p_flags = elf.PF_X | elf.PF_R,329 }
382 });
383 self.entry_addr = null;
384 self.phdr_table_dirty = true;
385 }330 }
386 if (self.phdr_got_index == null) {331
387 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);332 pub fn makeExecutable(self: *File.Elf) !void {
388 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;333 assert(self.owns_file_handle);
389 // We really only need ptr alignment but since we are using PROGBITS, linux requires334 if (self.file) |f| {
390 // page align.335 f.close();
391 const p_align = 0x1000;336 self.file = null;
392 const off = self.findFreeSpace(file_size, p_align);337 }
393 //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
394 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
395 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
396 // else in virtual memory.
397 const default_got_addr = 0x4000000;
398 try self.program_headers.append(self.allocator, .{
399 .p_type = elf.PT_LOAD,
400 .p_offset = off,
401 .p_filesz = file_size,
402 .p_vaddr = default_got_addr,
403 .p_paddr = default_got_addr,
404 .p_memsz = file_size,
405 .p_align = p_align,
406 .p_flags = elf.PF_R,
407 });
408 self.phdr_table_dirty = true;
409 }338 }
410 if (self.shstrtab_index == null) {339
411 self.shstrtab_index = @intCast(u16, self.sections.items.len);340 pub fn makeWritable(self: *File.Elf, dir: fs.Dir, sub_path: []const u8) !void {
412 assert(self.shstrtab.items.len == 0);341 assert(self.owns_file_handle);
413 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0342 if (self.file != null) return;
414 const off = self.findFreeSpace(self.shstrtab.items.len, 1);343 self.file = try dir.createFile(sub_path, .{
415 //std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });344 .truncate = false,
416 try self.sections.append(self.allocator, .{345 .read = true,
417 .sh_name = try self.makeString(".shstrtab"),346 .mode = determineMode(self.options),
418 .sh_type = elf.SHT_STRTAB,
419 .sh_flags = 0,
420 .sh_addr = 0,
421 .sh_offset = off,
422 .sh_size = self.shstrtab.items.len,
423 .sh_link = 0,
424 .sh_info = 0,
425 .sh_addralign = 1,
426 .sh_entsize = 0,
427 });347 });
428 self.shstrtab_dirty = true;
429 self.shdr_table_dirty = true;
430 }348 }
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, .{350 /// Returns end pos of collision, if any.
436 .sh_name = try self.makeString(".text"),351 fn detectAllocCollision(self: *File.Elf, start: u64, size: u64) ?u64 {
437 .sh_type = elf.SHT_PROGBITS,352 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
438 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,353 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
439 .sh_addr = phdr.p_vaddr,354 if (start < ehdr_size)
440 .sh_offset = phdr.p_offset,355 return ehdr_size;
441 .sh_size = phdr.p_filesz,356
442 .sh_link = 0,357 const end = start + satMul(size, alloc_num) / alloc_den;
443 .sh_info = 0,358
444 .sh_addralign = phdr.p_align,359 if (self.shdr_table_offset) |off| {
445 .sh_entsize = 0,360 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
446 });361 const tight_size = self.sections.items.len * shdr_size;
447 self.shdr_table_dirty = true;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;
448 }394 }
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, .{396 fn allocatedSize(self: *File.Elf, start: u64) u64 {
454 .sh_name = try self.makeString(".got"),397 var min_pos: u64 = std.math.maxInt(u64);
455 .sh_type = elf.SHT_PROGBITS,398 if (self.shdr_table_offset) |off| {
456 .sh_flags = elf.SHF_ALLOC,399 if (off > start and off < min_pos) min_pos = off;
457 .sh_addr = phdr.p_vaddr,400 }
458 .sh_offset = phdr.p_offset,401 if (self.phdr_table_offset) |off| {
459 .sh_size = phdr.p_filesz,402 if (off > start and off < min_pos) min_pos = off;
460 .sh_link = 0,403 }
461 .sh_info = 0,404 for (self.sections.items) |section| {
462 .sh_addralign = phdr.p_align,405 if (section.sh_offset <= start) continue;
463 .sh_entsize = 0,406 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
464 });407 }
465 self.shdr_table_dirty = true;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;
466 }413 }
467 if (self.symtab_section_index == null) {414
468 self.symtab_section_index = @intCast(u16, self.sections.items.len);415 fn findFreeSpace(self: *File.Elf, object_size: u64, min_alignment: u16) u64 {
469 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);416 var start: u64 = 0;
470 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);417 while (self.detectAllocCollision(start, object_size)) |item_end| {
471 const file_size = self.options.symbol_count_hint * each_size;418 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
472 const off = self.findFreeSpace(file_size, min_align);419 }
473 //std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });420 return start;
474
475 try self.sections.append(self.allocator, .{
476 .sh_name = try self.makeString(".symtab"),
477 .sh_type = elf.SHT_SYMTAB,
478 .sh_flags = 0,
479 .sh_addr = 0,
480 .sh_offset = off,
481 .sh_size = file_size,
482 // The section header index of the associated string table.
483 .sh_link = self.shstrtab_index.?,
484 .sh_info = @intCast(u32, self.local_symbols.items.len),
485 .sh_addralign = min_align,
486 .sh_entsize = each_size,
487 });
488 self.shdr_table_dirty = true;
489 try self.writeSymbol(0);
490 }421 }
491 const shsize: u64 = switch (self.ptr_width) {422
492 .p32 => @sizeOf(elf.Elf32_Shdr),423 fn makeString(self: *File.Elf, bytes: []const u8) !u32 {
493 .p64 => @sizeOf(elf.Elf64_Shdr),424 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
494 };425 const result = self.shstrtab.items.len;
495 const shalign: u16 = switch (self.ptr_width) {426 self.shstrtab.appendSliceAssumeCapacity(bytes);
496 .p32 => @alignOf(elf.Elf32_Shdr),427 self.shstrtab.appendAssumeCapacity(0);
497 .p64 => @alignOf(elf.Elf64_Shdr),428 return @intCast(u32, result);
498 };
499 if (self.shdr_table_offset == null) {
500 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
501 self.shdr_table_dirty = true;
502 }429 }
503 const phsize: u64 = switch (self.ptr_width) {430
504 .p32 => @sizeOf(elf.Elf32_Phdr),431 fn getString(self: *File.Elf, str_off: u32) []const u8 {
505 .p64 => @sizeOf(elf.Elf64_Phdr),432 assert(str_off < self.shstrtab.items.len);
506 };433 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
507 const phalign: u16 = switch (self.ptr_width) {
508 .p32 => @alignOf(elf.Elf32_Phdr),
509 .p64 => @alignOf(elf.Elf64_Phdr),
510 };
511 if (self.phdr_table_offset == null) {
512 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
513 self.phdr_table_dirty = true;
514 }434 }
515 {435
516 // Iterate over symbols, populating free_list and last_text_block.436 fn updateString(self: *File.Elf, old_str_off: u32, new_name: []const u8) !u32 {
517 if (self.local_symbols.items.len != 1) {437 const existing_name = self.getString(old_str_off);
518 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");438 if (mem.eql(u8, existing_name, new_name)) {
439 return old_str_off;
519 }440 }
520 // We are starting with an empty file. The default values are correct, null and empty list.441 return self.makeString(new_name);
521 }442 }
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 allow444 pub fn populateMissingMetadata(self: *File.Elf) !void {
529 // mixing local and global symbols within a symbol table.445 const small_ptr = switch (self.ptr_width) {
530 try self.writeAllGlobalSymbols();446 .p32 => true,
531447 .p64 => false,
532 if (self.phdr_table_dirty) {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 }
533 const phsize: u64 = switch (self.ptr_width) {589 const phsize: u64 = switch (self.ptr_width) {
534 .p32 => @sizeOf(elf.Elf32_Phdr),590 .p32 => @sizeOf(elf.Elf32_Phdr),
535 .p64 => @sizeOf(elf.Elf64_Phdr),591 .p64 => @sizeOf(elf.Elf64_Phdr),
...@@ -538,823 +594,854 @@ pub const ElfFile = struct {...@@ -538,823 +594,854 @@ pub const ElfFile = struct {
538 .p32 => @alignOf(elf.Elf32_Phdr),594 .p32 => @alignOf(elf.Elf32_Phdr),
539 .p64 => @alignOf(elf.Elf64_Phdr),595 .p64 => @alignOf(elf.Elf64_Phdr),
540 };596 };
541 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);597 if (self.phdr_table_offset == null) {
542 const needed_size = self.program_headers.items.len * phsize;598 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
543599 self.phdr_table_dirty = true;
544 if (needed_size > allocated_size) {
545 self.phdr_table_offset = null; // free the space
546 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
547 }600 }
548601 {
549 switch (self.ptr_width) {602 // Iterate over symbols, populating free_list and last_text_block.
550 .p32 => {603 if (self.local_symbols.items.len != 1) {
551 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);604 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
552 defer self.allocator.free(buf);605 }
553606 // We are starting with an empty file. The default values are correct, null and empty list.
554 for (buf) |*phdr, i| {
555 phdr.* = progHeaderTo32(self.program_headers.items[i]);
556 if (foreign_endian) {
557 bswapAllFields(elf.Elf32_Phdr, phdr);
558 }
559 }
560 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
561 },
562 .p64 => {
563 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
564 defer self.allocator.free(buf);
565
566 for (buf) |*phdr, i| {
567 phdr.* = self.program_headers.items[i];
568 if (foreign_endian) {
569 bswapAllFields(elf.Elf64_Phdr, phdr);
570 }
571 }
572 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
573 },
574 }607 }
575 self.phdr_table_dirty = false;
576 }608 }
577609
578 {610 /// Commit pending changes and write headers.
579 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];611 pub fn flush(self: *File.Elf) !void {
580 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {612 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
581 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);613
582 const needed_size = self.shstrtab.items.len;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
584 if (needed_size > allocated_size) {630 if (needed_size > allocated_size) {
585 shstrtab_sect.sh_size = 0; // free the space631 self.phdr_table_offset = null; // free the space
586 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);632 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
587 }633 }
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);635 switch (self.ptr_width) {
592 if (!self.shdr_table_dirty) {636 .p32 => {
593 // Then it won't get written with the others and we need to do it.637 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
594 try self.writeSectHeader(self.shstrtab_index.?);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 },
595 }660 }
596 self.shstrtab_dirty = false;661 self.phdr_table_dirty = false;
597 }662 }
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) {664 {
612 self.shdr_table_offset = null; // free the space665 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
613 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);666 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
614 }667 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
668 const needed_size = self.shstrtab.items.len;
615669
616 switch (self.ptr_width) {670 if (needed_size > allocated_size) {
617 .p32 => {671 shstrtab_sect.sh_size = 0; // free the space
618 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);672 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
619 defer self.allocator.free(buf);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| {677 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
622 shdr.* = sectHeaderTo32(self.sections.items[i]);678 if (!self.shdr_table_dirty) {
623 if (foreign_endian) {679 // Then it won't get written with the others and we need to do it.
624 bswapAllFields(elf.Elf32_Shdr, shdr);680 try self.writeSectHeader(self.shstrtab_index.?);
625 }
626 }681 }
627 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);682 self.shstrtab_dirty = false;
628 },683 }
629 .p64 => {684 }
630 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);685 if (self.shdr_table_dirty) {
631 defer self.allocator.free(buf);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| {697 if (needed_size > allocated_size) {
634 shdr.* = self.sections.items[i];698 self.shdr_table_offset = null; // free the space
635 //std.log.debug(.link, "writing section {}\n", .{shdr.*});699 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
636 if (foreign_endian) {700 }
637 bswapAllFields(elf.Elf64_Shdr, shdr);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 }
638 }712 }
639 }713 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
640 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);714 },
641 },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;
642 }730 }
643 self.shdr_table_dirty = false;731 if (self.entry_addr == null and self.options.output_mode == .Exe) {
644 }732 self.error_flags.no_entry_point_found = true;
645 if (self.entry_addr == null and self.options.output_mode == .Exe) {733 } else {
646 self.error_flags.no_entry_point_found = true;734 self.error_flags.no_entry_point_found = false;
647 } else {735 try self.writeElfHeader();
648 self.error_flags.no_entry_point_found = false;736 }
649 try self.writeElfHeader();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);
650 }745 }
651746
652 // The point of flush() is to commit changes, so nothing should be dirty after this.747 fn writeElfHeader(self: *File.Elf) !void {
653 assert(!self.phdr_table_dirty);748 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
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 }
660749
661 fn writeElfHeader(self: *ElfFile) !void {750 var index: usize = 0;
662 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;751 hdr_buf[0..4].* = "\x7fELF".*;
752 index += 4;
663753
664 var index: usize = 0;754 hdr_buf[index] = switch (self.ptr_width) {
665 hdr_buf[0..4].* = "\x7fELF".*;755 .p32 => elf.ELFCLASS32,
666 index += 4;756 .p64 => elf.ELFCLASS64,
757 };
758 index += 1;
667759
668 hdr_buf[index] = switch (self.ptr_width) {760 const endian = self.options.target.cpu.arch.endian();
669 .p32 => elf.ELFCLASS32,761 hdr_buf[index] = switch (endian) {
670 .p64 => elf.ELFCLASS64,762 .Little => elf.ELFDATA2LSB,
671 };763 .Big => elf.ELFDATA2MSB,
672 index += 1;764 };
765 index += 1;
673766
674 const endian = self.options.target.cpu.arch.endian();767 hdr_buf[index] = 1; // ELF version
675 hdr_buf[index] = switch (endian) {768 index += 1;
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 }
740769
741 const e_flags = 0;770 // OS ABI, often set to 0 regardless of target platform
742 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);771 // ABI Version, possibly used by glibc but not by static executables
743 index += 4;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) {776 assert(index == 16);
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;
751777
752 const e_phentsize: u16 = switch (self.ptr_width) {778 const elf_type = switch (self.options.output_mode) {
753 .p32 => @sizeOf(elf.Elf32_Phdr),779 .Exe => elf.ET.EXEC,
754 .p64 => @sizeOf(elf.Elf64_Phdr),780 .Obj => elf.ET.REL,
755 };781 .Lib => switch (self.options.link_mode) {
756 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);782 .Static => elf.ET.REL,
757 index += 2;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);789 const machine = self.options.target.cpu.arch.toElfMachine();
760 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);790 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
761 index += 2;791 index += 2;
762792
763 const e_shentsize: u16 = switch (self.ptr_width) {793 // ELF Version, again
764 .p32 => @sizeOf(elf.Elf32_Shdr),794 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
765 .p64 => @sizeOf(elf.Elf64_Shdr),795 index += 4;
766 };
767 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
768 index += 2;
769796
770 const e_shnum = @intCast(u16, self.sections.items.len);797 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
771 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
772 index += 2;
773798
774 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);799 switch (self.ptr_width) {
775 index += 2;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);808 // e_shoff
780 }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 {817 // e_phoff
783 var already_have_free_list_node = false;818 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
784 {819 index += 8;
785 var i: usize = 0;820
786 while (i < self.text_block_free_list.items.len) {821 // e_shoff
787 if (self.text_block_free_list.items[i] == text_block) {822 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
788 _ = self.text_block_free_list.swapRemove(i);823 index += 8;
789 continue;824 },
790 }
791 if (self.text_block_free_list.items[i] == text_block.prev) {
792 already_have_free_list_node = true;
793 }
794 i += 1;
795 }825 }
796 }
797826
798 if (self.last_text_block == text_block) {827 const e_flags = 0;
799 // TODO shrink the .text section size here828 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
800 self.last_text_block = text_block.prev;829 index += 4;
801 }
802830
803 if (text_block.prev) |prev| {831 const e_ehsize: u16 = switch (self.ptr_width) {
804 prev.next = text_block.next;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.*)) {838 const e_phentsize: u16 = switch (self.ptr_width) {
807 // The free list is heuristics, it doesn't have to be perfect, so we can839 .p32 => @sizeOf(elf.Elf32_Phdr),
808 // ignore the OOM here.840 .p64 => @sizeOf(elf.Elf64_Phdr),
809 self.text_block_free_list.append(self.allocator, prev) catch {};841 };
810 }842 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
811 } else {843 index += 2;
812 text_block.prev = null;
813 }
814844
815 if (text_block.next) |next| {845 const e_phnum = @intCast(u16, self.program_headers.items.len);
816 next.prev = text_block.prev;846 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
817 } else {847 index += 2;
818 text_block.next = null;
819 }
820 }
821848
822 fn shrinkTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64) void {849 const e_shentsize: u16 = switch (self.ptr_width) {
823 // TODO check the new capacity, and if it crosses the size threshold into a big enough850 .p32 => @sizeOf(elf.Elf32_Shdr),
824 // capacity, insert a free list node for it.851 .p64 => @sizeOf(elf.Elf64_Shdr),
825 }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 {856 const e_shnum = @intCast(u16, self.sections.items.len);
828 const sym = self.local_symbols.items[text_block.local_sym_index];857 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
829 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;858 index += 2;
830 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);859
831 if (!need_realloc) return sym.st_value;860 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
832 return self.allocateTextBlock(text_block, new_block_size, alignment);861 index += 2;
833 }862
863 assert(index == e_ehsize);
834864
835 fn allocateTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {865 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
836 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];866 }
837 const shdr = &self.sections.items[self.text_section_index.?];867
838 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;868 fn freeTextBlock(self: *File.Elf, text_block: *TextBlock) void {
839869 var already_have_free_list_node = false;
840 // We use these to indicate our intention to update metadata, placing the new block,870 {
841 // and possibly removing a free list node.871 var i: usize = 0;
842 // It would be simpler to do it inside the for loop below, but that would cause a872 while (i < self.text_block_free_list.items.len) {
843 // problem if an error was returned later in the function. So this action873 if (self.text_block_free_list.items[i] == text_block) {
844 // is actually carried out at the end of the function, when errors are no longer possible.
845 var block_placement: ?*TextBlock = null;
846 var free_list_removal: ?usize = null;
847
848 // First we look for an appropriately sized free list node.
849 // The list is unordered. We'll just take the first thing that works.
850 const vaddr = blk: {
851 var i: usize = 0;
852 while (i < self.text_block_free_list.items.len) {
853 const big_block = self.text_block_free_list.items[i];
854 // We now have a pointer to a live text block that has too much capacity.
855 // Is it enough that we could fit this new text block?
856 const sym = self.local_symbols.items[big_block.local_sym_index];
857 const capacity = big_block.capacity(self.*);
858 const ideal_capacity = capacity * alloc_num / alloc_den;
859 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
860 const capacity_end_vaddr = sym.st_value + capacity;
861 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
862 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
863 if (new_start_vaddr < ideal_capacity_end_vaddr) {
864 // Additional bookkeeping here to notice if this free list node
865 // should be deleted because the block that it points to has grown to take up
866 // more of the extra capacity.
867 if (!big_block.freeListEligible(self.*)) {
868 _ = self.text_block_free_list.swapRemove(i);874 _ = self.text_block_free_list.swapRemove(i);
869 } else {875 continue;
870 i += 1;
871 }876 }
872 continue;877 if (self.text_block_free_list.items[i] == text_block.prev) {
873 }878 already_have_free_list_node = true;
874 // At this point we know that we will place the new block here. But the879 }
875 // remaining question is whether there is still yet enough capacity left880 i += 1;
876 // over for there to still be a free list node.
877 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
878 const keep_free_list_node = remaining_capacity >= min_text_capacity;
879
880 // Set up the metadata to be updated, after errors are no longer possible.
881 block_placement = big_block;
882 if (!keep_free_list_node) {
883 free_list_removal = i;
884 }881 }
885 break :blk new_start_vaddr;
886 } else if (self.last_text_block) |last| {
887 const sym = self.local_symbols.items[last.local_sym_index];
888 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
889 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
890 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
891 // Set up the metadata to be updated, after errors are no longer possible.
892 block_placement = last;
893 break :blk new_start_vaddr;
894 } else {
895 break :blk phdr.p_vaddr;
896 }882 }
897 };
898883
899 const expand_text_section = block_placement == null or block_placement.?.next == null;884 if (self.last_text_block == text_block) {
900 if (expand_text_section) {885 // TODO shrink the .text section size here
901 const text_capacity = self.allocatedSize(shdr.sh_offset);886 self.last_text_block = text_block.prev;
902 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
903 if (needed_size > text_capacity) {
904 // Must move the entire text section.
905 const new_offset = self.findFreeSpace(needed_size, 0x1000);
906 const text_size = if (self.last_text_block) |last| blk: {
907 const sym = self.local_symbols.items[last.local_sym_index];
908 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
909 } else 0;
910 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
911 if (amt != text_size) return error.InputOutput;
912 shdr.sh_offset = new_offset;
913 phdr.p_offset = new_offset;
914 }887 }
915 self.last_text_block = text_block;
916888
917 shdr.sh_size = needed_size;889 if (text_block.prev) |prev| {
918 phdr.p_memsz = needed_size;890 prev.next = text_block.next;
919 phdr.p_filesz = needed_size;
920891
921 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty892 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
922 self.shdr_table_dirty = true; // TODO look into making only the one section dirty893 // The free list is heuristics, it doesn't have to be perfect, so we can
923 }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.901 if (text_block.next) |next| {
926 // In this case we need to "unplug" it from its previous location before902 next.prev = text_block.prev;
927 // plugging it in to its new location.903 } else {
928 if (text_block.prev) |prev| {904 text_block.next = null;
929 prev.next = text_block.next;905 }
930 }
931 if (text_block.next) |next| {
932 next.prev = text_block.prev;
933 }906 }
934907
935 if (block_placement) |big_block| {908 fn shrinkTextBlock(self: *File.Elf, text_block: *TextBlock, new_block_size: u64) void {
936 text_block.prev = big_block;909 // TODO check the new capacity, and if it crosses the size threshold into a big enough
937 text_block.next = big_block.next;910 // capacity, insert a free list node for it.
938 big_block.next = text_block;
939 } else {
940 text_block.prev = null;
941 text_block.next = null;
942 }
943 if (free_list_removal) |i| {
944 _ = self.text_block_free_list.swapRemove(i);
945 }911 }
946 return vaddr;
947 }
948912
949 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {913 fn growTextBlock(self: *File.Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
950 if (decl.link.local_sym_index != 0) return;914 const sym = self.local_symbols.items[text_block.local_sym_index];
951915 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
952 // Here we also ensure capacity for the free lists so that they can be appended to without fail.916 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
953 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);917 if (!need_realloc) return sym.st_value;
954 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);918 return self.allocateTextBlock(text_block, new_block_size, alignment);
955 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
956 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
957
958 if (self.local_symbol_free_list.popOrNull()) |i| {
959 //std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
960 decl.link.local_sym_index = i;
961 } else {
962 //std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
963 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
964 _ = self.local_symbols.addOneAssumeCapacity();
965 }919 }
966920
967 if (self.offset_table_free_list.popOrNull()) |i| {921 fn allocateTextBlock(self: *File.Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
968 decl.link.offset_table_index = i;922 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
969 } else {923 const shdr = &self.sections.items[self.text_section_index.?];
970 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);924 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
971 _ = self.offset_table.addOneAssumeCapacity();925
972 self.offset_table_count_dirty = true;926 // We use these to indicate our intention to update metadata, placing the new block,
973 }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] = .{1003 shdr.sh_size = needed_size;
978 .st_name = 0,1004 phdr.p_memsz = needed_size;
979 .st_info = 0,1005 phdr.p_filesz = needed_size;
980 .st_other = 0,
981 .st_shndx = 0,
982 .st_value = phdr.p_vaddr,
983 .st_size = 0,
984 };
985 self.offset_table.items[decl.link.offset_table_index] = 0;
986 }
9871006
988 pub fn freeDecl(self: *ElfFile, decl: *Module.Decl) void {1007 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
989 self.freeTextBlock(&decl.link);1008 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
990 if (decl.link.local_sym_index != 0) {1009 }
991 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
992 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
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;
997 }1033 }
998 }
9991034
1000 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {1035 pub fn allocateDeclIndexes(self: *File.Elf, decl: *Module.Decl) !void {
1001 var code_buffer = std.ArrayList(u8).init(self.allocator);1036 if (decl.link.local_sym_index != 0) return;
1002 defer code_buffer.deinit();
1003
1004 const typed_value = decl.typed_value.most_recent.typed_value;
1005 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1006 .externally_managed => |x| x,
1007 .appended => code_buffer.items,
1008 .fail => |em| {
1009 decl.analysis = .codegen_failure;
1010 _ = try module.failed_decls.put(decl, em);
1011 return;
1012 },
1013 };
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()) {1044 if (self.local_symbol_free_list.popOrNull()) |i| {
1018 .Fn => elf.STT_FUNC,1045 //std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
1019 else => elf.STT_OBJECT,1046 decl.link.local_sym_index = i;
1020 };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()1053 if (self.offset_table_free_list.popOrNull()) |i| {
1023 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];1054 decl.link.offset_table_index = i;
1024 if (local_sym.st_size != 0) {1055 } else {
1025 const capacity = decl.link.capacity(self.*);1056 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
1026 const need_realloc = code.len > capacity or1057 _ = self.offset_table.addOneAssumeCapacity();
1027 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);1058 self.offset_table_count_dirty = true;
1028 if (need_realloc) {
1029 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1030 //std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1031 if (vaddr != local_sym.st_value) {
1032 local_sym.st_value = vaddr;
1033
1034 //std.log.debug(.link, " (writing new offset table entry)\n", .{});
1035 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1036 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1037 }
1038 } else if (code.len < local_sym.st_size) {
1039 self.shrinkTextBlock(&decl.link, code.len);
1040 }1059 }
1041 local_sym.st_size = code.len;1060
1042 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));1061 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1043 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;1062
1044 local_sym.st_other = 0;1063 self.local_symbols.items[decl.link.local_sym_index] = .{
1045 local_sym.st_shndx = self.text_section_index.?;1064 .st_name = 0,
1046 // TODO this write could be avoided if no fields of the symbol were changed.1065 .st_info = 0,
1047 try self.writeSymbol(decl.link.local_sym_index);
1048 } else {
1049 const decl_name = mem.spanZ(decl.name);
1050 const name_str_index = try self.makeString(decl_name);
1051 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1052 //std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1053 errdefer self.freeTextBlock(&decl.link);
1054
1055 local_sym.* = .{
1056 .st_name = name_str_index,
1057 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1058 .st_other = 0,1066 .st_other = 0,
1059 .st_shndx = self.text_section_index.?,1067 .st_shndx = 0,
1060 .st_value = vaddr,1068 .st_value = phdr.p_vaddr,
1061 .st_size = code.len,1069 .st_size = 0,
1062 };1070 };
1063 self.offset_table.items[decl.link.offset_table_index] = vaddr;1071 self.offset_table.items[decl.link.offset_table_index] = 0;
1064
1065 try self.writeSymbol(decl.link.local_sym_index);
1066 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1067 }1072 }
10681073
1069 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;1074 pub fn freeDecl(self: *File.Elf, decl: *Module.Decl) void {
1070 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;1075 self.freeTextBlock(&decl.link);
1071 try self.file.?.pwriteAll(code, file_offset);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.1080 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
1074 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1075 return self.updateDeclExports(module, decl, decl_exports);
1076 }
10771081
1078 /// Must be called only after a successful call to `updateDecl`.1082 decl.link.local_sym_index = 0;
1079 pub fn updateDeclExports(
1080 self: *ElfFile,
1081 module: *Module,
1082 decl: *const Module.Decl,
1083 exports: []const *Module.Export,
1084 ) !void {
1085 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1086 // them, so that deleting exports is guaranteed to succeed.
1087 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1088 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1089 const typed_value = decl.typed_value.most_recent.typed_value;
1090 if (decl.link.local_sym_index == 0) return;
1091 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1092
1093 for (exports) |exp| {
1094 if (exp.options.section) |section_name| {
1095 if (!mem.eql(u8, section_name, ".text")) {
1096 try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1);
1097 module.failed_exports.putAssumeCapacityNoClobber(
1098 exp,
1099 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1100 );
1101 continue;
1102 }
1103 }1083 }
1104 const stb_bits: u8 = switch (exp.options.linkage) {1084 }
1105 .Internal => elf.STB_LOCAL,1085
1106 .Strong => blk: {1086 pub fn updateDecl(self: *File.Elf, module: *Module, decl: *Module.Decl) !void {
1107 if (mem.eql(u8, exp.options.name, "_start")) {1087 var code_buffer = std.ArrayList(u8).init(self.allocator);
1108 self.entry_addr = decl_sym.st_value;1088 defer code_buffer.deinit();
1109 }1089
1110 break :blk elf.STB_GLOBAL;1090 const typed_value = decl.typed_value.most_recent.typed_value;
1111 },1091 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1112 .Weak => elf.STB_WEAK,1092 .externally_managed => |x| x,
1113 .LinkOnce => {1093 .appended => code_buffer.items,
1114 try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1);1094 .fail => |em| {
1115 module.failed_exports.putAssumeCapacityNoClobber(1095 decl.analysis = .codegen_failure;
1116 exp,1096 _ = try module.failed_decls.put(decl, em);
1117 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),1097 return;
1118 );
1119 continue;
1120 },1098 },
1121 };1099 };
1122 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);1100
1123 if (exp.link.sym_index) |i| {1101 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1124 const sym = &self.global_symbols.items[i];1102
1125 sym.* = .{1103 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1126 .st_name = try self.updateString(sym.st_name, exp.options.name),1104 .Fn => elf.STT_FUNC,
1127 .st_info = (stb_bits << 4) | stt_bits,1105 else => elf.STT_OBJECT,
1128 .st_other = 0,1106 };
1129 .st_shndx = self.text_section_index.?,1107
1130 .st_value = decl_sym.st_value,1108 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1131 .st_size = decl_sym.st_size,1109 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1132 };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);
1133 } else {1134 } else {
1134 const name = try self.makeString(exp.options.name);1135 const decl_name = mem.spanZ(decl.name);
1135 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {1136 const name_str_index = try self.makeString(decl_name);
1136 _ = self.global_symbols.addOneAssumeCapacity();1137 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1137 break :blk self.global_symbols.items.len - 1;1138 //std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1138 };1139 errdefer self.freeTextBlock(&decl.link);
1139 self.global_symbols.items[i] = .{1140
1140 .st_name = name,1141 local_sym.* = .{
1141 .st_info = (stb_bits << 4) | stt_bits,1142 .st_name = name_str_index,
1143 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1142 .st_other = 0,1144 .st_other = 0,
1143 .st_shndx = self.text_section_index.?,1145 .st_shndx = self.text_section_index.?,
1144 .st_value = decl_sym.st_value,1146 .st_value = vaddr,
1145 .st_size = decl_sym.st_size,1147 .st_size = code.len,
1146 };1148 };
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);
1149 }1153 }
1150 }
1151 }
11521154
1153 pub fn deleteExport(self: *ElfFile, exp: Export) void {1155 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1154 const sym_index = exp.sym_index orelse return;1156 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1155 self.global_symbol_free_list.appendAssumeCapacity(sym_index);1157 try self.file.?.pwriteAll(code, file_offset);
1156 self.global_symbols.items[sym_index].st_info = 0;
1157 }
11581158
1159 fn writeProgHeader(self: *ElfFile, index: usize) !void {1159 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1160 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1160 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1161 const offset = self.program_headers.items[index].p_offset;1161 return self.updateDeclExports(module, decl, decl_exports);
1162 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1163 32 => {
1164 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1165 if (foreign_endian) {
1166 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1167 }
1168 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1169 },
1170 64 => {
1171 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1172 if (foreign_endian) {
1173 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1174 }
1175 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1176 },
1177 else => return error.UnsupportedArchitecture,
1178 }1162 }
1179 }
11801163
1181 fn writeSectHeader(self: *ElfFile, index: usize) !void {1164 /// Must be called only after a successful call to `updateDecl`.
1182 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1165 pub fn updateDeclExports(
1183 const offset = self.sections.items[index].sh_offset;1166 self: *File.Elf,
1184 switch (self.options.target.cpu.arch.ptrBitWidth()) {1167 module: *Module,
1185 32 => {1168 decl: *const Module.Decl,
1186 var shdr: [1]elf.Elf32_Shdr = undefined;1169 exports: []const *Module.Export,
1187 shdr[0] = sectHeaderTo32(self.sections.items[index]);1170 ) !void {
1188 if (foreign_endian) {1171 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1189 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);1172 // them, so that deleting exports is guaranteed to succeed.
1190 }1173 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1191 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);1174 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1192 },1175 const typed_value = decl.typed_value.most_recent.typed_value;
1193 64 => {1176 if (decl.link.local_sym_index == 0) return;
1194 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};1177 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1195 if (foreign_endian) {1178
1196 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);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 }
1197 }1189 }
1198 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);1190 const stb_bits: u8 = switch (exp.options.linkage) {
1199 },1191 .Internal => elf.STB_LOCAL,
1200 else => return error.UnsupportedArchitecture,1192 .Strong => blk: {
1201 }1193 if (mem.eql(u8, exp.options.name, "_start")) {
1202 }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 {1234 exp.link.sym_index = @intCast(u32, i);
1205 const shdr = &self.sections.items[self.got_section_index.?];1235 }
1206 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1207 const entry_size: u16 = switch (self.ptr_width) {
1208 .p32 => 4,
1209 .p64 => 8,
1210 };
1211 if (self.offset_table_count_dirty) {
1212 // TODO Also detect virtual address collisions.
1213 const allocated_size = self.allocatedSize(shdr.sh_offset);
1214 const needed_size = self.local_symbols.items.len * entry_size;
1215 if (needed_size > allocated_size) {
1216 // Must move the entire got section.
1217 const new_offset = self.findFreeSpace(needed_size, entry_size);
1218 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1219 if (amt != shdr.sh_size) return error.InputOutput;
1220 shdr.sh_offset = new_offset;
1221 phdr.p_offset = new_offset;
1222 }1236 }
1223 shdr.sh_size = needed_size;1237 }
1224 phdr.p_memsz = needed_size;
1225 phdr.p_filesz = needed_size;
12261238
1227 self.shdr_table_dirty = true; // TODO look into making only the one section dirty1239 pub fn deleteExport(self: *File.Elf, exp: Export) void {
1228 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty1240 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 }
1231 }1265 }
1232 const endian = self.options.target.cpu.arch.endian();1266
1233 const off = shdr.sh_offset + @as(u64, entry_size) * index;1267 fn writeSectHeader(self: *File.Elf, index: usize) !void {
1234 switch (self.ptr_width) {1268 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1235 .p32 => {1269 const offset = self.sections.items[index].sh_offset;
1236 var buf: [4]u8 = undefined;1270 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1237 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);1271 32 => {
1238 try self.file.?.pwriteAll(&buf, off);1272 var shdr: [1]elf.Elf32_Shdr = undefined;
1239 },1273 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1240 .p64 => {1274 if (foreign_endian) {
1241 var buf: [8]u8 = undefined;1275 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1242 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);1276 }
1243 try self.file.?.pwriteAll(&buf, off);1277 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1244 },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 }
1245 }1288 }
1246 }
12471289
1248 fn writeSymbol(self: *ElfFile, index: usize) !void {1290 fn writeOffsetTableEntry(self: *File.Elf, index: usize) !void {
1249 const syms_sect = &self.sections.items[self.symtab_section_index.?];1291 const shdr = &self.sections.items[self.got_section_index.?];
1250 // Make sure we are not pointlessly writing symbol data that will have to get relocated1292 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1251 // due to running out of space.1293 const entry_size: u16 = switch (self.ptr_width) {
1252 if (self.local_symbols.items.len != syms_sect.sh_info) {1294 .p32 => 4,
1253 const sym_size: u64 = switch (self.ptr_width) {1295 .p64 => 8,
1254 .p32 => @sizeOf(elf.Elf32_Sym),
1255 .p64 => @sizeOf(elf.Elf64_Sym),
1256 };
1257 const sym_align: u16 = switch (self.ptr_width) {
1258 .p32 => @alignOf(elf.Elf32_Sym),
1259 .p64 => @alignOf(elf.Elf64_Sym),
1260 };1296 };
1261 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;1297 if (self.offset_table_count_dirty) {
1262 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {1298 // TODO Also detect virtual address collisions.
1263 // Move all the symbols to a new file location.1299 const allocated_size = self.allocatedSize(shdr.sh_offset);
1264 const new_offset = self.findFreeSpace(needed_size, sym_align);1300 const needed_size = self.local_symbols.items.len * entry_size;
1265 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;1301 if (needed_size > allocated_size) {
1266 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);1302 // Must move the entire got section.
1267 if (amt != existing_size) return error.InputOutput;1303 const new_offset = self.findFreeSpace(needed_size, entry_size);
1268 syms_sect.sh_offset = new_offset;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 },
1269 }1331 }
1270 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1271 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1272 self.shdr_table_dirty = true; // TODO look into only writing one section
1273 }1332 }
1274 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1333
1275 switch (self.ptr_width) {1334 fn writeSymbol(self: *File.Elf, index: usize) !void {
1276 .p32 => {1335 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1277 var sym = [1]elf.Elf32_Sym{1336 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1278 .{1337 // due to running out of space.
1279 .st_name = self.local_symbols.items[index].st_name,1338 if (self.local_symbols.items.len != syms_sect.sh_info) {
1280 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),1339 const sym_size: u64 = switch (self.ptr_width) {
1281 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),1340 .p32 => @sizeOf(elf.Elf32_Sym),
1282 .st_info = self.local_symbols.items[index].st_info,1341 .p64 => @sizeOf(elf.Elf64_Sym),
1283 .st_other = self.local_symbols.items[index].st_other,
1284 .st_shndx = self.local_symbols.items[index].st_shndx,
1285 },
1286 };1342 };
1287 if (foreign_endian) {1343 const sym_align: u16 = switch (self.ptr_width) {
1288 bswapAllFields(elf.Elf32_Sym, &sym[0]);1344 .p32 => @alignOf(elf.Elf32_Sym),
1289 }1345 .p64 => @alignOf(elf.Elf64_Sym),
1290 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;1346 };
1291 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1347 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1292 },1348 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1293 .p64 => {1349 // Move all the symbols to a new file location.
1294 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};1350 const new_offset = self.findFreeSpace(needed_size, sym_align);
1295 if (foreign_endian) {1351 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1296 bswapAllFields(elf.Elf64_Sym, &sym[0]);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;
1297 }1355 }
1298 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;1356 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1299 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1357 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1300 },1358 self.shdr_table_dirty = true; // TODO look into only writing one section
1301 }1359 }
1302 }1360 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
13031361 switch (self.ptr_width) {
1304 fn writeAllGlobalSymbols(self: *ElfFile) !void {1362 .p32 => {
1305 const syms_sect = &self.sections.items[self.symtab_section_index.?];1363 var sym = [1]elf.Elf32_Sym{
1306 const sym_size: u64 = switch (self.ptr_width) {1364 .{
1307 .p32 => @sizeOf(elf.Elf32_Sym),1365 .st_name = self.local_symbols.items[index].st_name,
1308 .p64 => @sizeOf(elf.Elf64_Sym),1366 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1309 };1367 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1310 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1368 .st_info = self.local_symbols.items[index].st_info,
1311 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;1369 .st_other = self.local_symbols.items[index].st_other,
1312 switch (self.ptr_width) {1370 .st_shndx = self.local_symbols.items[index].st_shndx,
1313 .p32 => {1371 },
1314 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1315 defer self.allocator.free(buf);
1316
1317 for (buf) |*sym, i| {
1318 sym.* = .{
1319 .st_name = self.global_symbols.items[i].st_name,
1320 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1321 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1322 .st_info = self.global_symbols.items[i].st_info,
1323 .st_other = self.global_symbols.items[i].st_other,
1324 .st_shndx = self.global_symbols.items[i].st_shndx,
1325 };1372 };
1326 if (foreign_endian) {1373 if (foreign_endian) {
1327 bswapAllFields(elf.Elf32_Sym, sym);1374 bswapAllFields(elf.Elf32_Sym, &sym[0]);
1328 }1375 }
1329 }1376 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1330 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);1377 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1331 },1378 },
1332 .p64 => {1379 .p64 => {
1333 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);1380 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
1334 defer self.allocator.free(buf);
1335
1336 for (buf) |*sym, i| {
1337 sym.* = .{
1338 .st_name = self.global_symbols.items[i].st_name,
1339 .st_value = self.global_symbols.items[i].st_value,
1340 .st_size = self.global_symbols.items[i].st_size,
1341 .st_info = self.global_symbols.items[i].st_info,
1342 .st_other = self.global_symbols.items[i].st_other,
1343 .st_shndx = self.global_symbols.items[i].st_shndx,
1344 };
1345 if (foreign_endian) {1381 if (foreign_endian) {
1346 bswapAllFields(elf.Elf64_Sym, sym);1382 bswapAllFields(elf.Elf64_Sym, &sym[0]);
1347 }1383 }
1348 }1384 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1349 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);1385 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1350 },1386 },
1387 }
1351 }1388 }
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 };
1353};1440};
13541441
1355/// Truncates the existing file contents and overwrites the contents.1442/// Truncates the existing file contents and overwrites the contents.
1356/// Returns an error if `file` is not already open with +read +write +seek abilities.1443/// 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 {
1358 switch (options.output_mode) {1445 switch (options.output_mode) {
1359 .Exe => {},1446 .Exe => {},
1360 .Obj => {},1447 .Obj => {},
...@@ -1368,7 +1455,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El...@@ -1368,7 +1455,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
1368 .wasm => return error.TODOImplementWritingWasmObjects,1455 .wasm => return error.TODOImplementWritingWasmObjects,
1369 }1456 }
13701457
1371 var self: ElfFile = .{1458 var self: File.Elf = .{
1372 .allocator = allocator,1459 .allocator = allocator,
1373 .file = file,1460 .file = file,
1374 .options = options,1461 .options = options,
...@@ -1412,7 +1499,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El...@@ -1412,7 +1499,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
1412}1499}
14131500
1414/// Returns error.IncrFailed if incremental update could not be performed.1501/// 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 {
1416 switch (options.output_mode) {1503 switch (options.output_mode) {
1417 .Exe => {},1504 .Exe => {},
1418 .Obj => {},1505 .Obj => {},
...@@ -1425,7 +1512,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf...@@ -1425,7 +1512,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
1425 .macho => return error.IncrFailed,1512 .macho => return error.IncrFailed,
1426 .wasm => return error.IncrFailed,1513 .wasm => return error.IncrFailed,
1427 }1514 }
1428 var self: ElfFile = .{1515 var self: File.Elf = .{
1429 .allocator = allocator,1516 .allocator = allocator,
1430 .file = file,1517 .file = file,
1431 .owns_file_handle = false,1518 .owns_file_handle = false,
src-self-hosted/test.zig+22-3
...@@ -64,10 +64,11 @@ pub const TestContext = struct {...@@ -64,10 +64,11 @@ pub const TestContext = struct {
64 /// such as QEMU is required for tests to complete.64 /// such as QEMU is required for tests to complete.
65 target: std.zig.CrossTarget,65 target: std.zig.CrossTarget,
66 /// In order to be able to run e.g. Execution updates, this must be set66 /// 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.
68 output_mode: std.builtin.OutputMode,68 output_mode: std.builtin.OutputMode,
69 updates: std.ArrayList(Update),69 updates: std.ArrayList(Update),
70 extension: TestType,70 extension: TestType,
71 c_standard: ?Module.CStandard = null,
7172
72 /// Adds a subcase in which the module is updated with `src`, and the73 /// Adds a subcase in which the module is updated with `src`, and the
73 /// resulting ZIR is validated against `result`.74 /// resulting ZIR is validated against `result`.
...@@ -187,6 +188,22 @@ pub const TestContext = struct {...@@ -187,6 +188,22 @@ pub const TestContext = struct {
187 return ctx.addObj(name, target, .ZIR);188 return ctx.addObj(name, target, .ZIR);
188 }189 }
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
190 pub fn addCompareOutput(207 pub fn addCompareOutput(
191 ctx: *TestContext,208 ctx: *TestContext,
192 name: []const u8,209 name: []const u8,
...@@ -425,6 +442,7 @@ pub const TestContext = struct {...@@ -425,6 +442,7 @@ pub const TestContext = struct {
425 .bin_file_path = bin_name,442 .bin_file_path = bin_name,
426 .root_pkg = root_pkg,443 .root_pkg = root_pkg,
427 .keep_source_files_loaded = true,444 .keep_source_files_loaded = true,
445 .c_standard = case.c_standard,
428 });446 });
429 defer module.deinit();447 defer module.deinit();
430448
...@@ -463,14 +481,15 @@ pub const TestContext = struct {...@@ -463,14 +481,15 @@ pub const TestContext = struct {
463 var test_node = update_node.start("assert", null);481 var test_node = update_node.start("assert", null);
464 test_node.activate();482 test_node.activate();
465 defer test_node.end();483 defer test_node.end();
484 const label = if (case.c_standard) |_| "C" else "ZIR";
466 if (expected_output.len != out_zir.items.len) {485 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 });
468 std.process.exit(1);487 std.process.exit(1);
469 }488 }
470 for (expected_output) |e, i| {489 for (expected_output) |e, i| {
471 if (out_zir.items[i] != e) {490 if (out_zir.items[i] != e) {
472 if (expected_output.len != out_zir.items.len) {491 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 });
474 std.process.exit(1);493 std.process.exit(1);
475 }494 }
476 }495 }
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 {...@@ -4,4 +4,5 @@ pub fn addCases(ctx: *TestContext) !void {
4 try @import("compile_errors.zig").addCases(ctx);4 try @import("compile_errors.zig").addCases(ctx);
5 try @import("compare_output.zig").addCases(ctx);5 try @import("compare_output.zig").addCases(ctx);
6 try @import("zir.zig").addCases(ctx);6 try @import("zir.zig").addCases(ctx);
7 try @import("cbe.zig").addCases(ctx);
7}8}