authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-12 20:11:47-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-12 20:11:47-04:00
loge3a0fac1a77a8c637c790670ff749879298becad
tree517ab4cc5816e582e23be7aa46c580f942d4bfcb
parentfda0eef9fbf2fe73baf09127c8925910dcd35205

self-hosted: link: global offset table support for decls


4 files changed, 198 insertions(+), 100 deletions(-)

src-self-hosted/Package.zig+1
...@@ -50,3 +50,4 @@ pub fn add(self: *Package, name: []const u8, package: *Package) !void {...@@ -50,3 +50,4 @@ pub fn add(self: *Package, name: []const u8, package: *Package) !void {
50const std = @import("std");50const std = @import("std");
51const mem = std.mem;51const mem = std.mem;
52const assert = std.debug.assert;52const assert = std.debug.assert;
53const Package = @This();
src-self-hosted/codegen.zig+8-15
...@@ -5,13 +5,9 @@ const ir = @import("ir.zig");...@@ -5,13 +5,9 @@ const ir = @import("ir.zig");
5const Type = @import("type.zig").Type;5const Type = @import("type.zig").Type;
6const Value = @import("value.zig").Value;6const Value = @import("value.zig").Value;
7const Target = std.Target;7const Target = std.Target;
8const Allocator = mem.Allocator;
89
9pub fn generateSymbol(10pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !?*ir.ErrorMsg {
10 typed_value: ir.TypedValue,
11 module: ir.Module,
12 code: *std.ArrayList(u8),
13 errors: *std.ArrayList(ir.ErrorMsg),
14) !void {
15 switch (typed_value.ty.zigTypeTag()) {11 switch (typed_value.ty.zigTypeTag()) {
16 .Fn => {12 .Fn => {
17 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;13 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
...@@ -21,14 +17,14 @@ pub fn generateSymbol(...@@ -21,14 +17,14 @@ pub fn generateSymbol(
21 .mod_fn = module_fn,17 .mod_fn = module_fn,
22 .code = code,18 .code = code,
23 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator),19 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator),
24 .errors = errors,20 .err_msg = null,
25 };21 };
26 defer function.inst_table.deinit();22 defer function.inst_table.deinit();
2723
28 for (module_fn.body.instructions) |inst| {24 for (module_fn.body.instructions) |inst| {
29 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {25 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
30 error.CodegenFail => {26 error.CodegenFail => {
31 assert(function.errors.items.len != 0);27 assert(function.err_msg != null);
32 break;28 break;
33 },29 },
34 else => |e| return e,30 else => |e| return e,
...@@ -36,7 +32,7 @@ pub fn generateSymbol(...@@ -36,7 +32,7 @@ pub fn generateSymbol(
36 try function.inst_table.putNoClobber(inst, new_inst);32 try function.inst_table.putNoClobber(inst, new_inst);
37 }33 }
3834
39 return Symbol{ .errors = function.errors.toOwnedSlice() };35 return function.err_msg;
40 },36 },
41 else => @panic("TODO implement generateSymbol for non-function decls"),37 else => @panic("TODO implement generateSymbol for non-function decls"),
42 }38 }
...@@ -47,7 +43,7 @@ const Function = struct {...@@ -47,7 +43,7 @@ const Function = struct {
47 mod_fn: *const ir.Module.Fn,43 mod_fn: *const ir.Module.Fn,
48 code: *std.ArrayList(u8),44 code: *std.ArrayList(u8),
49 inst_table: std.AutoHashMap(*ir.Inst, MCValue),45 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
50 errors: *std.ArrayList(ir.ErrorMsg),46 err_msg: ?*ir.ErrorMsg,
5147
52 const MCValue = union(enum) {48 const MCValue = union(enum) {
53 none,49 none,
...@@ -428,11 +424,8 @@ const Function = struct {...@@ -428,11 +424,8 @@ const Function = struct {
428424
429 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {425 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {
430 @setCold(true);426 @setCold(true);
431 try self.errors.ensureCapacity(self.errors.items.len + 1);427 assert(self.err_msg == null);
432 self.errors.appendAssumeCapacity(.{428 self.err_msg = try ir.ErrorMsg.create(self.code.allocator, src, format, args);
433 .byte_offset = src,
434 .msg = try std.fmt.allocPrint(self.errors.allocator, format, args),
435 });
436 return error.CodegenFail;429 return error.CodegenFail;
437 }430 }
438};431};
src-self-hosted/ir.zig+32-37
...@@ -201,7 +201,7 @@ pub const Module = struct {...@@ -201,7 +201,7 @@ pub const Module = struct {
201 decl_table: std.AutoHashMap(Decl.Hash, *Decl),201 decl_table: std.AutoHashMap(Decl.Hash, *Decl),
202202
203 optimize_mode: std.builtin.Mode,203 optimize_mode: std.builtin.Mode,
204 link_error_flags: link.ElfFile.ErrorFlags = .{},204 link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{},
205205
206 /// We optimize memory usage for a compilation with no compile errors by storing the206 /// We optimize memory usage for a compilation with no compile errors by storing the
207 /// error messages and mapping outside of `Decl`.207 /// error messages and mapping outside of `Decl`.
...@@ -247,7 +247,7 @@ pub const Module = struct {...@@ -247,7 +247,7 @@ pub const Module = struct {
247 /// The most recent value of the Decl after a successful semantic analysis.247 /// The most recent value of the Decl after a successful semantic analysis.
248 /// The tag for this union is determined by the tag value of the analysis field.248 /// The tag for this union is determined by the tag value of the analysis field.
249 typed_value: union {249 typed_value: union {
250 never_succeeded,250 never_succeeded: void,
251 most_recent: TypedValue.Managed,251 most_recent: TypedValue.Managed,
252 },252 },
253 /// Represents the "shallow" analysis status. For example, for decls that are functions,253 /// Represents the "shallow" analysis status. For example, for decls that are functions,
...@@ -278,12 +278,11 @@ pub const Module = struct {...@@ -278,12 +278,11 @@ pub const Module = struct {
278 complete,278 complete,
279 },279 },
280280
281 /// Represents the position of the code, if any, in the output file.281 /// Represents the position of the code in the output file.
282 /// This is populated regardless of semantic analysis and code generation.282 /// This is populated regardless of semantic analysis and code generation.
283 /// This value is `undefined` if the type has no runtime bits.283 link: link.ElfFile.Decl = link.ElfFile.Decl.empty,
284 link: link.ElfFile.Decl,
285284
286 /// The set of other decls whose typed_value could possibly change if this Decl's285 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
287 /// typed_value is modified.286 /// typed_value is modified.
288 /// TODO look into using a lightweight map/set data structure rather than a linear array.287 /// TODO look into using a lightweight map/set data structure rather than a linear array.
289 dependants: ArrayListUnmanaged(*Decl) = .{},288 dependants: ArrayListUnmanaged(*Decl) = .{},
...@@ -368,11 +367,11 @@ pub const Module = struct {...@@ -368,11 +367,11 @@ pub const Module = struct {
368 /// Reference to external memory, not owned by ZIRModule.367 /// Reference to external memory, not owned by ZIRModule.
369 sub_file_path: []const u8,368 sub_file_path: []const u8,
370 source: union {369 source: union {
371 unloaded,370 unloaded: void,
372 bytes: [:0]const u8,371 bytes: [:0]const u8,
373 },372 },
374 contents: union {373 contents: union {
375 not_available,374 not_available: void,
376 module: *text.Module,375 module: *text.Module,
377 },376 },
378 status: enum {377 status: enum {
...@@ -575,9 +574,9 @@ pub const Module = struct {...@@ -575,9 +574,9 @@ pub const Module = struct {
575 try self.failed_files.ensureCapacity(self.failed_files.size + 1);574 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
576575
577 var keep_source = false;576 var keep_source = false;
578 const source = try self.root_pkg_dir.readFileAllocOptions(577 const source = try self.root_pkg.root_src_dir.readFileAllocOptions(
579 self.allocator,578 self.allocator,
580 self.root_src_path,579 self.root_pkg.root_src_path,
581 std.math.maxInt(u32),580 std.math.maxInt(u32),
582 1,581 1,
583 0,582 0,
...@@ -628,20 +627,7 @@ pub const Module = struct {...@@ -628,20 +627,7 @@ pub const Module = struct {
628 while (self.analysis_queue.popOrNull()) |work_item| {627 while (self.analysis_queue.popOrNull()) |work_item| {
629 switch (work_item) {628 switch (work_item) {
630 .decl => |decl| switch (decl.analysis) {629 .decl => |decl| switch (decl.analysis) {
631 .success => |typed_value| {630 .success => try self.bin_file.updateDecl(self, decl),
632 var arena = decl.arena.promote(self.allocator);
633 const update_result = self.bin_file.updateDecl(
634 self.*,
635 typed_value,
636 decl.export_node,
637 decl.fullyQualifiedNameHash(),
638 &arena.allocator,
639 );
640 decl.arena = arena.state;
641 if (try update_result) |err_msg| {
642 decl.analysis = .{ .codegen_failure = err_msg };
643 }
644 },
645 },631 },
646 }632 }
647 }633 }
...@@ -653,22 +639,22 @@ pub const Module = struct {...@@ -653,22 +639,22 @@ pub const Module = struct {
653 return kv.value;639 return kv.value;
654 } else {640 } else {
655 const new_decl = blk: {641 const new_decl = blk: {
656 var decl_arena = std.heap.ArenaAllocator.init(self.allocator);642 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
657 errdefer decl_arena.deinit();643 const new_decl = try self.allocator.create(Decl);
658 const new_decl = try decl_arena.allocator.create(Decl);644 errdefer self.allocator.destroy(new_decl);
659 const name = try mem.dupeZ(&decl_arena.allocator, u8, old_inst.name);645 const name = try mem.dupeZ(self.allocator, u8, old_inst.name);
646 errdefer self.allocator.free(name);
660 new_decl.* = .{647 new_decl.* = .{
661 .arena = decl_arena.state,
662 .name = name,648 .name = name,
663 .src = old_inst.src,
664 .analysis = .in_progress,
665 .scope = scope.findZIRModule(),649 .scope = scope.findZIRModule(),
650 .src = old_inst.src,
651 .typed_value = .{ .never_succeeded = {} },
652 .analysis = .initial_in_progress,
666 };653 };
667 try self.decl_table.putNoClobber(hash, new_decl);654 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
668 break :blk new_decl;655 break :blk new_decl;
669 };656 };
670657
671 swapRemoveElem(self.allocator, *Scope.ZIRModule, root_scope, self.failed_decls);
672 var decl_scope: Scope.DeclAnalysis = .{658 var decl_scope: Scope.DeclAnalysis = .{
673 .base = .{ .parent = scope },659 .base = .{ .parent = scope },
674 .decl = new_decl,660 .decl = new_decl,
...@@ -1838,6 +1824,7 @@ pub fn main() anyerror!void {...@@ -1838,6 +1824,7 @@ pub fn main() anyerror!void {
1838 const bin_path = args[2];1824 const bin_path = args[2];
1839 const debug_error_trace = true;1825 const debug_error_trace = true;
1840 const output_zir = true;1826 const output_zir = true;
1827 const object_format: ?std.builtin.ObjectFormat = null;
18411828
1842 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});1829 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
18431830
...@@ -1845,9 +1832,9 @@ pub fn main() anyerror!void {...@@ -1845,9 +1832,9 @@ pub fn main() anyerror!void {
1845 .target = native_info.target,1832 .target = native_info.target,
1846 .output_mode = .Exe,1833 .output_mode = .Exe,
1847 .link_mode = .Static,1834 .link_mode = .Static,
1848 .object_format = options.object_format orelse native_info.target.getObjectFormat(),1835 .object_format = object_format orelse native_info.target.getObjectFormat(),
1849 });1836 });
1850 defer bin_file.deinit(allocator);1837 defer bin_file.deinit();
18511838
1852 var module = blk: {1839 var module = blk: {
1853 const root_pkg = try Package.create(allocator, std.fs.cwd(), ".", src_path);1840 const root_pkg = try Package.create(allocator, std.fs.cwd(), ".", src_path);
...@@ -1857,7 +1844,9 @@ pub fn main() anyerror!void {...@@ -1857,7 +1844,9 @@ pub fn main() anyerror!void {
1857 errdefer allocator.destroy(root_scope);1844 errdefer allocator.destroy(root_scope);
1858 root_scope.* = .{1845 root_scope.* = .{
1859 .sub_file_path = root_pkg.root_src_path,1846 .sub_file_path = root_pkg.root_src_path,
1860 .contents = .unloaded,1847 .source = .{ .unloaded = {} },
1848 .contents = .{ .not_available = {} },
1849 .status = .unloaded,
1861 };1850 };
18621851
1863 break :blk Module{1852 break :blk Module{
...@@ -1866,7 +1855,13 @@ pub fn main() anyerror!void {...@@ -1866,7 +1855,13 @@ pub fn main() anyerror!void {
1866 .root_scope = root_scope,1855 .root_scope = root_scope,
1867 .bin_file = &bin_file,1856 .bin_file = &bin_file,
1868 .optimize_mode = .Debug,1857 .optimize_mode = .Debug,
1869 .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(allocator),1858 .decl_table = std.AutoHashMap(Module.Decl.Hash, *Module.Decl).init(allocator),
1859 .decl_exports = std.AutoHashMap(*Module.Decl, []*Module.Export).init(allocator),
1860 .export_owners = std.AutoHashMap(*Module.Decl, []*Module.Export).init(allocator),
1861 .failed_decls = std.AutoHashMap(*Module.Decl, *ErrorMsg).init(allocator),
1862 .failed_fns = std.AutoHashMap(*Module.Fn, *ErrorMsg).init(allocator),
1863 .failed_files = std.AutoHashMap(*Module.Scope.ZIRModule, *ErrorMsg).init(allocator),
1864 .failed_exports = std.AutoHashMap(*Module.Export, *ErrorMsg).init(allocator),
1870 };1865 };
1871 };1866 };
1872 defer module.deinit();1867 defer module.deinit();
src-self-hosted/link.zig+157-48
...@@ -94,35 +94,40 @@ pub const ElfFile = struct {...@@ -94,35 +94,40 @@ pub const ElfFile = struct {
9494
95 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.95 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
96 /// Same order as in the file.96 /// Same order as in the file.
97 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},97 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
98 shdr_table_offset: ?u64 = null,98 shdr_table_offset: ?u64 = null,
9999
100 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.100 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
101 /// Same order as in the file.101 /// Same order as in the file.
102 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},102 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
103 phdr_table_offset: ?u64 = null,103 phdr_table_offset: ?u64 = null,
104 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags104 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
105 phdr_load_re_index: ?u16 = null,105 phdr_load_re_index: ?u16 = null,
106 /// The index into the program headers of the global offset table.
107 /// It needs PT_LOAD and Read flags.
108 phdr_got_index: ?u16 = null,
106 entry_addr: ?u64 = null,109 entry_addr: ?u64 = null,
107110
108 shstrtab: std.ArrayListUnmanaged(u8) = .{},111 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
109 shstrtab_index: ?u16 = null,112 shstrtab_index: ?u16 = null,
110113
111 text_section_index: ?u16 = null,114 text_section_index: ?u16 = null,
112 symtab_section_index: ?u16 = null,115 symtab_section_index: ?u16 = null,
116 got_section_index: ?u16 = null,
113117
114 /// The same order as in the file118 /// The same order as in the file
115 symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},119 symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
116120
117 /// Same order as in the file.121 /// Same order as in the file. The value is the absolute vaddr value.
118 offset_table: std.ArrayListUnmanaged(aoeu) = .{},122 /// If the vaddr of the executable program header changes, the entire
123 /// offset table needs to be rewritten.
124 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
119125
120 /// This means the entire read-only executable program code needs to be rewritten.
121 phdr_load_re_dirty: bool = false,
122 phdr_table_dirty: bool = false,126 phdr_table_dirty: bool = false,
123 shdr_table_dirty: bool = false,127 shdr_table_dirty: bool = false,
124 shstrtab_dirty: bool = false,128 shstrtab_dirty: bool = false,
125 symtab_dirty: bool = false,129 offset_table_count_dirty: bool = false,
130 symbol_count_dirty: bool = false,
126131
127 error_flags: ErrorFlags = ErrorFlags{},132 error_flags: ErrorFlags = ErrorFlags{},
128133
...@@ -130,18 +135,25 @@ pub const ElfFile = struct {...@@ -130,18 +135,25 @@ pub const ElfFile = struct {
130 no_entry_point_found: bool = false,135 no_entry_point_found: bool = false,
131 };136 };
132137
133 /// TODO it's too bad this optional takes up double the memory it should
134 pub const Decl = struct {138 pub const Decl = struct {
135 /// Each decl always gets a local symbol with the fully qualified name.139 /// Each decl always gets a local symbol with the fully qualified name.
136 /// The vaddr and size are found here directly.140 /// The vaddr and size are found here directly.
137 /// The file offset is found by computing the vaddr offset from the section vaddr141 /// 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.142 /// the symbol references, and adding that to the file offset of the section.
139 local_sym_index: ?usize = null,143 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
144 /// offset table entry.
145 local_sym_index: u32,
146 /// This field is undefined for symbols with size = 0.
147 offset_table_index: u32,
148
149 pub const empty = Decl{
150 .local_sym_index = 0,
151 .offset_table_index = undefined,
152 };
140 };153 };
141154
142 /// TODO it's too bad this optional takes up double the memory it should
143 pub const Export = struct {155 pub const Export = struct {
144 sym_index: ?usize = null,156 sym_index: usize,
145 };157 };
146158
147 pub fn deinit(self: *ElfFile) void {159 pub fn deinit(self: *ElfFile) void {
...@@ -250,33 +262,57 @@ pub const ElfFile = struct {...@@ -250,33 +262,57 @@ pub const ElfFile = struct {
250 .p32 => true,262 .p32 => true,
251 .p64 => false,263 .p64 => false,
252 };264 };
265 const ptr_size: u8 = switch (self.ptr_width) {
266 .p32 => 4,
267 .p64 => 8,
268 };
253 if (self.phdr_load_re_index == null) {269 if (self.phdr_load_re_index == null) {
254 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);270 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
255 const file_size = self.options.program_code_size_hint;271 const file_size = self.options.program_code_size_hint;
256 const p_align = 0x1000;272 const p_align = 0x1000;
257 const off = self.findFreeSpace(file_size, p_align);273 const off = self.findFreeSpace(file_size, p_align);
258 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });274 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
259 try self.program_headers.append(.{275 try self.program_headers.append(self.allocator, .{
260 .p_type = elf.PT_LOAD,276 .p_type = elf.PT_LOAD,
261 .p_offset = off,277 .p_offset = off,
262 .p_filesz = file_size,278 .p_filesz = file_size,
263 .p_vaddr = default_entry_addr,279 .p_vaddr = default_entry_addr,
264 .p_paddr = default_entry_addr,280 .p_paddr = default_entry_addr,
265 .p_memsz = 0,281 .p_memsz = file_size,
266 .p_align = p_align,282 .p_align = p_align,
267 .p_flags = elf.PF_X | elf.PF_R,283 .p_flags = elf.PF_X | elf.PF_R,
268 });284 });
269 self.entry_addr = null;285 self.entry_addr = null;
270 self.phdr_load_re_dirty = true;286 self.phdr_table_dirty = true;
287 }
288 if (self.phdr_got_index == null) {
289 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
290 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
291 const off = self.findFreeSpace(file_size, ptr_size);
292 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
293 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
294 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
295 // else in virtual memory.
296 const default_got_addr = 0x80000000;
297 try self.program_headers.append(self.allocator, .{
298 .p_type = elf.PT_LOAD,
299 .p_offset = off,
300 .p_filesz = file_size,
301 .p_vaddr = default_got_addr,
302 .p_paddr = default_got_addr,
303 .p_memsz = file_size,
304 .p_align = ptr_size,
305 .p_flags = elf.PF_R,
306 });
271 self.phdr_table_dirty = true;307 self.phdr_table_dirty = true;
272 }308 }
273 if (self.shstrtab_index == null) {309 if (self.shstrtab_index == null) {
274 self.shstrtab_index = @intCast(u16, self.sections.items.len);310 self.shstrtab_index = @intCast(u16, self.sections.items.len);
275 assert(self.shstrtab.items.len == 0);311 assert(self.shstrtab.items.len == 0);
276 try self.shstrtab.append(0); // need a 0 at position 0312 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
277 const off = self.findFreeSpace(self.shstrtab.items.len, 1);313 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
278 //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });314 //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
279 try self.sections.append(.{315 try self.sections.append(self.allocator, .{
280 .sh_name = try self.makeString(".shstrtab"),316 .sh_name = try self.makeString(".shstrtab"),
281 .sh_type = elf.SHT_STRTAB,317 .sh_type = elf.SHT_STRTAB,
282 .sh_flags = 0,318 .sh_flags = 0,
...@@ -295,7 +331,7 @@ pub const ElfFile = struct {...@@ -295,7 +331,7 @@ pub const ElfFile = struct {
295 self.text_section_index = @intCast(u16, self.sections.items.len);331 self.text_section_index = @intCast(u16, self.sections.items.len);
296 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];332 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
297333
298 try self.sections.append(.{334 try self.sections.append(self.allocator, .{
299 .sh_name = try self.makeString(".text"),335 .sh_name = try self.makeString(".text"),
300 .sh_type = elf.SHT_PROGBITS,336 .sh_type = elf.SHT_PROGBITS,
301 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,337 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
...@@ -309,6 +345,24 @@ pub const ElfFile = struct {...@@ -309,6 +345,24 @@ pub const ElfFile = struct {
309 });345 });
310 self.shdr_table_dirty = true;346 self.shdr_table_dirty = true;
311 }347 }
348 if (self.got_section_index == null) {
349 self.got_section_index = @intCast(u16, self.sections.items.len);
350 const phdr = &self.program_headers.items[self.phdr_got_index.?];
351
352 try self.sections.append(self.allocator, .{
353 .sh_name = try self.makeString(".got"),
354 .sh_type = elf.SHT_PROGBITS,
355 .sh_flags = elf.SHF_ALLOC,
356 .sh_addr = phdr.p_vaddr,
357 .sh_offset = phdr.p_offset,
358 .sh_size = phdr.p_filesz,
359 .sh_link = 0,
360 .sh_info = 0,
361 .sh_addralign = phdr.p_align,
362 .sh_entsize = ptr_size,
363 });
364 self.shdr_table_dirty = true;
365 }
312 if (self.symtab_section_index == null) {366 if (self.symtab_section_index == null) {
313 self.symtab_section_index = @intCast(u16, self.sections.items.len);367 self.symtab_section_index = @intCast(u16, self.sections.items.len);
314 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);368 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
...@@ -317,7 +371,7 @@ pub const ElfFile = struct {...@@ -317,7 +371,7 @@ pub const ElfFile = struct {
317 const off = self.findFreeSpace(file_size, min_align);371 const off = self.findFreeSpace(file_size, min_align);
318 //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });372 //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
319373
320 try self.sections.append(.{374 try self.sections.append(self.allocator, .{
321 .sh_name = try self.makeString(".symtab"),375 .sh_name = try self.makeString(".symtab"),
322 .sh_type = elf.SHT_SYMTAB,376 .sh_type = elf.SHT_SYMTAB,
323 .sh_flags = 0,377 .sh_flags = 0,
...@@ -330,14 +384,14 @@ pub const ElfFile = struct {...@@ -330,14 +384,14 @@ pub const ElfFile = struct {
330 .sh_addralign = min_align,384 .sh_addralign = min_align,
331 .sh_entsize = each_size,385 .sh_entsize = each_size,
332 });386 });
333 self.symtab_dirty = true;
334 self.shdr_table_dirty = true;387 self.shdr_table_dirty = true;
388 try self.writeAllSymbols();
335 }389 }
336 const shsize: u64 = switch (ptr_width) {390 const shsize: u64 = switch (self.ptr_width) {
337 .p32 => @sizeOf(elf.Elf32_Shdr),391 .p32 => @sizeOf(elf.Elf32_Shdr),
338 .p64 => @sizeOf(elf.Elf64_Shdr),392 .p64 => @sizeOf(elf.Elf64_Shdr),
339 };393 };
340 const shalign: u16 = switch (ptr_width) {394 const shalign: u16 = switch (self.ptr_width) {
341 .p32 => @alignOf(elf.Elf32_Shdr),395 .p32 => @alignOf(elf.Elf32_Shdr),
342 .p64 => @alignOf(elf.Elf64_Shdr),396 .p64 => @alignOf(elf.Elf64_Shdr),
343 };397 };
...@@ -345,11 +399,11 @@ pub const ElfFile = struct {...@@ -345,11 +399,11 @@ pub const ElfFile = struct {
345 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);399 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
346 self.shdr_table_dirty = true;400 self.shdr_table_dirty = true;
347 }401 }
348 const phsize: u64 = switch (ptr_width) {402 const phsize: u64 = switch (self.ptr_width) {
349 .p32 => @sizeOf(elf.Elf32_Phdr),403 .p32 => @sizeOf(elf.Elf32_Phdr),
350 .p64 => @sizeOf(elf.Elf64_Phdr),404 .p64 => @sizeOf(elf.Elf64_Phdr),
351 };405 };
352 const phalign: u16 = switch (ptr_width) {406 const phalign: u16 = switch (self.ptr_width) {
353 .p32 => @alignOf(elf.Elf32_Phdr),407 .p32 => @alignOf(elf.Elf32_Phdr),
354 .p64 => @alignOf(elf.Elf64_Phdr),408 .p64 => @alignOf(elf.Elf64_Phdr),
355 };409 };
...@@ -399,7 +453,7 @@ pub const ElfFile = struct {...@@ -399,7 +453,7 @@ pub const ElfFile = struct {
399 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);453 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
400 },454 },
401 }455 }
402 self.phdr_table_offset = false;456 self.phdr_table_dirty = false;
403 }457 }
404458
405 {459 {
...@@ -432,11 +486,10 @@ pub const ElfFile = struct {...@@ -432,11 +486,10 @@ pub const ElfFile = struct {
432 self.shdr_table_offset = self.findFreeSpace(needed_size, phalign);486 self.shdr_table_offset = self.findFreeSpace(needed_size, phalign);
433 }487 }
434488
435 const allocator = self.sections.allocator;
436 switch (self.ptr_width) {489 switch (self.ptr_width) {
437 .p32 => {490 .p32 => {
438 const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);491 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
439 defer allocator.free(buf);492 defer self.allocator.free(buf);
440493
441 for (buf) |*shdr, i| {494 for (buf) |*shdr, i| {
442 shdr.* = sectHeaderTo32(self.sections.items[i]);495 shdr.* = sectHeaderTo32(self.sections.items[i]);
...@@ -447,8 +500,8 @@ pub const ElfFile = struct {...@@ -447,8 +500,8 @@ pub const ElfFile = struct {
447 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);500 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
448 },501 },
449 .p64 => {502 .p64 => {
450 const buf = try allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);503 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
451 defer allocator.free(buf);504 defer self.allocator.free(buf);
452505
453 for (buf) |*shdr, i| {506 for (buf) |*shdr, i| {
454 shdr.* = self.sections.items[i];507 shdr.* = self.sections.items[i];
...@@ -460,6 +513,7 @@ pub const ElfFile = struct {...@@ -460,6 +513,7 @@ pub const ElfFile = struct {
460 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);513 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
461 },514 },
462 }515 }
516 self.shdr_table_dirty = false;
463 }517 }
464 if (self.entry_addr == null and self.options.output_mode == .Exe) {518 if (self.entry_addr == null and self.options.output_mode == .Exe) {
465 self.error_flags.no_entry_point_found = true;519 self.error_flags.no_entry_point_found = true;
...@@ -470,11 +524,11 @@ pub const ElfFile = struct {...@@ -470,11 +524,11 @@ pub const ElfFile = struct {
470 // TODO find end pos and truncate524 // TODO find end pos and truncate
471525
472 // The point of flush() is to commit changes, so nothing should be dirty after this.526 // The point of flush() is to commit changes, so nothing should be dirty after this.
473 assert(!self.phdr_load_re_dirty);
474 assert(!self.phdr_table_dirty);527 assert(!self.phdr_table_dirty);
475 assert(!self.shdr_table_dirty);528 assert(!self.shdr_table_dirty);
476 assert(!self.shstrtab_dirty);529 assert(!self.shstrtab_dirty);
477 assert(!self.symtab_dirty);530 assert(!self.symbol_count_dirty);
531 assert(!self.offset_table_count_dirty);
478 }532 }
479533
480 fn writeElfHeader(self: *ElfFile) !void {534 fn writeElfHeader(self: *ElfFile) !void {
...@@ -608,6 +662,7 @@ pub const ElfFile = struct {...@@ -608,6 +662,7 @@ pub const ElfFile = struct {
608 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];662 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
609 const shdr = &self.sections.items[self.text_section_index.?];663 const shdr = &self.sections.items[self.text_section_index.?];
610664
665 // TODO Also detect virtual address collisions.
611 const text_capacity = self.allocatedSize(shdr.sh_offset);666 const text_capacity = self.allocatedSize(shdr.sh_offset);
612 // TODO instead of looping here, maintain a free list and a pointer to the end.667 // TODO instead of looping here, maintain a free list and a pointer to the end.
613 const end_vaddr = blk: {668 const end_vaddr = blk: {
...@@ -664,7 +719,7 @@ pub const ElfFile = struct {...@@ -664,7 +719,7 @@ pub const ElfFile = struct {
664 defer code.deinit();719 defer code.deinit();
665720
666 const typed_value = decl.typed_value.most_recent.typed_value;721 const typed_value = decl.typed_value.most_recent.typed_value;
667 const err_msg = try codegen.generateSymbol(typed_value, module, &code, module.allocator);722 const err_msg = try codegen.generateSymbol(typed_value, module, &code);
668 if (err_msg != null) |em| {723 if (err_msg != null) |em| {
669 decl.analysis = .codegen_failure;724 decl.analysis = .codegen_failure;
670 _ = try module.failed_decls.put(decl, em);725 _ = try module.failed_decls.put(decl, em);
...@@ -678,26 +733,31 @@ pub const ElfFile = struct {...@@ -678,26 +733,31 @@ pub const ElfFile = struct {
678 else => elf.STT_OBJECT,733 else => elf.STT_OBJECT,
679 };734 };
680735
681 if (decl.link.local_sym_index) |local_sym_index| {736 if (decl.link.local_sym_index != 0) {
682 const local_sym = &self.symbols.items[local_sym_index];737 const local_sym = &self.symbols.items[decl.link.local_sym_index];
683 const existing_block = self.findAllocatedTextBlock(local_sym);738 const existing_block = self.findAllocatedTextBlock(local_sym);
684 const file_offset = if (code_size > existing_block.size_capacity) fo: {739 const file_offset = if (code_size > existing_block.size_capacity) fo: {
685 const new_block = self.allocateTextBlock(code_size);740 const new_block = try self.allocateTextBlock(code_size);
686 local_sym.st_value = new_block.vaddr;741 local_sym.st_value = new_block.vaddr;
687 local_sym.st_size = code_size;742 local_sym.st_size = code_size;
743
744 try self.writeOffsetTableEntry(decl.link.offset_table_index);
745
688 break :fo new_block.file_offset;746 break :fo new_block.file_offset;
689 } else existing_block.file_offset;747 } else existing_block.file_offset;
690 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(u8, decl.name));748 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(u8, decl.name));
691 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;749 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
692 // TODO this write could be avoided if no fields of the symbol were changed.750 // TODO this write could be avoided if no fields of the symbol were changed.
693 try self.writeSymbol(local_sym_index);751 try self.writeSymbol(decl.link.local_sym_index);
694 break :blk file_offset;752 break :blk file_offset;
695 } else {753 } else {
696 try self.symbols.ensureCapacity(self.symbols.items.len + 1);754 try self.symbols.ensureCapacity(self.symbols.items.len + 1);
755 try self.offset_table.ensureCapacity(self.offset_table.items.len + 1);
697 const decl_name = mem.spanZ(u8, decl.name);756 const decl_name = mem.spanZ(u8, decl.name);
698 const name_str_index = try self.makeString(decl_name);757 const name_str_index = try self.makeString(decl_name);
699 const new_block = self.allocateTextBlock(code_size);758 const new_block = try self.allocateTextBlock(code_size);
700 const local_sym_index = self.symbols.items.len;759 const local_sym_index = self.symbols.items.len;
760 const offset_table_index = self.offset_table.items.len;
701761
702 self.symbols.appendAssumeCapacity(self.allocator, .{762 self.symbols.appendAssumeCapacity(self.allocator, .{
703 .st_name = name_str_index,763 .st_name = name_str_index,
...@@ -708,10 +768,17 @@ pub const ElfFile = struct {...@@ -708,10 +768,17 @@ pub const ElfFile = struct {
708 .st_size = code_size,768 .st_size = code_size,
709 });769 });
710 errdefer self.symbols.shrink(self.symbols.items.len - 1);770 errdefer self.symbols.shrink(self.symbols.items.len - 1);
771 self.offset_table.appendAssumeCapacity(self.allocator, new_block.vaddr);
772 errdefer self.offset_table.shrink(self.offset_table.items.len - 1);
711 try self.writeSymbol(local_sym_index);773 try self.writeSymbol(local_sym_index);
774 try self.writeOffsetTableEntry(offset_table_index);
712775
713 self.symbol_count_dirty = true;776 self.symbol_count_dirty = true;
714 decl.link.local_sym_index = local_sym_index;777 self.offset_table_count_dirty = true;
778 decl.link = .{
779 .local_sym_index = local_sym_index,
780 .offset_table_index = offset_table_index,
781 };
715782
716 break :blk new_block.file_offset;783 break :blk new_block.file_offset;
717 }784 }
...@@ -839,6 +906,45 @@ pub const ElfFile = struct {...@@ -839,6 +906,45 @@ pub const ElfFile = struct {
839 }906 }
840 }907 }
841908
909 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
910 const shdr = &self.sections.items[self.got_section_index.?];
911 const phdr = &self.program_headers.items[self.phdr_got_index.?];
912 if (self.offset_table_count_dirty) {
913 // TODO Also detect virtual address collisions.
914 const allocated_size = self.allocatedSize(shdr.sh_offset);
915 const needed_size = self.symbols.items.len * shdr.sh_entsize;
916 if (needed_size > allocated_size) {
917 // Must move the entire got section.
918 const new_offset = self.findFreeSpace(needed_size, shdr.sh_entsize);
919 const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size);
920 if (amt != text_size) return error.InputOutput;
921 shdr.sh_offset = new_offset;
922 }
923 shdr.sh_size = needed_size;
924 phdr.p_memsz = needed_size;
925 phdr.p_filesz = needed_size;
926
927 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
928 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
929
930 self.offset_table_count_dirty = false;
931 }
932 const endian = self.options.target.cpu.arch.endian();
933 const off = shdr.sh_offset + shdr.sh_entsize * index;
934 switch (self.ptr_width) {
935 .p32 => {
936 var buf: [4]u8 = undefined;
937 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
938 try self.file.pwriteAll(&buf, off);
939 },
940 .p64 => {
941 var buf: [8]u8 = undefined;
942 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
943 try self.file.pwriteAll(&buf, off);
944 },
945 }
946 }
947
842 fn writeSymbol(self: *ElfFile, index: usize) !void {948 fn writeSymbol(self: *ElfFile, index: usize) !void {
843 const syms_sect = &self.sections.items[self.symtab_section_index.?];949 const syms_sect = &self.sections.items[self.symtab_section_index.?];
844 // Make sure we are not pointlessly writing symbol data that will have to get relocated950 // Make sure we are not pointlessly writing symbol data that will have to get relocated
...@@ -849,6 +955,9 @@ pub const ElfFile = struct {...@@ -849,6 +955,9 @@ pub const ElfFile = struct {
849 if (needed_size > allocated_size) {955 if (needed_size > allocated_size) {
850 return self.writeAllSymbols();956 return self.writeAllSymbols();
851 }957 }
958 syms_sect.sh_info = @intCast(u32, self.symbols.items.len);
959 self.shdr_table_dirty = true; // TODO look into only writing one section
960 self.symbol_count_dirty = false;
852 }961 }
853 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();962 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
854 switch (self.ptr_width) {963 switch (self.ptr_width) {
...@@ -896,12 +1005,13 @@ pub const ElfFile = struct {...@@ -896,12 +1005,13 @@ pub const ElfFile = struct {
896 //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });1005 //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
897 syms_sect.sh_size = needed_size;1006 syms_sect.sh_size = needed_size;
898 syms_sect.sh_info = @intCast(u32, self.symbols.items.len);1007 syms_sect.sh_info = @intCast(u32, self.symbols.items.len);
899 const allocator = self.symbols.allocator;1008 self.symbol_count_dirty = false;
1009 self.shdr_table_dirty = true; // TODO look into only writing one section
900 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1010 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
901 switch (self.ptr_width) {1011 switch (self.ptr_width) {
902 .p32 => {1012 .p32 => {
903 const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len);1013 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.symbols.items.len);
904 defer allocator.free(buf);1014 defer self.allocator.free(buf);
9051015
906 for (buf) |*sym, i| {1016 for (buf) |*sym, i| {
907 sym.* = .{1017 sym.* = .{
...@@ -919,8 +1029,8 @@ pub const ElfFile = struct {...@@ -919,8 +1029,8 @@ pub const ElfFile = struct {
919 try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);1029 try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);
920 },1030 },
921 .p64 => {1031 .p64 => {
922 const buf = try allocator.alloc(elf.Elf64_Sym, self.symbols.items.len);1032 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.symbols.items.len);
923 defer allocator.free(buf);1033 defer self.allocator.free(buf);
9241034
925 for (buf) |*sym, i| {1035 for (buf) |*sym, i| {
926 sym.* = .{1036 sym.* = .{
...@@ -961,12 +1071,11 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El...@@ -961,12 +1071,11 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
961 .allocator = allocator,1071 .allocator = allocator,
962 .file = file,1072 .file = file,
963 .options = options,1073 .options = options,
964 .ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) {1074 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
965 32 => .p32,1075 32 => .p32,
966 64 => .p64,1076 64 => .p64,
967 else => return error.UnsupportedELFArchitecture,1077 else => return error.UnsupportedELFArchitecture,
968 },1078 },
969 .symtab_dirty = true,
970 .shdr_table_dirty = true,1079 .shdr_table_dirty = true,
971 };1080 };
972 errdefer self.deinit();1081 errdefer self.deinit();
...@@ -1018,7 +1127,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf...@@ -1018,7 +1127,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
1018 .allocator = allocator,1127 .allocator = allocator,
1019 .file = file,1128 .file = file,
1020 .options = options,1129 .options = options,
1021 .ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) {1130 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
1022 32 => .p32,1131 32 => .p32,
1023 64 => .p64,1132 64 => .p64,
1024 else => return error.UnsupportedELFArchitecture,1133 else => return error.UnsupportedELFArchitecture,