authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-22 23:42:58-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-22 23:42:58-04:00
loga3dfe36ca1dac946f507c8b69241a93891bf7da5
treeb42c10aebf181fbb317ecc7a94d5f6be64f042a3
parente8545db9d4ced8978c5594c637d9bf76dc26209d

zir-to-elf skeleton


5 files changed, 565 insertions(+), 530 deletions(-)

lib/std/fs.zig+4-2
...@@ -1345,8 +1345,10 @@ pub const Dir = struct {...@@ -1345,8 +1345,10 @@ pub const Dir = struct {
1345 mode: File.Mode = File.default_mode,1345 mode: File.Mode = File.default_mode,
1346 };1346 };
13471347
1348 /// `dest_path` must remain valid for the lifetime of `AtomicFile`.1348 /// Directly access the `.file` field, and then call `AtomicFile.finish`
1349 /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.1349 /// to atomically replace `dest_path` with contents.
1350 /// Always call `AtomicFile.deinit` to clean up, regardless of whether `AtomicFile.finish` succeeded.
1351 /// `dest_path` must remain valid until `AtomicFile.deinit` is called.
1350 pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {1352 pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {
1351 if (path.dirname(dest_path)) |dirname| {1353 if (path.dirname(dest_path)) |dirname| {
1352 const dir = try self.openDir(dirname, .{});1354 const dir = try self.openDir(dirname, .{});
lib/std/fs/file.zig+1-1
...@@ -93,7 +93,7 @@ pub const File = struct {...@@ -93,7 +93,7 @@ pub const File = struct {
93 /// This means that a process that does not respect the locking API can still get access93 /// This means that a process that does not respect the locking API can still get access
94 /// to the file, despite the lock.94 /// to the file, despite the lock.
95 ///95 ///
96 /// Windows' file locks are mandatory, and any process attempting to access the file will96 /// Windows's file locks are mandatory, and any process attempting to access the file will
97 /// receive an error.97 /// receive an error.
98 ///98 ///
99 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt99 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
lib/std/mem.zig+14-2
...@@ -2027,7 +2027,13 @@ test "sliceAsBytes and bytesAsSlice back" {...@@ -2027,7 +2027,13 @@ test "sliceAsBytes and bytesAsSlice back" {
2027/// Round an address up to the nearest aligned address2027/// Round an address up to the nearest aligned address
2028/// The alignment must be a power of 2 and greater than 0.2028/// The alignment must be a power of 2 and greater than 0.
2029pub fn alignForward(addr: usize, alignment: usize) usize {2029pub fn alignForward(addr: usize, alignment: usize) usize {
2030 return alignBackward(addr + (alignment - 1), alignment);2030 return alignForwardGeneric(usize, addr, alignment);
2031}
2032
2033/// Round an address up to the nearest aligned address
2034/// The alignment must be a power of 2 and greater than 0.
2035pub fn alignForwardGeneric(comptime T: type, addr: T, alignment: T) T {
2036 return alignBackwardGeneric(T, addr + (alignment - 1), alignment);
2031}2037}
20322038
2033test "alignForward" {2039test "alignForward" {
...@@ -2048,7 +2054,13 @@ test "alignForward" {...@@ -2048,7 +2054,13 @@ test "alignForward" {
2048/// Round an address up to the previous aligned address2054/// Round an address up to the previous aligned address
2049/// The alignment must be a power of 2 and greater than 0.2055/// The alignment must be a power of 2 and greater than 0.
2050pub fn alignBackward(addr: usize, alignment: usize) usize {2056pub fn alignBackward(addr: usize, alignment: usize) usize {
2051 assert(@popCount(usize, alignment) == 1);2057 return alignBackwardGeneric(usize, addr, alignment);
2058}
2059
2060/// Round an address up to the previous aligned address
2061/// The alignment must be a power of 2 and greater than 0.
2062pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {
2063 assert(@popCount(T, alignment) == 1);
2052 // 000010000 // example addr2064 // 000010000 // example addr
2053 // 000001111 // subtract 12065 // 000001111 // subtract 1
2054 // 111110000 // binary not2066 // 111110000 // binary not
src-self-hosted/ir.zig+18-10
...@@ -96,6 +96,7 @@ pub const Module = struct {...@@ -96,6 +96,7 @@ pub const Module = struct {
96 errors: []ErrorMsg,96 errors: []ErrorMsg,
97 arena: std.heap.ArenaAllocator,97 arena: std.heap.ArenaAllocator,
98 fns: []Fn,98 fns: []Fn,
99 target: Target,
99100
100 pub const Export = struct {101 pub const Export = struct {
101 name: []const u8,102 name: []const u8,
...@@ -122,9 +123,7 @@ pub const ErrorMsg = struct {...@@ -122,9 +123,7 @@ pub const ErrorMsg = struct {
122 msg: []const u8,123 msg: []const u8,
123};124};
124125
125pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {126pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !Module {
126 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
127
128 var ctx = Analyze{127 var ctx = Analyze{
129 .allocator = allocator,128 .allocator = allocator,
130 .arena = std.heap.ArenaAllocator.init(allocator),129 .arena = std.heap.ArenaAllocator.init(allocator),
...@@ -133,7 +132,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {...@@ -133,7 +132,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
133 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),132 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
134 .exports = std.ArrayList(Module.Export).init(allocator),133 .exports = std.ArrayList(Module.Export).init(allocator),
135 .fns = std.ArrayList(Module.Fn).init(allocator),134 .fns = std.ArrayList(Module.Fn).init(allocator),
136 .target = native_info.target,135 .target = target,
137 };136 };
138 defer ctx.errors.deinit();137 defer ctx.errors.deinit();
139 defer ctx.decl_table.deinit();138 defer ctx.decl_table.deinit();
...@@ -152,6 +151,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {...@@ -152,6 +151,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
152 .errors = ctx.errors.toOwnedSlice(),151 .errors = ctx.errors.toOwnedSlice(),
153 .fns = ctx.fns.toOwnedSlice(),152 .fns = ctx.fns.toOwnedSlice(),
154 .arena = ctx.arena,153 .arena = ctx.arena,
154 .target = target,
155 };155 };
156}156}
157157
...@@ -699,7 +699,9 @@ pub fn main() anyerror!void {...@@ -699,7 +699,9 @@ pub fn main() anyerror!void {
699 std.process.exit(1);699 std.process.exit(1);
700 }700 }
701701
702 var analyzed_module = try analyze(allocator, zir_module);702 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
703
704 var analyzed_module = try analyze(allocator, zir_module, native_info.target);
703 defer analyzed_module.deinit(allocator);705 defer analyzed_module.deinit(allocator);
704706
705 if (analyzed_module.errors.len != 0) {707 if (analyzed_module.errors.len != 0) {
...@@ -711,12 +713,18 @@ pub fn main() anyerror!void {...@@ -711,12 +713,18 @@ pub fn main() anyerror!void {
711 std.process.exit(1);713 std.process.exit(1);
712 }714 }
713715
714 var new_zir_module = try text.emit_zir(allocator, analyzed_module);716 const output_zir = false;
715 defer new_zir_module.deinit(allocator);717 if (output_zir) {
718 var new_zir_module = try text.emit_zir(allocator, analyzed_module);
719 defer new_zir_module.deinit(allocator);
720
721 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
722 try new_zir_module.writeToStream(allocator, bos.outStream());
723 try bos.flush();
724 }
716725
717 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());726 const link = @import("link.zig");
718 try new_zir_module.writeToStream(allocator, bos.outStream());727 try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out");
719 try bos.flush();
720}728}
721729
722fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {730fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
src-self-hosted/link.zig+528-515
...@@ -1,576 +1,589 @@...@@ -1,576 +1,589 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
3const c = @import("c.zig");
4const Compilation = @import("compilation.zig").Compilation;
5const Target = std.Target;
6const ObjectFormat = Target.ObjectFormat;
7const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
8const assert = std.debug.assert;3const assert = std.debug.assert;
9const util = @import("util.zig");4const Allocator = std.mem.Allocator;
105const ir = @import("ir.zig");
11const Context = struct {6const fs = std.fs;
12 comp: *Compilation,7const elf = std.elf;
13 arena: std.heap.ArenaAllocator,8
14 args: std.ArrayList([*:0]const u8),9const executable_mode = 0o755;
15 link_in_crt: bool,10const default_entry_addr = 0x8000000;
11
12/// Attempts incremental linking, if the file already exists.
13/// If incremental linking fails, falls back to truncating the file and rewriting it.
14/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
15/// This operation is not atomic.
16pub fn updateExecutableFilePath(allocator: *Allocator, module: ir.Module, dir: fs.Dir, sub_path: []const u8) !void {
17 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode });
18 defer file.close();
19
20 return updateExecutableFile(allocator, module, file);
21}
1622
17 link_err: error{OutOfMemory}!void,23/// Atomically overwrites the old file, if present.
18 link_msg: std.ArrayListSentineled(u8, 0),24pub fn writeExecutableFilePath(allocator: *Allocator, module: ir.Module, dir: fs.Dir, sub_path: []const u8) !void {
25 const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode });
26 defer af.deinit();
1927
20 libc: *LibCInstallation,28 try writeExecutableFile(allocator, module, af.file);
21 out_file_path: std.ArrayListSentineled(u8, 0),29 try af.finish();
22};30}
2331
24pub fn link(comp: *Compilation) !void {32/// Attempts incremental linking, if the file already exists.
25 var ctx = Context{33/// If incremental linking fails, falls back to truncating the file and rewriting it.
26 .comp = comp,34/// Returns an error if `file` is not already open with +read +write +seek abilities.
27 .arena = std.heap.ArenaAllocator.init(comp.gpa()),35/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
28 .args = undefined,36/// This operation is not atomic.
29 .link_in_crt = comp.haveLibC() and comp.kind == .Exe,37pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !void {
30 .link_err = {},38 updateExecutableFileInner(allocator, module, file) catch |err| switch (err) {
31 .link_msg = undefined,39 error.IncrFailed => {
32 .libc = undefined,40 return writeExecutableFile(allocator, module, file);
33 .out_file_path = undefined,
34 };
35 defer ctx.arena.deinit();
36 ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator);
37 ctx.link_msg = std.ArrayListSentineled(u8, 0).initNull(&ctx.arena.allocator);
38
39 ctx.out_file_path = try std.ArrayListSentineled(u8, 0).init(&ctx.arena.allocator, comp.name.span());
40 switch (comp.kind) {
41 .Exe => {
42 try ctx.out_file_path.append(comp.target.exeFileExt());
43 },
44 .Lib => {
45 try ctx.out_file_path.append(if (comp.is_static) comp.target.staticLibSuffix() else comp.target.dynamicLibSuffix());
46 },
47 .Obj => {
48 try ctx.out_file_path.append(comp.target.oFileExt());
49 },41 },
50 }42 else => |e| return e,
43 };
44}
5145
52 // even though we're calling LLD as a library it thinks the first46const Update = struct {
53 // argument is its own exe name47 file: fs.File,
54 try ctx.args.append("lld");48 module: *const ir.Module,
5549
56 if (comp.haveLibC()) {50 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
57 // TODO https://github.com/ziglang/zig/issues/319051 /// Same order as in the file.
58 var libc = ctx.comp.override_libc orelse blk: {52 sections: std.ArrayList(elf.Elf64_Shdr),
59 @panic("this code has bitrotted");53 shdr_table_offset: ?u64,
60 //switch (comp.target) {54
61 // Target.Native => {55 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
62 // break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;56 /// Same order as in the file.
63 // },57 program_headers: std.ArrayList(elf.Elf64_Phdr),
64 // else => return error.LibCRequiredButNotProvidedOrFound,58 phdr_table_offset: ?u64,
65 //}59 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
66 };60 phdr_load_re_index: ?u16,
67 ctx.libc = libc;61 entry_addr: ?u64,
62
63 shstrtab: std.ArrayList(u8),
64 shstrtab_index: ?u16,
65
66 text_section_index: ?u16,
67 symtab_section_index: ?u16,
68
69 /// Key: index into strtab. Value: index into symbols.
70 symbol_table: std.AutoHashMap(usize, usize),
71 /// The same order as in the file
72 symbols: std.ArrayList(elf.Elf64_Sym),
73 /// Sorted by address, index into symbols
74 symbols_by_addr: std.ArrayList(usize),
75
76 fn deinit(self: *Update) void {
77 self.sections.deinit();
78 self.program_headers.deinit();
79 self.shstrtab.deinit();
80 self.symbol_table.deinit();
81 self.symbols.deinit();
82 self.symbols_by_addr.deinit();
68 }83 }
6984
70 try constructLinkerArgs(&ctx);85 // `expand_num / expand_den` is the factor of padding when allocation
86 const alloc_num = 4;
87 const alloc_den = 3;
88
89 /// Returns end pos of collision, if any.
90 fn detectAllocCollision(self: *Update, start: u64, size: u64) ?u64 {
91 const small_ptr = self.module.target.cpu.arch.ptrBitWidth() == 32;
92 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
93 if (start < ehdr_size)
94 return ehdr_size;
95
96 const end = start + satMul(size, alloc_num) / alloc_den;
97
98 if (self.shdr_table_offset) |off| {
99 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
100 const tight_size = self.sections.items.len * shdr_size;
101 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
102 const test_end = off + increased_size;
103 if (end > off and start < test_end) {
104 return test_end;
105 }
106 }
71107
72 if (comp.verbose_link) {108 if (self.phdr_table_offset) |off| {
73 for (ctx.args.span()) |arg, i| {109 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
74 const space = if (i == 0) "" else " ";110 const tight_size = self.sections.items.len * phdr_size;
75 std.debug.warn("{}{s}", .{ space, arg });111 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
112 const test_end = off + increased_size;
113 if (end > off and start < test_end) {
114 return test_end;
115 }
76 }116 }
77 std.debug.warn("\n", .{});
78 }
79117
80 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());118 for (self.sections.items) |section| {
81 const args_slice = ctx.args.span();119 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
82120 const test_end = section.sh_offset + increased_size;
83 {121 if (end > section.sh_offset and start < test_end) {
84 // LLD is not thread-safe, so we grab a global lock.122 return test_end;
85 const held = comp.zig_compiler.lld_lock.acquire();123 }
86 defer held.release();124 }
87125 for (self.program_headers.items) |program_header| {
88 // Not evented I/O. LLD does its own multithreading internally.126 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
89 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {127 const test_end = program_header.p_offset + increased_size;
90 if (!ctx.link_msg.isNull()) {128 if (end > program_header.p_offset and start < test_end) {
91 // TODO capture these messages and pass them through the system, reporting them through the129 return test_end;
92 // event system instead of printing them directly here.
93 // perhaps try to parse and understand them.
94 std.debug.warn("{}\n", .{ctx.link_msg.span()});
95 }130 }
96 return error.LinkFailed;
97 }131 }
132 return null;
98 }133 }
99}
100134
101extern fn ZigLLDLink(135 fn allocatedSize(self: *Update, start: u64) u64 {
102 oformat: c.ZigLLVM_ObjectFormatType,136 var min_pos: u64 = std.math.maxInt(u64);
103 args: [*]const [*]const u8,137 if (self.shdr_table_offset) |off| {
104 arg_count: usize,138 if (off > start and off < min_pos) min_pos = off;
105 append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,139 }
106 context: *c_void,140 if (self.phdr_table_offset) |off| {
107) bool;141 if (off > start and off < min_pos) min_pos = off;
108142 }
109fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) callconv(.C) void {143 for (self.sections.items) |section| {
110 const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));144 if (section.sh_offset <= start) continue;
111 ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);145 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
112}146 }
113147 for (self.program_headers.items) |program_header| {
114fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {148 if (program_header.p_offset <= start) continue;
115 if (ctx.link_msg.isNull()) {149 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
116 try ctx.link_msg.resize(0);150 }
151 return min_pos;
117 }152 }
118 try ctx.link_msg.append(msg);
119}
120
121fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType {
122 return switch (ofmt) {
123 .unknown => .ZigLLVM_UnknownObjectFormat,
124 .coff => .ZigLLVM_COFF,
125 .elf => .ZigLLVM_ELF,
126 .macho => .ZigLLVM_MachO,
127 .wasm => .ZigLLVM_Wasm,
128 };
129}
130153
131fn constructLinkerArgs(ctx: *Context) !void {154 fn findFreeSpace(self: *Update, object_size: u64, min_alignment: u16) u64 {
132 switch (ctx.comp.target.getObjectFormat()) {155 var start: u64 = 0;
133 .unknown => unreachable,156 while (self.detectAllocCollision(start, object_size)) |item_end| {
134 .coff => return constructLinkerArgsCoff(ctx),157 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
135 .elf => return constructLinkerArgsElf(ctx),158 }
136 .macho => return constructLinkerArgsMachO(ctx),159 return start;
137 .wasm => return constructLinkerArgsWasm(ctx),
138 }160 }
139}
140161
141fn constructLinkerArgsElf(ctx: *Context) !void {162 fn makeString(self: *Update, bytes: []const u8) !u32 {
142 // TODO commented out code in this function163 const result = self.shstrtab.items.len;
143 //if (g->linker_script) {164 try self.shstrtab.appendSlice(bytes);
144 // lj->args.append("-T");165 return @intCast(u32, result);
145 // lj->args.append(g->linker_script);
146 //}
147 try ctx.args.append("--gc-sections");
148 if (ctx.comp.link_eh_frame_hdr) {
149 try ctx.args.append("--eh-frame-hdr");
150 }166 }
151167
152 //lj->args.append("-m");168 fn perform(self: *Update) !void {
153 //lj->args.append(getLDMOption(&g->zig_target));169 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
154170 32 => .p32,
155 //bool is_lib = g->out_type == OutTypeLib;171 64 => .p64,
156 //bool shared = !g->is_static && is_lib;172 else => return error.UnsupportedArchitecture,
157 //Buf *soname = nullptr;173 };
158 if (ctx.comp.is_static) {174 const small_ptr = switch (ptr_width) {
159 //if (util.isArmOrThumb(ctx.comp.target)) {175 .p32 => true,
160 // try ctx.args.append("-Bstatic");176 .p64 => false,
161 //} else {177 };
162 // try ctx.args.append("-static");178 // This means the entire read-only executable program code needs to be rewritten.
163 //}179 var phdr_load_re_dirty = false;
164 }180 var phdr_table_dirty = false;
165 //} else if (shared) {181 var shdr_table_dirty = false;
166 // lj->args.append("-shared");182 var shstrtab_dirty = false;
167183 var symtab_dirty = false;
168 // if (buf_len(&lj->out_file) == 0) {184
169 // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "",185 if (self.phdr_load_re_index == null) {
170 // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);186 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
171 // }187 const file_size = 256 * 1024;
172 // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);188 const p_align = 0x1000;
173 //}189 const off = self.findFreeSpace(file_size, p_align);
174190 try self.program_headers.append(.{
175 try ctx.args.append("-o");191 .p_type = elf.PT_LOAD,
176 try ctx.args.append(ctx.out_file_path.span());192 .p_offset = off,
177193 .p_filesz = file_size,
178 if (ctx.link_in_crt) {194 .p_vaddr = default_entry_addr,
179 const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";195 .p_paddr = default_entry_addr,
180 try addPathJoin(ctx, ctx.libc.crt_dir.?, crt1o);196 .p_memsz = 0,
181 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crti.o");197 .p_align = 0x1000,
182 }198 .p_flags = elf.PF_X | elf.PF_R,
199 });
200 self.entry_addr = default_entry_addr;
201 phdr_load_re_dirty = true;
202 phdr_table_dirty = true;
203 }
204 if (self.sections.items.len == 0) {
205 // There must always be a null section in index 0
206 try self.sections.append(.{
207 .sh_name = 0,
208 .sh_type = 0,
209 .sh_flags = 0,
210 .sh_addr = 0,
211 .sh_offset = 0,
212 .sh_size = 0,
213 .sh_link = 0,
214 .sh_info = 0,
215 .sh_addralign = 0,
216 .sh_entsize = 0,
217 });
218 shdr_table_dirty = true;
219 }
220 if (self.shstrtab_index == null) {
221 self.shstrtab_index = @intCast(u16, self.sections.items.len);
222 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
223 try self.sections.append(.{
224 .sh_name = try self.makeString(".shstrtab"),
225 .sh_type = elf.SHT_STRTAB,
226 .sh_flags = 0,
227 .sh_addr = 0,
228 .sh_offset = off,
229 .sh_size = self.shstrtab.items.len,
230 .sh_link = 0,
231 .sh_info = 0,
232 .sh_addralign = 1,
233 .sh_entsize = 0,
234 });
235 shstrtab_dirty = true;
236 shdr_table_dirty = true;
237 }
238 if (self.text_section_index == null) {
239 self.text_section_index = @intCast(u16, self.sections.items.len);
240 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
241
242 try self.sections.append(.{
243 .sh_name = try self.makeString(".text"),
244 .sh_type = elf.SHT_PROGBITS,
245 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
246 .sh_addr = phdr.p_vaddr,
247 .sh_offset = phdr.p_offset,
248 .sh_size = phdr.p_filesz,
249 .sh_link = 0,
250 .sh_info = 0,
251 .sh_addralign = phdr.p_align,
252 .sh_entsize = 0,
253 });
254 shdr_table_dirty = true;
255 }
256 if (self.symtab_section_index == null) {
257 self.symtab_section_index = @intCast(u16, self.sections.items.len);
258 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
259 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
260 const file_size = self.module.exports.len * each_size;
261 const off = self.findFreeSpace(file_size, min_align);
262
263 try self.sections.append(.{
264 .sh_name = try self.makeString(".symtab"),
265 .sh_type = elf.SHT_SYMTAB,
266 .sh_flags = 0,
267 .sh_addr = 0,
268 .sh_offset = off,
269 .sh_size = file_size,
270 // The section header index of the associated string table.
271 .sh_link = self.shstrtab_index.?,
272 // One greater than the symbol table index of the last local symbol (binding STB_LOCAL).
273 .sh_info = @intCast(u32, self.module.exports.len),
274 .sh_addralign = min_align,
275 .sh_entsize = each_size,
276 });
277 symtab_dirty = true;
278 shdr_table_dirty = true;
279 }
280 const shsize: u64 = switch (ptr_width) {
281 .p32 => @sizeOf(elf.Elf32_Shdr),
282 .p64 => @sizeOf(elf.Elf64_Shdr),
283 };
284 const shalign: u16 = switch (ptr_width) {
285 .p32 => @alignOf(elf.Elf32_Shdr),
286 .p64 => @alignOf(elf.Elf64_Shdr),
287 };
288 if (self.shdr_table_offset == null) {
289 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
290 shdr_table_dirty = true;
291 }
292 const phsize: u64 = switch (ptr_width) {
293 .p32 => @sizeOf(elf.Elf32_Phdr),
294 .p64 => @sizeOf(elf.Elf64_Phdr),
295 };
296 const phalign: u16 = switch (ptr_width) {
297 .p32 => @alignOf(elf.Elf32_Phdr),
298 .p64 => @alignOf(elf.Elf64_Phdr),
299 };
300 if (self.phdr_table_offset == null) {
301 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
302 phdr_table_dirty = true;
303 }
304 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
305 if (phdr_table_dirty) {
306 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
307 const needed_size = self.program_headers.items.len * phsize;
183308
184 if (ctx.comp.haveLibC()) {309 if (needed_size > allocated_size) {
185 try ctx.args.append("-L");310 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
186 // TODO addNullByte should probably return [:0]u8311 }
187 try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.crt_dir.?)).ptr));
188
189 //if (!ctx.comp.is_static) {
190 // const dl = blk: {
191 // //if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
192 // //if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl;
193 // return error.LibCMissingDynamicLinker;
194 // };
195 // try ctx.args.append("-dynamic-linker");
196 // try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
197 //}
198 }
199312
200 //if (shared) {313 const allocator = self.program_headers.allocator;
201 // lj->args.append("-soname");314 switch (ptr_width) {
202 // lj->args.append(buf_ptr(soname));315 .p32 => {
203 //}316 const buf = try allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
317 defer allocator.free(buf);
318
319 for (buf) |*phdr, i| {
320 phdr.* = .{
321 .p_type = self.program_headers.items[i].p_type,
322 .p_flags = self.program_headers.items[i].p_flags,
323 .p_offset = @intCast(u32, self.program_headers.items[i].p_offset),
324 .p_vaddr = @intCast(u32, self.program_headers.items[i].p_vaddr),
325 .p_paddr = @intCast(u32, self.program_headers.items[i].p_paddr),
326 .p_filesz = @intCast(u32, self.program_headers.items[i].p_filesz),
327 .p_memsz = @intCast(u32, self.program_headers.items[i].p_memsz),
328 .p_align = @intCast(u32, self.program_headers.items[i].p_align),
329 };
330 if (foreign_endian) {
331 bswapAllFields(elf.Elf32_Phdr, phdr);
332 }
333 }
334 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
335 },
336 .p64 => {
337 const buf = try allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
338 defer allocator.free(buf);
339
340 for (buf) |*phdr, i| {
341 phdr.* = .{
342 .p_type = self.program_headers.items[i].p_type,
343 .p_flags = self.program_headers.items[i].p_flags,
344 .p_offset = self.program_headers.items[i].p_offset,
345 .p_vaddr = self.program_headers.items[i].p_vaddr,
346 .p_paddr = self.program_headers.items[i].p_paddr,
347 .p_filesz = self.program_headers.items[i].p_filesz,
348 .p_memsz = self.program_headers.items[i].p_memsz,
349 .p_align = self.program_headers.items[i].p_align,
350 };
351 if (foreign_endian) {
352 bswapAllFields(elf.Elf64_Phdr, phdr);
353 }
354 }
355 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
356 },
357 }
358 }
359 if (shdr_table_dirty) {
360 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
361 const needed_size = self.sections.items.len * phsize;
204362
205 // .o files363 if (needed_size > allocated_size) {
206 for (ctx.comp.link_objects) |link_object| {364 self.shdr_table_offset = self.findFreeSpace(needed_size, phalign);
207 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);365 }
208 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));366
209 }367 const allocator = self.sections.allocator;
210 try addFnObjects(ctx);368 switch (ptr_width) {
211369 .p32 => {
212 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {370 const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
213 // if (g->libc_link_lib == nullptr) {371 defer allocator.free(buf);
214 // Buf *builtin_o_path = build_o(g, "builtin");372
215 // lj->args.append(buf_ptr(builtin_o_path));373 for (buf) |*shdr, i| {
216 // }374 shdr.* = .{
217375 .sh_name = self.sections.items[i].sh_name,
218 // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage376 .sh_type = self.sections.items[i].sh_type,
219 // Buf *compiler_rt_o_path = build_compiler_rt(g);377 .sh_flags = @intCast(u32, self.sections.items[i].sh_flags),
220 // lj->args.append(buf_ptr(compiler_rt_o_path));378 .sh_addr = @intCast(u32, self.sections.items[i].sh_addr),
221 //}379 .sh_offset = @intCast(u32, self.sections.items[i].sh_offset),
222380 .sh_size = @intCast(u32, self.sections.items[i].sh_size),
223 //for (size_t i = 0; i < g->link_libs_list.length; i += 1) {381 .sh_link = self.sections.items[i].sh_link,
224 // LinkLib *link_lib = g->link_libs_list.at(i);382 .sh_info = self.sections.items[i].sh_info,
225 // if (buf_eql_str(link_lib->name, "c")) {383 .sh_addralign = @intCast(u32, self.sections.items[i].sh_addralign),
226 // continue;384 .sh_entsize = @intCast(u32, self.sections.items[i].sh_entsize),
227 // }385 };
228 // Buf *arg;386 if (foreign_endian) {
229 // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||387 bswapAllFields(elf.Elf32_Shdr, shdr);
230 // buf_ends_with_str(link_lib->name, ".so"))388 }
231 // {389 }
232 // arg = link_lib->name;390 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
233 // } else {391 },
234 // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));392 .p64 => {
235 // }393 const buf = try allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
236 // lj->args.append(buf_ptr(arg));394 defer allocator.free(buf);
237 //}395
238396 for (buf) |*shdr, i| {
239 // libc dep397 shdr.* = .{
240 if (ctx.comp.haveLibC()) {398 .sh_name = self.sections.items[i].sh_name,
241 if (ctx.comp.is_static) {399 .sh_type = self.sections.items[i].sh_type,
242 try ctx.args.append("--start-group");400 .sh_flags = self.sections.items[i].sh_flags,
243 try ctx.args.append("-lgcc");401 .sh_addr = self.sections.items[i].sh_addr,
244 try ctx.args.append("-lgcc_eh");402 .sh_offset = self.sections.items[i].sh_offset,
245 try ctx.args.append("-lc");403 .sh_size = self.sections.items[i].sh_size,
246 try ctx.args.append("-lm");404 .sh_link = self.sections.items[i].sh_link,
247 try ctx.args.append("--end-group");405 .sh_info = self.sections.items[i].sh_info,
248 } else {406 .sh_addralign = self.sections.items[i].sh_addralign,
249 try ctx.args.append("-lgcc");407 .sh_entsize = self.sections.items[i].sh_entsize,
250 try ctx.args.append("--as-needed");408 };
251 try ctx.args.append("-lgcc_s");409 if (foreign_endian) {
252 try ctx.args.append("--no-as-needed");410 bswapAllFields(elf.Elf64_Shdr, shdr);
253 try ctx.args.append("-lc");411 }
254 try ctx.args.append("-lm");412 }
255 try ctx.args.append("-lgcc");413 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
256 try ctx.args.append("--as-needed");414 },
257 try ctx.args.append("-lgcc_s");415 }
258 try ctx.args.append("--no-as-needed");416 }
417 if (shstrtab_dirty) {
418 try self.file.pwriteAll(self.shstrtab.items, self.sections.items[self.shstrtab_index.?].sh_offset);
259 }419 }
420 try self.writeCodeAndSymbols();
421 try self.writeElfHeader();
422 // TODO find end pos and truncate
260 }423 }
261424
262 // crt end425 fn writeElfHeader(self: *Update) !void {
263 if (ctx.link_in_crt) {426 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
264 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crtn.o");
265 }
266427
267 //if (ctx.comp.target != Target.Native) {428 var index: usize = 0;
268 // try ctx.args.append("--allow-shlib-undefined");429 hdr_buf[0..4].* = "\x7fELF".*;
269 //}430 index += 4;
270}
271431
272fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {432 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
273 const full_path = try std.fs.path.join(&ctx.arena.allocator, &[_][]const u8{ dirname, basename });433 32 => .p32,
274 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);434 64 => .p64,
275 try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr));435 else => return error.UnsupportedArchitecture,
276}436 };
437 hdr_buf[index] = switch (ptr_width) {
438 .p32 => elf.ELFCLASS32,
439 .p64 => elf.ELFCLASS64,
440 };
441 index += 1;
277442
278fn constructLinkerArgsCoff(ctx: *Context) !void {443 const endian = self.module.target.cpu.arch.endian();
279 try ctx.args.append("-NOLOGO");444 hdr_buf[index] = switch (endian) {
445 .Little => elf.ELFDATA2LSB,
446 .Big => elf.ELFDATA2MSB,
447 };
448 index += 1;
280449
281 if (!ctx.comp.strip) {450 hdr_buf[index] = 1; // ELF version
282 try ctx.args.append("-DEBUG");451 index += 1;
283 }
284452
285 switch (ctx.comp.target.cpu.arch) {453 // OS ABI, often set to 0 regardless of target platform
286 .i386 => try ctx.args.append("-MACHINE:X86"),454 // ABI Version, possibly used by glibc but not by static executables
287 .x86_64 => try ctx.args.append("-MACHINE:X64"),455 // padding
288 .aarch64 => try ctx.args.append("-MACHINE:ARM"),456 mem.set(u8, hdr_buf[index..][0..9], 0);
289 else => return error.UnsupportedLinkArchitecture,457 index += 9;
290 }
291458
292 const is_library = ctx.comp.kind == .Lib;459 assert(index == 16);
293460
294 const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", .{ctx.out_file_path.span()});461 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian);
295 try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr));462 index += 2;
296463
297 if (ctx.comp.haveLibC()) {464 const machine = self.module.target.cpu.arch.toElfMachine();
298 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr));465 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
299 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr));466 index += 2;
300 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.crt_dir.?})).ptr));
301 }
302467
303 if (ctx.link_in_crt) {468 // ELF Version, again
304 const lib_str = if (ctx.comp.is_static) "lib" else "";469 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
305 const d_str = if (ctx.comp.build_mode == .Debug) "d" else "";470 index += 4;
306471
307 if (ctx.comp.is_static) {472 switch (ptr_width) {
308 const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str});473 .p32 => {
309 try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr));474 // e_entry
310 } else {475 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian);
311 const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str});476 index += 4;
312 try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr));
313 }
314477
315 const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", .{478 // e_phoff
316 lib_str,479 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
317 d_str,480 index += 4;
318 });481
319 try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr));482 // e_shoff
320483 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
321 const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str });484 index += 4;
322 try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr));485 },
323486 .p64 => {
324 // Visual C++ 2015 Conformance Changes487 // e_entry
325 // https://msdn.microsoft.com/en-us/library/bb531344.aspx488 mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian);
326 try ctx.args.append("legacy_stdio_definitions.lib");489 index += 8;
327490
328 // msvcrt depends on kernel32491 // e_phoff
329 try ctx.args.append("kernel32.lib");492 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
330 } else {493 index += 8;
331 try ctx.args.append("-NODEFAULTLIB");494
332 if (!is_library) {495 // e_shoff
333 try ctx.args.append("-ENTRY:WinMainCRTStartup");496 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
497 index += 8;
498 },
334 }499 }
335 }
336500
337 if (is_library and !ctx.comp.is_static) {501 const e_flags = 0;
338 try ctx.args.append("-DLL");502 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
339 }503 index += 4;
340504
341 for (ctx.comp.link_objects) |link_object| {505 const e_ehsize: u16 = switch (ptr_width) {
342 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);506 .p32 => @sizeOf(elf.Elf32_Ehdr),
343 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));507 .p64 => @sizeOf(elf.Elf64_Ehdr),
344 }508 };
345 try addFnObjects(ctx);509 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
510 index += 2;
346511
347 switch (ctx.comp.kind) {512 const e_phentsize: u16 = switch (ptr_width) {
348 .Exe, .Lib => {513 .p32 => @sizeOf(elf.Elf32_Phdr),
349 if (!ctx.comp.haveLibC()) {514 .p64 => @sizeOf(elf.Elf64_Phdr),
350 @panic("TODO");515 };
351 }516 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
352 },517 index += 2;
353 .Obj => {},
354 }
355}
356518
357fn constructLinkerArgsMachO(ctx: *Context) !void {519 const e_phnum = @intCast(u16, self.program_headers.items.len);
358 try ctx.args.append("-demangle");520 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
521 index += 2;
359522
360 if (ctx.comp.linker_rdynamic) {523 const e_shentsize: u16 = switch (ptr_width) {
361 try ctx.args.append("-export_dynamic");524 .p32 => @sizeOf(elf.Elf32_Shdr),
362 }525 .p64 => @sizeOf(elf.Elf64_Shdr),
526 };
527 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
528 index += 2;
363529
364 const is_lib = ctx.comp.kind == .Lib;530 const e_shnum = @intCast(u16, self.sections.items.len);
365 const shared = !ctx.comp.is_static and is_lib;531 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
366 if (ctx.comp.is_static) {532 index += 2;
367 try ctx.args.append("-static");
368 } else {
369 try ctx.args.append("-dynamic");
370 }
371533
372 try ctx.args.append("-arch");534 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
373 try ctx.args.append(util.getDarwinArchString(ctx.comp.target));535 index += 2;
374536
375 const platform = try DarwinPlatform.get(ctx.comp);537 assert(index == e_ehsize);
376 switch (platform.kind) {
377 .MacOS => try ctx.args.append("-macosx_version_min"),
378 .IPhoneOS => try ctx.args.append("-iphoneos_version_min"),
379 .IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"),
380 }
381 const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", .{
382 platform.major,
383 platform.minor,
384 platform.micro,
385 });
386 try ctx.args.append(@ptrCast([*:0]const u8, ver_str.ptr));
387
388 if (ctx.comp.kind == .Exe) {
389 if (ctx.comp.is_static) {
390 try ctx.args.append("-no_pie");
391 } else {
392 try ctx.args.append("-pie");
393 }
394 }
395538
396 try ctx.args.append("-o");539 try self.file.pwriteAll(hdr_buf[0..index], 0);
397 try ctx.args.append(ctx.out_file_path.span());
398
399 if (shared) {
400 try ctx.args.append("-headerpad_max_install_names");
401 } else if (ctx.comp.is_static) {
402 try ctx.args.append("-lcrt0.o");
403 } else {
404 switch (platform.kind) {
405 .MacOS => {
406 if (platform.versionLessThan(10, 5)) {
407 try ctx.args.append("-lcrt1.o");
408 } else if (platform.versionLessThan(10, 6)) {
409 try ctx.args.append("-lcrt1.10.5.o");
410 } else if (platform.versionLessThan(10, 8)) {
411 try ctx.args.append("-lcrt1.10.6.o");
412 }
413 },
414 .IPhoneOS => {
415 if (ctx.comp.target.cpu.arch == .aarch64) {
416 // iOS does not need any crt1 files for arm64
417 } else if (platform.versionLessThan(3, 1)) {
418 try ctx.args.append("-lcrt1.o");
419 } else if (platform.versionLessThan(6, 0)) {
420 try ctx.args.append("-lcrt1.3.1.o");
421 }
422 },
423 .IPhoneOSSimulator => {}, // no crt1.o needed
424 }
425 }
426
427 for (ctx.comp.link_objects) |link_object| {
428 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
429 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
430 }540 }
431 try addFnObjects(ctx);
432
433 // TODO
434 //if (ctx.comp.target == Target.Native) {
435 // for (ctx.comp.link_libs_list.span()) |lib| {
436 // if (mem.eql(u8, lib.name, "c")) {
437 // // on Darwin, libSystem has libc in it, but also you have to use it
438 // // to make syscalls because the syscall numbers are not documented
439 // // and change between versions.
440 // // so we always link against libSystem
441 // try ctx.args.append("-lSystem");
442 // } else {
443 // if (mem.indexOfScalar(u8, lib.name, '/') == null) {
444 // const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name});
445 // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
446 // } else {
447 // const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
448 // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
449 // }
450 // }
451 // }
452 //} else {
453 // try ctx.args.append("-undefined");
454 // try ctx.args.append("dynamic_lookup");
455 //}
456
457 if (platform.kind == .MacOS) {
458 if (platform.versionLessThan(10, 5)) {
459 try ctx.args.append("-lgcc_s.10.4");
460 } else if (platform.versionLessThan(10, 6)) {
461 try ctx.args.append("-lgcc_s.10.5");
462 }
463 } else {
464 @panic("TODO");
465 }
466}
467541
468fn constructLinkerArgsWasm(ctx: *Context) void {542 fn writeCodeAndSymbols(self: *Update) !void {
469 @panic("TODO");543 @panic("TODO writeCodeAndSymbols");
470}
471
472fn addFnObjects(ctx: *Context) !void {
473 const held = ctx.comp.fn_link_set.acquire();
474 defer held.release();
475
476 var it = held.value.first;
477 while (it) |node| {
478 const fn_val = node.data orelse {
479 // handle the tombstone. See Value.Fn.destroy.
480 it = node.next;
481 held.value.remove(node);
482 ctx.comp.gpa().destroy(node);
483 continue;
484 };
485 try ctx.args.append(fn_val.containing_object.span());
486 it = node.next;
487 }544 }
488}545};
489
490const DarwinPlatform = struct {
491 kind: Kind,
492 major: u32,
493 minor: u32,
494 micro: u32,
495546
496 const Kind = enum {547/// Truncates the existing file contents and overwrites the contents.
497 MacOS,548/// Returns an error if `file` is not already open with +read +write +seek abilities.
498 IPhoneOS,549pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !void {
499 IPhoneOSSimulator,550 var update = Update{
551 .file = file,
552 .module = &module,
553 .sections = std.ArrayList(elf.Elf64_Shdr).init(allocator),
554 .shdr_table_offset = null,
555 .program_headers = std.ArrayList(elf.Elf64_Phdr).init(allocator),
556 .phdr_table_offset = null,
557 .phdr_load_re_index = null,
558 .entry_addr = null,
559 .shstrtab = std.ArrayList(u8).init(allocator),
560 .shstrtab_index = null,
561 .text_section_index = null,
562 .symtab_section_index = null,
563
564 .symbol_table = std.AutoHashMap(usize, usize).init(allocator),
565 .symbols = std.ArrayList(elf.Elf64_Sym).init(allocator),
566 .symbols_by_addr = std.ArrayList(usize).init(allocator),
500 };567 };
568 defer update.deinit();
501569
502 fn get(comp: *Compilation) !DarwinPlatform {570 return update.perform();
503 var result: DarwinPlatform = undefined;571}
504 const ver_str = switch (comp.darwin_version_min) {
505 .MacOS => |ver| blk: {
506 result.kind = .MacOS;
507 break :blk ver;
508 },
509 .Ios => |ver| blk: {
510 result.kind = .IPhoneOS;
511 break :blk ver;
512 },
513 .None => blk: {
514 assert(comp.target.os.tag == .macosx);
515 result.kind = .MacOS;
516 break :blk "10.14";
517 },
518 };
519572
520 var had_extra: bool = undefined;573/// Returns error.IncrFailed if incremental update could not be performed.
521 try darwinGetReleaseVersion(574fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !void {
522 ver_str,575 //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
523 &result.major,
524 &result.minor,
525 &result.micro,
526 &had_extra,
527 );
528 if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
529 return error.InvalidDarwinVersionString;
530 }
531576
532 if (result.kind == .IPhoneOS) {577 // TODO implement incremental linking
533 switch (comp.target.cpu.arch) {578 return error.IncrFailed;
534 .i386,579}
535 .x86_64,
536 => result.kind = .IPhoneOSSimulator,
537 else => {},
538 }
539 }
540 return result;
541 }
542580
543 fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {581/// Saturating multiplication
544 if (self.major < major)582fn satMul(a: var, b: var) @TypeOf(a, b) {
545 return true;583 const T = @TypeOf(a, b);
546 if (self.major > major)584 return std.math.mul(T, a, b) catch std.math.maxInt(T);
547 return false;585}
548 if (self.minor < minor)
549 return true;
550 return false;
551 }
552};
553586
554/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the587fn bswapAllFields(comptime S: type, ptr: *S) void {
555/// grouped values as integers. Numbers which are not provided are set to 0.588 @panic("TODO implement bswapAllFields");
556/// return true if the entire string was parsed (9.2), or all groups were
557/// parsed (10.3.5extrastuff).
558fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {
559 major.* = 0;
560 minor.* = 0;
561 micro.* = 0;
562 had_extra.* = false;
563
564 if (str.len == 0)
565 return error.InvalidDarwinVersionString;
566
567 var start_pos: usize = 0;
568 for ([_]*u32{ major, minor, micro }) |v| {
569 const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
570 const end_pos = dot_pos orelse str.len;
571 v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
572 start_pos = (dot_pos orelse return) + 1;
573 if (start_pos == str.len) return;
574 }
575 had_extra.* = true;
576}589}