| ... | ... | @@ -1,576 +1,589 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const mem = std.mem; |
| 3 | | const c = @import("c.zig"); |
| 4 | | const Compilation = @import("compilation.zig").Compilation; |
| 5 | | const Target = std.Target; |
| 6 | | const ObjectFormat = Target.ObjectFormat; |
| 7 | | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 8 | 3 | const assert = std.debug.assert; |
| 9 | | const util = @import("util.zig"); |
| 10 | | |
| 11 | | const Context = struct { |
| 12 | | comp: *Compilation, |
| 13 | | arena: std.heap.ArenaAllocator, |
| 14 | | args: std.ArrayList([*:0]const u8), |
| 15 | | link_in_crt: bool, |
| 4 | const Allocator = std.mem.Allocator; |
| 5 | const ir = @import("ir.zig"); |
| 6 | const fs = std.fs; |
| 7 | const elf = std.elf; |
| 8 | |
| 9 | const executable_mode = 0o755; |
| 10 | const 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. |
| 16 | pub 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 | } |
| 16 | 22 | |
| 17 | | link_err: error{OutOfMemory}!void, |
| 18 | | link_msg: std.ArrayListSentineled(u8, 0), |
| 23 | /// Atomically overwrites the old file, if present. |
| 24 | pub 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(); |
| 19 | 27 | |
| 20 | | libc: *LibCInstallation, |
| 21 | | out_file_path: std.ArrayListSentineled(u8, 0), |
| 22 | | }; |
| 28 | try writeExecutableFile(allocator, module, af.file); |
| 29 | try af.finish(); |
| 30 | } |
| 23 | 31 | |
| 24 | | pub 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. |
| 37 | pub 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); |
| 49 | 41 | }, |
| 50 | | } |
| 42 | else => |e| return e, |
| 43 | }; |
| 44 | } |
| 51 | 45 | |
| 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; |
| 46 | const 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(); |
| 68 | 83 | } |
| 69 | 84 | |
| 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 | } |
| 71 | 107 | |
| 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 | } |
| 76 | 116 | } |
| 77 | | std.debug.warn("\n", .{}); |
| 78 | | } |
| 79 | 117 | |
| 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; |
| 95 | 130 | } |
| 96 | | return error.LinkFailed; |
| 97 | 131 | } |
| 132 | return null; |
| 98 | 133 | } |
| 99 | | } |
| 100 | 134 | |
| 101 | | extern 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 | | |
| 109 | | fn 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 | | |
| 114 | | fn 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; |
| 117 | 152 | } |
| 118 | | try ctx.link_msg.append(msg); |
| 119 | | } |
| 120 | | |
| 121 | | fn 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 | | } |
| 130 | 153 | |
| 131 | | fn 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; |
| 138 | 160 | } |
| 139 | | } |
| 140 | 161 | |
| 141 | | fn 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); |
| 150 | 166 | } |
| 151 | 167 | |
| 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; |
| 183 | 308 | |
| 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 | } |
| 199 | 312 | |
| 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; |
| 204 | 362 | |
| 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); |
| 259 | 419 | } |
| 420 | try self.writeCodeAndSymbols(); |
| 421 | try self.writeElfHeader(); |
| 422 | // TODO find end pos and truncate |
| 260 | 423 | } |
| 261 | 424 | |
| 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; |
| 266 | 427 | |
| 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; |
| 271 | 431 | |
| 272 | | fn 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; |
| 277 | 442 | |
| 278 | | fn 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; |
| 280 | 449 | |
| 281 | | if (!ctx.comp.strip) { |
| 282 | | try ctx.args.append("-DEBUG"); |
| 283 | | } |
| 450 | hdr_buf[index] = 1; // ELF version |
| 451 | index += 1; |
| 284 | 452 | |
| 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; |
| 291 | 458 | |
| 292 | | const is_library = ctx.comp.kind == .Lib; |
| 459 | assert(index == 16); |
| 293 | 460 | |
| 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; |
| 296 | 463 | |
| 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; |
| 302 | 467 | |
| 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; |
| 306 | 471 | |
| 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; |
| 314 | 477 | |
| 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 | }, |
| 334 | 499 | } |
| 335 | | } |
| 336 | 500 | |
| 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; |
| 340 | 504 | |
| 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; |
| 346 | 511 | |
| 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; |
| 356 | 518 | |
| 357 | | fn 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; |
| 359 | 522 | |
| 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; |
| 363 | 529 | |
| 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; |
| 371 | 533 | |
| 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; |
| 374 | 536 | |
| 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); |
| 395 | 538 | |
| 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); |
| 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 | | } |
| 467 | 541 | |
| 468 | | fn constructLinkerArgsWasm(ctx: *Context) void { |
| 469 | | @panic("TODO"); |
| 470 | | } |
| 471 | | |
| 472 | | fn 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"); |
| 487 | 544 | } |
| 488 | | } |
| 489 | | |
| 490 | | const DarwinPlatform = struct { |
| 491 | | kind: Kind, |
| 492 | | major: u32, |
| 493 | | minor: u32, |
| 494 | | micro: u32, |
| 545 | }; |
| 495 | 546 | |
| 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. |
| 549 | pub 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), |
| 500 | 567 | }; |
| 568 | defer update.deinit(); |
| 501 | 569 | |
| 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 | } |
| 519 | 572 | |
| 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. |
| 574 | fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !void { |
| 575 | //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; |
| 531 | 576 | |
| 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 | } |
| 542 | 580 | |
| 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 |
| 582 | fn 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 | } |
| 553 | 586 | |
| 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). |
| 558 | | fn 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; |
| 587 | fn bswapAllFields(comptime S: type, ptr: *S) void { |
| 588 | @panic("TODO implement bswapAllFields"); |
| 576 | 589 | } |