| ... | @@ -1,576 +1,589 @@ | ... | @@ -1,576 +1,589 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | 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 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 9 | const util = @import("util.zig"); | 4 | const Allocator = std.mem.Allocator; |
| 10 | | 5 | const ir = @import("ir.zig"); |
| 11 | const Context = struct { | 6 | const fs = std.fs; |
| 12 | comp: *Compilation, | 7 | const elf = std.elf; |
| 13 | arena: std.heap.ArenaAllocator, | 8 | |
| 14 | args: std.ArrayList([*:0]const u8), | 9 | const executable_mode = 0o755; |
| 15 | link_in_crt: bool, | 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, | 23 | /// Atomically overwrites the old file, if present. |
| 18 | link_msg: std.ArrayListSentineled(u8, 0), | 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, | 28 | try writeExecutableFile(allocator, module, af.file); |
| 21 | out_file_path: std.ArrayListSentineled(u8, 0), | 29 | try af.finish(); |
| 22 | }; | 30 | } |
| 23 | | 31 | |
| 24 | pub 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, | 37 | pub 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 | } |
| 51 | | 45 | |
| 52 | // even though we're calling LLD as a library it thinks the first | 46 | const Update = struct { |
| 53 | // argument is its own exe name | 47 | file: fs.File, |
| 54 | try ctx.args.append("lld"); | 48 | module: *const ir.Module, |
| 55 | | 49 | |
| 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/3190 | 51 | /// 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 | } |
| 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) { | 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 | } | | |
| 79 | | 117 | |
| 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; |
| 82 | | 120 | 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 | } |
| 87 | | 125 | 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 the | 129 | 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 | } | | |
| 100 | | 134 | |
| 101 | extern 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; |
| 108 | | 142 | } |
| 109 | fn 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 | } |
| 113 | | 147 | for (self.program_headers.items) |program_header| { |
| 114 | fn 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 | | | |
| 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 { | 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 | } | | |
| 140 | | 161 | |
| 141 | fn constructLinkerArgsElf(ctx: *Context) !void { | 162 | fn makeString(self: *Update, bytes: []const u8) !u32 { |
| 142 | // TODO commented out code in this function | 163 | 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 | } |
| 151 | | 167 | |
| 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()) { |
| 154 | | 170 | 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; |
| 167 | | 183 | 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); |
| 174 | | 190 | 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, |
| 177 | | 193 | .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; |
| 183 | | 308 | |
| 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]u8 | 311 | } |
| 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 | } | | |
| 199 | | 312 | |
| 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; |
| 204 | | 362 | |
| 205 | // .o files | 363 | 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) { |
| 211 | | 369 | .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.* = .{ |
| 217 | | 375 | .sh_name = self.sections.items[i].sh_name, |
| 218 | // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage | 376 | .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), |
| 222 | | 380 | .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 | |
| 238 | | 396 | for (buf) |*shdr, i| { |
| 239 | // libc dep | 397 | 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 | } |
| 261 | | 424 | |
| 262 | // crt end | 425 | 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 | } | | |
| 266 | | 427 | |
| 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 | } | | |
| 271 | | 431 | |
| 272 | fn 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; |
| 277 | | 442 | |
| 278 | fn 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; |
| 280 | | 449 | |
| 281 | if (!ctx.comp.strip) { | 450 | hdr_buf[index] = 1; // ELF version |
| 282 | try ctx.args.append("-DEBUG"); | 451 | index += 1; |
| 283 | } | | |
| 284 | | 452 | |
| 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 | } | | |
| 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()}); | 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; |
| 296 | | 463 | |
| 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 | } | | |
| 302 | | 467 | |
| 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; |
| 306 | | 471 | |
| 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 | } | | |
| 314 | | 477 | |
| 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 |
| 320 | | 483 | 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 | }, |
| 323 | | 486 | .p64 => { |
| 324 | // Visual C++ 2015 Conformance Changes | 487 | // e_entry |
| 325 | // https://msdn.microsoft.com/en-us/library/bb531344.aspx | 488 | mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian); |
| 326 | try ctx.args.append("legacy_stdio_definitions.lib"); | 489 | index += 8; |
| 327 | | 490 | |
| 328 | // msvcrt depends on kernel32 | 491 | // 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 | } | | |
| 336 | | 500 | |
| 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; |
| 340 | | 504 | |
| 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; |
| 346 | | 511 | |
| 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 | } | | |
| 356 | | 518 | |
| 357 | fn 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; |
| 359 | | 522 | |
| 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; |
| 363 | | 529 | |
| 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 | } | | |
| 371 | | 533 | |
| 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; |
| 374 | | 536 | |
| 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 | } | | |
| 395 | | 538 | |
| 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 | } | | |
| 467 | | 541 | |
| 468 | fn constructLinkerArgsWasm(ctx: *Context) void { | 542 | fn writeCodeAndSymbols(self: *Update) !void { |
| 469 | @panic("TODO"); | 543 | @panic("TODO writeCodeAndSymbols"); |
| 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; | | |
| 487 | } | 544 | } |
| 488 | } | 545 | }; |
| 489 | | | |
| 490 | const DarwinPlatform = struct { | | |
| 491 | kind: Kind, | | |
| 492 | major: u32, | | |
| 493 | minor: u32, | | |
| 494 | micro: u32, | | |
| 495 | | 546 | |
| 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, | 549 | pub 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(); |
| 501 | | 569 | |
| 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 | }; | | |
| 519 | | 572 | |
| 520 | var had_extra: bool = undefined; | 573 | /// Returns error.IncrFailed if incremental update could not be performed. |
| 521 | try darwinGetReleaseVersion( | 574 | fn 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 | } | | |
| 531 | | 576 | |
| 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 | } | | |
| 542 | | 580 | |
| 543 | fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool { | 581 | /// Saturating multiplication |
| 544 | if (self.major < major) | 582 | fn 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 | }; | | |
| 553 | | 586 | |
| 554 | /// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the | 587 | fn 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). | | |
| 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; | | |
| 576 | } | 589 | } |