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 {
13451345 mode: File.Mode = File.default_mode,
13461346 };
13471347
1348 /// `dest_path` must remain valid for the lifetime of `AtomicFile`.
1349 /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.
1348 /// Directly access the `.file` field, and then call `AtomicFile.finish`
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.
13501352 pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {
13511353 if (path.dirname(dest_path)) |dirname| {
13521354 const dir = try self.openDir(dirname, .{});
lib/std/fs/file.zig+1-1
......@@ -93,7 +93,7 @@ pub const File = struct {
9393 /// This means that a process that does not respect the locking API can still get access
9494 /// to the file, despite the lock.
9595 ///
96 /// Windows' file locks are mandatory, and any process attempting to access the file will
96 /// Windows's file locks are mandatory, and any process attempting to access the file will
9797 /// receive an error.
9898 ///
9999 /// [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" {
20272027/// Round an address up to the nearest aligned address
20282028/// The alignment must be a power of 2 and greater than 0.
20292029pub 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);
20312037}
20322038
20332039test "alignForward" {
......@@ -2048,7 +2054,13 @@ test "alignForward" {
20482054/// Round an address up to the previous aligned address
20492055/// The alignment must be a power of 2 and greater than 0.
20502056pub 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);
20522064 // 000010000 // example addr
20532065 // 000001111 // subtract 1
20542066 // 111110000 // binary not
src-self-hosted/ir.zig+18-10
......@@ -96,6 +96,7 @@ pub const Module = struct {
9696 errors: []ErrorMsg,
9797 arena: std.heap.ArenaAllocator,
9898 fns: []Fn,
99 target: Target,
99100
100101 pub const Export = struct {
101102 name: []const u8,
......@@ -122,9 +123,7 @@ pub const ErrorMsg = struct {
122123 msg: []const u8,
123124};
124125
125pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
126 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
127
126pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !Module {
128127 var ctx = Analyze{
129128 .allocator = allocator,
130129 .arena = std.heap.ArenaAllocator.init(allocator),
......@@ -133,7 +132,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
133132 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
134133 .exports = std.ArrayList(Module.Export).init(allocator),
135134 .fns = std.ArrayList(Module.Fn).init(allocator),
136 .target = native_info.target,
135 .target = target,
137136 };
138137 defer ctx.errors.deinit();
139138 defer ctx.decl_table.deinit();
......@@ -152,6 +151,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
152151 .errors = ctx.errors.toOwnedSlice(),
153152 .fns = ctx.fns.toOwnedSlice(),
154153 .arena = ctx.arena,
154 .target = target,
155155 };
156156}
157157
......@@ -699,7 +699,9 @@ pub fn main() anyerror!void {
699699 std.process.exit(1);
700700 }
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);
703705 defer analyzed_module.deinit(allocator);
704706
705707 if (analyzed_module.errors.len != 0) {
......@@ -711,12 +713,18 @@ pub fn main() anyerror!void {
711713 std.process.exit(1);
712714 }
713715
714 var new_zir_module = try text.emit_zir(allocator, analyzed_module);
715 defer new_zir_module.deinit(allocator);
716 const output_zir = false;
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());
718 try new_zir_module.writeToStream(allocator, bos.outStream());
719 try bos.flush();
726 const link = @import("link.zig");
727 try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out");
720728}
721729
722730fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
src-self-hosted/link.zig+528-515
......@@ -1,576 +1,589 @@
11const std = @import("std");
22const 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;
83const assert = std.debug.assert;
9const util = @import("util.zig");
10
11const Context = struct {
12 comp: *Compilation,
13 arena: std.heap.ArenaAllocator,
14 args: std.ArrayList([*:0]const u8),
15 link_in_crt: bool,
4const Allocator = std.mem.Allocator;
5const ir = @import("ir.zig");
6const fs = std.fs;
7const elf = std.elf;
8
9const executable_mode = 0o755;
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,
18 link_msg: std.ArrayListSentineled(u8, 0),
23/// Atomically overwrites the old file, if present.
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,
21 out_file_path: std.ArrayListSentineled(u8, 0),
22};
28 try writeExecutableFile(allocator, module, af.file);
29 try af.finish();
30}
2331
24pub fn link(comp: *Compilation) !void {
25 var ctx = Context{
26 .comp = comp,
27 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
28 .args = undefined,
29 .link_in_crt = comp.haveLibC() and comp.kind == .Exe,
30 .link_err = {},
31 .link_msg = undefined,
32 .libc = undefined,
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());
32/// Attempts incremental linking, if the file already exists.
33/// If incremental linking fails, falls back to truncating the file and rewriting it.
34/// Returns an error if `file` is not already open with +read +write +seek abilities.
35/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
36/// This operation is not atomic.
37pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !void {
38 updateExecutableFileInner(allocator, module, file) catch |err| switch (err) {
39 error.IncrFailed => {
40 return writeExecutableFile(allocator, module, file);
4941 },
50 }
42 else => |e| return e,
43 };
44}
5145
52 // even though we're calling LLD as a library it thinks the first
53 // argument is its own exe name
54 try ctx.args.append("lld");
55
56 if (comp.haveLibC()) {
57 // TODO https://github.com/ziglang/zig/issues/3190
58 var libc = ctx.comp.override_libc orelse blk: {
59 @panic("this code has bitrotted");
60 //switch (comp.target) {
61 // Target.Native => {
62 // break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
63 // },
64 // else => return error.LibCRequiredButNotProvidedOrFound,
65 //}
66 };
67 ctx.libc = libc;
46const Update = struct {
47 file: fs.File,
48 module: *const ir.Module,
49
50 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
51 /// Same order as in the file.
52 sections: std.ArrayList(elf.Elf64_Shdr),
53 shdr_table_offset: ?u64,
54
55 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
56 /// Same order as in the file.
57 program_headers: std.ArrayList(elf.Elf64_Phdr),
58 phdr_table_offset: ?u64,
59 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
60 phdr_load_re_index: ?u16,
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();
6883 }
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) {
73 for (ctx.args.span()) |arg, i| {
74 const space = if (i == 0) "" else " ";
75 std.debug.warn("{}{s}", .{ space, arg });
108 if (self.phdr_table_offset) |off| {
109 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
110 const tight_size = self.sections.items.len * phdr_size;
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 }
76116 }
77 std.debug.warn("\n", .{});
78 }
79117
80 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
81 const args_slice = ctx.args.span();
82
83 {
84 // LLD is not thread-safe, so we grab a global lock.
85 const held = comp.zig_compiler.lld_lock.acquire();
86 defer held.release();
87
88 // Not evented I/O. LLD does its own multithreading internally.
89 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
90 if (!ctx.link_msg.isNull()) {
91 // TODO capture these messages and pass them through the system, reporting them through the
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()});
118 for (self.sections.items) |section| {
119 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
120 const test_end = section.sh_offset + increased_size;
121 if (end > section.sh_offset and start < test_end) {
122 return test_end;
123 }
124 }
125 for (self.program_headers.items) |program_header| {
126 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
127 const test_end = program_header.p_offset + increased_size;
128 if (end > program_header.p_offset and start < test_end) {
129 return test_end;
95130 }
96 return error.LinkFailed;
97131 }
132 return null;
98133 }
99}
100134
101extern fn ZigLLDLink(
102 oformat: c.ZigLLVM_ObjectFormatType,
103 args: [*]const [*]const u8,
104 arg_count: usize,
105 append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,
106 context: *c_void,
107) bool;
108
109fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) callconv(.C) void {
110 const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
111 ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);
112}
113
114fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {
115 if (ctx.link_msg.isNull()) {
116 try ctx.link_msg.resize(0);
135 fn allocatedSize(self: *Update, start: u64) u64 {
136 var min_pos: u64 = std.math.maxInt(u64);
137 if (self.shdr_table_offset) |off| {
138 if (off > start and off < min_pos) min_pos = off;
139 }
140 if (self.phdr_table_offset) |off| {
141 if (off > start and off < min_pos) min_pos = off;
142 }
143 for (self.sections.items) |section| {
144 if (section.sh_offset <= start) continue;
145 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
146 }
147 for (self.program_headers.items) |program_header| {
148 if (program_header.p_offset <= start) continue;
149 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
150 }
151 return min_pos;
117152 }
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 {
132 switch (ctx.comp.target.getObjectFormat()) {
133 .unknown => unreachable,
134 .coff => return constructLinkerArgsCoff(ctx),
135 .elf => return constructLinkerArgsElf(ctx),
136 .macho => return constructLinkerArgsMachO(ctx),
137 .wasm => return constructLinkerArgsWasm(ctx),
154 fn findFreeSpace(self: *Update, object_size: u64, min_alignment: u16) u64 {
155 var start: u64 = 0;
156 while (self.detectAllocCollision(start, object_size)) |item_end| {
157 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
158 }
159 return start;
138160 }
139}
140161
141fn constructLinkerArgsElf(ctx: *Context) !void {
142 // TODO commented out code in this function
143 //if (g->linker_script) {
144 // lj->args.append("-T");
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");
162 fn makeString(self: *Update, bytes: []const u8) !u32 {
163 const result = self.shstrtab.items.len;
164 try self.shstrtab.appendSlice(bytes);
165 return @intCast(u32, result);
150166 }
151167
152 //lj->args.append("-m");
153 //lj->args.append(getLDMOption(&g->zig_target));
154
155 //bool is_lib = g->out_type == OutTypeLib;
156 //bool shared = !g->is_static && is_lib;
157 //Buf *soname = nullptr;
158 if (ctx.comp.is_static) {
159 //if (util.isArmOrThumb(ctx.comp.target)) {
160 // try ctx.args.append("-Bstatic");
161 //} else {
162 // try ctx.args.append("-static");
163 //}
164 }
165 //} else if (shared) {
166 // lj->args.append("-shared");
167
168 // if (buf_len(&lj->out_file) == 0) {
169 // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "",
170 // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
171 // }
172 // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
173 //}
174
175 try ctx.args.append("-o");
176 try ctx.args.append(ctx.out_file_path.span());
177
178 if (ctx.link_in_crt) {
179 const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
180 try addPathJoin(ctx, ctx.libc.crt_dir.?, crt1o);
181 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crti.o");
182 }
168 fn perform(self: *Update) !void {
169 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
170 32 => .p32,
171 64 => .p64,
172 else => return error.UnsupportedArchitecture,
173 };
174 const small_ptr = switch (ptr_width) {
175 .p32 => true,
176 .p64 => false,
177 };
178 // This means the entire read-only executable program code needs to be rewritten.
179 var phdr_load_re_dirty = false;
180 var phdr_table_dirty = false;
181 var shdr_table_dirty = false;
182 var shstrtab_dirty = false;
183 var symtab_dirty = false;
184
185 if (self.phdr_load_re_index == null) {
186 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
187 const file_size = 256 * 1024;
188 const p_align = 0x1000;
189 const off = self.findFreeSpace(file_size, p_align);
190 try self.program_headers.append(.{
191 .p_type = elf.PT_LOAD,
192 .p_offset = off,
193 .p_filesz = file_size,
194 .p_vaddr = default_entry_addr,
195 .p_paddr = default_entry_addr,
196 .p_memsz = 0,
197 .p_align = 0x1000,
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()) {
185 try ctx.args.append("-L");
186 // TODO addNullByte should probably return [:0]u8
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 }
309 if (needed_size > allocated_size) {
310 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
311 }
199312
200 //if (shared) {
201 // lj->args.append("-soname");
202 // lj->args.append(buf_ptr(soname));
203 //}
313 const allocator = self.program_headers.allocator;
314 switch (ptr_width) {
315 .p32 => {
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 files
206 for (ctx.comp.link_objects) |link_object| {
207 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
208 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
209 }
210 try addFnObjects(ctx);
211
212 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
213 // if (g->libc_link_lib == nullptr) {
214 // Buf *builtin_o_path = build_o(g, "builtin");
215 // lj->args.append(buf_ptr(builtin_o_path));
216 // }
217
218 // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
219 // Buf *compiler_rt_o_path = build_compiler_rt(g);
220 // lj->args.append(buf_ptr(compiler_rt_o_path));
221 //}
222
223 //for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
224 // LinkLib *link_lib = g->link_libs_list.at(i);
225 // if (buf_eql_str(link_lib->name, "c")) {
226 // continue;
227 // }
228 // Buf *arg;
229 // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
230 // buf_ends_with_str(link_lib->name, ".so"))
231 // {
232 // arg = link_lib->name;
233 // } else {
234 // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
235 // }
236 // lj->args.append(buf_ptr(arg));
237 //}
238
239 // libc dep
240 if (ctx.comp.haveLibC()) {
241 if (ctx.comp.is_static) {
242 try ctx.args.append("--start-group");
243 try ctx.args.append("-lgcc");
244 try ctx.args.append("-lgcc_eh");
245 try ctx.args.append("-lc");
246 try ctx.args.append("-lm");
247 try ctx.args.append("--end-group");
248 } else {
249 try ctx.args.append("-lgcc");
250 try ctx.args.append("--as-needed");
251 try ctx.args.append("-lgcc_s");
252 try ctx.args.append("--no-as-needed");
253 try ctx.args.append("-lc");
254 try ctx.args.append("-lm");
255 try ctx.args.append("-lgcc");
256 try ctx.args.append("--as-needed");
257 try ctx.args.append("-lgcc_s");
258 try ctx.args.append("--no-as-needed");
363 if (needed_size > allocated_size) {
364 self.shdr_table_offset = self.findFreeSpace(needed_size, phalign);
365 }
366
367 const allocator = self.sections.allocator;
368 switch (ptr_width) {
369 .p32 => {
370 const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
371 defer allocator.free(buf);
372
373 for (buf) |*shdr, i| {
374 shdr.* = .{
375 .sh_name = self.sections.items[i].sh_name,
376 .sh_type = self.sections.items[i].sh_type,
377 .sh_flags = @intCast(u32, self.sections.items[i].sh_flags),
378 .sh_addr = @intCast(u32, self.sections.items[i].sh_addr),
379 .sh_offset = @intCast(u32, self.sections.items[i].sh_offset),
380 .sh_size = @intCast(u32, self.sections.items[i].sh_size),
381 .sh_link = self.sections.items[i].sh_link,
382 .sh_info = self.sections.items[i].sh_info,
383 .sh_addralign = @intCast(u32, self.sections.items[i].sh_addralign),
384 .sh_entsize = @intCast(u32, self.sections.items[i].sh_entsize),
385 };
386 if (foreign_endian) {
387 bswapAllFields(elf.Elf32_Shdr, shdr);
388 }
389 }
390 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
391 },
392 .p64 => {
393 const buf = try allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
394 defer allocator.free(buf);
395
396 for (buf) |*shdr, i| {
397 shdr.* = .{
398 .sh_name = self.sections.items[i].sh_name,
399 .sh_type = self.sections.items[i].sh_type,
400 .sh_flags = self.sections.items[i].sh_flags,
401 .sh_addr = self.sections.items[i].sh_addr,
402 .sh_offset = self.sections.items[i].sh_offset,
403 .sh_size = self.sections.items[i].sh_size,
404 .sh_link = self.sections.items[i].sh_link,
405 .sh_info = self.sections.items[i].sh_info,
406 .sh_addralign = self.sections.items[i].sh_addralign,
407 .sh_entsize = self.sections.items[i].sh_entsize,
408 };
409 if (foreign_endian) {
410 bswapAllFields(elf.Elf64_Shdr, shdr);
411 }
412 }
413 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
414 },
415 }
416 }
417 if (shstrtab_dirty) {
418 try self.file.pwriteAll(self.shstrtab.items, self.sections.items[self.shstrtab_index.?].sh_offset);
259419 }
420 try self.writeCodeAndSymbols();
421 try self.writeElfHeader();
422 // TODO find end pos and truncate
260423 }
261424
262 // crt end
263 if (ctx.link_in_crt) {
264 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crtn.o");
265 }
425 fn writeElfHeader(self: *Update) !void {
426 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
266427
267 //if (ctx.comp.target != Target.Native) {
268 // try ctx.args.append("--allow-shlib-undefined");
269 //}
270}
428 var index: usize = 0;
429 hdr_buf[0..4].* = "\x7fELF".*;
430 index += 4;
271431
272fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
273 const full_path = try std.fs.path.join(&ctx.arena.allocator, &[_][]const u8{ dirname, basename });
274 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
275 try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr));
276}
432 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
433 32 => .p32,
434 64 => .p64,
435 else => return error.UnsupportedArchitecture,
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 {
279 try ctx.args.append("-NOLOGO");
443 const endian = self.module.target.cpu.arch.endian();
444 hdr_buf[index] = switch (endian) {
445 .Little => elf.ELFDATA2LSB,
446 .Big => elf.ELFDATA2MSB,
447 };
448 index += 1;
280449
281 if (!ctx.comp.strip) {
282 try ctx.args.append("-DEBUG");
283 }
450 hdr_buf[index] = 1; // ELF version
451 index += 1;
284452
285 switch (ctx.comp.target.cpu.arch) {
286 .i386 => try ctx.args.append("-MACHINE:X86"),
287 .x86_64 => try ctx.args.append("-MACHINE:X64"),
288 .aarch64 => try ctx.args.append("-MACHINE:ARM"),
289 else => return error.UnsupportedLinkArchitecture,
290 }
453 // OS ABI, often set to 0 regardless of target platform
454 // ABI Version, possibly used by glibc but not by static executables
455 // padding
456 mem.set(u8, hdr_buf[index..][0..9], 0);
457 index += 9;
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()});
295 try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr));
461 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian);
462 index += 2;
296463
297 if (ctx.comp.haveLibC()) {
298 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr));
299 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr));
300 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.crt_dir.?})).ptr));
301 }
464 const machine = self.module.target.cpu.arch.toElfMachine();
465 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
466 index += 2;
302467
303 if (ctx.link_in_crt) {
304 const lib_str = if (ctx.comp.is_static) "lib" else "";
305 const d_str = if (ctx.comp.build_mode == .Debug) "d" else "";
468 // ELF Version, again
469 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
470 index += 4;
306471
307 if (ctx.comp.is_static) {
308 const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str});
309 try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr));
310 } else {
311 const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str});
312 try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr));
313 }
472 switch (ptr_width) {
473 .p32 => {
474 // e_entry
475 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian);
476 index += 4;
314477
315 const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", .{
316 lib_str,
317 d_str,
318 });
319 try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr));
320
321 const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str });
322 try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr));
323
324 // Visual C++ 2015 Conformance Changes
325 // https://msdn.microsoft.com/en-us/library/bb531344.aspx
326 try ctx.args.append("legacy_stdio_definitions.lib");
327
328 // msvcrt depends on kernel32
329 try ctx.args.append("kernel32.lib");
330 } else {
331 try ctx.args.append("-NODEFAULTLIB");
332 if (!is_library) {
333 try ctx.args.append("-ENTRY:WinMainCRTStartup");
478 // e_phoff
479 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
480 index += 4;
481
482 // e_shoff
483 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
484 index += 4;
485 },
486 .p64 => {
487 // e_entry
488 mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian);
489 index += 8;
490
491 // e_phoff
492 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
493 index += 8;
494
495 // e_shoff
496 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
497 index += 8;
498 },
334499 }
335 }
336500
337 if (is_library and !ctx.comp.is_static) {
338 try ctx.args.append("-DLL");
339 }
501 const e_flags = 0;
502 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
503 index += 4;
340504
341 for (ctx.comp.link_objects) |link_object| {
342 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
343 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
344 }
345 try addFnObjects(ctx);
505 const e_ehsize: u16 = switch (ptr_width) {
506 .p32 => @sizeOf(elf.Elf32_Ehdr),
507 .p64 => @sizeOf(elf.Elf64_Ehdr),
508 };
509 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
510 index += 2;
346511
347 switch (ctx.comp.kind) {
348 .Exe, .Lib => {
349 if (!ctx.comp.haveLibC()) {
350 @panic("TODO");
351 }
352 },
353 .Obj => {},
354 }
355}
512 const e_phentsize: u16 = switch (ptr_width) {
513 .p32 => @sizeOf(elf.Elf32_Phdr),
514 .p64 => @sizeOf(elf.Elf64_Phdr),
515 };
516 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
517 index += 2;
356518
357fn constructLinkerArgsMachO(ctx: *Context) !void {
358 try ctx.args.append("-demangle");
519 const e_phnum = @intCast(u16, self.program_headers.items.len);
520 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
521 index += 2;
359522
360 if (ctx.comp.linker_rdynamic) {
361 try ctx.args.append("-export_dynamic");
362 }
523 const e_shentsize: u16 = switch (ptr_width) {
524 .p32 => @sizeOf(elf.Elf32_Shdr),
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;
365 const shared = !ctx.comp.is_static and is_lib;
366 if (ctx.comp.is_static) {
367 try ctx.args.append("-static");
368 } else {
369 try ctx.args.append("-dynamic");
370 }
530 const e_shnum = @intCast(u16, self.sections.items.len);
531 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
532 index += 2;
371533
372 try ctx.args.append("-arch");
373 try ctx.args.append(util.getDarwinArchString(ctx.comp.target));
534 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
535 index += 2;
374536
375 const platform = try DarwinPlatform.get(ctx.comp);
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 }
537 assert(index == e_ehsize);
395538
396 try ctx.args.append("-o");
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));
539 try self.file.pwriteAll(hdr_buf[0..index], 0);
430540 }
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 {
469 @panic("TODO");
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;
542 fn writeCodeAndSymbols(self: *Update) !void {
543 @panic("TODO writeCodeAndSymbols");
487544 }
488}
489
490const DarwinPlatform = struct {
491 kind: Kind,
492 major: u32,
493 minor: u32,
494 micro: u32,
545};
495546
496 const Kind = enum {
497 MacOS,
498 IPhoneOS,
499 IPhoneOSSimulator,
547/// Truncates the existing file contents and overwrites the contents.
548/// Returns an error if `file` is not already open with +read +write +seek abilities.
549pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !void {
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),
500567 };
568 defer update.deinit();
501569
502 fn get(comp: *Compilation) !DarwinPlatform {
503 var result: DarwinPlatform = undefined;
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 };
570 return update.perform();
571}
519572
520 var had_extra: bool = undefined;
521 try darwinGetReleaseVersion(
522 ver_str,
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 }
573/// Returns error.IncrFailed if incremental update could not be performed.
574fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !void {
575 //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
531576
532 if (result.kind == .IPhoneOS) {
533 switch (comp.target.cpu.arch) {
534 .i386,
535 .x86_64,
536 => result.kind = .IPhoneOSSimulator,
537 else => {},
538 }
539 }
540 return result;
541 }
577 // TODO implement incremental linking
578 return error.IncrFailed;
579}
542580
543 fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {
544 if (self.major < major)
545 return true;
546 if (self.major > major)
547 return false;
548 if (self.minor < minor)
549 return true;
550 return false;
551 }
552};
581/// Saturating multiplication
582fn satMul(a: var, b: var) @TypeOf(a, b) {
583 const T = @TypeOf(a, b);
584 return std.math.mul(T, a, b) catch std.math.maxInt(T);
585}
553586
554/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
555/// grouped values as integers. Numbers which are not provided are set to 0.
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;
587fn bswapAllFields(comptime S: type, ptr: *S) void {
588 @panic("TODO implement bswapAllFields");
576589}