From ca4341f7ba845e7af3c6f2be52cd60c51ec6d68f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Apr 2018 17:14:09 -0400 Subject: add --no-rosegment cli option this provides a workaround for #896 until valgrind adds support for clang/LLD (equivalent to gcc/gold -rosegment) --- src/link.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/link.cpp') diff --git a/src/link.cpp b/src/link.cpp index 3c6e27e331..d454d77aae 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(g->linker_script); } + if (g->no_rosegment_workaround) { + lj->args.append("--no-rosegment"); + } lj->args.append("--gc-sections"); lj->args.append("-m"); -- cgit v1.2.3 From d464b2532200de3778ac7362e701791a11150d55 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 10 Jun 2018 04:39:22 +0200 Subject: support `--target-arch wasm32` (#1094) Add wasm32 support to the build-obj, build-exe and build-lib commands of the stage 1 compiler. Wasm64 should work transparently once it's supported in upstream LLVM. To export a function: // lib.zig - for exposition, not necessary for this example pub use @import("add.zig"); // add.zig export fn add(a: i32, b: i32) i32 { return a + b; } To import a function: // cube.zig extern fn square(x: i32) i32; export fn cube(x: i32) i32 { return x * square(x); } --- build.zig | 1 + src/link.cpp | 15 ++++++++++++++- src/target.cpp | 7 +++++-- src/zig_llvm.cpp | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) (limited to 'src/link.cpp') diff --git a/build.zig b/build.zig index 109a799ac9..08a47570ef 100644 --- a/build.zig +++ b/build.zig @@ -63,6 +63,7 @@ pub fn build(b: *Builder) !void { exe.addObjectFile(lib); } } else { + addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib"); diff --git a/src/link.cpp b/src/link.cpp index d454d77aae..d2925cb5a8 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -391,6 +391,19 @@ static void construct_linker_job_elf(LinkJob *lj) { } } +static void construct_linker_job_wasm(LinkJob *lj) { + CodeGen *g = lj->codegen; + + lj->args.append("--relocatable"); // So lld doesn't look for _start. + lj->args.append("-o"); + lj->args.append(buf_ptr(&lj->out_file)); + + // .o files + for (size_t i = 0; i < g->link_objects.length; i += 1) { + lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); + } +} + //static bool is_target_cyg_mingw(const ZigTarget *target) { // return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) || // (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU); @@ -924,7 +937,7 @@ static void construct_linker_job(LinkJob *lj) { case ZigLLVM_MachO: return construct_linker_job_macho(lj); case ZigLLVM_Wasm: - zig_panic("TODO link wasm"); + return construct_linker_job_wasm(lj); } } diff --git a/src/target.cpp b/src/target.cpp index c53ed74d14..bd4aa4d4c2 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -597,12 +597,15 @@ void resolve_target_object_format(ZigTarget *target) { case ZigLLVM_tce: case ZigLLVM_tcele: case ZigLLVM_thumbeb: - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: case ZigLLVM_xcore: target->oformat= ZigLLVM_ELF; return; + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + target->oformat = ZigLLVM_Wasm; + return; + case ZigLLVM_ppc: case ZigLLVM_ppc64: if (is_os_darwin(target)) { diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 5905fa8167..24f2a8a343 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -838,7 +838,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ return lld::mach_o::link(array_ref_args, diag); case ZigLLVM_Wasm: - assert(false); // TODO ZigLLDLink for Wasm + return lld::wasm::link(array_ref_args, false, diag); } assert(false); // unreachable abort(); -- cgit v1.2.3 From 48de57d8248d9203b44d28d7749b5d7c1a00deba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 17:01:23 -0400 Subject: add basic std lib code for loading dynamic libraries this is going to only work for very basic libraries; I plan to slowly add more features over time to support more complicated libraries --- CMakeLists.txt | 1 + src/codegen.cpp | 12 +- src/link.cpp | 4 +- std/dynamic_library.zig | 161 +++++++++++++++++++++++++ std/elf.zig | 15 +++ std/index.zig | 1 + std/io.zig | 7 +- std/math/index.zig | 11 ++ std/os/file.zig | 11 +- std/os/index.zig | 14 +++ test/build_examples.zig | 5 + test/standalone/load_dynamic_library/add.zig | 3 + test/standalone/load_dynamic_library/build.zig | 22 ++++ test/standalone/load_dynamic_library/main.zig | 17 +++ 14 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 std/dynamic_library.zig create mode 100644 test/standalone/load_dynamic_library/add.zig create mode 100644 test/standalone/load_dynamic_library/build.zig create mode 100644 test/standalone/load_dynamic_library/main.zig (limited to 'src/link.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index dd4770ad72..e502901bd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -438,6 +438,7 @@ set(ZIG_STD_FILES "debug/failing_allocator.zig" "debug/index.zig" "dwarf.zig" + "dynamic_library.zig" "elf.zig" "empty.zig" "event.zig" diff --git a/src/codegen.cpp b/src/codegen.cpp index d05bcba2ce..fedfcfa744 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6768,7 +6768,7 @@ static void define_builtin_compile_vars(CodeGen *g) { int err; Buf *abs_full_path = buf_alloc(); if ((err = os_path_real(builtin_zig_path, abs_full_path))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err)); exit(1); } @@ -6936,11 +6936,11 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(&path_to_code_src, abs_full_path))) { - zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); + zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); } Buf *import_code = buf_alloc(); if ((err = os_fetch_file_path(abs_full_path, import_code, false))) { - zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); + zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); } return add_source_file(g, package, abs_full_path, import_code); @@ -7024,13 +7024,13 @@ static void gen_root_source(CodeGen *g) { Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(rel_full_path, abs_full_path))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err)); exit(1); } Buf *source_code = buf_alloc(); if ((err = os_fetch_file_path(rel_full_path, source_code, true))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err)); exit(1); } @@ -7374,7 +7374,7 @@ static void gen_h_file(CodeGen *g) { FILE *out_h = fopen(buf_ptr(g->out_h_path), "wb"); if (!out_h) - zig_panic("unable to open %s: %s", buf_ptr(g->out_h_path), strerror(errno)); + zig_panic("unable to open %s: %s\n", buf_ptr(g->out_h_path), strerror(errno)); Buf *export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); buf_upcase(export_macro); diff --git a/src/link.cpp b/src/link.cpp index d2925cb5a8..a4631b1daf 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -208,7 +208,7 @@ static Buf *get_dynamic_linker_path(CodeGen *g) { static void construct_linker_job_elf(LinkJob *lj) { CodeGen *g = lj->codegen; - if (lj->link_in_crt) { + if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } @@ -432,7 +432,7 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si static void construct_linker_job_coff(LinkJob *lj) { CodeGen *g = lj->codegen; - if (lj->link_in_crt) { + if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig new file mode 100644 index 0000000000..87b58ec207 --- /dev/null +++ b/std/dynamic_library.zig @@ -0,0 +1,161 @@ +const std = @import("index.zig"); +const mem = std.mem; +const elf = std.elf; +const cstr = std.cstr; +const linux = std.os.linux; + +pub const DynLib = struct { + allocator: *mem.Allocator, + elf_lib: ElfLib, + fd: i32, + map_addr: usize, + map_size: usize, + + /// Trusts the file + pub fn findAndOpen(allocator: *mem.Allocator, name: []const u8) !DynLib { + return open(allocator, name); + } + + /// Trusts the file + pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib { + const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY); + errdefer std.os.close(fd); + + const size = usize((try std.os.posixFStat(fd)).size); + + const addr = linux.mmap( + null, + size, + linux.PROT_READ | linux.PROT_EXEC, + linux.MAP_PRIVATE | linux.MAP_LOCKED, + fd, + 0, + ); + errdefer _ = linux.munmap(addr, size); + + const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size]; + + return DynLib{ + .allocator = allocator, + .elf_lib = try ElfLib.init(bytes), + .fd = fd, + .map_addr = addr, + .map_size = size, + }; + } + + pub fn close(self: *DynLib) void { + _ = linux.munmap(self.map_addr, self.map_size); + std.os.close(self.fd); + self.* = undefined; + } + + pub fn lookup(self: *DynLib, name: []const u8) ?usize { + return self.elf_lib.lookup("", name); + } +}; + +pub const ElfLib = struct { + strings: [*]u8, + syms: [*]elf.Sym, + hashtab: [*]linux.Elf_Symndx, + versym: ?[*]u16, + verdef: ?*elf.Verdef, + base: usize, + + // Trusts the memory + pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib { + const eh = @ptrCast(*elf.Ehdr, bytes.ptr); + if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; + if (eh.e_type != elf.ET_DYN) return error.NotDynamicLibrary; + + const elf_addr = @ptrToInt(bytes.ptr); + var ph_addr: usize = elf_addr + eh.e_phoff; + + var base: usize = @maxValue(usize); + var maybe_dynv: ?[*]usize = null; + { + var i: usize = 0; + while (i < eh.e_phnum) : ({ + i += 1; + ph_addr += eh.e_phentsize; + }) { + const ph = @intToPtr(*elf.Phdr, ph_addr); + switch (ph.p_type) { + elf.PT_LOAD => base = elf_addr + ph.p_offset - ph.p_vaddr, + elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset), + else => {}, + } + } + } + const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation; + if (base == @maxValue(usize)) return error.BaseNotFound; + + var maybe_strings: ?[*]u8 = null; + var maybe_syms: ?[*]elf.Sym = null; + var maybe_hashtab: ?[*]linux.Elf_Symndx = null; + var maybe_versym: ?[*]u16 = null; + var maybe_verdef: ?*elf.Verdef = null; + + { + var i: usize = 0; + while (dynv[i] != 0) : (i += 2) { + const p = base + dynv[i + 1]; + switch (dynv[i]) { + elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p), + elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p), + else => {}, + } + } + } + + return ElfLib{ + .base = base, + .strings = maybe_strings orelse return error.ElfStringSectionNotFound, + .syms = maybe_syms orelse return error.ElfSymSectionNotFound, + .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound, + .versym = maybe_versym, + .verdef = maybe_verdef, + }; + } + + /// Returns the address of the symbol + pub fn lookup(self: *const ElfLib, vername: []const u8, name: []const u8) ?usize { + const maybe_versym = if (self.verdef == null) null else self.versym; + + const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); + const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE); + + var i: usize = 0; + while (i < self.hashtab[1]) : (i += 1) { + if (0 == (u32(1) << u5(self.syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (u32(1) << u5(self.syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == self.syms[i].st_shndx) continue; + if (!mem.eql(u8, name, cstr.toSliceConst(self.strings + self.syms[i].st_name))) continue; + if (maybe_versym) |versym| { + if (!checkver(self.verdef.?, versym[i], vername, self.strings)) + continue; + } + return self.base + self.syms[i].st_value; + } + + return null; + } +}; + +fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool { + var def = def_arg; + const vsym = @bitCast(u32, vsym_arg) & 0x7fff; + while (true) { + if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) + break; + if (def.vd_next == 0) + return false; + def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); + } + const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); + return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name)); +} diff --git a/std/elf.zig b/std/elf.zig index 50e97ab271..8e6445c631 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -305,6 +305,21 @@ pub const STT_ARM_16BIT = STT_HIPROC; pub const VER_FLG_BASE = 0x1; pub const VER_FLG_WEAK = 0x2; +/// An unknown type. +pub const ET_NONE = 0; + +/// A relocatable file. +pub const ET_REL = 1; + +/// An executable file. +pub const ET_EXEC = 2; + +/// A shared object. +pub const ET_DYN = 3; + +/// A core file. +pub const ET_CORE = 4; + pub const FileType = enum { Relocatable, Executable, diff --git a/std/index.zig b/std/index.zig index 8abfa3db88..3b523f519f 100644 --- a/std/index.zig +++ b/std/index.zig @@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; +pub const DynLib = @import("dynamic_library.zig").DynLib; pub const atomic = @import("atomic/index.zig"); pub const base64 = @import("base64.zig"); diff --git a/std/io.zig b/std/io.zig index a603d0cf5e..cfe1a7f585 100644 --- a/std/io.zig +++ b/std/io.zig @@ -242,11 +242,16 @@ pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) /// On success, caller owns returned buffer. pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { + return readFileAllocAligned(allocator, path, @alignOf(u8)); +} + +/// On success, caller owns returned buffer. +pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 { var file = try File.openRead(allocator, path); defer file.close(); const size = try file.getEndPos(); - const buf = try allocator.alloc(u8, size); + const buf = try allocator.alignedAlloc(u8, A, size); errdefer allocator.free(buf); var adapter = FileInStream.init(&file); diff --git a/std/math/index.zig b/std/math/index.zig index cc1b833a37..8c1dcc32c4 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -536,6 +536,17 @@ test "math.cast" { assert(@typeOf(try cast(u8, u32(255))) == u8); } +pub const AlignCastError = error{UnalignedMemory}; + +/// Align cast a pointer but return an error if it's the wrong field +pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { + const addr = @ptrToInt(ptr); + if (addr % alignment != 0) { + return error.UnalignedMemory; + } + return @alignCast(alignment, ptr); +} + pub fn floorPowerOfTwo(comptime T: type, value: T) T { var x = value; diff --git a/std/os/file.zig b/std/os/file.zig index 56da4f73a6..41d3dfbf95 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -265,16 +265,7 @@ pub const File = struct { pub fn getEndPos(self: *File) !usize { if (is_posix) { - var stat: posix.Stat = undefined; - const err = posix.getErrno(posix.fstat(self.handle, &stat)); - if (err > 0) { - return switch (err) { - posix.EBADF => error.BadFd, - posix.ENOMEM => error.SystemResources, - else => os.unexpectedErrorPosix(err), - }; - } - + const stat = try os.posixFStat(self.handle); return usize(stat.size); } else if (is_windows) { var file_size: windows.LARGE_INTEGER = undefined; diff --git a/std/os/index.zig b/std/os/index.zig index 62eeb7e43e..fb4605fce0 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2697,3 +2697,17 @@ pub fn posixWait(pid: i32) i32 { } } } + +pub fn posixFStat(fd: i32) !posix.Stat { + var stat: posix.Stat = undefined; + const err = posix.getErrno(posix.fstat(fd, &stat)); + if (err > 0) { + return switch (err) { + posix.EBADF => error.BadFd, + posix.ENOMEM => error.SystemResources, + else => os.unexpectedErrorPosix(err), + }; + } + + return stat; +} diff --git a/test/build_examples.zig b/test/build_examples.zig index 1ba0ca46cf..7cae734677 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -18,4 +18,9 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.addBuildFile("test/standalone/pkg_import/build.zig"); cases.addBuildFile("test/standalone/use_alias/build.zig"); cases.addBuildFile("test/standalone/brace_expansion/build.zig"); + if (builtin.os == builtin.Os.linux) { + // TODO hook up the DynLib API for windows using LoadLibraryA + // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it + cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); + } } diff --git a/test/standalone/load_dynamic_library/add.zig b/test/standalone/load_dynamic_library/add.zig new file mode 100644 index 0000000000..a04ec1544d --- /dev/null +++ b/test/standalone/load_dynamic_library/add.zig @@ -0,0 +1,3 @@ +export fn add(a: i32, b: i32) i32 { + return a + b; +} diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig new file mode 100644 index 0000000000..1f981a5c7d --- /dev/null +++ b/test/standalone/load_dynamic_library/build.zig @@ -0,0 +1,22 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const opts = b.standardReleaseOptions(); + + const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0)); + lib.setBuildMode(opts); + lib.linkSystemLibrary("c"); + + const main = b.addExecutable("main", "main.zig"); + main.setBuildMode(opts); + + const run = b.addCommand(".", b.env_map, [][]const u8{ + main.getOutputPath(), + lib.getOutputPath(), + }); + run.step.dependOn(&lib.step); + run.step.dependOn(&main.step); + + const test_step = b.step("test", "Test the program"); + test_step.dependOn(&run.step); +} diff --git a/test/standalone/load_dynamic_library/main.zig b/test/standalone/load_dynamic_library/main.zig new file mode 100644 index 0000000000..4c45ad6fde --- /dev/null +++ b/test/standalone/load_dynamic_library/main.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn main() !void { + const args = try std.os.argsAlloc(std.debug.global_allocator); + defer std.os.argsFree(std.debug.global_allocator, args); + + const dynlib_name = args[1]; + + var lib = try std.DynLib.open(std.debug.global_allocator, dynlib_name); + defer lib.close(); + + const addr = lib.lookup("add") orelse return error.SymbolNotFound; + const addFn = @intToPtr(extern fn (i32, i32) i32, addr); + + const result = addFn(12, 34); + std.debug.assert(result == 46); +} -- cgit v1.2.3 From 2759c7951da050d825cf765c4b660f5562fb01a4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Jul 2018 14:10:27 -0400 Subject: always link against compiler_rt.o even when linking libc sometimes libgcc is missing things we need, so we always link compiler_rt and rely on weak linkage to allow libgcc to override. --- src/link.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/link.cpp') diff --git a/src/link.cpp b/src/link.cpp index a4631b1daf..2d9a79585f 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -325,10 +325,13 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - if (g->libc_link_lib == nullptr && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { - Buf *builtin_o_path = build_o(g, "builtin"); - lj->args.append(buf_ptr(builtin_o_path)); + if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + if (g->libc_link_lib == nullptr) { + Buf *builtin_o_path = build_o(g, "builtin"); + lj->args.append(buf_ptr(builtin_o_path)); + } + // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } @@ -554,7 +557,7 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append(buf_ptr(builtin_o_path)); } - // msvc compiler_rt is missing some stuff, so we still build it and rely on LinkOnce + // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } -- cgit v1.2.3 From 2614ef056a83842ec9501bf2e4f21ae840f981f8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Jul 2018 17:38:03 -0400 Subject: self-hosted: basic linker code for macos --- src-self-hosted/compilation.zig | 1 + src-self-hosted/libc_installation.zig | 6 +- src-self-hosted/link.zig | 243 +++++++++++++++++++++++++++++++++- src-self-hosted/target.zig | 33 +++++ src/link.cpp | 2 +- 5 files changed, 277 insertions(+), 8 deletions(-) (limited to 'src/link.cpp') diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index f1886bd93d..6abb650a62 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -275,6 +275,7 @@ pub const Compilation = struct { LinkFailed, LibCRequiredButNotProvidedOrFound, LibCMissingDynamicLinker, + InvalidDarwinVersionString, }; pub const Event = union(enum) { diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 5a9b7561fa..c9c631a7fb 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -170,7 +170,7 @@ pub const LibCInstallation = struct { try group.call(findNativeDynamicLinker, self, loop); }, builtin.Os.macosx => { - try group.call(findNativeIncludeDirMacOS, self, loop); + self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); }, else => @compileError("unimplemented: find libc for this OS"), } @@ -254,10 +254,6 @@ pub const LibCInstallation = struct { @panic("TODO"); } - async fn findNativeIncludeDirMacOS(self: *LibCInstallation, loop: *event.Loop) !void { - self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); - } - async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) FindError!void { // TODO //ZigWindowsSDK *sdk = get_windows_sdk(g); diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 16d9939fff..0a83743ef8 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -1,10 +1,12 @@ const std = @import("std"); +const mem = std.mem; const c = @import("c.zig"); const builtin = @import("builtin"); const ObjectFormat = builtin.ObjectFormat; const Compilation = @import("compilation.zig").Compilation; const Target = @import("target.zig").Target; const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const assert = std.debug.assert; const Context = struct { comp: *Compilation, @@ -315,8 +317,163 @@ fn constructLinkerArgsCoff(ctx: *Context) void { @panic("TODO"); } -fn constructLinkerArgsMachO(ctx: *Context) void { - @panic("TODO"); +fn constructLinkerArgsMachO(ctx: *Context) !void { + try ctx.args.append(c"-demangle"); + + if (ctx.comp.linker_rdynamic) { + try ctx.args.append(c"-export_dynamic"); + } + + const is_lib = ctx.comp.kind == Compilation.Kind.Lib; + const shared = !ctx.comp.is_static and is_lib; + if (ctx.comp.is_static) { + try ctx.args.append(c"-static"); + } else { + try ctx.args.append(c"-dynamic"); + } + + //if (is_lib) { + // if (!g->is_static) { + // lj->args.append("-dylib"); + + // Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); + // lj->args.append("-compatibility_version"); + // lj->args.append(buf_ptr(compat_vers)); + + // Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, + // g->version_major, g->version_minor, g->version_patch); + // lj->args.append("-current_version"); + // lj->args.append(buf_ptr(cur_vers)); + + // // TODO getting an error when running an executable when doing this rpath thing + // //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", + // // buf_ptr(g->root_out_name), g->version_major); + // //lj->args.append("-install_name"); + // //lj->args.append(buf_ptr(dylib_install_name)); + + // if (buf_len(&lj->out_file) == 0) { + // buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", + // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); + // } + // } + //} + + try ctx.args.append(c"-arch"); + const darwin_arch_str = try std.cstr.addNullByte( + &ctx.arena.allocator, + ctx.comp.target.getDarwinArchString(), + ); + try ctx.args.append(darwin_arch_str.ptr); + + const platform = try DarwinPlatform.get(ctx.comp); + switch (platform.kind) { + DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"), + DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"), + DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"), + } + const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro); + try ctx.args.append(ver_str.ptr); + + if (ctx.comp.kind == Compilation.Kind.Exe) { + if (ctx.comp.is_static) { + try ctx.args.append(c"-no_pie"); + } else { + try ctx.args.append(c"-pie"); + } + } + + try ctx.args.append(c"-o"); + try ctx.args.append(ctx.out_file_path.ptr()); + + //for (size_t i = 0; i < g->rpath_list.length; i += 1) { + // Buf *rpath = g->rpath_list.at(i); + // add_rpath(lj, rpath); + //} + //add_rpath(lj, &lj->out_file); + + if (shared) { + try ctx.args.append(c"-headerpad_max_install_names"); + } else if (ctx.comp.is_static) { + try ctx.args.append(c"-lcrt0.o"); + } else { + switch (platform.kind) { + DarwinPlatform.Kind.MacOS => { + if (platform.versionLessThan(10, 5)) { + try ctx.args.append(c"-lcrt1.o"); + } else if (platform.versionLessThan(10, 6)) { + try ctx.args.append(c"-lcrt1.10.5.o"); + } else if (platform.versionLessThan(10, 8)) { + try ctx.args.append(c"-lcrt1.10.6.o"); + } + }, + DarwinPlatform.Kind.IPhoneOS => { + if (ctx.comp.target.getArch() == builtin.Arch.aarch64) { + // iOS does not need any crt1 files for arm64 + } else if (platform.versionLessThan(3, 1)) { + try ctx.args.append(c"-lcrt1.o"); + } else if (platform.versionLessThan(6, 0)) { + try ctx.args.append(c"-lcrt1.3.1.o"); + } + }, + DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed + } + } + + //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // lj->args.append("-L"); + // lj->args.append(lib_dir); + //} + + for (ctx.comp.link_objects) |link_object| { + const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); + try ctx.args.append(link_obj_with_null.ptr); + } + try addFnObjects(ctx); + + //// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce + //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + // Buf *compiler_rt_o_path = build_compiler_rt(g); + // lj->args.append(buf_ptr(compiler_rt_o_path)); + //} + + if (ctx.comp.target == Target.Native) { + for (ctx.comp.link_libs_list.toSliceConst()) |lib| { + if (mem.eql(u8, lib.name, "c")) { + // on Darwin, libSystem has libc in it, but also you have to use it + // to make syscalls because the syscall numbers are not documented + // and change between versions. + // so we always link against libSystem + try ctx.args.append(c"-lSystem"); + } else { + if (mem.indexOfScalar(u8, lib.name, '/') == null) { + const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name); + try ctx.args.append(arg.ptr); + } else { + const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); + try ctx.args.append(arg.ptr); + } + } + } + } else { + try ctx.args.append(c"-undefined"); + try ctx.args.append(c"dynamic_lookup"); + } + + if (platform.kind == DarwinPlatform.Kind.MacOS) { + if (platform.versionLessThan(10, 5)) { + try ctx.args.append(c"-lgcc_s.10.4"); + } else if (platform.versionLessThan(10, 6)) { + try ctx.args.append(c"-lgcc_s.10.5"); + } + } else { + @panic("TODO"); + } + + //for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { + // lj->args.append("-framework"); + // lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); + //} } fn constructLinkerArgsWasm(ctx: *Context) void { @@ -341,3 +498,85 @@ fn addFnObjects(ctx: *Context) !void { it = node.next; } } + +const DarwinPlatform = struct { + kind: Kind, + major: u32, + minor: u32, + micro: u32, + + const Kind = enum { + MacOS, + IPhoneOS, + IPhoneOSSimulator, + }; + + fn get(comp: *Compilation) !DarwinPlatform { + var result: DarwinPlatform = undefined; + const ver_str = switch (comp.darwin_version_min) { + Compilation.DarwinVersionMin.MacOS => |ver| blk: { + result.kind = Kind.MacOS; + break :blk ver; + }, + Compilation.DarwinVersionMin.Ios => |ver| blk: { + result.kind = Kind.IPhoneOS; + break :blk ver; + }, + Compilation.DarwinVersionMin.None => blk: { + assert(comp.target.getOs() == builtin.Os.macosx); + result.kind = Kind.MacOS; + break :blk "10.10"; + }, + }; + + var had_extra: bool = undefined; + try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,); + if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { + return error.InvalidDarwinVersionString; + } + + if (result.kind == Kind.IPhoneOS) { + switch (comp.target.getArch()) { + builtin.Arch.i386, + builtin.Arch.x86_64, + => result.kind = Kind.IPhoneOSSimulator, + else => {}, + } + } + return result; + } + + fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool { + if (self.major < major) + return true; + if (self.major > major) + return false; + if (self.minor < minor) + return true; + return false; + } +}; + +/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the +/// grouped values as integers. Numbers which are not provided are set to 0. +/// return true if the entire string was parsed (9.2), or all groups were +/// parsed (10.3.5extrastuff). +fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void { + major.* = 0; + minor.* = 0; + micro.* = 0; + had_extra.* = false; + + if (str.len == 0) + return error.InvalidDarwinVersionString; + + var start_pos: usize = 0; + for ([]*u32{major, minor, micro}) |v| { + const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); + const end_pos = dot_pos orelse str.len; + v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString; + start_pos = (dot_pos orelse return) + 1; + if (start_pos == str.len) return; + } + had_extra.* = true; +} diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index dccf937a47..0cc8d02a62 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -526,4 +526,37 @@ pub const Target = union(enum) { => @panic("TODO specify the C integer type sizes for this OS"), } } + + pub fn getDarwinArchString(self: Target) []const u8 { + const arch = self.getArch(); + switch (arch) { + builtin.Arch.aarch64 => return "arm64", + builtin.Arch.thumb, + builtin.Arch.armv8_3a, + builtin.Arch.armv8_2a, + builtin.Arch.armv8_1a, + builtin.Arch.armv8, + builtin.Arch.armv8r, + builtin.Arch.armv8m_baseline, + builtin.Arch.armv8m_mainline, + builtin.Arch.armv7, + builtin.Arch.armv7em, + builtin.Arch.armv7m, + builtin.Arch.armv7s, + builtin.Arch.armv7k, + builtin.Arch.armv7ve, + builtin.Arch.armv6, + builtin.Arch.armv6m, + builtin.Arch.armv6k, + builtin.Arch.armv6t2, + builtin.Arch.armv5, + builtin.Arch.armv5te, + builtin.Arch.armv4t, + => return "arm", + builtin.Arch.powerpc => return "ppc", + builtin.Arch.powerpc64 => return "ppc64", + builtin.Arch.powerpc64le => return "ppc64le", + else => return @tagName(arch), + } + } }; diff --git a/src/link.cpp b/src/link.cpp index 2d9a79585f..f65c072bac 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) { if (strchr(buf_ptr(link_lib->name), '/') == nullptr) { Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); lj->args.append(buf_ptr(arg)); - } else { + } else { lj->args.append(buf_ptr(link_lib->name)); } } -- cgit v1.2.3