authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-21 13:24:13-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-21 13:25:59-07:00
log89b6c47e042b343fba754ed8f39efa1b385093bb
treea27e6ef0c793e496877cb21cfddc2b3da5ddbb56
parent37ad9f38dc89b568abddb56a044bdfb36eca7d49

stage2: decouple codegen.zig from ELF

See #6113 for an alternate way of doing this that we didn't end up following. Closes #6079. I also took the opportunity here to extract C.zig and Elf.zig from link.zig.

8 files changed, 2841 insertions(+), 2796 deletions(-)

src-self-hosted/cbe.h deleted-15
......@@ -1,15 +0,0 @@
1#if __STDC_VERSION__ >= 201112L
2#define zig_noreturn _Noreturn
3#elif __GNUC__
4#define zig_noreturn __attribute__ ((noreturn))
5#elif _MSC_VER
6#define zig_noreturn __declspec(noreturn)
7#else
8#define zig_noreturn
9#endif
10
11#if __GNUC__
12#define zig_unreachable() __builtin_unreachable()
13#else
14#define zig_unreachable()
15#endif
src-self-hosted/codegen.zig+106-95
......@@ -59,20 +59,20 @@ pub const GenerateSymbolError = error{
5959};
6060
6161pub fn generateSymbol(
62 bin_file: *link.File.Elf,
62 bin_file: *link.File,
6363 src: usize,
6464 typed_value: TypedValue,
6565 code: *std.ArrayList(u8),
6666 dbg_line: *std.ArrayList(u8),
6767 dbg_info: *std.ArrayList(u8),
68 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
68 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
6969) GenerateSymbolError!Result {
7070 const tracy = trace(@src());
7171 defer tracy.end();
7272
7373 switch (typed_value.ty.zigTypeTag()) {
7474 .Fn => {
75 switch (bin_file.base.options.target.cpu.arch) {
75 switch (bin_file.options.target.cpu.arch) {
7676 .wasm32 => unreachable, // has its own code path
7777 .wasm64 => unreachable, // has its own code path
7878 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
......@@ -151,7 +151,7 @@ pub fn generateSymbol(
151151 }
152152 return Result{
153153 .fail = try ErrorMsg.create(
154 bin_file.base.allocator,
154 bin_file.allocator,
155155 src,
156156 "TODO implement generateSymbol for more kinds of arrays",
157157 .{},
......@@ -164,12 +164,11 @@ pub fn generateSymbol(
164164 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
165165 const decl = payload.decl;
166166 if (decl.analysis != .complete) return error.AnalysisFail;
167 assert(decl.link.elf.local_sym_index != 0);
168167 // TODO handle the dependency of this symbol on the decl's vaddr.
169168 // If the decl changes vaddr, then this symbol needs to get regenerated.
170 const vaddr = bin_file.local_symbols.items[decl.link.elf.local_sym_index].st_value;
171 const endian = bin_file.base.options.target.cpu.arch.endian();
172 switch (bin_file.base.options.target.cpu.arch.ptrBitWidth()) {
169 const vaddr = bin_file.getDeclVAddr(decl);
170 const endian = bin_file.options.target.cpu.arch.endian();
171 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
173172 16 => {
174173 try code.resize(2);
175174 mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
......@@ -188,7 +187,7 @@ pub fn generateSymbol(
188187 }
189188 return Result{
190189 .fail = try ErrorMsg.create(
191 bin_file.base.allocator,
190 bin_file.allocator,
192191 src,
193192 "TODO implement generateSymbol for pointer {}",
194193 .{typed_value.val},
......@@ -198,7 +197,7 @@ pub fn generateSymbol(
198197 .Int => {
199198 // TODO populate .debug_info for the integer
200199
201 const info = typed_value.ty.intInfo(bin_file.base.options.target);
200 const info = typed_value.ty.intInfo(bin_file.options.target);
202201 if (info.bits == 8 and !info.signed) {
203202 const x = typed_value.val.toUnsignedInt();
204203 try code.append(@intCast(u8, x));
......@@ -206,7 +205,7 @@ pub fn generateSymbol(
206205 }
207206 return Result{
208207 .fail = try ErrorMsg.create(
209 bin_file.base.allocator,
208 bin_file.allocator,
210209 src,
211210 "TODO implement generateSymbol for int type '{}'",
212211 .{typed_value.ty},
......@@ -216,7 +215,7 @@ pub fn generateSymbol(
216215 else => |t| {
217216 return Result{
218217 .fail = try ErrorMsg.create(
219 bin_file.base.allocator,
218 bin_file.allocator,
220219 src,
221220 "TODO implement generateSymbol for type '{}'",
222221 .{@tagName(t)},
......@@ -234,13 +233,13 @@ const InnerError = error{
234233fn Function(comptime arch: std.Target.Cpu.Arch) type {
235234 return struct {
236235 gpa: *Allocator,
237 bin_file: *link.File.Elf,
236 bin_file: *link.File,
238237 target: *const std.Target,
239238 mod_fn: *const Module.Fn,
240239 code: *std.ArrayList(u8),
241240 dbg_line: *std.ArrayList(u8),
242241 dbg_info: *std.ArrayList(u8),
243 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
242 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
244243 err_msg: ?*ErrorMsg,
245244 args: []MCValue,
246245 ret_mcv: MCValue,
......@@ -405,22 +404,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
405404 const Self = @This();
406405
407406 fn generateSymbol(
408 bin_file: *link.File.Elf,
407 bin_file: *link.File,
409408 src: usize,
410409 typed_value: TypedValue,
411410 code: *std.ArrayList(u8),
412411 dbg_line: *std.ArrayList(u8),
413412 dbg_info: *std.ArrayList(u8),
414 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
413 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
415414 ) GenerateSymbolError!Result {
416415 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
417416
418417 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
419418
420 var branch_stack = std.ArrayList(Branch).init(bin_file.base.allocator);
419 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
421420 defer {
422421 assert(branch_stack.items.len == 1);
423 branch_stack.items[0].deinit(bin_file.base.allocator);
422 branch_stack.items[0].deinit(bin_file.allocator);
424423 branch_stack.deinit();
425424 }
426425 const branch = try branch_stack.addOne();
......@@ -443,8 +442,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
443442 };
444443
445444 var function = Self{
446 .gpa = bin_file.base.allocator,
447 .target = &bin_file.base.options.target,
445 .gpa = bin_file.allocator,
446 .target = &bin_file.options.target,
448447 .bin_file = bin_file,
449448 .mod_fn = module_fn,
450449 .code = code,
......@@ -464,7 +463,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
464463 .rbrace_src = src_data.rbrace_src,
465464 .source = src_data.source,
466465 };
467 defer function.exitlude_jump_relocs.deinit(bin_file.base.allocator);
466 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
468467
469468 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
470469 error.CodegenFail => return Result{ .fail = function.err_msg.? },
......@@ -1144,80 +1143,88 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11441143 var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);
11451144 defer info.deinit(self);
11461145
1147 switch (arch) {
1148 .x86_64 => {
1149 for (info.args) |mc_arg, arg_i| {
1150 const arg = inst.args[arg_i];
1151 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
1152 // Here we do not use setRegOrMem even though the logic is similar, because
1153 // the function call will move the stack pointer, so the offsets are different.
1154 switch (mc_arg) {
1155 .none => continue,
1156 .register => |reg| {
1157 try self.genSetReg(arg.src, reg, arg_mcv);
1158 // TODO interact with the register allocator to mark the instruction as moved.
1159 },
1160 .stack_offset => {
1161 // Here we need to emit instructions like this:
1162 // mov qword ptr [rsp + stack_offset], x
1163 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
1164 },
1165 .ptr_stack_offset => {
1166 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1167 },
1168 .ptr_embedded_in_code => {
1169 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1170 },
1171 .undef => unreachable,
1172 .immediate => unreachable,
1173 .unreach => unreachable,
1174 .dead => unreachable,
1175 .embedded_in_code => unreachable,
1176 .memory => unreachable,
1177 .compare_flags_signed => unreachable,
1178 .compare_flags_unsigned => unreachable,
1146 // Due to incremental compilation, how function calls are generated depends
1147 // on linking.
1148 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
1149 switch (arch) {
1150 .x86_64 => {
1151 for (info.args) |mc_arg, arg_i| {
1152 const arg = inst.args[arg_i];
1153 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
1154 // Here we do not use setRegOrMem even though the logic is similar, because
1155 // the function call will move the stack pointer, so the offsets are different.
1156 switch (mc_arg) {
1157 .none => continue,
1158 .register => |reg| {
1159 try self.genSetReg(arg.src, reg, arg_mcv);
1160 // TODO interact with the register allocator to mark the instruction as moved.
1161 },
1162 .stack_offset => {
1163 // Here we need to emit instructions like this:
1164 // mov qword ptr [rsp + stack_offset], x
1165 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
1166 },
1167 .ptr_stack_offset => {
1168 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1169 },
1170 .ptr_embedded_in_code => {
1171 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1172 },
1173 .undef => unreachable,
1174 .immediate => unreachable,
1175 .unreach => unreachable,
1176 .dead => unreachable,
1177 .embedded_in_code => unreachable,
1178 .memory => unreachable,
1179 .compare_flags_signed => unreachable,
1180 .compare_flags_unsigned => unreachable,
1181 }
11791182 }
1180 }
11811183
1182 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1183 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1184 const func = func_val.func;
1185 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1186 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1187 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1188 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1189 // ff 14 25 xx xx xx xx call [addr]
1190 try self.code.ensureCapacity(self.code.items.len + 7);
1191 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
1192 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
1184 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1185 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1186 const func = func_val.func;
1187 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1188 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1189 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1190 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1191 // ff 14 25 xx xx xx xx call [addr]
1192 try self.code.ensureCapacity(self.code.items.len + 7);
1193 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
1194 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
1195 } else {
1196 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1197 }
11931198 } else {
1194 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1199 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
11951200 }
1196 } else {
1197 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1198 }
1199 },
1200 .riscv64 => {
1201 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1202
1203 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1204 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1205 const func = func_val.func;
1206 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1207 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1208 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1209 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1210
1211 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1212 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
1201 },
1202 .riscv64 => {
1203 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1204
1205 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1206 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1207 const func = func_val.func;
1208 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1209 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1210 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1211 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1212
1213 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1214 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
1215 } else {
1216 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1217 }
12131218 } else {
1214 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1219 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
12151220 }
1216 } else {
1217 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1218 }
1219 },
1220 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
1221 },
1222 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
1223 }
1224 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
1225 return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO", .{});
1226 } else {
1227 unreachable;
12211228 }
12221229
12231230 return info.return_value;
......@@ -2036,10 +2043,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20362043 switch (typed_value.ty.zigTypeTag()) {
20372044 .Pointer => {
20382045 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
2039 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
2040 const decl = payload.decl;
2041 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
2042 return MCValue{ .memory = got_addr };
2046 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2047 const decl = payload.decl;
2048 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
2049 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
2050 return MCValue{ .memory = got_addr };
2051 } else {
2052 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
2053 }
20432054 }
20442055 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
20452056 },
......@@ -2167,7 +2178,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21672178
21682179 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
21692180 fn wantSafety(self: *Self) bool {
2170 return switch (self.bin_file.base.options.optimize_mode) {
2181 return switch (self.bin_file.options.optimize_mode) {
21712182 .Debug => true,
21722183 .ReleaseSafe => true,
21732184 .ReleaseFast => false,
......@@ -2178,7 +2189,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21782189 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError {
21792190 @setCold(true);
21802191 assert(self.err_msg == null);
2181 self.err_msg = try ErrorMsg.create(self.bin_file.base.allocator, src, format, args);
2192 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
21822193 return error.CodegenFail;
21832194 }
21842195
src-self-hosted/link.zig+30-2684
......@@ -1,28 +1,10 @@
11const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
42const Allocator = std.mem.Allocator;
5const ir = @import("ir.zig");
63const Module = @import("Module.zig");
74const fs = std.fs;
8const elf = std.elf;
9const codegen = @import("codegen.zig");
10const c_codegen = @import("codegen/c.zig");
11const log = std.log.scoped(.link);
12const DW = std.dwarf;
135const trace = @import("tracy.zig").trace;
14const leb128 = std.debug.leb;
156const Package = @import("Package.zig");
16const Value = @import("value.zig").Value;
177const Type = @import("type.zig").Type;
18const build_options = @import("build_options");
19
20const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
21
22// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
23// zig fmt: off
24
25const default_entry_addr = 0x8000000;
268
279pub const Options = struct {
2810 target: std.Target,
......@@ -41,6 +23,11 @@ pub const Options = struct {
4123};
4224
4325pub const File = struct {
26 tag: Tag,
27 options: Options,
28 file: ?fs.File,
29 allocator: *Allocator,
30
4431 pub const LinkBlock = union {
4532 elf: Elf.TextBlock,
4633 macho: MachO.TextBlock,
......@@ -55,16 +42,24 @@ pub const File = struct {
5542 wasm: ?Wasm.FnData,
5643 };
5744
58 tag: Tag,
59 options: Options,
60 file: ?fs.File,
61 allocator: *Allocator,
45 /// For DWARF .debug_info.
46 pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, true);
47
48 /// For DWARF .debug_info.
49 pub const DbgInfoTypeReloc = struct {
50 /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
51 /// This is where the .debug_info tag for the type is.
52 off: u32,
53 /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
54 /// List of DW.AT_type / DW.FORM_ref4 that points to the type.
55 relocs: std.ArrayListUnmanaged(u32),
56 };
6257
6358 /// Attempts incremental linking, if the file already exists. If
6459 /// incremental linking fails, falls back to truncating the file and
6560 /// rewriting it. A malicious file is detected as incremental link failure
6661 /// and does not cause Illegal Behavior. This operation is not atomic.
67 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
62 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
6863 switch (options.object_format) {
6964 .unknown => unreachable,
7065 .coff => return error.TODOImplementCoff,
......@@ -219,6 +214,15 @@ pub const File = struct {
219214 }
220215 }
221216
217 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
218 switch (base.tag) {
219 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
220 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
221 .c => unreachable,
222 .wasm => unreachable,
223 }
224 }
225
222226 pub const Tag = enum {
223227 elf,
224228 macho,
......@@ -230,2670 +234,12 @@ pub const File = struct {
230234 no_entry_point_found: bool = false,
231235 };
232236
233 pub const C = struct {
234 pub const base_tag: Tag = .c;
235
236 base: File,
237
238 header: std.ArrayList(u8),
239 constants: std.ArrayList(u8),
240 main: std.ArrayList(u8),
241
242 called: std.StringHashMap(void),
243 need_stddef: bool = false,
244 need_stdint: bool = false,
245 error_msg: *Module.ErrorMsg = undefined,
246
247 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
248 assert(options.object_format == .c);
249
250 const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = determineMode(options) });
251 errdefer file.close();
252
253 var c_file = try allocator.create(C);
254 errdefer allocator.destroy(c_file);
255
256 c_file.* = File.C{
257 .base = .{
258 .tag = .c,
259 .options = options,
260 .file = file,
261 .allocator = allocator,
262 },
263 .main = std.ArrayList(u8).init(allocator),
264 .header = std.ArrayList(u8).init(allocator),
265 .constants = std.ArrayList(u8).init(allocator),
266 .called = std.StringHashMap(void).init(allocator),
267 };
268
269 return &c_file.base;
270 }
271
272 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{AnalysisFail, OutOfMemory} {
273 self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);
274 return error.AnalysisFail;
275 }
276
277 pub fn deinit(self: *File.C) void {
278 self.main.deinit();
279 self.header.deinit();
280 self.constants.deinit();
281 self.called.deinit();
282 }
283
284 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
285 c_codegen.generate(self, decl) catch |err| {
286 if (err == error.AnalysisFail) {
287 try module.failed_decls.put(module.gpa, decl, self.error_msg);
288 }
289 return err;
290 };
291 }
292
293 pub fn flush(self: *File.C, module: *Module) !void {
294 const writer = self.base.file.?.writer();
295 try writer.writeAll(@embedFile("cbe.h"));
296 var includes = false;
297 if (self.need_stddef) {
298 try writer.writeAll("#include <stddef.h>\n");
299 includes = true;
300 }
301 if (self.need_stdint) {
302 try writer.writeAll("#include <stdint.h>\n");
303 includes = true;
304 }
305 if (includes) {
306 try writer.writeByte('\n');
307 }
308 if (self.header.items.len > 0) {
309 try writer.print("{}\n", .{self.header.items});
310 }
311 if (self.constants.items.len > 0) {
312 try writer.print("{}\n", .{self.constants.items});
313 }
314 if (self.main.items.len > 1) {
315 const last_two = self.main.items[self.main.items.len - 2 ..];
316 if (std.mem.eql(u8, last_two, "\n\n")) {
317 self.main.items.len -= 1;
318 }
319 }
320 try writer.writeAll(self.main.items);
321 self.base.file.?.close();
322 self.base.file = null;
323 }
324 };
325
326 pub const Elf = struct {
327 pub const base_tag: Tag = .elf;
328
329 base: File,
330
331 ptr_width: enum { p32, p64 },
332
333 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
334 /// Same order as in the file.
335 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
336 shdr_table_offset: ?u64 = null,
337
338 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
339 /// Same order as in the file.
340 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
341 phdr_table_offset: ?u64 = null,
342 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
343 phdr_load_re_index: ?u16 = null,
344 /// The index into the program headers of the global offset table.
345 /// It needs PT_LOAD and Read flags.
346 phdr_got_index: ?u16 = null,
347 entry_addr: ?u64 = null,
348
349 debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
350 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
351 shstrtab_index: ?u16 = null,
352
353 text_section_index: ?u16 = null,
354 symtab_section_index: ?u16 = null,
355 got_section_index: ?u16 = null,
356 debug_info_section_index: ?u16 = null,
357 debug_abbrev_section_index: ?u16 = null,
358 debug_str_section_index: ?u16 = null,
359 debug_aranges_section_index: ?u16 = null,
360 debug_line_section_index: ?u16 = null,
361
362 debug_abbrev_table_offset: ?u64 = null,
363
364 /// The same order as in the file. ELF requires global symbols to all be after the
365 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
366 /// write them at the end. These are only the local symbols. The length of this array
367 /// is the value used for sh_info in the .symtab section.
368 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
369 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
370
371 local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
372 global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
373 offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
374
375 /// Same order as in the file. The value is the absolute vaddr value.
376 /// If the vaddr of the executable program header changes, the entire
377 /// offset table needs to be rewritten.
378 offset_table: std.ArrayListUnmanaged(u64) = .{},
379
380 phdr_table_dirty: bool = false,
381 shdr_table_dirty: bool = false,
382 shstrtab_dirty: bool = false,
383 debug_strtab_dirty: bool = false,
384 offset_table_count_dirty: bool = false,
385 debug_abbrev_section_dirty: bool = false,
386 debug_aranges_section_dirty: bool = false,
387
388 debug_info_header_dirty: bool = false,
389 debug_line_header_dirty: bool = false,
390
391 error_flags: ErrorFlags = ErrorFlags{},
392
393 /// A list of text blocks that have surplus capacity. This list can have false
394 /// positives, as functions grow and shrink over time, only sometimes being added
395 /// or removed from the freelist.
396 ///
397 /// A text block has surplus capacity when its overcapacity value is greater than
398 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
399 /// much extra capacity, that we could fit a small new symbol in it, itself with
400 /// ideal_capacity or more.
401 ///
402 /// Ideal capacity is defined by size * alloc_num / alloc_den.
403 ///
404 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
405 /// overcapacity can be negative. A simple way to have negative overcapacity is to
406 /// allocate a fresh text block, which will have ideal capacity, and then grow it
407 /// by 1 byte. It will then have -1 overcapacity.
408 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
409 last_text_block: ?*TextBlock = null,
410
411 /// A list of `SrcFn` whose Line Number Programs have surplus capacity.
412 /// This is the same concept as `text_block_free_list`; see those doc comments.
413 dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
414 dbg_line_fn_first: ?*SrcFn = null,
415 dbg_line_fn_last: ?*SrcFn = null,
416
417 /// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity.
418 /// This is the same concept as `text_block_free_list`; see those doc comments.
419 dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
420 dbg_info_decl_first: ?*TextBlock = null,
421 dbg_info_decl_last: ?*TextBlock = null,
422
423 /// `alloc_num / alloc_den` is the factor of padding when allocating.
424 const alloc_num = 4;
425 const alloc_den = 3;
426
427 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
428 /// it as a possible place to put new symbols, it must have enough room for this many bytes
429 /// (plus extra for reserved capacity).
430 const minimum_text_block_size = 64;
431 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
432
433 pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, true);
434
435 const DbgInfoTypeReloc = struct {
436 /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
437 /// This is where the .debug_info tag for the type is.
438 off: u32,
439 /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
440 /// List of DW.AT_type / DW.FORM_ref4 that points to the type.
441 relocs: std.ArrayListUnmanaged(u32),
442 };
443
444 pub const TextBlock = struct {
445 /// Each decl always gets a local symbol with the fully qualified name.
446 /// The vaddr and size are found here directly.
447 /// The file offset is found by computing the vaddr offset from the section vaddr
448 /// the symbol references, and adding that to the file offset of the section.
449 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
450 /// offset table entry.
451 local_sym_index: u32,
452 /// This field is undefined for symbols with size = 0.
453 offset_table_index: u32,
454 /// Points to the previous and next neighbors, based on the `text_offset`.
455 /// This can be used to find, for example, the capacity of this `TextBlock`.
456 prev: ?*TextBlock,
457 next: ?*TextBlock,
458
459 /// Previous/next linked list pointers. This value is `next ^ prev`.
460 /// This is the linked list node for this Decl's corresponding .debug_info tag.
461 dbg_info_prev: ?*TextBlock,
462 dbg_info_next: ?*TextBlock,
463 /// Offset into .debug_info pointing to the tag for this Decl.
464 dbg_info_off: u32,
465 /// Size of the .debug_info tag for this Decl, not including padding.
466 dbg_info_len: u32,
467
468 pub const empty = TextBlock{
469 .local_sym_index = 0,
470 .offset_table_index = undefined,
471 .prev = null,
472 .next = null,
473 .dbg_info_prev = null,
474 .dbg_info_next = null,
475 .dbg_info_off = undefined,
476 .dbg_info_len = undefined,
477 };
478
479 /// Returns how much room there is to grow in virtual address space.
480 /// File offset relocation happens transparently, so it is not included in
481 /// this calculation.
482 fn capacity(self: TextBlock, elf_file: Elf) u64 {
483 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
484 if (self.next) |next| {
485 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
486 return next_sym.st_value - self_sym.st_value;
487 } else {
488 // We are the last block. The capacity is limited only by virtual address space.
489 return std.math.maxInt(u32) - self_sym.st_value;
490 }
491 }
492
493 fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
494 // No need to keep a free list node for the last block.
495 const next = self.next orelse return false;
496 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
497 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
498 const cap = next_sym.st_value - self_sym.st_value;
499 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
500 if (cap <= ideal_cap) return false;
501 const surplus = cap - ideal_cap;
502 return surplus >= min_text_capacity;
503 }
504 };
505
506 pub const Export = struct {
507 sym_index: ?u32 = null,
508 };
509
510 pub const SrcFn = struct {
511 /// Offset from the beginning of the Debug Line Program header that contains this function.
512 off: u32,
513 /// Size of the line number program component belonging to this function, not
514 /// including padding.
515 len: u32,
516
517 /// Points to the previous and next neighbors, based on the offset from .debug_line.
518 /// This can be used to find, for example, the capacity of this `SrcFn`.
519 prev: ?*SrcFn,
520 next: ?*SrcFn,
521
522 pub const empty: SrcFn = .{
523 .off = 0,
524 .len = 0,
525 .prev = null,
526 .next = null,
527 };
528 };
529
530 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
531 assert(options.object_format == .elf);
532
533 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });
534 errdefer file.close();
535
536 var elf_file = try allocator.create(Elf);
537 errdefer allocator.destroy(elf_file);
538
539 elf_file.* = openFile(allocator, file, options) catch |err| switch (err) {
540 error.IncrFailed => try createFile(allocator, file, options),
541 else => |e| return e,
542 };
543
544 return &elf_file.base;
545 }
546
547 /// Returns error.IncrFailed if incremental update could not be performed.
548 fn openFile(allocator: *Allocator, file: fs.File, options: Options) !Elf {
549 switch (options.output_mode) {
550 .Exe => {},
551 .Obj => {},
552 .Lib => return error.IncrFailed,
553 }
554 var self: Elf = .{
555 .base = .{
556 .file = file,
557 .tag = .elf,
558 .options = options,
559 .allocator = allocator,
560 },
561 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
562 32 => .p32,
563 64 => .p64,
564 else => return error.UnsupportedELFArchitecture,
565 },
566 };
567 errdefer self.deinit();
568
569 // TODO implement reading the elf file
570 return error.IncrFailed;
571 //try self.populateMissingMetadata();
572 //return self;
573 }
574
575 /// Truncates the existing file contents and overwrites the contents.
576 /// Returns an error if `file` is not already open with +read +write +seek abilities.
577 fn createFile(allocator: *Allocator, file: fs.File, options: Options) !Elf {
578 switch (options.output_mode) {
579 .Exe => {},
580 .Obj => {},
581 .Lib => return error.TODOImplementWritingLibFiles,
582 }
583 var self: Elf = .{
584 .base = .{
585 .tag = .elf,
586 .options = options,
587 .allocator = allocator,
588 .file = file,
589 },
590 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
591 32 => .p32,
592 64 => .p64,
593 else => return error.UnsupportedELFArchitecture,
594 },
595 .shdr_table_dirty = true,
596 };
597 errdefer self.deinit();
598
599 // Index 0 is always a null symbol.
600 try self.local_symbols.append(allocator, .{
601 .st_name = 0,
602 .st_info = 0,
603 .st_other = 0,
604 .st_shndx = 0,
605 .st_value = 0,
606 .st_size = 0,
607 });
608
609 // There must always be a null section in index 0
610 try self.sections.append(allocator, .{
611 .sh_name = 0,
612 .sh_type = elf.SHT_NULL,
613 .sh_flags = 0,
614 .sh_addr = 0,
615 .sh_offset = 0,
616 .sh_size = 0,
617 .sh_link = 0,
618 .sh_info = 0,
619 .sh_addralign = 0,
620 .sh_entsize = 0,
621 });
622
623 try self.populateMissingMetadata();
624
625 return self;
626 }
627
628 pub fn deinit(self: *Elf) void {
629 self.sections.deinit(self.base.allocator);
630 self.program_headers.deinit(self.base.allocator);
631 self.shstrtab.deinit(self.base.allocator);
632 self.debug_strtab.deinit(self.base.allocator);
633 self.local_symbols.deinit(self.base.allocator);
634 self.global_symbols.deinit(self.base.allocator);
635 self.global_symbol_free_list.deinit(self.base.allocator);
636 self.local_symbol_free_list.deinit(self.base.allocator);
637 self.offset_table_free_list.deinit(self.base.allocator);
638 self.text_block_free_list.deinit(self.base.allocator);
639 self.dbg_line_fn_free_list.deinit(self.base.allocator);
640 self.dbg_info_decl_free_list.deinit(self.base.allocator);
641 self.offset_table.deinit(self.base.allocator);
642 }
643
644 fn getDebugLineProgramOff(self: Elf) u32 {
645 return self.dbg_line_fn_first.?.off;
646 }
647
648 fn getDebugLineProgramEnd(self: Elf) u32 {
649 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
650 }
651
652 /// Returns end pos of collision, if any.
653 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
654 const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;
655 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
656 if (start < ehdr_size)
657 return ehdr_size;
658
659 const end = start + satMul(size, alloc_num) / alloc_den;
660
661 if (self.shdr_table_offset) |off| {
662 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
663 const tight_size = self.sections.items.len * shdr_size;
664 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
665 const test_end = off + increased_size;
666 if (end > off and start < test_end) {
667 return test_end;
668 }
669 }
670
671 if (self.phdr_table_offset) |off| {
672 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
673 const tight_size = self.sections.items.len * phdr_size;
674 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
675 const test_end = off + increased_size;
676 if (end > off and start < test_end) {
677 return test_end;
678 }
679 }
680
681 for (self.sections.items) |section| {
682 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
683 const test_end = section.sh_offset + increased_size;
684 if (end > section.sh_offset and start < test_end) {
685 return test_end;
686 }
687 }
688 for (self.program_headers.items) |program_header| {
689 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
690 const test_end = program_header.p_offset + increased_size;
691 if (end > program_header.p_offset and start < test_end) {
692 return test_end;
693 }
694 }
695 return null;
696 }
697
698 fn allocatedSize(self: *Elf, start: u64) u64 {
699 if (start == 0)
700 return 0;
701 var min_pos: u64 = std.math.maxInt(u64);
702 if (self.shdr_table_offset) |off| {
703 if (off > start and off < min_pos) min_pos = off;
704 }
705 if (self.phdr_table_offset) |off| {
706 if (off > start and off < min_pos) min_pos = off;
707 }
708 for (self.sections.items) |section| {
709 if (section.sh_offset <= start) continue;
710 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
711 }
712 for (self.program_headers.items) |program_header| {
713 if (program_header.p_offset <= start) continue;
714 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
715 }
716 return min_pos - start;
717 }
718
719 fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
720 var start: u64 = 0;
721 while (self.detectAllocCollision(start, object_size)) |item_end| {
722 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
723 }
724 return start;
725 }
726
727 /// TODO Improve this to use a table.
728 fn makeString(self: *Elf, bytes: []const u8) !u32 {
729 try self.shstrtab.ensureCapacity(self.base.allocator, self.shstrtab.items.len + bytes.len + 1);
730 const result = self.shstrtab.items.len;
731 self.shstrtab.appendSliceAssumeCapacity(bytes);
732 self.shstrtab.appendAssumeCapacity(0);
733 return @intCast(u32, result);
734 }
735
736 /// TODO Improve this to use a table.
737 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
738 try self.debug_strtab.ensureCapacity(self.base.allocator, self.debug_strtab.items.len + bytes.len + 1);
739 const result = self.debug_strtab.items.len;
740 self.debug_strtab.appendSliceAssumeCapacity(bytes);
741 self.debug_strtab.appendAssumeCapacity(0);
742 return @intCast(u32, result);
743 }
744
745 fn getString(self: *Elf, str_off: u32) []const u8 {
746 assert(str_off < self.shstrtab.items.len);
747 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
748 }
749
750 fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
751 const existing_name = self.getString(old_str_off);
752 if (mem.eql(u8, existing_name, new_name)) {
753 return old_str_off;
754 }
755 return self.makeString(new_name);
756 }
757
758 pub fn populateMissingMetadata(self: *Elf) !void {
759 const small_ptr = switch (self.ptr_width) {
760 .p32 => true,
761 .p64 => false,
762 };
763 const ptr_size: u8 = self.ptrWidthBytes();
764 if (self.phdr_load_re_index == null) {
765 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
766 const file_size = self.base.options.program_code_size_hint;
767 const p_align = 0x1000;
768 const off = self.findFreeSpace(file_size, p_align);
769 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
770 try self.program_headers.append(self.base.allocator, .{
771 .p_type = elf.PT_LOAD,
772 .p_offset = off,
773 .p_filesz = file_size,
774 .p_vaddr = default_entry_addr,
775 .p_paddr = default_entry_addr,
776 .p_memsz = file_size,
777 .p_align = p_align,
778 .p_flags = elf.PF_X | elf.PF_R,
779 });
780 self.entry_addr = null;
781 self.phdr_table_dirty = true;
782 }
783 if (self.phdr_got_index == null) {
784 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
785 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
786 // We really only need ptr alignment but since we are using PROGBITS, linux requires
787 // page align.
788 const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size);
789 const off = self.findFreeSpace(file_size, p_align);
790 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
791 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
792 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
793 // else in virtual memory.
794 const default_got_addr = if (ptr_size == 2) @as(u32, 0x8000) else 0x4000000;
795 try self.program_headers.append(self.base.allocator, .{
796 .p_type = elf.PT_LOAD,
797 .p_offset = off,
798 .p_filesz = file_size,
799 .p_vaddr = default_got_addr,
800 .p_paddr = default_got_addr,
801 .p_memsz = file_size,
802 .p_align = p_align,
803 .p_flags = elf.PF_R,
804 });
805 self.phdr_table_dirty = true;
806 }
807 if (self.shstrtab_index == null) {
808 self.shstrtab_index = @intCast(u16, self.sections.items.len);
809 assert(self.shstrtab.items.len == 0);
810 try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
811 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
812 log.debug("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
813 try self.sections.append(self.base.allocator, .{
814 .sh_name = try self.makeString(".shstrtab"),
815 .sh_type = elf.SHT_STRTAB,
816 .sh_flags = 0,
817 .sh_addr = 0,
818 .sh_offset = off,
819 .sh_size = self.shstrtab.items.len,
820 .sh_link = 0,
821 .sh_info = 0,
822 .sh_addralign = 1,
823 .sh_entsize = 0,
824 });
825 self.shstrtab_dirty = true;
826 self.shdr_table_dirty = true;
827 }
828 if (self.text_section_index == null) {
829 self.text_section_index = @intCast(u16, self.sections.items.len);
830 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
831
832 try self.sections.append(self.base.allocator, .{
833 .sh_name = try self.makeString(".text"),
834 .sh_type = elf.SHT_PROGBITS,
835 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
836 .sh_addr = phdr.p_vaddr,
837 .sh_offset = phdr.p_offset,
838 .sh_size = phdr.p_filesz,
839 .sh_link = 0,
840 .sh_info = 0,
841 .sh_addralign = phdr.p_align,
842 .sh_entsize = 0,
843 });
844 self.shdr_table_dirty = true;
845 }
846 if (self.got_section_index == null) {
847 self.got_section_index = @intCast(u16, self.sections.items.len);
848 const phdr = &self.program_headers.items[self.phdr_got_index.?];
849
850 try self.sections.append(self.base.allocator, .{
851 .sh_name = try self.makeString(".got"),
852 .sh_type = elf.SHT_PROGBITS,
853 .sh_flags = elf.SHF_ALLOC,
854 .sh_addr = phdr.p_vaddr,
855 .sh_offset = phdr.p_offset,
856 .sh_size = phdr.p_filesz,
857 .sh_link = 0,
858 .sh_info = 0,
859 .sh_addralign = phdr.p_align,
860 .sh_entsize = 0,
861 });
862 self.shdr_table_dirty = true;
863 }
864 if (self.symtab_section_index == null) {
865 self.symtab_section_index = @intCast(u16, self.sections.items.len);
866 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
867 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
868 const file_size = self.base.options.symbol_count_hint * each_size;
869 const off = self.findFreeSpace(file_size, min_align);
870 log.debug("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
871
872 try self.sections.append(self.base.allocator, .{
873 .sh_name = try self.makeString(".symtab"),
874 .sh_type = elf.SHT_SYMTAB,
875 .sh_flags = 0,
876 .sh_addr = 0,
877 .sh_offset = off,
878 .sh_size = file_size,
879 // The section header index of the associated string table.
880 .sh_link = self.shstrtab_index.?,
881 .sh_info = @intCast(u32, self.local_symbols.items.len),
882 .sh_addralign = min_align,
883 .sh_entsize = each_size,
884 });
885 self.shdr_table_dirty = true;
886 try self.writeSymbol(0);
887 }
888 if (self.debug_str_section_index == null) {
889 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
890 assert(self.debug_strtab.items.len == 0);
891 try self.sections.append(self.base.allocator, .{
892 .sh_name = try self.makeString(".debug_str"),
893 .sh_type = elf.SHT_PROGBITS,
894 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
895 .sh_addr = 0,
896 .sh_offset = 0,
897 .sh_size = self.debug_strtab.items.len,
898 .sh_link = 0,
899 .sh_info = 0,
900 .sh_addralign = 1,
901 .sh_entsize = 1,
902 });
903 self.debug_strtab_dirty = true;
904 self.shdr_table_dirty = true;
905 }
906 if (self.debug_info_section_index == null) {
907 self.debug_info_section_index = @intCast(u16, self.sections.items.len);
908
909 const file_size_hint = 200;
910 const p_align = 1;
911 const off = self.findFreeSpace(file_size_hint, p_align);
912 log.debug("found .debug_info free space 0x{x} to 0x{x}\n", .{
913 off,
914 off + file_size_hint,
915 });
916 try self.sections.append(self.base.allocator, .{
917 .sh_name = try self.makeString(".debug_info"),
918 .sh_type = elf.SHT_PROGBITS,
919 .sh_flags = 0,
920 .sh_addr = 0,
921 .sh_offset = off,
922 .sh_size = file_size_hint,
923 .sh_link = 0,
924 .sh_info = 0,
925 .sh_addralign = p_align,
926 .sh_entsize = 0,
927 });
928 self.shdr_table_dirty = true;
929 self.debug_info_header_dirty = true;
930 }
931 if (self.debug_abbrev_section_index == null) {
932 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
933
934 const file_size_hint = 128;
935 const p_align = 1;
936 const off = self.findFreeSpace(file_size_hint, p_align);
937 log.debug("found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
938 off,
939 off + file_size_hint,
940 });
941 try self.sections.append(self.base.allocator, .{
942 .sh_name = try self.makeString(".debug_abbrev"),
943 .sh_type = elf.SHT_PROGBITS,
944 .sh_flags = 0,
945 .sh_addr = 0,
946 .sh_offset = off,
947 .sh_size = file_size_hint,
948 .sh_link = 0,
949 .sh_info = 0,
950 .sh_addralign = p_align,
951 .sh_entsize = 0,
952 });
953 self.shdr_table_dirty = true;
954 self.debug_abbrev_section_dirty = true;
955 }
956 if (self.debug_aranges_section_index == null) {
957 self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);
958
959 const file_size_hint = 160;
960 const p_align = 16;
961 const off = self.findFreeSpace(file_size_hint, p_align);
962 log.debug("found .debug_aranges free space 0x{x} to 0x{x}\n", .{
963 off,
964 off + file_size_hint,
965 });
966 try self.sections.append(self.base.allocator, .{
967 .sh_name = try self.makeString(".debug_aranges"),
968 .sh_type = elf.SHT_PROGBITS,
969 .sh_flags = 0,
970 .sh_addr = 0,
971 .sh_offset = off,
972 .sh_size = file_size_hint,
973 .sh_link = 0,
974 .sh_info = 0,
975 .sh_addralign = p_align,
976 .sh_entsize = 0,
977 });
978 self.shdr_table_dirty = true;
979 self.debug_aranges_section_dirty = true;
980 }
981 if (self.debug_line_section_index == null) {
982 self.debug_line_section_index = @intCast(u16, self.sections.items.len);
983
984 const file_size_hint = 250;
985 const p_align = 1;
986 const off = self.findFreeSpace(file_size_hint, p_align);
987 log.debug("found .debug_line free space 0x{x} to 0x{x}\n", .{
988 off,
989 off + file_size_hint,
990 });
991 try self.sections.append(self.base.allocator, .{
992 .sh_name = try self.makeString(".debug_line"),
993 .sh_type = elf.SHT_PROGBITS,
994 .sh_flags = 0,
995 .sh_addr = 0,
996 .sh_offset = off,
997 .sh_size = file_size_hint,
998 .sh_link = 0,
999 .sh_info = 0,
1000 .sh_addralign = p_align,
1001 .sh_entsize = 0,
1002 });
1003 self.shdr_table_dirty = true;
1004 self.debug_line_header_dirty = true;
1005 }
1006 const shsize: u64 = switch (self.ptr_width) {
1007 .p32 => @sizeOf(elf.Elf32_Shdr),
1008 .p64 => @sizeOf(elf.Elf64_Shdr),
1009 };
1010 const shalign: u16 = switch (self.ptr_width) {
1011 .p32 => @alignOf(elf.Elf32_Shdr),
1012 .p64 => @alignOf(elf.Elf64_Shdr),
1013 };
1014 if (self.shdr_table_offset == null) {
1015 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
1016 self.shdr_table_dirty = true;
1017 }
1018 const phsize: u64 = switch (self.ptr_width) {
1019 .p32 => @sizeOf(elf.Elf32_Phdr),
1020 .p64 => @sizeOf(elf.Elf64_Phdr),
1021 };
1022 const phalign: u16 = switch (self.ptr_width) {
1023 .p32 => @alignOf(elf.Elf32_Phdr),
1024 .p64 => @alignOf(elf.Elf64_Phdr),
1025 };
1026 if (self.phdr_table_offset == null) {
1027 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
1028 self.phdr_table_dirty = true;
1029 }
1030 {
1031 // Iterate over symbols, populating free_list and last_text_block.
1032 if (self.local_symbols.items.len != 1) {
1033 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
1034 }
1035 // We are starting with an empty file. The default values are correct, null and empty list.
1036 }
1037 }
1038
1039 pub const abbrev_compile_unit = 1;
1040 pub const abbrev_subprogram = 2;
1041 pub const abbrev_subprogram_retvoid = 3;
1042 pub const abbrev_base_type = 4;
1043 pub const abbrev_pad1 = 5;
1044 pub const abbrev_parameter = 6;
1045
1046 /// Commit pending changes and write headers.
1047 pub fn flush(self: *Elf, module: *Module) !void {
1048 const target_endian = self.base.options.target.cpu.arch.endian();
1049 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
1050 const ptr_width_bytes: u8 = self.ptrWidthBytes();
1051 const init_len_size: usize = switch (self.ptr_width) {
1052 .p32 => 4,
1053 .p64 => 12,
1054 };
1055
1056 // Unfortunately these have to be buffered and done at the end because ELF does not allow
1057 // mixing local and global symbols within a symbol table.
1058 try self.writeAllGlobalSymbols();
1059
1060 if (self.debug_abbrev_section_dirty) {
1061 const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];
1062
1063 // These are LEB encoded but since the values are all less than 127
1064 // we can simply append these bytes.
1065 const abbrev_buf = [_]u8{
1066 abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
1067 DW.AT_stmt_list, DW.FORM_sec_offset,
1068 DW.AT_low_pc , DW.FORM_addr,
1069 DW.AT_high_pc , DW.FORM_addr,
1070 DW.AT_name , DW.FORM_strp,
1071 DW.AT_comp_dir , DW.FORM_strp,
1072 DW.AT_producer , DW.FORM_strp,
1073 DW.AT_language , DW.FORM_data2,
1074 0, 0, // table sentinel
1075
1076 abbrev_subprogram, DW.TAG_subprogram, DW.CHILDREN_yes, // header
1077 DW.AT_low_pc , DW.FORM_addr,
1078 DW.AT_high_pc , DW.FORM_data4,
1079 DW.AT_type , DW.FORM_ref4,
1080 DW.AT_name , DW.FORM_string,
1081 0, 0, // table sentinel
1082
1083 abbrev_subprogram_retvoid, DW.TAG_subprogram, DW.CHILDREN_yes, // header
1084 DW.AT_low_pc , DW.FORM_addr,
1085 DW.AT_high_pc , DW.FORM_data4,
1086 DW.AT_name , DW.FORM_string,
1087 0, 0, // table sentinel
1088
1089 abbrev_base_type, DW.TAG_base_type, DW.CHILDREN_no, // header
1090 DW.AT_encoding , DW.FORM_data1,
1091 DW.AT_byte_size, DW.FORM_data1,
1092 DW.AT_name , DW.FORM_string,
1093 0, 0, // table sentinel
1094
1095 abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
1096 0, 0, // table sentinel
1097
1098 abbrev_parameter, DW.TAG_formal_parameter, DW.CHILDREN_no, // header
1099 DW.AT_location , DW.FORM_exprloc,
1100 DW.AT_type , DW.FORM_ref4,
1101 DW.AT_name , DW.FORM_string,
1102 0, 0, // table sentinel
1103
1104 0, 0, 0, // section sentinel
1105 };
1106
1107 const needed_size = abbrev_buf.len;
1108 const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
1109 if (needed_size > allocated_size) {
1110 debug_abbrev_sect.sh_size = 0; // free the space
1111 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1112 }
1113 debug_abbrev_sect.sh_size = needed_size;
1114 log.debug(".debug_abbrev start=0x{x} end=0x{x}\n", .{
1115 debug_abbrev_sect.sh_offset,
1116 debug_abbrev_sect.sh_offset + needed_size,
1117 });
1118
1119 const abbrev_offset = 0;
1120 self.debug_abbrev_table_offset = abbrev_offset;
1121 try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
1122 if (!self.shdr_table_dirty) {
1123 // Then it won't get written with the others and we need to do it.
1124 try self.writeSectHeader(self.debug_abbrev_section_index.?);
1125 }
1126
1127 self.debug_abbrev_section_dirty = false;
1128 }
1129
1130 if (self.debug_info_header_dirty) debug_info: {
1131 // If this value is null it means there is an error in the module;
1132 // leave debug_info_header_dirty=true.
1133 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
1134 const last_dbg_info_decl = self.dbg_info_decl_last.?;
1135 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
1136
1137 var di_buf = std.ArrayList(u8).init(self.base.allocator);
1138 defer di_buf.deinit();
1139
1140 // We have a function to compute the upper bound size, because it's needed
1141 // for determining where to put the offset of the first `LinkBlock`.
1142 try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes());
1143
1144 // initial length - length of the .debug_info contribution for this compilation unit,
1145 // not including the initial length itself.
1146 // We have to come back and write it later after we know the size.
1147 const after_init_len = di_buf.items.len + init_len_size;
1148 // +1 for the final 0 that ends the compilation unit children.
1149 const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
1150 const init_len = dbg_info_end - after_init_len;
1151 switch (self.ptr_width) {
1152 .p32 => {
1153 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
1154 },
1155 .p64 => {
1156 di_buf.appendNTimesAssumeCapacity(0xff, 4);
1157 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
1158 },
1159 }
1160 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
1161 const abbrev_offset = self.debug_abbrev_table_offset.?;
1162 switch (self.ptr_width) {
1163 .p32 => {
1164 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
1165 di_buf.appendAssumeCapacity(4); // address size
1166 },
1167 .p64 => {
1168 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
1169 di_buf.appendAssumeCapacity(8); // address size
1170 },
1171 }
1172 // Write the form for the compile unit, which must match the abbrev table above.
1173 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
1174 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);
1175 const producer_strp = try self.makeDebugString(producer_string);
1176 // Currently only one compilation unit is supported, so the address range is simply
1177 // identical to the main program header virtual address and memory size.
1178 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1179 const low_pc = text_phdr.p_vaddr;
1180 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
1181
1182 di_buf.appendAssumeCapacity(abbrev_compile_unit);
1183 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
1184 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
1185 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
1186 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
1187 self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
1188 self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
1189 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
1190 // http://dwarfstd.org/ShowIssue.php?issue=171115.1
1191 // Until then we say it is C99.
1192 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
1193
1194 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
1195 // Move the first N decls to the end to make more padding for the header.
1196 @panic("TODO: handle .debug_info header exceeding its padding");
1197 }
1198 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
1199 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset);
1200 self.debug_info_header_dirty = false;
1201 }
1202
1203 if (self.debug_aranges_section_dirty) {
1204 const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
1205
1206 var di_buf = std.ArrayList(u8).init(self.base.allocator);
1207 defer di_buf.deinit();
1208
1209 // Enough for all the data without resizing. When support for more compilation units
1210 // is added, the size of this section will become more variable.
1211 try di_buf.ensureCapacity(100);
1212
1213 // initial length - length of the .debug_aranges contribution for this compilation unit,
1214 // not including the initial length itself.
1215 // We have to come back and write it later after we know the size.
1216 const init_len_index = di_buf.items.len;
1217 di_buf.items.len += init_len_size;
1218 const after_init_len = di_buf.items.len;
1219 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
1220 // When more than one compilation unit is supported, this will be the offset to it.
1221 // For now it is always at offset 0 in .debug_info.
1222 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset
1223 di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
1224 di_buf.appendAssumeCapacity(0); // segment_selector_size
1225
1226 const end_header_offset = di_buf.items.len;
1227 const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2);
1228 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
1229
1230 // Currently only one compilation unit is supported, so the address range is simply
1231 // identical to the main program header virtual address and memory size.
1232 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1233 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr);
1234 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz);
1235
1236 // Sentinel.
1237 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
1238 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
1239
1240 // Go back and populate the initial length.
1241 const init_len = di_buf.items.len - after_init_len;
1242 switch (self.ptr_width) {
1243 .p32 => {
1244 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
1245 },
1246 .p64 => {
1247 // initial length - length of the .debug_aranges contribution for this compilation unit,
1248 // not including the initial length itself.
1249 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
1250 mem.writeInt(u64, di_buf.items[init_len_index + 4..][0..8], init_len, target_endian);
1251 },
1252 }
1253
1254 const needed_size = di_buf.items.len;
1255 const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset);
1256 if (needed_size > allocated_size) {
1257 debug_aranges_sect.sh_size = 0; // free the space
1258 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
1259 }
1260 debug_aranges_sect.sh_size = needed_size;
1261 log.debug(".debug_aranges start=0x{x} end=0x{x}\n", .{
1262 debug_aranges_sect.sh_offset,
1263 debug_aranges_sect.sh_offset + needed_size,
1264 });
1265
1266 try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
1267 if (!self.shdr_table_dirty) {
1268 // Then it won't get written with the others and we need to do it.
1269 try self.writeSectHeader(self.debug_aranges_section_index.?);
1270 }
1271
1272 self.debug_aranges_section_dirty = false;
1273 }
1274 if (self.debug_line_header_dirty) debug_line: {
1275 if (self.dbg_line_fn_first == null) {
1276 break :debug_line; // Error in module; leave debug_line_header_dirty=true.
1277 }
1278 const dbg_line_prg_off = self.getDebugLineProgramOff();
1279 const dbg_line_prg_end = self.getDebugLineProgramEnd();
1280 assert(dbg_line_prg_end != 0);
1281
1282 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1283
1284 var di_buf = std.ArrayList(u8).init(self.base.allocator);
1285 defer di_buf.deinit();
1286
1287 // The size of this header is variable, depending on the number of directories,
1288 // files, and padding. We have a function to compute the upper bound size, however,
1289 // because it's needed for determining where to put the offset of the first `SrcFn`.
1290 try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes());
1291
1292 // initial length - length of the .debug_line contribution for this compilation unit,
1293 // not including the initial length itself.
1294 const after_init_len = di_buf.items.len + init_len_size;
1295 const init_len = dbg_line_prg_end - after_init_len;
1296 switch (self.ptr_width) {
1297 .p32 => {
1298 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
1299 },
1300 .p64 => {
1301 di_buf.appendNTimesAssumeCapacity(0xff, 4);
1302 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
1303 },
1304 }
1305
1306 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
1307
1308 // Empirically, debug info consumers do not respect this field, or otherwise
1309 // consider it to be an error when it does not point exactly to the end of the header.
1310 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
1311 // padding rather than this field.
1312 const before_header_len = di_buf.items.len;
1313 di_buf.items.len += ptr_width_bytes; // We will come back and write this.
1314 const after_header_len = di_buf.items.len;
1315
1316 const opcode_base = DW.LNS_set_isa + 1;
1317 di_buf.appendSliceAssumeCapacity(&[_]u8{
1318 1, // minimum_instruction_length
1319 1, // maximum_operations_per_instruction
1320 1, // default_is_stmt
1321 1, // line_base (signed)
1322 1, // line_range
1323 opcode_base,
1324
1325 // Standard opcode lengths. The number of items here is based on `opcode_base`.
1326 // The value is the number of LEB128 operands the instruction takes.
1327 0, // `DW.LNS_copy`
1328 1, // `DW.LNS_advance_pc`
1329 1, // `DW.LNS_advance_line`
1330 1, // `DW.LNS_set_file`
1331 1, // `DW.LNS_set_column`
1332 0, // `DW.LNS_negate_stmt`
1333 0, // `DW.LNS_set_basic_block`
1334 0, // `DW.LNS_const_add_pc`
1335 1, // `DW.LNS_fixed_advance_pc`
1336 0, // `DW.LNS_set_prologue_end`
1337 0, // `DW.LNS_set_epilogue_begin`
1338 1, // `DW.LNS_set_isa`
1339
1340 0, // include_directories (none except the compilation unit cwd)
1341 });
1342 // file_names[0]
1343 di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.root_src_path); // relative path name
1344 di_buf.appendSliceAssumeCapacity(&[_]u8{
1345 0, // null byte for the relative path name
1346 0, // directory_index
1347 0, // mtime (TODO supply this)
1348 0, // file size bytes (TODO supply this)
1349 0, // file_names sentinel
1350 });
1351
1352 const header_len = di_buf.items.len - after_header_len;
1353 switch (self.ptr_width) {
1354 .p32 => {
1355 mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian);
1356 },
1357 .p64 => {
1358 mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian);
1359 },
1360 }
1361
1362 // We use NOPs because consumers empirically do not respect the header length field.
1363 if (di_buf.items.len > dbg_line_prg_off) {
1364 // Move the first N files to the end to make more padding for the header.
1365 @panic("TODO: handle .debug_line header exceeding its padding");
1366 }
1367 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
1368 try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
1369 self.debug_line_header_dirty = false;
1370 }
1371
1372 if (self.phdr_table_dirty) {
1373 const phsize: u64 = switch (self.ptr_width) {
1374 .p32 => @sizeOf(elf.Elf32_Phdr),
1375 .p64 => @sizeOf(elf.Elf64_Phdr),
1376 };
1377 const phalign: u16 = switch (self.ptr_width) {
1378 .p32 => @alignOf(elf.Elf32_Phdr),
1379 .p64 => @alignOf(elf.Elf64_Phdr),
1380 };
1381 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
1382 const needed_size = self.program_headers.items.len * phsize;
1383
1384 if (needed_size > allocated_size) {
1385 self.phdr_table_offset = null; // free the space
1386 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
1387 }
1388
1389 switch (self.ptr_width) {
1390 .p32 => {
1391 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1392 defer self.base.allocator.free(buf);
1393
1394 for (buf) |*phdr, i| {
1395 phdr.* = progHeaderTo32(self.program_headers.items[i]);
1396 if (foreign_endian) {
1397 bswapAllFields(elf.Elf32_Phdr, phdr);
1398 }
1399 }
1400 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1401 },
1402 .p64 => {
1403 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1404 defer self.base.allocator.free(buf);
1405
1406 for (buf) |*phdr, i| {
1407 phdr.* = self.program_headers.items[i];
1408 if (foreign_endian) {
1409 bswapAllFields(elf.Elf64_Phdr, phdr);
1410 }
1411 }
1412 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1413 },
1414 }
1415 self.phdr_table_dirty = false;
1416 }
1417
1418 {
1419 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
1420 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
1421 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
1422 const needed_size = self.shstrtab.items.len;
1423
1424 if (needed_size > allocated_size) {
1425 shstrtab_sect.sh_size = 0; // free the space
1426 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1427 }
1428 shstrtab_sect.sh_size = needed_size;
1429 log.debug("writing shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
1430
1431 try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
1432 if (!self.shdr_table_dirty) {
1433 // Then it won't get written with the others and we need to do it.
1434 try self.writeSectHeader(self.shstrtab_index.?);
1435 }
1436 self.shstrtab_dirty = false;
1437 }
1438 }
1439 {
1440 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
1441 if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {
1442 const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
1443 const needed_size = self.debug_strtab.items.len;
1444
1445 if (needed_size > allocated_size) {
1446 debug_strtab_sect.sh_size = 0; // free the space
1447 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1448 }
1449 debug_strtab_sect.sh_size = needed_size;
1450 log.debug("debug_strtab start=0x{x} end=0x{x}\n", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
1451
1452 try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
1453 if (!self.shdr_table_dirty) {
1454 // Then it won't get written with the others and we need to do it.
1455 try self.writeSectHeader(self.debug_str_section_index.?);
1456 }
1457 self.debug_strtab_dirty = false;
1458 }
1459 }
1460 if (self.shdr_table_dirty) {
1461 const shsize: u64 = switch (self.ptr_width) {
1462 .p32 => @sizeOf(elf.Elf32_Shdr),
1463 .p64 => @sizeOf(elf.Elf64_Shdr),
1464 };
1465 const shalign: u16 = switch (self.ptr_width) {
1466 .p32 => @alignOf(elf.Elf32_Shdr),
1467 .p64 => @alignOf(elf.Elf64_Shdr),
1468 };
1469 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1470 const needed_size = self.sections.items.len * shsize;
1471
1472 if (needed_size > allocated_size) {
1473 self.shdr_table_offset = null; // free the space
1474 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
1475 }
1476
1477 switch (self.ptr_width) {
1478 .p32 => {
1479 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
1480 defer self.base.allocator.free(buf);
1481
1482 for (buf) |*shdr, i| {
1483 shdr.* = sectHeaderTo32(self.sections.items[i]);
1484 log.debug("writing section {}\n", .{shdr.*});
1485 if (foreign_endian) {
1486 bswapAllFields(elf.Elf32_Shdr, shdr);
1487 }
1488 }
1489 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1490 },
1491 .p64 => {
1492 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
1493 defer self.base.allocator.free(buf);
1494
1495 for (buf) |*shdr, i| {
1496 shdr.* = self.sections.items[i];
1497 log.debug("writing section {}\n", .{shdr.*});
1498 if (foreign_endian) {
1499 bswapAllFields(elf.Elf64_Shdr, shdr);
1500 }
1501 }
1502 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1503 },
1504 }
1505 self.shdr_table_dirty = false;
1506 }
1507 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
1508 log.debug("flushing. no_entry_point_found = true\n", .{});
1509 self.error_flags.no_entry_point_found = true;
1510 } else {
1511 log.debug("flushing. no_entry_point_found = false\n", .{});
1512 self.error_flags.no_entry_point_found = false;
1513 try self.writeElfHeader();
1514 }
1515
1516 // The point of flush() is to commit changes, so in theory, nothing should
1517 // be dirty after this. However, it is possible for some things to remain
1518 // dirty because they fail to be written in the event of compile errors,
1519 // such as debug_line_header_dirty and debug_info_header_dirty.
1520 assert(!self.debug_abbrev_section_dirty);
1521 assert(!self.debug_aranges_section_dirty);
1522 assert(!self.phdr_table_dirty);
1523 assert(!self.shdr_table_dirty);
1524 assert(!self.shstrtab_dirty);
1525 assert(!self.debug_strtab_dirty);
1526 }
1527
1528 fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
1529 const target_endian = self.base.options.target.cpu.arch.endian();
1530 switch (self.ptr_width) {
1531 .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian),
1532 .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
1533 }
1534 }
1535
1536 fn writeElfHeader(self: *Elf) !void {
1537 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
1538
1539 var index: usize = 0;
1540 hdr_buf[0..4].* = "\x7fELF".*;
1541 index += 4;
1542
1543 hdr_buf[index] = switch (self.ptr_width) {
1544 .p32 => elf.ELFCLASS32,
1545 .p64 => elf.ELFCLASS64,
1546 };
1547 index += 1;
1548
1549 const endian = self.base.options.target.cpu.arch.endian();
1550 hdr_buf[index] = switch (endian) {
1551 .Little => elf.ELFDATA2LSB,
1552 .Big => elf.ELFDATA2MSB,
1553 };
1554 index += 1;
1555
1556 hdr_buf[index] = 1; // ELF version
1557 index += 1;
1558
1559 // OS ABI, often set to 0 regardless of target platform
1560 // ABI Version, possibly used by glibc but not by static executables
1561 // padding
1562 mem.set(u8, hdr_buf[index..][0..9], 0);
1563 index += 9;
1564
1565 assert(index == 16);
1566
1567 const elf_type = switch (self.base.options.output_mode) {
1568 .Exe => elf.ET.EXEC,
1569 .Obj => elf.ET.REL,
1570 .Lib => switch (self.base.options.link_mode) {
1571 .Static => elf.ET.REL,
1572 .Dynamic => elf.ET.DYN,
1573 },
1574 };
1575 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
1576 index += 2;
1577
1578 const machine = self.base.options.target.cpu.arch.toElfMachine();
1579 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
1580 index += 2;
1581
1582 // ELF Version, again
1583 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
1584 index += 4;
1585
1586 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
1587
1588 switch (self.ptr_width) {
1589 .p32 => {
1590 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
1591 index += 4;
1592
1593 // e_phoff
1594 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
1595 index += 4;
1596
1597 // e_shoff
1598 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
1599 index += 4;
1600 },
1601 .p64 => {
1602 // e_entry
1603 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
1604 index += 8;
1605
1606 // e_phoff
1607 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
1608 index += 8;
1609
1610 // e_shoff
1611 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
1612 index += 8;
1613 },
1614 }
1615
1616 const e_flags = 0;
1617 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
1618 index += 4;
1619
1620 const e_ehsize: u16 = switch (self.ptr_width) {
1621 .p32 => @sizeOf(elf.Elf32_Ehdr),
1622 .p64 => @sizeOf(elf.Elf64_Ehdr),
1623 };
1624 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
1625 index += 2;
1626
1627 const e_phentsize: u16 = switch (self.ptr_width) {
1628 .p32 => @sizeOf(elf.Elf32_Phdr),
1629 .p64 => @sizeOf(elf.Elf64_Phdr),
1630 };
1631 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
1632 index += 2;
1633
1634 const e_phnum = @intCast(u16, self.program_headers.items.len);
1635 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
1636 index += 2;
1637
1638 const e_shentsize: u16 = switch (self.ptr_width) {
1639 .p32 => @sizeOf(elf.Elf32_Shdr),
1640 .p64 => @sizeOf(elf.Elf64_Shdr),
1641 };
1642 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
1643 index += 2;
1644
1645 const e_shnum = @intCast(u16, self.sections.items.len);
1646 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
1647 index += 2;
1648
1649 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
1650 index += 2;
1651
1652 assert(index == e_ehsize);
1653
1654 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
1655 }
1656
1657 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
1658 var already_have_free_list_node = false;
1659 {
1660 var i: usize = 0;
1661 while (i < self.text_block_free_list.items.len) {
1662 if (self.text_block_free_list.items[i] == text_block) {
1663 _ = self.text_block_free_list.swapRemove(i);
1664 continue;
1665 }
1666 if (self.text_block_free_list.items[i] == text_block.prev) {
1667 already_have_free_list_node = true;
1668 }
1669 i += 1;
1670 }
1671 }
1672
1673 if (self.last_text_block == text_block) {
1674 // TODO shrink the .text section size here
1675 self.last_text_block = text_block.prev;
1676 }
1677
1678 if (text_block.prev) |prev| {
1679 prev.next = text_block.next;
1680
1681 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
1682 // The free list is heuristics, it doesn't have to be perfect, so we can
1683 // ignore the OOM here.
1684 self.text_block_free_list.append(self.base.allocator, prev) catch {};
1685 }
1686 } else {
1687 text_block.prev = null;
1688 }
1689
1690 if (text_block.next) |next| {
1691 next.prev = text_block.prev;
1692 } else {
1693 text_block.next = null;
1694 }
1695 }
1696
1697 fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
1698 // TODO check the new capacity, and if it crosses the size threshold into a big enough
1699 // capacity, insert a free list node for it.
1700 }
1701
1702 fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1703 const sym = self.local_symbols.items[text_block.local_sym_index];
1704 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
1705 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
1706 if (!need_realloc) return sym.st_value;
1707 return self.allocateTextBlock(text_block, new_block_size, alignment);
1708 }
1709
1710 fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1711 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1712 const shdr = &self.sections.items[self.text_section_index.?];
1713 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
1714
1715 // We use these to indicate our intention to update metadata, placing the new block,
1716 // and possibly removing a free list node.
1717 // It would be simpler to do it inside the for loop below, but that would cause a
1718 // problem if an error was returned later in the function. So this action
1719 // is actually carried out at the end of the function, when errors are no longer possible.
1720 var block_placement: ?*TextBlock = null;
1721 var free_list_removal: ?usize = null;
1722
1723 // First we look for an appropriately sized free list node.
1724 // The list is unordered. We'll just take the first thing that works.
1725 const vaddr = blk: {
1726 var i: usize = 0;
1727 while (i < self.text_block_free_list.items.len) {
1728 const big_block = self.text_block_free_list.items[i];
1729 // We now have a pointer to a live text block that has too much capacity.
1730 // Is it enough that we could fit this new text block?
1731 const sym = self.local_symbols.items[big_block.local_sym_index];
1732 const capacity = big_block.capacity(self.*);
1733 const ideal_capacity = capacity * alloc_num / alloc_den;
1734 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1735 const capacity_end_vaddr = sym.st_value + capacity;
1736 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
1737 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
1738 if (new_start_vaddr < ideal_capacity_end_vaddr) {
1739 // Additional bookkeeping here to notice if this free list node
1740 // should be deleted because the block that it points to has grown to take up
1741 // more of the extra capacity.
1742 if (!big_block.freeListEligible(self.*)) {
1743 _ = self.text_block_free_list.swapRemove(i);
1744 } else {
1745 i += 1;
1746 }
1747 continue;
1748 }
1749 // At this point we know that we will place the new block here. But the
1750 // remaining question is whether there is still yet enough capacity left
1751 // over for there to still be a free list node.
1752 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
1753 const keep_free_list_node = remaining_capacity >= min_text_capacity;
1754
1755 // Set up the metadata to be updated, after errors are no longer possible.
1756 block_placement = big_block;
1757 if (!keep_free_list_node) {
1758 free_list_removal = i;
1759 }
1760 break :blk new_start_vaddr;
1761 } else if (self.last_text_block) |last| {
1762 const sym = self.local_symbols.items[last.local_sym_index];
1763 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
1764 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1765 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
1766 // Set up the metadata to be updated, after errors are no longer possible.
1767 block_placement = last;
1768 break :blk new_start_vaddr;
1769 } else {
1770 break :blk phdr.p_vaddr;
1771 }
1772 };
1773
1774 const expand_text_section = block_placement == null or block_placement.?.next == null;
1775 if (expand_text_section) {
1776 const text_capacity = self.allocatedSize(shdr.sh_offset);
1777 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
1778 if (needed_size > text_capacity) {
1779 // Must move the entire text section.
1780 const new_offset = self.findFreeSpace(needed_size, 0x1000);
1781 const text_size = if (self.last_text_block) |last| blk: {
1782 const sym = self.local_symbols.items[last.local_sym_index];
1783 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
1784 } else 0;
1785 const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size);
1786 if (amt != text_size) return error.InputOutput;
1787 shdr.sh_offset = new_offset;
1788 phdr.p_offset = new_offset;
1789 }
1790 self.last_text_block = text_block;
1791
1792 shdr.sh_size = needed_size;
1793 phdr.p_memsz = needed_size;
1794 phdr.p_filesz = needed_size;
1795
1796 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
1797 // range of the compilation unit. When we expand the text section, this range changes,
1798 // so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty.
1799 self.debug_info_header_dirty = true;
1800 // This becomes dirty for the same reason. We could potentially make this more
1801 // fine-grained with the addition of support for more compilation units. It is planned to
1802 // model each package as a different compilation unit.
1803 self.debug_aranges_section_dirty = true;
1804
1805 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1806 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1807 }
1808
1809 // This function can also reallocate a text block.
1810 // In this case we need to "unplug" it from its previous location before
1811 // plugging it in to its new location.
1812 if (text_block.prev) |prev| {
1813 prev.next = text_block.next;
1814 }
1815 if (text_block.next) |next| {
1816 next.prev = text_block.prev;
1817 }
1818
1819 if (block_placement) |big_block| {
1820 text_block.prev = big_block;
1821 text_block.next = big_block.next;
1822 big_block.next = text_block;
1823 } else {
1824 text_block.prev = null;
1825 text_block.next = null;
1826 }
1827 if (free_list_removal) |i| {
1828 _ = self.text_block_free_list.swapRemove(i);
1829 }
1830 return vaddr;
1831 }
1832
1833 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1834 if (decl.link.elf.local_sym_index != 0) return;
1835
1836 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
1837 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
1838
1839 if (self.local_symbol_free_list.popOrNull()) |i| {
1840 log.debug("reusing symbol index {} for {}\n", .{ i, decl.name });
1841 decl.link.elf.local_sym_index = i;
1842 } else {
1843 log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
1844 decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1845 _ = self.local_symbols.addOneAssumeCapacity();
1846 }
1847
1848 if (self.offset_table_free_list.popOrNull()) |i| {
1849 decl.link.elf.offset_table_index = i;
1850 } else {
1851 decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
1852 _ = self.offset_table.addOneAssumeCapacity();
1853 self.offset_table_count_dirty = true;
1854 }
1855
1856 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1857
1858 self.local_symbols.items[decl.link.elf.local_sym_index] = .{
1859 .st_name = 0,
1860 .st_info = 0,
1861 .st_other = 0,
1862 .st_shndx = 0,
1863 .st_value = phdr.p_vaddr,
1864 .st_size = 0,
1865 };
1866 self.offset_table.items[decl.link.elf.offset_table_index] = 0;
1867 }
1868
1869 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1870 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1871 self.freeTextBlock(&decl.link.elf);
1872 if (decl.link.elf.local_sym_index != 0) {
1873 self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
1874 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
1875
1876 self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
1877
1878 decl.link.elf.local_sym_index = 0;
1879 }
1880 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
1881 // is desired for both.
1882 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);
1883 if (decl.fn_link.elf.prev) |prev| {
1884 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
1885 prev.next = decl.fn_link.elf.next;
1886 if (decl.fn_link.elf.next) |next| {
1887 next.prev = prev;
1888 } else {
1889 self.dbg_line_fn_last = prev;
1890 }
1891 } else if (decl.fn_link.elf.next) |next| {
1892 self.dbg_line_fn_first = next;
1893 next.prev = null;
1894 }
1895 if (self.dbg_line_fn_first == &decl.fn_link.elf) {
1896 self.dbg_line_fn_first = null;
1897 }
1898 if (self.dbg_line_fn_last == &decl.fn_link.elf) {
1899 self.dbg_line_fn_last = null;
1900 }
1901 }
1902
1903 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1904 const tracy = trace(@src());
1905 defer tracy.end();
1906
1907 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
1908 defer code_buffer.deinit();
1909
1910 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
1911 defer dbg_line_buffer.deinit();
1912
1913 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
1914 defer dbg_info_buffer.deinit();
1915
1916 var dbg_info_type_relocs: DbgInfoTypeRelocsTable = .{};
1917 defer {
1918 for (dbg_info_type_relocs.items()) |*entry| {
1919 entry.value.relocs.deinit(self.base.allocator);
1920 }
1921 dbg_info_type_relocs.deinit(self.base.allocator);
1922 }
1923
1924 const typed_value = decl.typed_value.most_recent.typed_value;
1925 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
1926 .Fn => true,
1927 else => false,
1928 };
1929 if (is_fn) {
1930 //if (mem.eql(u8, mem.spanZ(decl.name), "add")) {
1931 // typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
1932 //}
1933
1934 // For functions we need to add a prologue to the debug line program.
1935 try dbg_line_buffer.ensureCapacity(26);
1936
1937 const line_off: u28 = blk: {
1938 if (decl.scope.cast(Module.Scope.File)) |scope_file| {
1939 const tree = scope_file.contents.tree;
1940 const file_ast_decls = tree.root_node.decls();
1941 // TODO Look into improving the performance here by adding a token-index-to-line
1942 // lookup table. Currently this involves scanning over the source code for newlines.
1943 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1944 const block = fn_proto.body().?.castTag(.Block).?;
1945 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1946 break :blk @intCast(u28, line_delta);
1947 } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
1948 const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
1949 const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
1950 break :blk @intCast(u28, line_delta);
1951 } else {
1952 unreachable;
1953 }
1954 };
1955
1956 const ptr_width_bytes = self.ptrWidthBytes();
1957 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
1958 DW.LNS_extended_op,
1959 ptr_width_bytes + 1,
1960 DW.LNE_set_address,
1961 });
1962 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
1963 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
1964 dbg_line_buffer.items.len += ptr_width_bytes;
1965
1966 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
1967 // This is the "relocatable" relative line offset from the previous function's end curly
1968 // to this function's begin curly.
1969 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
1970 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
1971 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
1972
1973 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
1974 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
1975 // Once we support more than one source file, this will have the ability to be more
1976 // than one possible value.
1977 const file_index = 1;
1978 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
1979
1980 // Emit a line for the begin curly with prologue_end=false. The codegen will
1981 // do the work of setting prologue_end=true and epilogue_begin=true.
1982 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
1983
1984 // .debug_info subprogram
1985 const decl_name_with_null = decl.name[0..mem.lenZ(decl.name) + 1];
1986 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len);
1987
1988 const fn_ret_type = typed_value.ty.fnReturnType();
1989 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
1990 if (fn_ret_has_bits) {
1991 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
1992 } else {
1993 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
1994 }
1995 // These get overwritten after generating the machine code. These values are
1996 // "relocations" and have to be in this fixed place so that functions can be
1997 // moved in virtual address space.
1998 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
1999 dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr
2000 assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
2001 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
2002 if (fn_ret_has_bits) {
2003 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
2004 if (!gop.found_existing) {
2005 gop.entry.value = .{
2006 .off = undefined,
2007 .relocs = .{},
2008 };
2009 }
2010 try gop.entry.value.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
2011 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
2012 }
2013 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
2014 } else {
2015 // TODO implement .debug_info for global variables
2016 }
2017 const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer, &dbg_info_buffer, &dbg_info_type_relocs);
2018 const code = switch (res) {
2019 .externally_managed => |x| x,
2020 .appended => code_buffer.items,
2021 .fail => |em| {
2022 decl.analysis = .codegen_failure;
2023 try module.failed_decls.put(module.gpa, decl, em);
2024 return;
2025 },
2026 };
2027
2028 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2029
2030 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
2031
2032 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
2033 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
2034 if (local_sym.st_size != 0) {
2035 const capacity = decl.link.elf.capacity(self.*);
2036 const need_realloc = code.len > capacity or
2037 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
2038 if (need_realloc) {
2039 const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
2040 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
2041 if (vaddr != local_sym.st_value) {
2042 local_sym.st_value = vaddr;
2043
2044 log.debug(" (writing new offset table entry)\n", .{});
2045 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
2046 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
2047 }
2048 } else if (code.len < local_sym.st_size) {
2049 self.shrinkTextBlock(&decl.link.elf, code.len);
2050 }
2051 local_sym.st_size = code.len;
2052 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
2053 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
2054 local_sym.st_other = 0;
2055 local_sym.st_shndx = self.text_section_index.?;
2056 // TODO this write could be avoided if no fields of the symbol were changed.
2057 try self.writeSymbol(decl.link.elf.local_sym_index);
2058 } else {
2059 const decl_name = mem.spanZ(decl.name);
2060 const name_str_index = try self.makeString(decl_name);
2061 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
2062 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
2063 errdefer self.freeTextBlock(&decl.link.elf);
2064
2065 local_sym.* = .{
2066 .st_name = name_str_index,
2067 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
2068 .st_other = 0,
2069 .st_shndx = self.text_section_index.?,
2070 .st_value = vaddr,
2071 .st_size = code.len,
2072 };
2073 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
2074
2075 try self.writeSymbol(decl.link.elf.local_sym_index);
2076 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
2077 }
2078
2079 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
2080 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
2081 try self.base.file.?.pwriteAll(code, file_offset);
2082
2083 const target_endian = self.base.options.target.cpu.arch.endian();
2084
2085 const text_block = &decl.link.elf;
2086
2087 // If the Decl is a function, we need to update the .debug_line program.
2088 if (is_fn) {
2089 // Perform the relocations based on vaddr.
2090 switch (self.ptr_width) {
2091 .p32 => {
2092 {
2093 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
2094 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2095 }
2096 {
2097 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
2098 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2099 }
2100 },
2101 .p64 => {
2102 {
2103 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
2104 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2105 }
2106 {
2107 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
2108 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2109 }
2110 },
2111 }
2112 {
2113 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
2114 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
2115 }
2116
2117 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
2118
2119 // Now we have the full contents and may allocate a region to store it.
2120
2121 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
2122 // `TextBlock` and the .debug_info. If you are editing this logic, you
2123 // probably need to edit that logic too.
2124
2125 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
2126 const src_fn = &decl.fn_link.elf;
2127 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
2128 if (self.dbg_line_fn_last) |last| {
2129 if (src_fn.next) |next| {
2130 // Update existing function - non-last item.
2131 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
2132 // It grew too big, so we move it to a new location.
2133 if (src_fn.prev) |prev| {
2134 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
2135 prev.next = src_fn.next;
2136 }
2137 next.prev = src_fn.prev;
2138 src_fn.next = null;
2139 // Populate where it used to be with NOPs.
2140 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2141 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
2142 // TODO Look at the free list before appending at the end.
2143 src_fn.prev = last;
2144 last.next = src_fn;
2145 self.dbg_line_fn_last = src_fn;
2146
2147 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
2148 }
2149 } else if (src_fn.prev == null) {
2150 // Append new function.
2151 // TODO Look at the free list before appending at the end.
2152 src_fn.prev = last;
2153 last.next = src_fn;
2154 self.dbg_line_fn_last = src_fn;
2155
2156 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
2157 }
2158 } else {
2159 // This is the first function of the Line Number Program.
2160 self.dbg_line_fn_first = src_fn;
2161 self.dbg_line_fn_last = src_fn;
2162
2163 src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;
2164 }
2165
2166 const last_src_fn = self.dbg_line_fn_last.?;
2167 const needed_size = last_src_fn.off + last_src_fn.len;
2168 if (needed_size != debug_line_sect.sh_size) {
2169 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
2170 const new_offset = self.findFreeSpace(needed_size, 1);
2171 const existing_size = last_src_fn.off;
2172 log.debug("moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{
2173 existing_size,
2174 debug_line_sect.sh_offset,
2175 new_offset,
2176 });
2177 const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
2178 if (amt != existing_size) return error.InputOutput;
2179 debug_line_sect.sh_offset = new_offset;
2180 }
2181 debug_line_sect.sh_size = needed_size;
2182 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2183 self.debug_line_header_dirty = true;
2184 }
2185 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
2186 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
2187
2188 // We only have support for one compilation unit so far, so the offsets are directly
2189 // from the .debug_line section.
2190 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2191 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
2192
2193 // .debug_info - End the TAG_subprogram children.
2194 try dbg_info_buffer.append(0);
2195 }
2196
2197 // Now we emit the .debug_info types of the Decl. These will count towards the size of
2198 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
2199 // relocations yet.
2200 for (dbg_info_type_relocs.items()) |*entry| {
2201 entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
2202 try self.addDbgInfoType(entry.key, &dbg_info_buffer);
2203 }
2204
2205 try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len));
2206
2207 // Now that we have the offset assigned we can finally perform type relocations.
2208 for (dbg_info_type_relocs.items()) |entry| {
2209 for (entry.value.relocs.items) |off| {
2210 mem.writeInt(
2211 u32,
2212 dbg_info_buffer.items[off..][0..4],
2213 text_block.dbg_info_off + entry.value.off,
2214 target_endian,
2215 );
2216 }
2217 }
2218
2219 try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
2220
2221 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
2222 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
2223 return self.updateDeclExports(module, decl, decl_exports);
2224 }
2225
2226 /// Asserts the type has codegen bits.
2227 fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
2228 switch (ty.zigTypeTag()) {
2229 .Void => unreachable,
2230 .NoReturn => unreachable,
2231 .Bool => {
2232 try dbg_info_buffer.appendSlice(&[_]u8{
2233 abbrev_base_type,
2234 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
2235 1, // DW.AT_byte_size, DW.FORM_data1
2236 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string
2237 });
2238 },
2239 .Int => {
2240 const info = ty.intInfo(self.base.options.target);
2241 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
2242 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
2243 // DW.AT_encoding, DW.FORM_data1
2244 dbg_info_buffer.appendAssumeCapacity(if (info.signed) DW.ATE_signed else DW.ATE_unsigned);
2245 // DW.AT_byte_size, DW.FORM_data1
2246 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
2247 // DW.AT_name, DW.FORM_string
2248 try dbg_info_buffer.writer().print("{}\x00", .{ty});
2249 },
2250 else => {
2251 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
2252 try dbg_info_buffer.append(abbrev_pad1);
2253 },
2254 }
2255 }
2256
2257 fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void {
2258 const tracy = trace(@src());
2259 defer tracy.end();
2260
2261 // This logic is nearly identical to the logic above in `updateDecl` for
2262 // `SrcFn` and the line number programs. If you are editing this logic, you
2263 // probably need to edit that logic too.
2264
2265 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
2266 text_block.dbg_info_len = len;
2267 if (self.dbg_info_decl_last) |last| {
2268 if (text_block.dbg_info_next) |next| {
2269 // Update existing Decl - non-last item.
2270 if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) {
2271 // It grew too big, so we move it to a new location.
2272 if (text_block.dbg_info_prev) |prev| {
2273 _ = self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {};
2274 prev.dbg_info_next = text_block.dbg_info_next;
2275 }
2276 next.dbg_info_prev = text_block.dbg_info_prev;
2277 text_block.dbg_info_next = null;
2278 // Populate where it used to be with NOPs.
2279 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
2280 try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos);
2281 // TODO Look at the free list before appending at the end.
2282 text_block.dbg_info_prev = last;
2283 last.dbg_info_next = text_block;
2284 self.dbg_info_decl_last = text_block;
2285
2286 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
2287 }
2288 } else if (text_block.dbg_info_prev == null) {
2289 // Append new Decl.
2290 // TODO Look at the free list before appending at the end.
2291 text_block.dbg_info_prev = last;
2292 last.dbg_info_next = text_block;
2293 self.dbg_info_decl_last = text_block;
2294
2295 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
2296 }
2297 } else {
2298 // This is the first Decl of the .debug_info
2299 self.dbg_info_decl_first = text_block;
2300 self.dbg_info_decl_last = text_block;
2301
2302 text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;
2303 }
2304 }
2305
2306 fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
2307 const tracy = trace(@src());
2308 defer tracy.end();
2309
2310 // This logic is nearly identical to the logic above in `updateDecl` for
2311 // `SrcFn` and the line number programs. If you are editing this logic, you
2312 // probably need to edit that logic too.
2313
2314 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
2315
2316 const last_decl = self.dbg_info_decl_last.?;
2317 // +1 for a trailing zero to end the children of the decl tag.
2318 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
2319 if (needed_size != debug_info_sect.sh_size) {
2320 if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) {
2321 const new_offset = self.findFreeSpace(needed_size, 1);
2322 const existing_size = last_decl.dbg_info_off;
2323 log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}\n", .{
2324 existing_size,
2325 debug_info_sect.sh_offset,
2326 new_offset,
2327 });
2328 const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size);
2329 if (amt != existing_size) return error.InputOutput;
2330 debug_info_sect.sh_offset = new_offset;
2331 }
2332 debug_info_sect.sh_size = needed_size;
2333 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2334 self.debug_info_header_dirty = true;
2335 }
2336 const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev|
2337 text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len)
2338 else
2339 0;
2340 const next_padding_size: u32 = if (text_block.dbg_info_next) |next|
2341 next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len)
2342 else
2343 0;
2344
2345 // To end the children of the decl tag.
2346 const trailing_zero = text_block.dbg_info_next == null;
2347
2348 // We only have support for one compilation unit so far, so the offsets are directly
2349 // from the .debug_info section.
2350 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
2351 try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos);
2352 }
2353
2354 pub fn updateDeclExports(
2355 self: *Elf,
2356 module: *Module,
2357 decl: *const Module.Decl,
2358 exports: []const *Module.Export,
2359 ) !void {
2360 const tracy = trace(@src());
2361 defer tracy.end();
2362
2363 try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
2364 const typed_value = decl.typed_value.most_recent.typed_value;
2365 if (decl.link.elf.local_sym_index == 0) return;
2366 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
2367
2368 for (exports) |exp| {
2369 if (exp.options.section) |section_name| {
2370 if (!mem.eql(u8, section_name, ".text")) {
2371 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
2372 module.failed_exports.putAssumeCapacityNoClobber(
2373 exp,
2374 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
2375 );
2376 continue;
2377 }
2378 }
2379 const stb_bits: u8 = switch (exp.options.linkage) {
2380 .Internal => elf.STB_LOCAL,
2381 .Strong => blk: {
2382 if (mem.eql(u8, exp.options.name, "_start")) {
2383 self.entry_addr = decl_sym.st_value;
2384 }
2385 break :blk elf.STB_GLOBAL;
2386 },
2387 .Weak => elf.STB_WEAK,
2388 .LinkOnce => {
2389 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
2390 module.failed_exports.putAssumeCapacityNoClobber(
2391 exp,
2392 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
2393 );
2394 continue;
2395 },
2396 };
2397 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
2398 if (exp.link.sym_index) |i| {
2399 const sym = &self.global_symbols.items[i];
2400 sym.* = .{
2401 .st_name = try self.updateString(sym.st_name, exp.options.name),
2402 .st_info = (stb_bits << 4) | stt_bits,
2403 .st_other = 0,
2404 .st_shndx = self.text_section_index.?,
2405 .st_value = decl_sym.st_value,
2406 .st_size = decl_sym.st_size,
2407 };
2408 } else {
2409 const name = try self.makeString(exp.options.name);
2410 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
2411 _ = self.global_symbols.addOneAssumeCapacity();
2412 break :blk self.global_symbols.items.len - 1;
2413 };
2414 self.global_symbols.items[i] = .{
2415 .st_name = name,
2416 .st_info = (stb_bits << 4) | stt_bits,
2417 .st_other = 0,
2418 .st_shndx = self.text_section_index.?,
2419 .st_value = decl_sym.st_value,
2420 .st_size = decl_sym.st_size,
2421 };
2422
2423 exp.link.sym_index = @intCast(u32, i);
2424 }
2425 }
2426 }
2427
2428 /// Must be called only after a successful call to `updateDecl`.
2429 pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
2430 const tracy = trace(@src());
2431 defer tracy.end();
2432
2433 const scope_file = decl.scope.cast(Module.Scope.File).?;
2434 const tree = scope_file.contents.tree;
2435 const file_ast_decls = tree.root_node.decls();
2436 // TODO Look into improving the performance here by adding a token-index-to-line
2437 // lookup table. Currently this involves scanning over the source code for newlines.
2438 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
2439 const block = fn_proto.body().?.castTag(.Block).?;
2440 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
2441 const casted_line_off = @intCast(u28, line_delta);
2442
2443 const shdr = &self.sections.items[self.debug_line_section_index.?];
2444 const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
2445 var data: [4]u8 = undefined;
2446 leb128.writeUnsignedFixed(4, &data, casted_line_off);
2447 try self.base.file.?.pwriteAll(&data, file_pos);
2448 }
2449
2450 pub fn deleteExport(self: *Elf, exp: Export) void {
2451 const sym_index = exp.sym_index orelse return;
2452 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
2453 self.global_symbols.items[sym_index].st_info = 0;
2454 }
2455
2456 fn writeProgHeader(self: *Elf, index: usize) !void {
2457 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2458 const offset = self.program_headers.items[index].p_offset;
2459 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
2460 32 => {
2461 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
2462 if (foreign_endian) {
2463 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
2464 }
2465 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2466 },
2467 64 => {
2468 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
2469 if (foreign_endian) {
2470 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
2471 }
2472 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2473 },
2474 else => return error.UnsupportedArchitecture,
2475 }
2476 }
2477
2478 fn writeSectHeader(self: *Elf, index: usize) !void {
2479 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2480 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
2481 32 => {
2482 var shdr: [1]elf.Elf32_Shdr = undefined;
2483 shdr[0] = sectHeaderTo32(self.sections.items[index]);
2484 if (foreign_endian) {
2485 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
2486 }
2487 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
2488 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2489 },
2490 64 => {
2491 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
2492 if (foreign_endian) {
2493 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
2494 }
2495 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
2496 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2497 },
2498 else => return error.UnsupportedArchitecture,
2499 }
2500 }
2501
2502 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2503 const shdr = &self.sections.items[self.got_section_index.?];
2504 const phdr = &self.program_headers.items[self.phdr_got_index.?];
2505 const entry_size: u16 = self.ptrWidthBytes();
2506 if (self.offset_table_count_dirty) {
2507 // TODO Also detect virtual address collisions.
2508 const allocated_size = self.allocatedSize(shdr.sh_offset);
2509 const needed_size = self.local_symbols.items.len * entry_size;
2510 if (needed_size > allocated_size) {
2511 // Must move the entire got section.
2512 const new_offset = self.findFreeSpace(needed_size, entry_size);
2513 const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size);
2514 if (amt != shdr.sh_size) return error.InputOutput;
2515 shdr.sh_offset = new_offset;
2516 phdr.p_offset = new_offset;
2517 }
2518 shdr.sh_size = needed_size;
2519 phdr.p_memsz = needed_size;
2520 phdr.p_filesz = needed_size;
2521
2522 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2523 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
2524
2525 self.offset_table_count_dirty = false;
2526 }
2527 const endian = self.base.options.target.cpu.arch.endian();
2528 const off = shdr.sh_offset + @as(u64, entry_size) * index;
2529 switch (self.ptr_width) {
2530 .p32 => {
2531 var buf: [4]u8 = undefined;
2532 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
2533 try self.base.file.?.pwriteAll(&buf, off);
2534 },
2535 .p64 => {
2536 var buf: [8]u8 = undefined;
2537 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
2538 try self.base.file.?.pwriteAll(&buf, off);
2539 },
2540 }
2541 }
2542
2543 fn writeSymbol(self: *Elf, index: usize) !void {
2544 const tracy = trace(@src());
2545 defer tracy.end();
2546
2547 const syms_sect = &self.sections.items[self.symtab_section_index.?];
2548 // Make sure we are not pointlessly writing symbol data that will have to get relocated
2549 // due to running out of space.
2550 if (self.local_symbols.items.len != syms_sect.sh_info) {
2551 const sym_size: u64 = switch (self.ptr_width) {
2552 .p32 => @sizeOf(elf.Elf32_Sym),
2553 .p64 => @sizeOf(elf.Elf64_Sym),
2554 };
2555 const sym_align: u16 = switch (self.ptr_width) {
2556 .p32 => @alignOf(elf.Elf32_Sym),
2557 .p64 => @alignOf(elf.Elf64_Sym),
2558 };
2559 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
2560 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
2561 // Move all the symbols to a new file location.
2562 const new_offset = self.findFreeSpace(needed_size, sym_align);
2563 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
2564 const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size);
2565 if (amt != existing_size) return error.InputOutput;
2566 syms_sect.sh_offset = new_offset;
2567 }
2568 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
2569 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
2570 self.shdr_table_dirty = true; // TODO look into only writing one section
2571 }
2572 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2573 switch (self.ptr_width) {
2574 .p32 => {
2575 var sym = [1]elf.Elf32_Sym{
2576 .{
2577 .st_name = self.local_symbols.items[index].st_name,
2578 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
2579 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
2580 .st_info = self.local_symbols.items[index].st_info,
2581 .st_other = self.local_symbols.items[index].st_other,
2582 .st_shndx = self.local_symbols.items[index].st_shndx,
2583 },
2584 };
2585 if (foreign_endian) {
2586 bswapAllFields(elf.Elf32_Sym, &sym[0]);
2587 }
2588 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
2589 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
2590 },
2591 .p64 => {
2592 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
2593 if (foreign_endian) {
2594 bswapAllFields(elf.Elf64_Sym, &sym[0]);
2595 }
2596 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
2597 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
2598 },
2599 }
2600 }
2601
2602 fn writeAllGlobalSymbols(self: *Elf) !void {
2603 const syms_sect = &self.sections.items[self.symtab_section_index.?];
2604 const sym_size: u64 = switch (self.ptr_width) {
2605 .p32 => @sizeOf(elf.Elf32_Sym),
2606 .p64 => @sizeOf(elf.Elf64_Sym),
2607 };
2608 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2609 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
2610 switch (self.ptr_width) {
2611 .p32 => {
2612 const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
2613 defer self.base.allocator.free(buf);
2614
2615 for (buf) |*sym, i| {
2616 sym.* = .{
2617 .st_name = self.global_symbols.items[i].st_name,
2618 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
2619 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
2620 .st_info = self.global_symbols.items[i].st_info,
2621 .st_other = self.global_symbols.items[i].st_other,
2622 .st_shndx = self.global_symbols.items[i].st_shndx,
2623 };
2624 if (foreign_endian) {
2625 bswapAllFields(elf.Elf32_Sym, sym);
2626 }
2627 }
2628 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
2629 },
2630 .p64 => {
2631 const buf = try self.base.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
2632 defer self.base.allocator.free(buf);
2633
2634 for (buf) |*sym, i| {
2635 sym.* = .{
2636 .st_name = self.global_symbols.items[i].st_name,
2637 .st_value = self.global_symbols.items[i].st_value,
2638 .st_size = self.global_symbols.items[i].st_size,
2639 .st_info = self.global_symbols.items[i].st_info,
2640 .st_other = self.global_symbols.items[i].st_other,
2641 .st_shndx = self.global_symbols.items[i].st_shndx,
2642 };
2643 if (foreign_endian) {
2644 bswapAllFields(elf.Elf64_Sym, sym);
2645 }
2646 }
2647 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
2648 },
2649 }
2650 }
2651
2652 fn ptrWidthBytes(self: Elf) u8 {
2653 return switch (self.ptr_width) {
2654 .p32 => 4,
2655 .p64 => 8,
2656 };
2657 }
2658
2659 /// The reloc offset for the virtual address of a function in its Line Number Program.
2660 /// Size is a virtual address integer.
2661 const dbg_line_vaddr_reloc_index = 3;
2662 /// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
2663 /// Size is a virtual address integer.
2664 const dbg_info_low_pc_reloc_index = 1;
2665
2666 /// The reloc offset for the line offset of a function from the previous function's line.
2667 /// It's a fixed-size 4-byte ULEB128.
2668 fn getRelocDbgLineOff(self: Elf) usize {
2669 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
2670 }
2671
2672 fn getRelocDbgFileIndex(self: Elf) usize {
2673 return self.getRelocDbgLineOff() + 5;
2674 }
2675
2676 fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 {
2677 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
2678 }
2679
2680 fn dbgLineNeededHeaderBytes(self: Elf) u32 {
2681 const directory_entry_format_count = 1;
2682 const file_name_entry_format_count = 1;
2683 const directory_count = 1;
2684 const file_name_count = 1;
2685 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
2686 directory_count * 8 + file_name_count * 8 +
2687 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
2688 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
2689 self.base.options.root_pkg.root_src_dir_path.len +
2690 self.base.options.root_pkg.root_src_path.len);
2691
2692 }
2693
2694 fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
2695 return 120;
2696 }
2697
2698 const min_nop_size = 2;
2699
2700 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
2701 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
2702 /// are less than 126,976 bytes (if this limit is ever reached, this function can be
2703 /// improved to make more than one pwritev call, or the limit can be raised by a fixed
2704 /// amount by increasing the length of `vecs`).
2705 fn pwriteDbgLineNops(
2706 self: *Elf,
2707 prev_padding_size: usize,
2708 buf: []const u8,
2709 next_padding_size: usize,
2710 offset: usize,
2711 ) !void {
2712 const tracy = trace(@src());
2713 defer tracy.end();
2714
2715 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
2716 const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0};
2717 var vecs: [32]std.os.iovec_const = undefined;
2718 var vec_index: usize = 0;
2719 {
2720 var padding_left = prev_padding_size;
2721 if (padding_left % 2 != 0) {
2722 vecs[vec_index] = .{
2723 .iov_base = &three_byte_nop,
2724 .iov_len = three_byte_nop.len,
2725 };
2726 vec_index += 1;
2727 padding_left -= three_byte_nop.len;
2728 }
2729 while (padding_left > page_of_nops.len) {
2730 vecs[vec_index] = .{
2731 .iov_base = &page_of_nops,
2732 .iov_len = page_of_nops.len,
2733 };
2734 vec_index += 1;
2735 padding_left -= page_of_nops.len;
2736 }
2737 if (padding_left > 0) {
2738 vecs[vec_index] = .{
2739 .iov_base = &page_of_nops,
2740 .iov_len = padding_left,
2741 };
2742 vec_index += 1;
2743 }
2744 }
2745
2746 vecs[vec_index] = .{
2747 .iov_base = buf.ptr,
2748 .iov_len = buf.len,
2749 };
2750 vec_index += 1;
2751
2752 {
2753 var padding_left = next_padding_size;
2754 if (padding_left % 2 != 0) {
2755 vecs[vec_index] = .{
2756 .iov_base = &three_byte_nop,
2757 .iov_len = three_byte_nop.len,
2758 };
2759 vec_index += 1;
2760 padding_left -= three_byte_nop.len;
2761 }
2762 while (padding_left > page_of_nops.len) {
2763 vecs[vec_index] = .{
2764 .iov_base = &page_of_nops,
2765 .iov_len = page_of_nops.len,
2766 };
2767 vec_index += 1;
2768 padding_left -= page_of_nops.len;
2769 }
2770 if (padding_left > 0) {
2771 vecs[vec_index] = .{
2772 .iov_base = &page_of_nops,
2773 .iov_len = padding_left,
2774 };
2775 vec_index += 1;
2776 }
2777 }
2778 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
2779 }
2780
2781 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
2782 /// bytes of padding.
2783 fn pwriteDbgInfoNops(
2784 self: *Elf,
2785 prev_padding_size: usize,
2786 buf: []const u8,
2787 next_padding_size: usize,
2788 trailing_zero: bool,
2789 offset: usize,
2790 ) !void {
2791 const tracy = trace(@src());
2792 defer tracy.end();
2793
2794 const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
2795 var vecs: [32]std.os.iovec_const = undefined;
2796 var vec_index: usize = 0;
2797 {
2798 var padding_left = prev_padding_size;
2799 while (padding_left > page_of_nops.len) {
2800 vecs[vec_index] = .{
2801 .iov_base = &page_of_nops,
2802 .iov_len = page_of_nops.len,
2803 };
2804 vec_index += 1;
2805 padding_left -= page_of_nops.len;
2806 }
2807 if (padding_left > 0) {
2808 vecs[vec_index] = .{
2809 .iov_base = &page_of_nops,
2810 .iov_len = padding_left,
2811 };
2812 vec_index += 1;
2813 }
2814 }
2815
2816 vecs[vec_index] = .{
2817 .iov_base = buf.ptr,
2818 .iov_len = buf.len,
2819 };
2820 vec_index += 1;
2821
2822 {
2823 var padding_left = next_padding_size;
2824 while (padding_left > page_of_nops.len) {
2825 vecs[vec_index] = .{
2826 .iov_base = &page_of_nops,
2827 .iov_len = page_of_nops.len,
2828 };
2829 vec_index += 1;
2830 padding_left -= page_of_nops.len;
2831 }
2832 if (padding_left > 0) {
2833 vecs[vec_index] = .{
2834 .iov_base = &page_of_nops,
2835 .iov_len = padding_left,
2836 };
2837 vec_index += 1;
2838 }
2839 }
2840
2841 if (trailing_zero) {
2842 var zbuf = [1]u8{0};
2843 vecs[vec_index] = .{
2844 .iov_base = &zbuf,
2845 .iov_len = zbuf.len,
2846 };
2847 vec_index += 1;
2848 }
2849
2850 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
2851 }
2852
2853 };
2854
237 pub const C = @import("link/C.zig");
238 pub const Elf = @import("link/Elf.zig");
2855239 pub const MachO = @import("link/MachO.zig");
2856 const Wasm = @import("link/Wasm.zig");
240 pub const Wasm = @import("link/Wasm.zig");
2857241};
2858242
2859/// Saturating multiplication
2860fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
2861 const T = @TypeOf(a, b);
2862 return std.math.mul(T, a, b) catch std.math.maxInt(T);
2863}
2864
2865fn bswapAllFields(comptime S: type, ptr: *S) void {
2866 @panic("TODO implement bswapAllFields");
2867}
2868
2869fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
2870 return .{
2871 .p_type = phdr.p_type,
2872 .p_flags = phdr.p_flags,
2873 .p_offset = @intCast(u32, phdr.p_offset),
2874 .p_vaddr = @intCast(u32, phdr.p_vaddr),
2875 .p_paddr = @intCast(u32, phdr.p_paddr),
2876 .p_filesz = @intCast(u32, phdr.p_filesz),
2877 .p_memsz = @intCast(u32, phdr.p_memsz),
2878 .p_align = @intCast(u32, phdr.p_align),
2879 };
2880}
2881
2882fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
2883 return .{
2884 .sh_name = shdr.sh_name,
2885 .sh_type = shdr.sh_type,
2886 .sh_flags = @intCast(u32, shdr.sh_flags),
2887 .sh_addr = @intCast(u32, shdr.sh_addr),
2888 .sh_offset = @intCast(u32, shdr.sh_offset),
2889 .sh_size = @intCast(u32, shdr.sh_size),
2890 .sh_link = shdr.sh_link,
2891 .sh_info = shdr.sh_info,
2892 .sh_addralign = @intCast(u32, shdr.sh_addralign),
2893 .sh_entsize = @intCast(u32, shdr.sh_entsize),
2894 };
2895}
2896
2897243pub fn determineMode(options: Options) fs.File.Mode {
2898244 // On common systems with a 0o022 umask, 0o777 will still result in a file created
2899245 // with 0o755 permissions, but it works appropriately if the system is configured
src-self-hosted/link/C.zig created+101
......@@ -0,0 +1,101 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;
5const Module = @import("../Module.zig");
6const fs = std.fs;
7const codegen = @import("../codegen/c.zig");
8const link = @import("../link.zig");
9const File = link.File;
10const C = @This();
11
12pub const base_tag: File.Tag = .c;
13
14base: File,
15
16header: std.ArrayList(u8),
17constants: std.ArrayList(u8),
18main: std.ArrayList(u8),
19
20called: std.StringHashMap(void),
21need_stddef: bool = false,
22need_stdint: bool = false,
23error_msg: *Module.ErrorMsg = undefined,
24
25pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
26 assert(options.object_format == .c);
27
28 const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
29 errdefer file.close();
30
31 var c_file = try allocator.create(C);
32 errdefer allocator.destroy(c_file);
33
34 c_file.* = C{
35 .base = .{
36 .tag = .c,
37 .options = options,
38 .file = file,
39 .allocator = allocator,
40 },
41 .main = std.ArrayList(u8).init(allocator),
42 .header = std.ArrayList(u8).init(allocator),
43 .constants = std.ArrayList(u8).init(allocator),
44 .called = std.StringHashMap(void).init(allocator),
45 };
46
47 return &c_file.base;
48}
49
50pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
51 self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);
52 return error.AnalysisFail;
53}
54
55pub fn deinit(self: *C) void {
56 self.main.deinit();
57 self.header.deinit();
58 self.constants.deinit();
59 self.called.deinit();
60}
61
62pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
63 codegen.generate(self, decl) catch |err| {
64 if (err == error.AnalysisFail) {
65 try module.failed_decls.put(module.gpa, decl, self.error_msg);
66 }
67 return err;
68 };
69}
70
71pub fn flush(self: *C, module: *Module) !void {
72 const writer = self.base.file.?.writer();
73 try writer.writeAll(@embedFile("cbe.h"));
74 var includes = false;
75 if (self.need_stddef) {
76 try writer.writeAll("#include <stddef.h>\n");
77 includes = true;
78 }
79 if (self.need_stdint) {
80 try writer.writeAll("#include <stdint.h>\n");
81 includes = true;
82 }
83 if (includes) {
84 try writer.writeByte('\n');
85 }
86 if (self.header.items.len > 0) {
87 try writer.print("{}\n", .{self.header.items});
88 }
89 if (self.constants.items.len > 0) {
90 try writer.print("{}\n", .{self.constants.items});
91 }
92 if (self.main.items.len > 1) {
93 const last_two = self.main.items[self.main.items.len - 2 ..];
94 if (std.mem.eql(u8, last_two, "\n\n")) {
95 self.main.items.len -= 1;
96 }
97 }
98 try writer.writeAll(self.main.items);
99 self.base.file.?.close();
100 self.base.file = null;
101}
src-self-hosted/link/Elf.zig created+2583
......@@ -0,0 +1,2583 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;
5const ir = @import("../ir.zig");
6const Module = @import("../Module.zig");
7const fs = std.fs;
8const elf = std.elf;
9const codegen = @import("../codegen.zig");
10const log = std.log.scoped(.link);
11const DW = std.dwarf;
12const trace = @import("../tracy.zig").trace;
13const leb128 = std.debug.leb;
14const Package = @import("../Package.zig");
15const Value = @import("../value.zig").Value;
16const Type = @import("../type.zig").Type;
17const build_options = @import("build_options");
18const link = @import("../link.zig");
19const File = link.File;
20const Elf = @This();
21
22const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
23const default_entry_addr = 0x8000000;
24
25// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
26// zig fmt: off
27
28pub const base_tag: File.Tag = .elf;
29
30base: File,
31
32ptr_width: enum { p32, p64 },
33
34/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
35/// Same order as in the file.
36sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
37shdr_table_offset: ?u64 = null,
38
39/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
40/// Same order as in the file.
41program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
42phdr_table_offset: ?u64 = null,
43/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
44phdr_load_re_index: ?u16 = null,
45/// The index into the program headers of the global offset table.
46/// It needs PT_LOAD and Read flags.
47phdr_got_index: ?u16 = null,
48entry_addr: ?u64 = null,
49
50debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
51shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
52shstrtab_index: ?u16 = null,
53
54text_section_index: ?u16 = null,
55symtab_section_index: ?u16 = null,
56got_section_index: ?u16 = null,
57debug_info_section_index: ?u16 = null,
58debug_abbrev_section_index: ?u16 = null,
59debug_str_section_index: ?u16 = null,
60debug_aranges_section_index: ?u16 = null,
61debug_line_section_index: ?u16 = null,
62
63debug_abbrev_table_offset: ?u64 = null,
64
65/// The same order as in the file. ELF requires global symbols to all be after the
66/// local symbols, they cannot be mixed. So we must buffer all the global symbols and
67/// write them at the end. These are only the local symbols. The length of this array
68/// is the value used for sh_info in the .symtab section.
69local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
70global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
71
72local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
73global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
74offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
75
76/// Same order as in the file. The value is the absolute vaddr value.
77/// If the vaddr of the executable program header changes, the entire
78/// offset table needs to be rewritten.
79offset_table: std.ArrayListUnmanaged(u64) = .{},
80
81phdr_table_dirty: bool = false,
82shdr_table_dirty: bool = false,
83shstrtab_dirty: bool = false,
84debug_strtab_dirty: bool = false,
85offset_table_count_dirty: bool = false,
86debug_abbrev_section_dirty: bool = false,
87debug_aranges_section_dirty: bool = false,
88
89debug_info_header_dirty: bool = false,
90debug_line_header_dirty: bool = false,
91
92error_flags: File.ErrorFlags = File.ErrorFlags{},
93
94/// A list of text blocks that have surplus capacity. This list can have false
95/// positives, as functions grow and shrink over time, only sometimes being added
96/// or removed from the freelist.
97///
98/// A text block has surplus capacity when its overcapacity value is greater than
99/// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
100/// much extra capacity, that we could fit a small new symbol in it, itself with
101/// ideal_capacity or more.
102///
103/// Ideal capacity is defined by size * alloc_num / alloc_den.
104///
105/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
106/// overcapacity can be negative. A simple way to have negative overcapacity is to
107/// allocate a fresh text block, which will have ideal capacity, and then grow it
108/// by 1 byte. It will then have -1 overcapacity.
109text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
110last_text_block: ?*TextBlock = null,
111
112/// A list of `SrcFn` whose Line Number Programs have surplus capacity.
113/// This is the same concept as `text_block_free_list`; see those doc comments.
114dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
115dbg_line_fn_first: ?*SrcFn = null,
116dbg_line_fn_last: ?*SrcFn = null,
117
118/// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity.
119/// This is the same concept as `text_block_free_list`; see those doc comments.
120dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
121dbg_info_decl_first: ?*TextBlock = null,
122dbg_info_decl_last: ?*TextBlock = null,
123
124/// `alloc_num / alloc_den` is the factor of padding when allocating.
125const alloc_num = 4;
126const alloc_den = 3;
127
128/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
129/// it as a possible place to put new symbols, it must have enough room for this many bytes
130/// (plus extra for reserved capacity).
131const minimum_text_block_size = 64;
132const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
133
134pub const TextBlock = struct {
135 /// Each decl always gets a local symbol with the fully qualified name.
136 /// The vaddr and size are found here directly.
137 /// The file offset is found by computing the vaddr offset from the section vaddr
138 /// the symbol references, and adding that to the file offset of the section.
139 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
140 /// offset table entry.
141 local_sym_index: u32,
142 /// This field is undefined for symbols with size = 0.
143 offset_table_index: u32,
144 /// Points to the previous and next neighbors, based on the `text_offset`.
145 /// This can be used to find, for example, the capacity of this `TextBlock`.
146 prev: ?*TextBlock,
147 next: ?*TextBlock,
148
149 /// Previous/next linked list pointers. This value is `next ^ prev`.
150 /// This is the linked list node for this Decl's corresponding .debug_info tag.
151 dbg_info_prev: ?*TextBlock,
152 dbg_info_next: ?*TextBlock,
153 /// Offset into .debug_info pointing to the tag for this Decl.
154 dbg_info_off: u32,
155 /// Size of the .debug_info tag for this Decl, not including padding.
156 dbg_info_len: u32,
157
158 pub const empty = TextBlock{
159 .local_sym_index = 0,
160 .offset_table_index = undefined,
161 .prev = null,
162 .next = null,
163 .dbg_info_prev = null,
164 .dbg_info_next = null,
165 .dbg_info_off = undefined,
166 .dbg_info_len = undefined,
167 };
168
169 /// Returns how much room there is to grow in virtual address space.
170 /// File offset relocation happens transparently, so it is not included in
171 /// this calculation.
172 fn capacity(self: TextBlock, elf_file: Elf) u64 {
173 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
174 if (self.next) |next| {
175 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
176 return next_sym.st_value - self_sym.st_value;
177 } else {
178 // We are the last block. The capacity is limited only by virtual address space.
179 return std.math.maxInt(u32) - self_sym.st_value;
180 }
181 }
182
183 fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
184 // No need to keep a free list node for the last block.
185 const next = self.next orelse return false;
186 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
187 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
188 const cap = next_sym.st_value - self_sym.st_value;
189 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
190 if (cap <= ideal_cap) return false;
191 const surplus = cap - ideal_cap;
192 return surplus >= min_text_capacity;
193 }
194};
195
196pub const Export = struct {
197 sym_index: ?u32 = null,
198};
199
200pub const SrcFn = struct {
201 /// Offset from the beginning of the Debug Line Program header that contains this function.
202 off: u32,
203 /// Size of the line number program component belonging to this function, not
204 /// including padding.
205 len: u32,
206
207 /// Points to the previous and next neighbors, based on the offset from .debug_line.
208 /// This can be used to find, for example, the capacity of this `SrcFn`.
209 prev: ?*SrcFn,
210 next: ?*SrcFn,
211
212 pub const empty: SrcFn = .{
213 .off = 0,
214 .len = 0,
215 .prev = null,
216 .next = null,
217 };
218};
219
220pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
221 assert(options.object_format == .elf);
222
223 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
224 errdefer file.close();
225
226 var elf_file = try allocator.create(Elf);
227 errdefer allocator.destroy(elf_file);
228
229 elf_file.* = openFile(allocator, file, options) catch |err| switch (err) {
230 error.IncrFailed => try createFile(allocator, file, options),
231 else => |e| return e,
232 };
233
234 return &elf_file.base;
235}
236
237/// Returns error.IncrFailed if incremental update could not be performed.
238fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
239 switch (options.output_mode) {
240 .Exe => {},
241 .Obj => {},
242 .Lib => return error.IncrFailed,
243 }
244 var self: Elf = .{
245 .base = .{
246 .file = file,
247 .tag = .elf,
248 .options = options,
249 .allocator = allocator,
250 },
251 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
252 32 => .p32,
253 64 => .p64,
254 else => return error.UnsupportedELFArchitecture,
255 },
256 };
257 errdefer self.deinit();
258
259 // TODO implement reading the elf file
260 return error.IncrFailed;
261 //try self.populateMissingMetadata();
262 //return self;
263}
264
265/// Truncates the existing file contents and overwrites the contents.
266/// Returns an error if `file` is not already open with +read +write +seek abilities.
267fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
268 switch (options.output_mode) {
269 .Exe => {},
270 .Obj => {},
271 .Lib => return error.TODOImplementWritingLibFiles,
272 }
273 var self: Elf = .{
274 .base = .{
275 .tag = .elf,
276 .options = options,
277 .allocator = allocator,
278 .file = file,
279 },
280 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
281 32 => .p32,
282 64 => .p64,
283 else => return error.UnsupportedELFArchitecture,
284 },
285 .shdr_table_dirty = true,
286 };
287 errdefer self.deinit();
288
289 // Index 0 is always a null symbol.
290 try self.local_symbols.append(allocator, .{
291 .st_name = 0,
292 .st_info = 0,
293 .st_other = 0,
294 .st_shndx = 0,
295 .st_value = 0,
296 .st_size = 0,
297 });
298
299 // There must always be a null section in index 0
300 try self.sections.append(allocator, .{
301 .sh_name = 0,
302 .sh_type = elf.SHT_NULL,
303 .sh_flags = 0,
304 .sh_addr = 0,
305 .sh_offset = 0,
306 .sh_size = 0,
307 .sh_link = 0,
308 .sh_info = 0,
309 .sh_addralign = 0,
310 .sh_entsize = 0,
311 });
312
313 try self.populateMissingMetadata();
314
315 return self;
316}
317
318pub fn deinit(self: *Elf) void {
319 self.sections.deinit(self.base.allocator);
320 self.program_headers.deinit(self.base.allocator);
321 self.shstrtab.deinit(self.base.allocator);
322 self.debug_strtab.deinit(self.base.allocator);
323 self.local_symbols.deinit(self.base.allocator);
324 self.global_symbols.deinit(self.base.allocator);
325 self.global_symbol_free_list.deinit(self.base.allocator);
326 self.local_symbol_free_list.deinit(self.base.allocator);
327 self.offset_table_free_list.deinit(self.base.allocator);
328 self.text_block_free_list.deinit(self.base.allocator);
329 self.dbg_line_fn_free_list.deinit(self.base.allocator);
330 self.dbg_info_decl_free_list.deinit(self.base.allocator);
331 self.offset_table.deinit(self.base.allocator);
332}
333
334pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 {
335 assert(decl.link.elf.local_sym_index != 0);
336 return self.local_symbols.items[decl.link.elf.local_sym_index].st_value;
337}
338
339fn getDebugLineProgramOff(self: Elf) u32 {
340 return self.dbg_line_fn_first.?.off;
341}
342
343fn getDebugLineProgramEnd(self: Elf) u32 {
344 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
345}
346
347/// Returns end pos of collision, if any.
348fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
349 const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;
350 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
351 if (start < ehdr_size)
352 return ehdr_size;
353
354 const end = start + satMul(size, alloc_num) / alloc_den;
355
356 if (self.shdr_table_offset) |off| {
357 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
358 const tight_size = self.sections.items.len * shdr_size;
359 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
360 const test_end = off + increased_size;
361 if (end > off and start < test_end) {
362 return test_end;
363 }
364 }
365
366 if (self.phdr_table_offset) |off| {
367 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
368 const tight_size = self.sections.items.len * phdr_size;
369 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
370 const test_end = off + increased_size;
371 if (end > off and start < test_end) {
372 return test_end;
373 }
374 }
375
376 for (self.sections.items) |section| {
377 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
378 const test_end = section.sh_offset + increased_size;
379 if (end > section.sh_offset and start < test_end) {
380 return test_end;
381 }
382 }
383 for (self.program_headers.items) |program_header| {
384 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
385 const test_end = program_header.p_offset + increased_size;
386 if (end > program_header.p_offset and start < test_end) {
387 return test_end;
388 }
389 }
390 return null;
391}
392
393fn allocatedSize(self: *Elf, start: u64) u64 {
394 if (start == 0)
395 return 0;
396 var min_pos: u64 = std.math.maxInt(u64);
397 if (self.shdr_table_offset) |off| {
398 if (off > start and off < min_pos) min_pos = off;
399 }
400 if (self.phdr_table_offset) |off| {
401 if (off > start and off < min_pos) min_pos = off;
402 }
403 for (self.sections.items) |section| {
404 if (section.sh_offset <= start) continue;
405 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
406 }
407 for (self.program_headers.items) |program_header| {
408 if (program_header.p_offset <= start) continue;
409 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
410 }
411 return min_pos - start;
412}
413
414fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
415 var start: u64 = 0;
416 while (self.detectAllocCollision(start, object_size)) |item_end| {
417 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
418 }
419 return start;
420}
421
422/// TODO Improve this to use a table.
423fn makeString(self: *Elf, bytes: []const u8) !u32 {
424 try self.shstrtab.ensureCapacity(self.base.allocator, self.shstrtab.items.len + bytes.len + 1);
425 const result = self.shstrtab.items.len;
426 self.shstrtab.appendSliceAssumeCapacity(bytes);
427 self.shstrtab.appendAssumeCapacity(0);
428 return @intCast(u32, result);
429}
430
431/// TODO Improve this to use a table.
432fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
433 try self.debug_strtab.ensureCapacity(self.base.allocator, self.debug_strtab.items.len + bytes.len + 1);
434 const result = self.debug_strtab.items.len;
435 self.debug_strtab.appendSliceAssumeCapacity(bytes);
436 self.debug_strtab.appendAssumeCapacity(0);
437 return @intCast(u32, result);
438}
439
440fn getString(self: *Elf, str_off: u32) []const u8 {
441 assert(str_off < self.shstrtab.items.len);
442 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
443}
444
445fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
446 const existing_name = self.getString(old_str_off);
447 if (mem.eql(u8, existing_name, new_name)) {
448 return old_str_off;
449 }
450 return self.makeString(new_name);
451}
452
453pub fn populateMissingMetadata(self: *Elf) !void {
454 const small_ptr = switch (self.ptr_width) {
455 .p32 => true,
456 .p64 => false,
457 };
458 const ptr_size: u8 = self.ptrWidthBytes();
459 if (self.phdr_load_re_index == null) {
460 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
461 const file_size = self.base.options.program_code_size_hint;
462 const p_align = 0x1000;
463 const off = self.findFreeSpace(file_size, p_align);
464 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
465 try self.program_headers.append(self.base.allocator, .{
466 .p_type = elf.PT_LOAD,
467 .p_offset = off,
468 .p_filesz = file_size,
469 .p_vaddr = default_entry_addr,
470 .p_paddr = default_entry_addr,
471 .p_memsz = file_size,
472 .p_align = p_align,
473 .p_flags = elf.PF_X | elf.PF_R,
474 });
475 self.entry_addr = null;
476 self.phdr_table_dirty = true;
477 }
478 if (self.phdr_got_index == null) {
479 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
480 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
481 // We really only need ptr alignment but since we are using PROGBITS, linux requires
482 // page align.
483 const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size);
484 const off = self.findFreeSpace(file_size, p_align);
485 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
486 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
487 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
488 // else in virtual memory.
489 const default_got_addr = if (ptr_size == 2) @as(u32, 0x8000) else 0x4000000;
490 try self.program_headers.append(self.base.allocator, .{
491 .p_type = elf.PT_LOAD,
492 .p_offset = off,
493 .p_filesz = file_size,
494 .p_vaddr = default_got_addr,
495 .p_paddr = default_got_addr,
496 .p_memsz = file_size,
497 .p_align = p_align,
498 .p_flags = elf.PF_R,
499 });
500 self.phdr_table_dirty = true;
501 }
502 if (self.shstrtab_index == null) {
503 self.shstrtab_index = @intCast(u16, self.sections.items.len);
504 assert(self.shstrtab.items.len == 0);
505 try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
506 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
507 log.debug("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
508 try self.sections.append(self.base.allocator, .{
509 .sh_name = try self.makeString(".shstrtab"),
510 .sh_type = elf.SHT_STRTAB,
511 .sh_flags = 0,
512 .sh_addr = 0,
513 .sh_offset = off,
514 .sh_size = self.shstrtab.items.len,
515 .sh_link = 0,
516 .sh_info = 0,
517 .sh_addralign = 1,
518 .sh_entsize = 0,
519 });
520 self.shstrtab_dirty = true;
521 self.shdr_table_dirty = true;
522 }
523 if (self.text_section_index == null) {
524 self.text_section_index = @intCast(u16, self.sections.items.len);
525 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
526
527 try self.sections.append(self.base.allocator, .{
528 .sh_name = try self.makeString(".text"),
529 .sh_type = elf.SHT_PROGBITS,
530 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
531 .sh_addr = phdr.p_vaddr,
532 .sh_offset = phdr.p_offset,
533 .sh_size = phdr.p_filesz,
534 .sh_link = 0,
535 .sh_info = 0,
536 .sh_addralign = phdr.p_align,
537 .sh_entsize = 0,
538 });
539 self.shdr_table_dirty = true;
540 }
541 if (self.got_section_index == null) {
542 self.got_section_index = @intCast(u16, self.sections.items.len);
543 const phdr = &self.program_headers.items[self.phdr_got_index.?];
544
545 try self.sections.append(self.base.allocator, .{
546 .sh_name = try self.makeString(".got"),
547 .sh_type = elf.SHT_PROGBITS,
548 .sh_flags = elf.SHF_ALLOC,
549 .sh_addr = phdr.p_vaddr,
550 .sh_offset = phdr.p_offset,
551 .sh_size = phdr.p_filesz,
552 .sh_link = 0,
553 .sh_info = 0,
554 .sh_addralign = phdr.p_align,
555 .sh_entsize = 0,
556 });
557 self.shdr_table_dirty = true;
558 }
559 if (self.symtab_section_index == null) {
560 self.symtab_section_index = @intCast(u16, self.sections.items.len);
561 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
562 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
563 const file_size = self.base.options.symbol_count_hint * each_size;
564 const off = self.findFreeSpace(file_size, min_align);
565 log.debug("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
566
567 try self.sections.append(self.base.allocator, .{
568 .sh_name = try self.makeString(".symtab"),
569 .sh_type = elf.SHT_SYMTAB,
570 .sh_flags = 0,
571 .sh_addr = 0,
572 .sh_offset = off,
573 .sh_size = file_size,
574 // The section header index of the associated string table.
575 .sh_link = self.shstrtab_index.?,
576 .sh_info = @intCast(u32, self.local_symbols.items.len),
577 .sh_addralign = min_align,
578 .sh_entsize = each_size,
579 });
580 self.shdr_table_dirty = true;
581 try self.writeSymbol(0);
582 }
583 if (self.debug_str_section_index == null) {
584 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
585 assert(self.debug_strtab.items.len == 0);
586 try self.sections.append(self.base.allocator, .{
587 .sh_name = try self.makeString(".debug_str"),
588 .sh_type = elf.SHT_PROGBITS,
589 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
590 .sh_addr = 0,
591 .sh_offset = 0,
592 .sh_size = self.debug_strtab.items.len,
593 .sh_link = 0,
594 .sh_info = 0,
595 .sh_addralign = 1,
596 .sh_entsize = 1,
597 });
598 self.debug_strtab_dirty = true;
599 self.shdr_table_dirty = true;
600 }
601 if (self.debug_info_section_index == null) {
602 self.debug_info_section_index = @intCast(u16, self.sections.items.len);
603
604 const file_size_hint = 200;
605 const p_align = 1;
606 const off = self.findFreeSpace(file_size_hint, p_align);
607 log.debug("found .debug_info free space 0x{x} to 0x{x}\n", .{
608 off,
609 off + file_size_hint,
610 });
611 try self.sections.append(self.base.allocator, .{
612 .sh_name = try self.makeString(".debug_info"),
613 .sh_type = elf.SHT_PROGBITS,
614 .sh_flags = 0,
615 .sh_addr = 0,
616 .sh_offset = off,
617 .sh_size = file_size_hint,
618 .sh_link = 0,
619 .sh_info = 0,
620 .sh_addralign = p_align,
621 .sh_entsize = 0,
622 });
623 self.shdr_table_dirty = true;
624 self.debug_info_header_dirty = true;
625 }
626 if (self.debug_abbrev_section_index == null) {
627 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
628
629 const file_size_hint = 128;
630 const p_align = 1;
631 const off = self.findFreeSpace(file_size_hint, p_align);
632 log.debug("found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
633 off,
634 off + file_size_hint,
635 });
636 try self.sections.append(self.base.allocator, .{
637 .sh_name = try self.makeString(".debug_abbrev"),
638 .sh_type = elf.SHT_PROGBITS,
639 .sh_flags = 0,
640 .sh_addr = 0,
641 .sh_offset = off,
642 .sh_size = file_size_hint,
643 .sh_link = 0,
644 .sh_info = 0,
645 .sh_addralign = p_align,
646 .sh_entsize = 0,
647 });
648 self.shdr_table_dirty = true;
649 self.debug_abbrev_section_dirty = true;
650 }
651 if (self.debug_aranges_section_index == null) {
652 self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);
653
654 const file_size_hint = 160;
655 const p_align = 16;
656 const off = self.findFreeSpace(file_size_hint, p_align);
657 log.debug("found .debug_aranges free space 0x{x} to 0x{x}\n", .{
658 off,
659 off + file_size_hint,
660 });
661 try self.sections.append(self.base.allocator, .{
662 .sh_name = try self.makeString(".debug_aranges"),
663 .sh_type = elf.SHT_PROGBITS,
664 .sh_flags = 0,
665 .sh_addr = 0,
666 .sh_offset = off,
667 .sh_size = file_size_hint,
668 .sh_link = 0,
669 .sh_info = 0,
670 .sh_addralign = p_align,
671 .sh_entsize = 0,
672 });
673 self.shdr_table_dirty = true;
674 self.debug_aranges_section_dirty = true;
675 }
676 if (self.debug_line_section_index == null) {
677 self.debug_line_section_index = @intCast(u16, self.sections.items.len);
678
679 const file_size_hint = 250;
680 const p_align = 1;
681 const off = self.findFreeSpace(file_size_hint, p_align);
682 log.debug("found .debug_line free space 0x{x} to 0x{x}\n", .{
683 off,
684 off + file_size_hint,
685 });
686 try self.sections.append(self.base.allocator, .{
687 .sh_name = try self.makeString(".debug_line"),
688 .sh_type = elf.SHT_PROGBITS,
689 .sh_flags = 0,
690 .sh_addr = 0,
691 .sh_offset = off,
692 .sh_size = file_size_hint,
693 .sh_link = 0,
694 .sh_info = 0,
695 .sh_addralign = p_align,
696 .sh_entsize = 0,
697 });
698 self.shdr_table_dirty = true;
699 self.debug_line_header_dirty = true;
700 }
701 const shsize: u64 = switch (self.ptr_width) {
702 .p32 => @sizeOf(elf.Elf32_Shdr),
703 .p64 => @sizeOf(elf.Elf64_Shdr),
704 };
705 const shalign: u16 = switch (self.ptr_width) {
706 .p32 => @alignOf(elf.Elf32_Shdr),
707 .p64 => @alignOf(elf.Elf64_Shdr),
708 };
709 if (self.shdr_table_offset == null) {
710 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
711 self.shdr_table_dirty = true;
712 }
713 const phsize: u64 = switch (self.ptr_width) {
714 .p32 => @sizeOf(elf.Elf32_Phdr),
715 .p64 => @sizeOf(elf.Elf64_Phdr),
716 };
717 const phalign: u16 = switch (self.ptr_width) {
718 .p32 => @alignOf(elf.Elf32_Phdr),
719 .p64 => @alignOf(elf.Elf64_Phdr),
720 };
721 if (self.phdr_table_offset == null) {
722 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
723 self.phdr_table_dirty = true;
724 }
725 {
726 // Iterate over symbols, populating free_list and last_text_block.
727 if (self.local_symbols.items.len != 1) {
728 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
729 }
730 // We are starting with an empty file. The default values are correct, null and empty list.
731 }
732}
733
734pub const abbrev_compile_unit = 1;
735pub const abbrev_subprogram = 2;
736pub const abbrev_subprogram_retvoid = 3;
737pub const abbrev_base_type = 4;
738pub const abbrev_pad1 = 5;
739pub const abbrev_parameter = 6;
740
741/// Commit pending changes and write headers.
742pub fn flush(self: *Elf, module: *Module) !void {
743 const target_endian = self.base.options.target.cpu.arch.endian();
744 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
745 const ptr_width_bytes: u8 = self.ptrWidthBytes();
746 const init_len_size: usize = switch (self.ptr_width) {
747 .p32 => 4,
748 .p64 => 12,
749 };
750
751 // Unfortunately these have to be buffered and done at the end because ELF does not allow
752 // mixing local and global symbols within a symbol table.
753 try self.writeAllGlobalSymbols();
754
755 if (self.debug_abbrev_section_dirty) {
756 const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];
757
758 // These are LEB encoded but since the values are all less than 127
759 // we can simply append these bytes.
760 const abbrev_buf = [_]u8{
761 abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
762 DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc,
763 DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr,
764 DW.AT_name, DW.FORM_strp, DW.AT_comp_dir,
765 DW.FORM_strp, DW.AT_producer, DW.FORM_strp,
766 DW.AT_language, DW.FORM_data2, 0,
767 0, // table sentinel
768 abbrev_subprogram, DW.TAG_subprogram,
769 DW.CHILDREN_yes, // header
770 DW.AT_low_pc, DW.FORM_addr,
771 DW.AT_high_pc, DW.FORM_data4, DW.AT_type,
772 DW.FORM_ref4, DW.AT_name, DW.FORM_string,
773 0, 0, // table sentinel
774 abbrev_subprogram_retvoid,
775 DW.TAG_subprogram, DW.CHILDREN_yes, // header
776 DW.AT_low_pc,
777 DW.FORM_addr, DW.AT_high_pc, DW.FORM_data4,
778 DW.AT_name, DW.FORM_string, 0,
779 0, // table sentinel
780 abbrev_base_type, DW.TAG_base_type,
781 DW.CHILDREN_no, // header
782 DW.AT_encoding, DW.FORM_data1,
783 DW.AT_byte_size, DW.FORM_data1, DW.AT_name,
784 DW.FORM_string, 0, 0, // table sentinel
785
786 abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
787 0, 0, // table sentinel
788 abbrev_parameter,
789 DW.TAG_formal_parameter, DW.CHILDREN_no, // header
790 DW.AT_location,
791 DW.FORM_exprloc, DW.AT_type, DW.FORM_ref4,
792 DW.AT_name, DW.FORM_string, 0,
793 0, // table sentinel
794 0, 0,
795 0, // section sentinel
796 };
797
798 const needed_size = abbrev_buf.len;
799 const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
800 if (needed_size > allocated_size) {
801 debug_abbrev_sect.sh_size = 0; // free the space
802 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
803 }
804 debug_abbrev_sect.sh_size = needed_size;
805 log.debug(".debug_abbrev start=0x{x} end=0x{x}\n", .{
806 debug_abbrev_sect.sh_offset,
807 debug_abbrev_sect.sh_offset + needed_size,
808 });
809
810 const abbrev_offset = 0;
811 self.debug_abbrev_table_offset = abbrev_offset;
812 try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
813 if (!self.shdr_table_dirty) {
814 // Then it won't get written with the others and we need to do it.
815 try self.writeSectHeader(self.debug_abbrev_section_index.?);
816 }
817
818 self.debug_abbrev_section_dirty = false;
819 }
820
821 if (self.debug_info_header_dirty) debug_info: {
822 // If this value is null it means there is an error in the module;
823 // leave debug_info_header_dirty=true.
824 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
825 const last_dbg_info_decl = self.dbg_info_decl_last.?;
826 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
827
828 var di_buf = std.ArrayList(u8).init(self.base.allocator);
829 defer di_buf.deinit();
830
831 // We have a function to compute the upper bound size, because it's needed
832 // for determining where to put the offset of the first `LinkBlock`.
833 try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes());
834
835 // initial length - length of the .debug_info contribution for this compilation unit,
836 // not including the initial length itself.
837 // We have to come back and write it later after we know the size.
838 const after_init_len = di_buf.items.len + init_len_size;
839 // +1 for the final 0 that ends the compilation unit children.
840 const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
841 const init_len = dbg_info_end - after_init_len;
842 switch (self.ptr_width) {
843 .p32 => {
844 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
845 },
846 .p64 => {
847 di_buf.appendNTimesAssumeCapacity(0xff, 4);
848 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
849 },
850 }
851 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
852 const abbrev_offset = self.debug_abbrev_table_offset.?;
853 switch (self.ptr_width) {
854 .p32 => {
855 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
856 di_buf.appendAssumeCapacity(4); // address size
857 },
858 .p64 => {
859 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
860 di_buf.appendAssumeCapacity(8); // address size
861 },
862 }
863 // Write the form for the compile unit, which must match the abbrev table above.
864 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
865 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);
866 const producer_strp = try self.makeDebugString(producer_string);
867 // Currently only one compilation unit is supported, so the address range is simply
868 // identical to the main program header virtual address and memory size.
869 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
870 const low_pc = text_phdr.p_vaddr;
871 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
872
873 di_buf.appendAssumeCapacity(abbrev_compile_unit);
874 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
875 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
876 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
877 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
878 self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
879 self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
880 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
881 // http://dwarfstd.org/ShowIssue.php?issue=171115.1
882 // Until then we say it is C99.
883 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
884
885 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
886 // Move the first N decls to the end to make more padding for the header.
887 @panic("TODO: handle .debug_info header exceeding its padding");
888 }
889 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
890 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset);
891 self.debug_info_header_dirty = false;
892 }
893
894 if (self.debug_aranges_section_dirty) {
895 const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
896
897 var di_buf = std.ArrayList(u8).init(self.base.allocator);
898 defer di_buf.deinit();
899
900 // Enough for all the data without resizing. When support for more compilation units
901 // is added, the size of this section will become more variable.
902 try di_buf.ensureCapacity(100);
903
904 // initial length - length of the .debug_aranges contribution for this compilation unit,
905 // not including the initial length itself.
906 // We have to come back and write it later after we know the size.
907 const init_len_index = di_buf.items.len;
908 di_buf.items.len += init_len_size;
909 const after_init_len = di_buf.items.len;
910 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
911 // When more than one compilation unit is supported, this will be the offset to it.
912 // For now it is always at offset 0 in .debug_info.
913 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset
914 di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
915 di_buf.appendAssumeCapacity(0); // segment_selector_size
916
917 const end_header_offset = di_buf.items.len;
918 const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2);
919 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
920
921 // Currently only one compilation unit is supported, so the address range is simply
922 // identical to the main program header virtual address and memory size.
923 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
924 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr);
925 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz);
926
927 // Sentinel.
928 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
929 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
930
931 // Go back and populate the initial length.
932 const init_len = di_buf.items.len - after_init_len;
933 switch (self.ptr_width) {
934 .p32 => {
935 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
936 },
937 .p64 => {
938 // initial length - length of the .debug_aranges contribution for this compilation unit,
939 // not including the initial length itself.
940 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
941 mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian);
942 },
943 }
944
945 const needed_size = di_buf.items.len;
946 const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset);
947 if (needed_size > allocated_size) {
948 debug_aranges_sect.sh_size = 0; // free the space
949 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
950 }
951 debug_aranges_sect.sh_size = needed_size;
952 log.debug(".debug_aranges start=0x{x} end=0x{x}\n", .{
953 debug_aranges_sect.sh_offset,
954 debug_aranges_sect.sh_offset + needed_size,
955 });
956
957 try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
958 if (!self.shdr_table_dirty) {
959 // Then it won't get written with the others and we need to do it.
960 try self.writeSectHeader(self.debug_aranges_section_index.?);
961 }
962
963 self.debug_aranges_section_dirty = false;
964 }
965 if (self.debug_line_header_dirty) debug_line: {
966 if (self.dbg_line_fn_first == null) {
967 break :debug_line; // Error in module; leave debug_line_header_dirty=true.
968 }
969 const dbg_line_prg_off = self.getDebugLineProgramOff();
970 const dbg_line_prg_end = self.getDebugLineProgramEnd();
971 assert(dbg_line_prg_end != 0);
972
973 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
974
975 var di_buf = std.ArrayList(u8).init(self.base.allocator);
976 defer di_buf.deinit();
977
978 // The size of this header is variable, depending on the number of directories,
979 // files, and padding. We have a function to compute the upper bound size, however,
980 // because it's needed for determining where to put the offset of the first `SrcFn`.
981 try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes());
982
983 // initial length - length of the .debug_line contribution for this compilation unit,
984 // not including the initial length itself.
985 const after_init_len = di_buf.items.len + init_len_size;
986 const init_len = dbg_line_prg_end - after_init_len;
987 switch (self.ptr_width) {
988 .p32 => {
989 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
990 },
991 .p64 => {
992 di_buf.appendNTimesAssumeCapacity(0xff, 4);
993 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
994 },
995 }
996
997 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
998
999 // Empirically, debug info consumers do not respect this field, or otherwise
1000 // consider it to be an error when it does not point exactly to the end of the header.
1001 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
1002 // padding rather than this field.
1003 const before_header_len = di_buf.items.len;
1004 di_buf.items.len += ptr_width_bytes; // We will come back and write this.
1005 const after_header_len = di_buf.items.len;
1006
1007 const opcode_base = DW.LNS_set_isa + 1;
1008 di_buf.appendSliceAssumeCapacity(&[_]u8{
1009 1, // minimum_instruction_length
1010 1, // maximum_operations_per_instruction
1011 1, // default_is_stmt
1012 1, // line_base (signed)
1013 1, // line_range
1014 opcode_base,
1015
1016 // Standard opcode lengths. The number of items here is based on `opcode_base`.
1017 // The value is the number of LEB128 operands the instruction takes.
1018 0, // `DW.LNS_copy`
1019 1, // `DW.LNS_advance_pc`
1020 1, // `DW.LNS_advance_line`
1021 1, // `DW.LNS_set_file`
1022 1, // `DW.LNS_set_column`
1023 0, // `DW.LNS_negate_stmt`
1024 0, // `DW.LNS_set_basic_block`
1025 0, // `DW.LNS_const_add_pc`
1026 1, // `DW.LNS_fixed_advance_pc`
1027 0, // `DW.LNS_set_prologue_end`
1028 0, // `DW.LNS_set_epilogue_begin`
1029 1, // `DW.LNS_set_isa`
1030
1031 0, // include_directories (none except the compilation unit cwd)
1032 });
1033 // file_names[0]
1034 di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.root_src_path); // relative path name
1035 di_buf.appendSliceAssumeCapacity(&[_]u8{
1036 0, // null byte for the relative path name
1037 0, // directory_index
1038 0, // mtime (TODO supply this)
1039 0, // file size bytes (TODO supply this)
1040 0, // file_names sentinel
1041 });
1042
1043 const header_len = di_buf.items.len - after_header_len;
1044 switch (self.ptr_width) {
1045 .p32 => {
1046 mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian);
1047 },
1048 .p64 => {
1049 mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian);
1050 },
1051 }
1052
1053 // We use NOPs because consumers empirically do not respect the header length field.
1054 if (di_buf.items.len > dbg_line_prg_off) {
1055 // Move the first N files to the end to make more padding for the header.
1056 @panic("TODO: handle .debug_line header exceeding its padding");
1057 }
1058 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
1059 try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
1060 self.debug_line_header_dirty = false;
1061 }
1062
1063 if (self.phdr_table_dirty) {
1064 const phsize: u64 = switch (self.ptr_width) {
1065 .p32 => @sizeOf(elf.Elf32_Phdr),
1066 .p64 => @sizeOf(elf.Elf64_Phdr),
1067 };
1068 const phalign: u16 = switch (self.ptr_width) {
1069 .p32 => @alignOf(elf.Elf32_Phdr),
1070 .p64 => @alignOf(elf.Elf64_Phdr),
1071 };
1072 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
1073 const needed_size = self.program_headers.items.len * phsize;
1074
1075 if (needed_size > allocated_size) {
1076 self.phdr_table_offset = null; // free the space
1077 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
1078 }
1079
1080 switch (self.ptr_width) {
1081 .p32 => {
1082 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1083 defer self.base.allocator.free(buf);
1084
1085 for (buf) |*phdr, i| {
1086 phdr.* = progHeaderTo32(self.program_headers.items[i]);
1087 if (foreign_endian) {
1088 bswapAllFields(elf.Elf32_Phdr, phdr);
1089 }
1090 }
1091 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1092 },
1093 .p64 => {
1094 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1095 defer self.base.allocator.free(buf);
1096
1097 for (buf) |*phdr, i| {
1098 phdr.* = self.program_headers.items[i];
1099 if (foreign_endian) {
1100 bswapAllFields(elf.Elf64_Phdr, phdr);
1101 }
1102 }
1103 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1104 },
1105 }
1106 self.phdr_table_dirty = false;
1107 }
1108
1109 {
1110 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
1111 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
1112 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
1113 const needed_size = self.shstrtab.items.len;
1114
1115 if (needed_size > allocated_size) {
1116 shstrtab_sect.sh_size = 0; // free the space
1117 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1118 }
1119 shstrtab_sect.sh_size = needed_size;
1120 log.debug("writing shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
1121
1122 try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
1123 if (!self.shdr_table_dirty) {
1124 // Then it won't get written with the others and we need to do it.
1125 try self.writeSectHeader(self.shstrtab_index.?);
1126 }
1127 self.shstrtab_dirty = false;
1128 }
1129 }
1130 {
1131 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
1132 if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {
1133 const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
1134 const needed_size = self.debug_strtab.items.len;
1135
1136 if (needed_size > allocated_size) {
1137 debug_strtab_sect.sh_size = 0; // free the space
1138 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1139 }
1140 debug_strtab_sect.sh_size = needed_size;
1141 log.debug("debug_strtab start=0x{x} end=0x{x}\n", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
1142
1143 try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
1144 if (!self.shdr_table_dirty) {
1145 // Then it won't get written with the others and we need to do it.
1146 try self.writeSectHeader(self.debug_str_section_index.?);
1147 }
1148 self.debug_strtab_dirty = false;
1149 }
1150 }
1151 if (self.shdr_table_dirty) {
1152 const shsize: u64 = switch (self.ptr_width) {
1153 .p32 => @sizeOf(elf.Elf32_Shdr),
1154 .p64 => @sizeOf(elf.Elf64_Shdr),
1155 };
1156 const shalign: u16 = switch (self.ptr_width) {
1157 .p32 => @alignOf(elf.Elf32_Shdr),
1158 .p64 => @alignOf(elf.Elf64_Shdr),
1159 };
1160 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1161 const needed_size = self.sections.items.len * shsize;
1162
1163 if (needed_size > allocated_size) {
1164 self.shdr_table_offset = null; // free the space
1165 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
1166 }
1167
1168 switch (self.ptr_width) {
1169 .p32 => {
1170 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
1171 defer self.base.allocator.free(buf);
1172
1173 for (buf) |*shdr, i| {
1174 shdr.* = sectHeaderTo32(self.sections.items[i]);
1175 log.debug("writing section {}\n", .{shdr.*});
1176 if (foreign_endian) {
1177 bswapAllFields(elf.Elf32_Shdr, shdr);
1178 }
1179 }
1180 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1181 },
1182 .p64 => {
1183 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
1184 defer self.base.allocator.free(buf);
1185
1186 for (buf) |*shdr, i| {
1187 shdr.* = self.sections.items[i];
1188 log.debug("writing section {}\n", .{shdr.*});
1189 if (foreign_endian) {
1190 bswapAllFields(elf.Elf64_Shdr, shdr);
1191 }
1192 }
1193 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1194 },
1195 }
1196 self.shdr_table_dirty = false;
1197 }
1198 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
1199 log.debug("flushing. no_entry_point_found = true\n", .{});
1200 self.error_flags.no_entry_point_found = true;
1201 } else {
1202 log.debug("flushing. no_entry_point_found = false\n", .{});
1203 self.error_flags.no_entry_point_found = false;
1204 try self.writeElfHeader();
1205 }
1206
1207 // The point of flush() is to commit changes, so in theory, nothing should
1208 // be dirty after this. However, it is possible for some things to remain
1209 // dirty because they fail to be written in the event of compile errors,
1210 // such as debug_line_header_dirty and debug_info_header_dirty.
1211 assert(!self.debug_abbrev_section_dirty);
1212 assert(!self.debug_aranges_section_dirty);
1213 assert(!self.phdr_table_dirty);
1214 assert(!self.shdr_table_dirty);
1215 assert(!self.shstrtab_dirty);
1216 assert(!self.debug_strtab_dirty);
1217}
1218
1219fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
1220 const target_endian = self.base.options.target.cpu.arch.endian();
1221 switch (self.ptr_width) {
1222 .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian),
1223 .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
1224 }
1225}
1226
1227fn writeElfHeader(self: *Elf) !void {
1228 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
1229
1230 var index: usize = 0;
1231 hdr_buf[0..4].* = "\x7fELF".*;
1232 index += 4;
1233
1234 hdr_buf[index] = switch (self.ptr_width) {
1235 .p32 => elf.ELFCLASS32,
1236 .p64 => elf.ELFCLASS64,
1237 };
1238 index += 1;
1239
1240 const endian = self.base.options.target.cpu.arch.endian();
1241 hdr_buf[index] = switch (endian) {
1242 .Little => elf.ELFDATA2LSB,
1243 .Big => elf.ELFDATA2MSB,
1244 };
1245 index += 1;
1246
1247 hdr_buf[index] = 1; // ELF version
1248 index += 1;
1249
1250 // OS ABI, often set to 0 regardless of target platform
1251 // ABI Version, possibly used by glibc but not by static executables
1252 // padding
1253 mem.set(u8, hdr_buf[index..][0..9], 0);
1254 index += 9;
1255
1256 assert(index == 16);
1257
1258 const elf_type = switch (self.base.options.output_mode) {
1259 .Exe => elf.ET.EXEC,
1260 .Obj => elf.ET.REL,
1261 .Lib => switch (self.base.options.link_mode) {
1262 .Static => elf.ET.REL,
1263 .Dynamic => elf.ET.DYN,
1264 },
1265 };
1266 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
1267 index += 2;
1268
1269 const machine = self.base.options.target.cpu.arch.toElfMachine();
1270 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
1271 index += 2;
1272
1273 // ELF Version, again
1274 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
1275 index += 4;
1276
1277 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
1278
1279 switch (self.ptr_width) {
1280 .p32 => {
1281 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
1282 index += 4;
1283
1284 // e_phoff
1285 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
1286 index += 4;
1287
1288 // e_shoff
1289 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
1290 index += 4;
1291 },
1292 .p64 => {
1293 // e_entry
1294 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
1295 index += 8;
1296
1297 // e_phoff
1298 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
1299 index += 8;
1300
1301 // e_shoff
1302 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
1303 index += 8;
1304 },
1305 }
1306
1307 const e_flags = 0;
1308 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
1309 index += 4;
1310
1311 const e_ehsize: u16 = switch (self.ptr_width) {
1312 .p32 => @sizeOf(elf.Elf32_Ehdr),
1313 .p64 => @sizeOf(elf.Elf64_Ehdr),
1314 };
1315 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
1316 index += 2;
1317
1318 const e_phentsize: u16 = switch (self.ptr_width) {
1319 .p32 => @sizeOf(elf.Elf32_Phdr),
1320 .p64 => @sizeOf(elf.Elf64_Phdr),
1321 };
1322 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
1323 index += 2;
1324
1325 const e_phnum = @intCast(u16, self.program_headers.items.len);
1326 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
1327 index += 2;
1328
1329 const e_shentsize: u16 = switch (self.ptr_width) {
1330 .p32 => @sizeOf(elf.Elf32_Shdr),
1331 .p64 => @sizeOf(elf.Elf64_Shdr),
1332 };
1333 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
1334 index += 2;
1335
1336 const e_shnum = @intCast(u16, self.sections.items.len);
1337 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
1338 index += 2;
1339
1340 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
1341 index += 2;
1342
1343 assert(index == e_ehsize);
1344
1345 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
1346}
1347
1348fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
1349 var already_have_free_list_node = false;
1350 {
1351 var i: usize = 0;
1352 while (i < self.text_block_free_list.items.len) {
1353 if (self.text_block_free_list.items[i] == text_block) {
1354 _ = self.text_block_free_list.swapRemove(i);
1355 continue;
1356 }
1357 if (self.text_block_free_list.items[i] == text_block.prev) {
1358 already_have_free_list_node = true;
1359 }
1360 i += 1;
1361 }
1362 }
1363
1364 if (self.last_text_block == text_block) {
1365 // TODO shrink the .text section size here
1366 self.last_text_block = text_block.prev;
1367 }
1368
1369 if (text_block.prev) |prev| {
1370 prev.next = text_block.next;
1371
1372 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
1373 // The free list is heuristics, it doesn't have to be perfect, so we can
1374 // ignore the OOM here.
1375 self.text_block_free_list.append(self.base.allocator, prev) catch {};
1376 }
1377 } else {
1378 text_block.prev = null;
1379 }
1380
1381 if (text_block.next) |next| {
1382 next.prev = text_block.prev;
1383 } else {
1384 text_block.next = null;
1385 }
1386}
1387
1388fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
1389 // TODO check the new capacity, and if it crosses the size threshold into a big enough
1390 // capacity, insert a free list node for it.
1391}
1392
1393fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1394 const sym = self.local_symbols.items[text_block.local_sym_index];
1395 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
1396 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
1397 if (!need_realloc) return sym.st_value;
1398 return self.allocateTextBlock(text_block, new_block_size, alignment);
1399}
1400
1401fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1402 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1403 const shdr = &self.sections.items[self.text_section_index.?];
1404 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
1405
1406 // We use these to indicate our intention to update metadata, placing the new block,
1407 // and possibly removing a free list node.
1408 // It would be simpler to do it inside the for loop below, but that would cause a
1409 // problem if an error was returned later in the function. So this action
1410 // is actually carried out at the end of the function, when errors are no longer possible.
1411 var block_placement: ?*TextBlock = null;
1412 var free_list_removal: ?usize = null;
1413
1414 // First we look for an appropriately sized free list node.
1415 // The list is unordered. We'll just take the first thing that works.
1416 const vaddr = blk: {
1417 var i: usize = 0;
1418 while (i < self.text_block_free_list.items.len) {
1419 const big_block = self.text_block_free_list.items[i];
1420 // We now have a pointer to a live text block that has too much capacity.
1421 // Is it enough that we could fit this new text block?
1422 const sym = self.local_symbols.items[big_block.local_sym_index];
1423 const capacity = big_block.capacity(self.*);
1424 const ideal_capacity = capacity * alloc_num / alloc_den;
1425 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1426 const capacity_end_vaddr = sym.st_value + capacity;
1427 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
1428 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
1429 if (new_start_vaddr < ideal_capacity_end_vaddr) {
1430 // Additional bookkeeping here to notice if this free list node
1431 // should be deleted because the block that it points to has grown to take up
1432 // more of the extra capacity.
1433 if (!big_block.freeListEligible(self.*)) {
1434 _ = self.text_block_free_list.swapRemove(i);
1435 } else {
1436 i += 1;
1437 }
1438 continue;
1439 }
1440 // At this point we know that we will place the new block here. But the
1441 // remaining question is whether there is still yet enough capacity left
1442 // over for there to still be a free list node.
1443 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
1444 const keep_free_list_node = remaining_capacity >= min_text_capacity;
1445
1446 // Set up the metadata to be updated, after errors are no longer possible.
1447 block_placement = big_block;
1448 if (!keep_free_list_node) {
1449 free_list_removal = i;
1450 }
1451 break :blk new_start_vaddr;
1452 } else if (self.last_text_block) |last| {
1453 const sym = self.local_symbols.items[last.local_sym_index];
1454 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
1455 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1456 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
1457 // Set up the metadata to be updated, after errors are no longer possible.
1458 block_placement = last;
1459 break :blk new_start_vaddr;
1460 } else {
1461 break :blk phdr.p_vaddr;
1462 }
1463 };
1464
1465 const expand_text_section = block_placement == null or block_placement.?.next == null;
1466 if (expand_text_section) {
1467 const text_capacity = self.allocatedSize(shdr.sh_offset);
1468 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
1469 if (needed_size > text_capacity) {
1470 // Must move the entire text section.
1471 const new_offset = self.findFreeSpace(needed_size, 0x1000);
1472 const text_size = if (self.last_text_block) |last| blk: {
1473 const sym = self.local_symbols.items[last.local_sym_index];
1474 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
1475 } else 0;
1476 const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size);
1477 if (amt != text_size) return error.InputOutput;
1478 shdr.sh_offset = new_offset;
1479 phdr.p_offset = new_offset;
1480 }
1481 self.last_text_block = text_block;
1482
1483 shdr.sh_size = needed_size;
1484 phdr.p_memsz = needed_size;
1485 phdr.p_filesz = needed_size;
1486
1487 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
1488 // range of the compilation unit. When we expand the text section, this range changes,
1489 // so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty.
1490 self.debug_info_header_dirty = true;
1491 // This becomes dirty for the same reason. We could potentially make this more
1492 // fine-grained with the addition of support for more compilation units. It is planned to
1493 // model each package as a different compilation unit.
1494 self.debug_aranges_section_dirty = true;
1495
1496 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1497 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1498 }
1499
1500 // This function can also reallocate a text block.
1501 // In this case we need to "unplug" it from its previous location before
1502 // plugging it in to its new location.
1503 if (text_block.prev) |prev| {
1504 prev.next = text_block.next;
1505 }
1506 if (text_block.next) |next| {
1507 next.prev = text_block.prev;
1508 }
1509
1510 if (block_placement) |big_block| {
1511 text_block.prev = big_block;
1512 text_block.next = big_block.next;
1513 big_block.next = text_block;
1514 } else {
1515 text_block.prev = null;
1516 text_block.next = null;
1517 }
1518 if (free_list_removal) |i| {
1519 _ = self.text_block_free_list.swapRemove(i);
1520 }
1521 return vaddr;
1522}
1523
1524pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1525 if (decl.link.elf.local_sym_index != 0) return;
1526
1527 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
1528 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
1529
1530 if (self.local_symbol_free_list.popOrNull()) |i| {
1531 log.debug("reusing symbol index {} for {}\n", .{ i, decl.name });
1532 decl.link.elf.local_sym_index = i;
1533 } else {
1534 log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
1535 decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1536 _ = self.local_symbols.addOneAssumeCapacity();
1537 }
1538
1539 if (self.offset_table_free_list.popOrNull()) |i| {
1540 decl.link.elf.offset_table_index = i;
1541 } else {
1542 decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
1543 _ = self.offset_table.addOneAssumeCapacity();
1544 self.offset_table_count_dirty = true;
1545 }
1546
1547 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1548
1549 self.local_symbols.items[decl.link.elf.local_sym_index] = .{
1550 .st_name = 0,
1551 .st_info = 0,
1552 .st_other = 0,
1553 .st_shndx = 0,
1554 .st_value = phdr.p_vaddr,
1555 .st_size = 0,
1556 };
1557 self.offset_table.items[decl.link.elf.offset_table_index] = 0;
1558}
1559
1560pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1561 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1562 self.freeTextBlock(&decl.link.elf);
1563 if (decl.link.elf.local_sym_index != 0) {
1564 self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
1565 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
1566
1567 self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
1568
1569 decl.link.elf.local_sym_index = 0;
1570 }
1571 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
1572 // is desired for both.
1573 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);
1574 if (decl.fn_link.elf.prev) |prev| {
1575 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
1576 prev.next = decl.fn_link.elf.next;
1577 if (decl.fn_link.elf.next) |next| {
1578 next.prev = prev;
1579 } else {
1580 self.dbg_line_fn_last = prev;
1581 }
1582 } else if (decl.fn_link.elf.next) |next| {
1583 self.dbg_line_fn_first = next;
1584 next.prev = null;
1585 }
1586 if (self.dbg_line_fn_first == &decl.fn_link.elf) {
1587 self.dbg_line_fn_first = null;
1588 }
1589 if (self.dbg_line_fn_last == &decl.fn_link.elf) {
1590 self.dbg_line_fn_last = null;
1591 }
1592}
1593
1594pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1595 const tracy = trace(@src());
1596 defer tracy.end();
1597
1598 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
1599 defer code_buffer.deinit();
1600
1601 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
1602 defer dbg_line_buffer.deinit();
1603
1604 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
1605 defer dbg_info_buffer.deinit();
1606
1607 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
1608 defer {
1609 for (dbg_info_type_relocs.items()) |*entry| {
1610 entry.value.relocs.deinit(self.base.allocator);
1611 }
1612 dbg_info_type_relocs.deinit(self.base.allocator);
1613 }
1614
1615 const typed_value = decl.typed_value.most_recent.typed_value;
1616 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
1617 .Fn => true,
1618 else => false,
1619 };
1620 if (is_fn) {
1621 //if (mem.eql(u8, mem.spanZ(decl.name), "add")) {
1622 // typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
1623 //}
1624
1625 // For functions we need to add a prologue to the debug line program.
1626 try dbg_line_buffer.ensureCapacity(26);
1627
1628 const line_off: u28 = blk: {
1629 if (decl.scope.cast(Module.Scope.File)) |scope_file| {
1630 const tree = scope_file.contents.tree;
1631 const file_ast_decls = tree.root_node.decls();
1632 // TODO Look into improving the performance here by adding a token-index-to-line
1633 // lookup table. Currently this involves scanning over the source code for newlines.
1634 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1635 const block = fn_proto.body().?.castTag(.Block).?;
1636 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1637 break :blk @intCast(u28, line_delta);
1638 } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
1639 const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
1640 const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
1641 break :blk @intCast(u28, line_delta);
1642 } else {
1643 unreachable;
1644 }
1645 };
1646
1647 const ptr_width_bytes = self.ptrWidthBytes();
1648 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
1649 DW.LNS_extended_op,
1650 ptr_width_bytes + 1,
1651 DW.LNE_set_address,
1652 });
1653 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
1654 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
1655 dbg_line_buffer.items.len += ptr_width_bytes;
1656
1657 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
1658 // This is the "relocatable" relative line offset from the previous function's end curly
1659 // to this function's begin curly.
1660 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
1661 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
1662 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
1663
1664 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
1665 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
1666 // Once we support more than one source file, this will have the ability to be more
1667 // than one possible value.
1668 const file_index = 1;
1669 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
1670
1671 // Emit a line for the begin curly with prologue_end=false. The codegen will
1672 // do the work of setting prologue_end=true and epilogue_begin=true.
1673 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
1674
1675 // .debug_info subprogram
1676 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
1677 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len);
1678
1679 const fn_ret_type = typed_value.ty.fnReturnType();
1680 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
1681 if (fn_ret_has_bits) {
1682 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
1683 } else {
1684 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
1685 }
1686 // These get overwritten after generating the machine code. These values are
1687 // "relocations" and have to be in this fixed place so that functions can be
1688 // moved in virtual address space.
1689 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
1690 dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr
1691 assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
1692 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
1693 if (fn_ret_has_bits) {
1694 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
1695 if (!gop.found_existing) {
1696 gop.entry.value = .{
1697 .off = undefined,
1698 .relocs = .{},
1699 };
1700 }
1701 try gop.entry.value.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
1702 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
1703 }
1704 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
1705 } else {
1706 // TODO implement .debug_info for global variables
1707 }
1708 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, &dbg_line_buffer, &dbg_info_buffer, &dbg_info_type_relocs);
1709 const code = switch (res) {
1710 .externally_managed => |x| x,
1711 .appended => code_buffer.items,
1712 .fail => |em| {
1713 decl.analysis = .codegen_failure;
1714 try module.failed_decls.put(module.gpa, decl, em);
1715 return;
1716 },
1717 };
1718
1719 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
1720
1721 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
1722
1723 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1724 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
1725 if (local_sym.st_size != 0) {
1726 const capacity = decl.link.elf.capacity(self.*);
1727 const need_realloc = code.len > capacity or
1728 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1729 if (need_realloc) {
1730 const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
1731 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1732 if (vaddr != local_sym.st_value) {
1733 local_sym.st_value = vaddr;
1734
1735 log.debug(" (writing new offset table entry)\n", .{});
1736 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
1737 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
1738 }
1739 } else if (code.len < local_sym.st_size) {
1740 self.shrinkTextBlock(&decl.link.elf, code.len);
1741 }
1742 local_sym.st_size = code.len;
1743 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1744 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1745 local_sym.st_other = 0;
1746 local_sym.st_shndx = self.text_section_index.?;
1747 // TODO this write could be avoided if no fields of the symbol were changed.
1748 try self.writeSymbol(decl.link.elf.local_sym_index);
1749 } else {
1750 const decl_name = mem.spanZ(decl.name);
1751 const name_str_index = try self.makeString(decl_name);
1752 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
1753 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1754 errdefer self.freeTextBlock(&decl.link.elf);
1755
1756 local_sym.* = .{
1757 .st_name = name_str_index,
1758 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1759 .st_other = 0,
1760 .st_shndx = self.text_section_index.?,
1761 .st_value = vaddr,
1762 .st_size = code.len,
1763 };
1764 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
1765
1766 try self.writeSymbol(decl.link.elf.local_sym_index);
1767 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
1768 }
1769
1770 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1771 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1772 try self.base.file.?.pwriteAll(code, file_offset);
1773
1774 const target_endian = self.base.options.target.cpu.arch.endian();
1775
1776 const text_block = &decl.link.elf;
1777
1778 // If the Decl is a function, we need to update the .debug_line program.
1779 if (is_fn) {
1780 // Perform the relocations based on vaddr.
1781 switch (self.ptr_width) {
1782 .p32 => {
1783 {
1784 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
1785 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
1786 }
1787 {
1788 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
1789 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
1790 }
1791 },
1792 .p64 => {
1793 {
1794 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
1795 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
1796 }
1797 {
1798 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
1799 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
1800 }
1801 },
1802 }
1803 {
1804 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
1805 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
1806 }
1807
1808 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
1809
1810 // Now we have the full contents and may allocate a region to store it.
1811
1812 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
1813 // `TextBlock` and the .debug_info. If you are editing this logic, you
1814 // probably need to edit that logic too.
1815
1816 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1817 const src_fn = &decl.fn_link.elf;
1818 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1819 if (self.dbg_line_fn_last) |last| {
1820 if (src_fn.next) |next| {
1821 // Update existing function - non-last item.
1822 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
1823 // It grew too big, so we move it to a new location.
1824 if (src_fn.prev) |prev| {
1825 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
1826 prev.next = src_fn.next;
1827 }
1828 next.prev = src_fn.prev;
1829 src_fn.next = null;
1830 // Populate where it used to be with NOPs.
1831 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1832 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
1833 // TODO Look at the free list before appending at the end.
1834 src_fn.prev = last;
1835 last.next = src_fn;
1836 self.dbg_line_fn_last = src_fn;
1837
1838 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1839 }
1840 } else if (src_fn.prev == null) {
1841 // Append new function.
1842 // TODO Look at the free list before appending at the end.
1843 src_fn.prev = last;
1844 last.next = src_fn;
1845 self.dbg_line_fn_last = src_fn;
1846
1847 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1848 }
1849 } else {
1850 // This is the first function of the Line Number Program.
1851 self.dbg_line_fn_first = src_fn;
1852 self.dbg_line_fn_last = src_fn;
1853
1854 src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;
1855 }
1856
1857 const last_src_fn = self.dbg_line_fn_last.?;
1858 const needed_size = last_src_fn.off + last_src_fn.len;
1859 if (needed_size != debug_line_sect.sh_size) {
1860 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
1861 const new_offset = self.findFreeSpace(needed_size, 1);
1862 const existing_size = last_src_fn.off;
1863 log.debug("moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{
1864 existing_size,
1865 debug_line_sect.sh_offset,
1866 new_offset,
1867 });
1868 const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
1869 if (amt != existing_size) return error.InputOutput;
1870 debug_line_sect.sh_offset = new_offset;
1871 }
1872 debug_line_sect.sh_size = needed_size;
1873 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1874 self.debug_line_header_dirty = true;
1875 }
1876 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
1877 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
1878
1879 // We only have support for one compilation unit so far, so the offsets are directly
1880 // from the .debug_line section.
1881 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1882 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
1883
1884 // .debug_info - End the TAG_subprogram children.
1885 try dbg_info_buffer.append(0);
1886 }
1887
1888 // Now we emit the .debug_info types of the Decl. These will count towards the size of
1889 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
1890 // relocations yet.
1891 for (dbg_info_type_relocs.items()) |*entry| {
1892 entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
1893 try self.addDbgInfoType(entry.key, &dbg_info_buffer);
1894 }
1895
1896 try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len));
1897
1898 // Now that we have the offset assigned we can finally perform type relocations.
1899 for (dbg_info_type_relocs.items()) |entry| {
1900 for (entry.value.relocs.items) |off| {
1901 mem.writeInt(
1902 u32,
1903 dbg_info_buffer.items[off..][0..4],
1904 text_block.dbg_info_off + entry.value.off,
1905 target_endian,
1906 );
1907 }
1908 }
1909
1910 try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
1911
1912 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1913 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1914 return self.updateDeclExports(module, decl, decl_exports);
1915}
1916
1917/// Asserts the type has codegen bits.
1918fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
1919 switch (ty.zigTypeTag()) {
1920 .Void => unreachable,
1921 .NoReturn => unreachable,
1922 .Bool => {
1923 try dbg_info_buffer.appendSlice(&[_]u8{
1924 abbrev_base_type,
1925 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
1926 1, // DW.AT_byte_size, DW.FORM_data1
1927 'b',
1928 'o',
1929 'o',
1930 'l',
1931 0, // DW.AT_name, DW.FORM_string
1932 });
1933 },
1934 .Int => {
1935 const info = ty.intInfo(self.base.options.target);
1936 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
1937 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
1938 // DW.AT_encoding, DW.FORM_data1
1939 dbg_info_buffer.appendAssumeCapacity(if (info.signed) DW.ATE_signed else DW.ATE_unsigned);
1940 // DW.AT_byte_size, DW.FORM_data1
1941 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
1942 // DW.AT_name, DW.FORM_string
1943 try dbg_info_buffer.writer().print("{}\x00", .{ty});
1944 },
1945 else => {
1946 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
1947 try dbg_info_buffer.append(abbrev_pad1);
1948 },
1949 }
1950}
1951
1952fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void {
1953 const tracy = trace(@src());
1954 defer tracy.end();
1955
1956 // This logic is nearly identical to the logic above in `updateDecl` for
1957 // `SrcFn` and the line number programs. If you are editing this logic, you
1958 // probably need to edit that logic too.
1959
1960 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
1961 text_block.dbg_info_len = len;
1962 if (self.dbg_info_decl_last) |last| {
1963 if (text_block.dbg_info_next) |next| {
1964 // Update existing Decl - non-last item.
1965 if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) {
1966 // It grew too big, so we move it to a new location.
1967 if (text_block.dbg_info_prev) |prev| {
1968 _ = self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {};
1969 prev.dbg_info_next = text_block.dbg_info_next;
1970 }
1971 next.dbg_info_prev = text_block.dbg_info_prev;
1972 text_block.dbg_info_next = null;
1973 // Populate where it used to be with NOPs.
1974 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
1975 try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos);
1976 // TODO Look at the free list before appending at the end.
1977 text_block.dbg_info_prev = last;
1978 last.dbg_info_next = text_block;
1979 self.dbg_info_decl_last = text_block;
1980
1981 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
1982 }
1983 } else if (text_block.dbg_info_prev == null) {
1984 // Append new Decl.
1985 // TODO Look at the free list before appending at the end.
1986 text_block.dbg_info_prev = last;
1987 last.dbg_info_next = text_block;
1988 self.dbg_info_decl_last = text_block;
1989
1990 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
1991 }
1992 } else {
1993 // This is the first Decl of the .debug_info
1994 self.dbg_info_decl_first = text_block;
1995 self.dbg_info_decl_last = text_block;
1996
1997 text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;
1998 }
1999}
2000
2001fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
2002 const tracy = trace(@src());
2003 defer tracy.end();
2004
2005 // This logic is nearly identical to the logic above in `updateDecl` for
2006 // `SrcFn` and the line number programs. If you are editing this logic, you
2007 // probably need to edit that logic too.
2008
2009 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
2010
2011 const last_decl = self.dbg_info_decl_last.?;
2012 // +1 for a trailing zero to end the children of the decl tag.
2013 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
2014 if (needed_size != debug_info_sect.sh_size) {
2015 if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) {
2016 const new_offset = self.findFreeSpace(needed_size, 1);
2017 const existing_size = last_decl.dbg_info_off;
2018 log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}\n", .{
2019 existing_size,
2020 debug_info_sect.sh_offset,
2021 new_offset,
2022 });
2023 const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size);
2024 if (amt != existing_size) return error.InputOutput;
2025 debug_info_sect.sh_offset = new_offset;
2026 }
2027 debug_info_sect.sh_size = needed_size;
2028 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2029 self.debug_info_header_dirty = true;
2030 }
2031 const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev|
2032 text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len)
2033 else
2034 0;
2035 const next_padding_size: u32 = if (text_block.dbg_info_next) |next|
2036 next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len)
2037 else
2038 0;
2039
2040 // To end the children of the decl tag.
2041 const trailing_zero = text_block.dbg_info_next == null;
2042
2043 // We only have support for one compilation unit so far, so the offsets are directly
2044 // from the .debug_info section.
2045 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
2046 try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos);
2047}
2048
2049pub fn updateDeclExports(
2050 self: *Elf,
2051 module: *Module,
2052 decl: *const Module.Decl,
2053 exports: []const *Module.Export,
2054) !void {
2055 const tracy = trace(@src());
2056 defer tracy.end();
2057
2058 try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
2059 const typed_value = decl.typed_value.most_recent.typed_value;
2060 if (decl.link.elf.local_sym_index == 0) return;
2061 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
2062
2063 for (exports) |exp| {
2064 if (exp.options.section) |section_name| {
2065 if (!mem.eql(u8, section_name, ".text")) {
2066 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
2067 module.failed_exports.putAssumeCapacityNoClobber(
2068 exp,
2069 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
2070 );
2071 continue;
2072 }
2073 }
2074 const stb_bits: u8 = switch (exp.options.linkage) {
2075 .Internal => elf.STB_LOCAL,
2076 .Strong => blk: {
2077 if (mem.eql(u8, exp.options.name, "_start")) {
2078 self.entry_addr = decl_sym.st_value;
2079 }
2080 break :blk elf.STB_GLOBAL;
2081 },
2082 .Weak => elf.STB_WEAK,
2083 .LinkOnce => {
2084 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
2085 module.failed_exports.putAssumeCapacityNoClobber(
2086 exp,
2087 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
2088 );
2089 continue;
2090 },
2091 };
2092 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
2093 if (exp.link.sym_index) |i| {
2094 const sym = &self.global_symbols.items[i];
2095 sym.* = .{
2096 .st_name = try self.updateString(sym.st_name, exp.options.name),
2097 .st_info = (stb_bits << 4) | stt_bits,
2098 .st_other = 0,
2099 .st_shndx = self.text_section_index.?,
2100 .st_value = decl_sym.st_value,
2101 .st_size = decl_sym.st_size,
2102 };
2103 } else {
2104 const name = try self.makeString(exp.options.name);
2105 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
2106 _ = self.global_symbols.addOneAssumeCapacity();
2107 break :blk self.global_symbols.items.len - 1;
2108 };
2109 self.global_symbols.items[i] = .{
2110 .st_name = name,
2111 .st_info = (stb_bits << 4) | stt_bits,
2112 .st_other = 0,
2113 .st_shndx = self.text_section_index.?,
2114 .st_value = decl_sym.st_value,
2115 .st_size = decl_sym.st_size,
2116 };
2117
2118 exp.link.sym_index = @intCast(u32, i);
2119 }
2120 }
2121}
2122
2123/// Must be called only after a successful call to `updateDecl`.
2124pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
2125 const tracy = trace(@src());
2126 defer tracy.end();
2127
2128 const scope_file = decl.scope.cast(Module.Scope.File).?;
2129 const tree = scope_file.contents.tree;
2130 const file_ast_decls = tree.root_node.decls();
2131 // TODO Look into improving the performance here by adding a token-index-to-line
2132 // lookup table. Currently this involves scanning over the source code for newlines.
2133 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
2134 const block = fn_proto.body().?.castTag(.Block).?;
2135 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
2136 const casted_line_off = @intCast(u28, line_delta);
2137
2138 const shdr = &self.sections.items[self.debug_line_section_index.?];
2139 const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
2140 var data: [4]u8 = undefined;
2141 leb128.writeUnsignedFixed(4, &data, casted_line_off);
2142 try self.base.file.?.pwriteAll(&data, file_pos);
2143}
2144
2145pub fn deleteExport(self: *Elf, exp: Export) void {
2146 const sym_index = exp.sym_index orelse return;
2147 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
2148 self.global_symbols.items[sym_index].st_info = 0;
2149}
2150
2151fn writeProgHeader(self: *Elf, index: usize) !void {
2152 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2153 const offset = self.program_headers.items[index].p_offset;
2154 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
2155 32 => {
2156 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
2157 if (foreign_endian) {
2158 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
2159 }
2160 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2161 },
2162 64 => {
2163 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
2164 if (foreign_endian) {
2165 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
2166 }
2167 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2168 },
2169 else => return error.UnsupportedArchitecture,
2170 }
2171}
2172
2173fn writeSectHeader(self: *Elf, index: usize) !void {
2174 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2175 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
2176 32 => {
2177 var shdr: [1]elf.Elf32_Shdr = undefined;
2178 shdr[0] = sectHeaderTo32(self.sections.items[index]);
2179 if (foreign_endian) {
2180 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
2181 }
2182 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
2183 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2184 },
2185 64 => {
2186 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
2187 if (foreign_endian) {
2188 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
2189 }
2190 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
2191 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2192 },
2193 else => return error.UnsupportedArchitecture,
2194 }
2195}
2196
2197fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2198 const shdr = &self.sections.items[self.got_section_index.?];
2199 const phdr = &self.program_headers.items[self.phdr_got_index.?];
2200 const entry_size: u16 = self.ptrWidthBytes();
2201 if (self.offset_table_count_dirty) {
2202 // TODO Also detect virtual address collisions.
2203 const allocated_size = self.allocatedSize(shdr.sh_offset);
2204 const needed_size = self.local_symbols.items.len * entry_size;
2205 if (needed_size > allocated_size) {
2206 // Must move the entire got section.
2207 const new_offset = self.findFreeSpace(needed_size, entry_size);
2208 const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size);
2209 if (amt != shdr.sh_size) return error.InputOutput;
2210 shdr.sh_offset = new_offset;
2211 phdr.p_offset = new_offset;
2212 }
2213 shdr.sh_size = needed_size;
2214 phdr.p_memsz = needed_size;
2215 phdr.p_filesz = needed_size;
2216
2217 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2218 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
2219
2220 self.offset_table_count_dirty = false;
2221 }
2222 const endian = self.base.options.target.cpu.arch.endian();
2223 const off = shdr.sh_offset + @as(u64, entry_size) * index;
2224 switch (self.ptr_width) {
2225 .p32 => {
2226 var buf: [4]u8 = undefined;
2227 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
2228 try self.base.file.?.pwriteAll(&buf, off);
2229 },
2230 .p64 => {
2231 var buf: [8]u8 = undefined;
2232 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
2233 try self.base.file.?.pwriteAll(&buf, off);
2234 },
2235 }
2236}
2237
2238fn writeSymbol(self: *Elf, index: usize) !void {
2239 const tracy = trace(@src());
2240 defer tracy.end();
2241
2242 const syms_sect = &self.sections.items[self.symtab_section_index.?];
2243 // Make sure we are not pointlessly writing symbol data that will have to get relocated
2244 // due to running out of space.
2245 if (self.local_symbols.items.len != syms_sect.sh_info) {
2246 const sym_size: u64 = switch (self.ptr_width) {
2247 .p32 => @sizeOf(elf.Elf32_Sym),
2248 .p64 => @sizeOf(elf.Elf64_Sym),
2249 };
2250 const sym_align: u16 = switch (self.ptr_width) {
2251 .p32 => @alignOf(elf.Elf32_Sym),
2252 .p64 => @alignOf(elf.Elf64_Sym),
2253 };
2254 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
2255 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
2256 // Move all the symbols to a new file location.
2257 const new_offset = self.findFreeSpace(needed_size, sym_align);
2258 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
2259 const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size);
2260 if (amt != existing_size) return error.InputOutput;
2261 syms_sect.sh_offset = new_offset;
2262 }
2263 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
2264 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
2265 self.shdr_table_dirty = true; // TODO look into only writing one section
2266 }
2267 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2268 switch (self.ptr_width) {
2269 .p32 => {
2270 var sym = [1]elf.Elf32_Sym{
2271 .{
2272 .st_name = self.local_symbols.items[index].st_name,
2273 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
2274 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
2275 .st_info = self.local_symbols.items[index].st_info,
2276 .st_other = self.local_symbols.items[index].st_other,
2277 .st_shndx = self.local_symbols.items[index].st_shndx,
2278 },
2279 };
2280 if (foreign_endian) {
2281 bswapAllFields(elf.Elf32_Sym, &sym[0]);
2282 }
2283 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
2284 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
2285 },
2286 .p64 => {
2287 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
2288 if (foreign_endian) {
2289 bswapAllFields(elf.Elf64_Sym, &sym[0]);
2290 }
2291 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
2292 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
2293 },
2294 }
2295}
2296
2297fn writeAllGlobalSymbols(self: *Elf) !void {
2298 const syms_sect = &self.sections.items[self.symtab_section_index.?];
2299 const sym_size: u64 = switch (self.ptr_width) {
2300 .p32 => @sizeOf(elf.Elf32_Sym),
2301 .p64 => @sizeOf(elf.Elf64_Sym),
2302 };
2303 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2304 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
2305 switch (self.ptr_width) {
2306 .p32 => {
2307 const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
2308 defer self.base.allocator.free(buf);
2309
2310 for (buf) |*sym, i| {
2311 sym.* = .{
2312 .st_name = self.global_symbols.items[i].st_name,
2313 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
2314 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
2315 .st_info = self.global_symbols.items[i].st_info,
2316 .st_other = self.global_symbols.items[i].st_other,
2317 .st_shndx = self.global_symbols.items[i].st_shndx,
2318 };
2319 if (foreign_endian) {
2320 bswapAllFields(elf.Elf32_Sym, sym);
2321 }
2322 }
2323 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
2324 },
2325 .p64 => {
2326 const buf = try self.base.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
2327 defer self.base.allocator.free(buf);
2328
2329 for (buf) |*sym, i| {
2330 sym.* = .{
2331 .st_name = self.global_symbols.items[i].st_name,
2332 .st_value = self.global_symbols.items[i].st_value,
2333 .st_size = self.global_symbols.items[i].st_size,
2334 .st_info = self.global_symbols.items[i].st_info,
2335 .st_other = self.global_symbols.items[i].st_other,
2336 .st_shndx = self.global_symbols.items[i].st_shndx,
2337 };
2338 if (foreign_endian) {
2339 bswapAllFields(elf.Elf64_Sym, sym);
2340 }
2341 }
2342 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
2343 },
2344 }
2345}
2346
2347fn ptrWidthBytes(self: Elf) u8 {
2348 return switch (self.ptr_width) {
2349 .p32 => 4,
2350 .p64 => 8,
2351 };
2352}
2353
2354/// The reloc offset for the virtual address of a function in its Line Number Program.
2355/// Size is a virtual address integer.
2356const dbg_line_vaddr_reloc_index = 3;
2357/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
2358/// Size is a virtual address integer.
2359const dbg_info_low_pc_reloc_index = 1;
2360
2361/// The reloc offset for the line offset of a function from the previous function's line.
2362/// It's a fixed-size 4-byte ULEB128.
2363fn getRelocDbgLineOff(self: Elf) usize {
2364 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
2365}
2366
2367fn getRelocDbgFileIndex(self: Elf) usize {
2368 return self.getRelocDbgLineOff() + 5;
2369}
2370
2371fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 {
2372 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
2373}
2374
2375fn dbgLineNeededHeaderBytes(self: Elf) u32 {
2376 const directory_entry_format_count = 1;
2377 const file_name_entry_format_count = 1;
2378 const directory_count = 1;
2379 const file_name_count = 1;
2380 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
2381 directory_count * 8 + file_name_count * 8 +
2382 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
2383 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
2384 self.base.options.root_pkg.root_src_dir_path.len +
2385 self.base.options.root_pkg.root_src_path.len);
2386}
2387
2388fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
2389 return 120;
2390}
2391
2392const min_nop_size = 2;
2393
2394/// Writes to the file a buffer, prefixed and suffixed by the specified number of
2395/// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
2396/// are less than 126,976 bytes (if this limit is ever reached, this function can be
2397/// improved to make more than one pwritev call, or the limit can be raised by a fixed
2398/// amount by increasing the length of `vecs`).
2399fn pwriteDbgLineNops(
2400 self: *Elf,
2401 prev_padding_size: usize,
2402 buf: []const u8,
2403 next_padding_size: usize,
2404 offset: usize,
2405) !void {
2406 const tracy = trace(@src());
2407 defer tracy.end();
2408
2409 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
2410 const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
2411 var vecs: [32]std.os.iovec_const = undefined;
2412 var vec_index: usize = 0;
2413 {
2414 var padding_left = prev_padding_size;
2415 if (padding_left % 2 != 0) {
2416 vecs[vec_index] = .{
2417 .iov_base = &three_byte_nop,
2418 .iov_len = three_byte_nop.len,
2419 };
2420 vec_index += 1;
2421 padding_left -= three_byte_nop.len;
2422 }
2423 while (padding_left > page_of_nops.len) {
2424 vecs[vec_index] = .{
2425 .iov_base = &page_of_nops,
2426 .iov_len = page_of_nops.len,
2427 };
2428 vec_index += 1;
2429 padding_left -= page_of_nops.len;
2430 }
2431 if (padding_left > 0) {
2432 vecs[vec_index] = .{
2433 .iov_base = &page_of_nops,
2434 .iov_len = padding_left,
2435 };
2436 vec_index += 1;
2437 }
2438 }
2439
2440 vecs[vec_index] = .{
2441 .iov_base = buf.ptr,
2442 .iov_len = buf.len,
2443 };
2444 vec_index += 1;
2445
2446 {
2447 var padding_left = next_padding_size;
2448 if (padding_left % 2 != 0) {
2449 vecs[vec_index] = .{
2450 .iov_base = &three_byte_nop,
2451 .iov_len = three_byte_nop.len,
2452 };
2453 vec_index += 1;
2454 padding_left -= three_byte_nop.len;
2455 }
2456 while (padding_left > page_of_nops.len) {
2457 vecs[vec_index] = .{
2458 .iov_base = &page_of_nops,
2459 .iov_len = page_of_nops.len,
2460 };
2461 vec_index += 1;
2462 padding_left -= page_of_nops.len;
2463 }
2464 if (padding_left > 0) {
2465 vecs[vec_index] = .{
2466 .iov_base = &page_of_nops,
2467 .iov_len = padding_left,
2468 };
2469 vec_index += 1;
2470 }
2471 }
2472 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
2473}
2474
2475/// Writes to the file a buffer, prefixed and suffixed by the specified number of
2476/// bytes of padding.
2477fn pwriteDbgInfoNops(
2478 self: *Elf,
2479 prev_padding_size: usize,
2480 buf: []const u8,
2481 next_padding_size: usize,
2482 trailing_zero: bool,
2483 offset: usize,
2484) !void {
2485 const tracy = trace(@src());
2486 defer tracy.end();
2487
2488 const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
2489 var vecs: [32]std.os.iovec_const = undefined;
2490 var vec_index: usize = 0;
2491 {
2492 var padding_left = prev_padding_size;
2493 while (padding_left > page_of_nops.len) {
2494 vecs[vec_index] = .{
2495 .iov_base = &page_of_nops,
2496 .iov_len = page_of_nops.len,
2497 };
2498 vec_index += 1;
2499 padding_left -= page_of_nops.len;
2500 }
2501 if (padding_left > 0) {
2502 vecs[vec_index] = .{
2503 .iov_base = &page_of_nops,
2504 .iov_len = padding_left,
2505 };
2506 vec_index += 1;
2507 }
2508 }
2509
2510 vecs[vec_index] = .{
2511 .iov_base = buf.ptr,
2512 .iov_len = buf.len,
2513 };
2514 vec_index += 1;
2515
2516 {
2517 var padding_left = next_padding_size;
2518 while (padding_left > page_of_nops.len) {
2519 vecs[vec_index] = .{
2520 .iov_base = &page_of_nops,
2521 .iov_len = page_of_nops.len,
2522 };
2523 vec_index += 1;
2524 padding_left -= page_of_nops.len;
2525 }
2526 if (padding_left > 0) {
2527 vecs[vec_index] = .{
2528 .iov_base = &page_of_nops,
2529 .iov_len = padding_left,
2530 };
2531 vec_index += 1;
2532 }
2533 }
2534
2535 if (trailing_zero) {
2536 var zbuf = [1]u8{0};
2537 vecs[vec_index] = .{
2538 .iov_base = &zbuf,
2539 .iov_len = zbuf.len,
2540 };
2541 vec_index += 1;
2542 }
2543
2544 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
2545}
2546
2547/// Saturating multiplication
2548fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
2549 const T = @TypeOf(a, b);
2550 return std.math.mul(T, a, b) catch std.math.maxInt(T);
2551}
2552
2553fn bswapAllFields(comptime S: type, ptr: *S) void {
2554 @panic("TODO implement bswapAllFields");
2555}
2556
2557fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
2558 return .{
2559 .p_type = phdr.p_type,
2560 .p_flags = phdr.p_flags,
2561 .p_offset = @intCast(u32, phdr.p_offset),
2562 .p_vaddr = @intCast(u32, phdr.p_vaddr),
2563 .p_paddr = @intCast(u32, phdr.p_paddr),
2564 .p_filesz = @intCast(u32, phdr.p_filesz),
2565 .p_memsz = @intCast(u32, phdr.p_memsz),
2566 .p_align = @intCast(u32, phdr.p_align),
2567 };
2568}
2569
2570fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
2571 return .{
2572 .sh_name = shdr.sh_name,
2573 .sh_type = shdr.sh_type,
2574 .sh_flags = @intCast(u32, shdr.sh_flags),
2575 .sh_addr = @intCast(u32, shdr.sh_addr),
2576 .sh_offset = @intCast(u32, shdr.sh_offset),
2577 .sh_size = @intCast(u32, shdr.sh_size),
2578 .sh_link = shdr.sh_link,
2579 .sh_info = shdr.sh_info,
2580 .sh_addralign = @intCast(u32, shdr.sh_addralign),
2581 .sh_entsize = @intCast(u32, shdr.sh_entsize),
2582 };
2583}
src-self-hosted/link/MachO.zig+5-1
......@@ -13,7 +13,7 @@ const Module = @import("../Module.zig");
1313const link = @import("../link.zig");
1414const File = link.File;
1515
16pub const base_tag: Tag = File.Tag.macho;
16pub const base_tag: File.Tag = File.Tag.macho;
1717
1818base: File,
1919
......@@ -210,3 +210,7 @@ pub fn updateDeclExports(
210210) !void {}
211211
212212pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
213
214pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
215 @panic("TODO implement getDeclVAddr for MachO");
216}
src-self-hosted/link/cbe.h created+15
......@@ -0,0 +1,15 @@
1#if __STDC_VERSION__ >= 201112L
2#define zig_noreturn _Noreturn
3#elif __GNUC__
4#define zig_noreturn __attribute__ ((noreturn))
5#elif _MSC_VER
6#define zig_noreturn __declspec(noreturn)
7#else
8#define zig_noreturn
9#endif
10
11#if __GNUC__
12#define zig_unreachable() __builtin_unreachable()
13#else
14#define zig_unreachable()
15#endif
src-self-hosted/test.zig+1-1
......@@ -10,7 +10,7 @@ const enable_wine: bool = build_options.enable_wine;
1010const enable_wasmtime: bool = build_options.enable_wasmtime;
1111const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
1212
13const cheader = @embedFile("cbe.h");
13const cheader = @embedFile("link/cbe.h");
1414
1515test "self-hosted" {
1616 var ctx = TestContext.init();