aboutsummaryrefslogtreecommitdiff
path: root/src/link.cpp
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2016-02-11 01:33:27 -0700
committerAndrew Kelley <superjoe30@gmail.com>2016-02-11 01:33:27 -0700
commit2bf6c28bc3e535deb651d5b37e332226e0d38930 (patch)
treebb206f61ac612f1c991a7c1c58176a163d51a74f /src/link.cpp
parent54a8b6a110fa779a2fc12b901b96b6e072bca7cf (diff)
downloadzig-2bf6c28bc3e535deb651d5b37e332226e0d38930.tar.gz
zig-2bf6c28bc3e535deb651d5b37e332226e0d38930.zip
ability to cross compile
hello_libc.zig can produce a windows build
Diffstat (limited to 'src/link.cpp')
-rw-r--r--src/link.cpp518
1 files changed, 518 insertions, 0 deletions
diff --git a/src/link.cpp b/src/link.cpp
new file mode 100644
index 0000000000..753e30d05f
--- /dev/null
+++ b/src/link.cpp
@@ -0,0 +1,518 @@
+/*
+ * Copyright (c) 2015 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "link.hpp"
+#include "os.hpp"
+#include "config.h"
+#include "codegen.hpp"
+#include "analyze.hpp"
+
+struct LinkJob {
+ CodeGen *codegen;
+ Buf out_file;
+ ZigList<const char *> args;
+ bool link_in_crt;
+ Buf out_file_o;
+};
+
+static const char *get_libc_file(CodeGen *g, const char *file) {
+ Buf *out_buf = buf_alloc();
+ os_path_join(g->libc_lib_dir, buf_create_from_str(file), out_buf);
+ return buf_ptr(out_buf);
+}
+
+static const char *get_libc_static_file(CodeGen *g, const char *file) {
+ Buf *out_buf = buf_alloc();
+ os_path_join(g->libc_static_lib_dir, buf_create_from_str(file), out_buf);
+ return buf_ptr(out_buf);
+}
+
+static Buf *build_o(CodeGen *parent_gen, const char *oname) {
+ Buf *source_basename = buf_sprintf("%s.zig", oname);
+ Buf *std_dir_path = buf_create_from_str(ZIG_STD_DIR);
+
+ ZigTarget *child_target = parent_gen->is_native_target ? nullptr : &parent_gen->zig_target;
+ CodeGen *child_gen = codegen_create(std_dir_path, child_target);
+ child_gen->link_libc = parent_gen->link_libc;
+
+ codegen_set_is_release(child_gen, parent_gen->is_release_build);
+
+ codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
+ codegen_set_is_static(child_gen, parent_gen->is_static);
+
+ codegen_set_out_type(child_gen, OutTypeObj);
+ codegen_set_out_name(child_gen, buf_create_from_str(oname));
+
+ codegen_set_verbose(child_gen, parent_gen->verbose);
+ codegen_set_errmsg_color(child_gen, parent_gen->err_color);
+
+ Buf *full_path = buf_alloc();
+ os_path_join(std_dir_path, source_basename, full_path);
+ Buf source_code = BUF_INIT;
+ if (os_fetch_file_path(full_path, &source_code)) {
+ zig_panic("unable to fetch file: %s\n", buf_ptr(full_path));
+ }
+
+ codegen_add_root_code(child_gen, std_dir_path, source_basename, &source_code);
+ Buf *o_out = buf_sprintf("%s.o", oname);
+ codegen_link(child_gen, buf_ptr(o_out));
+
+ return o_out;
+}
+
+static const char *get_o_file_extension(CodeGen *g) {
+ if (g->zig_target.environ == ZigLLVM_MSVC) {
+ return ".obj";
+ } else {
+ return ".o";
+ }
+}
+
+static const char *get_exe_file_extension(CodeGen *g) {
+ if (g->zig_target.os == ZigLLVM_Win32) {
+ return ".exe";
+ } else {
+ return "";
+ }
+}
+
+static void construct_linker_job_linux(LinkJob *lj) {
+ CodeGen *g = lj->codegen;
+
+ lj->args.append("-m");
+ lj->args.append("elf_x86_64");
+
+ if (g->is_static) {
+ lj->args.append("-static");
+ }
+
+ lj->args.append("-o");
+ lj->args.append(buf_ptr(&lj->out_file));
+
+ if (lj->link_in_crt) {
+ const char *crt1o;
+ const char *crtbegino;
+ if (g->is_static) {
+ crt1o = "crt1.o";
+ crtbegino = "crtbeginT.o";
+ } else {
+ crt1o = "Scrt1.o";
+ crtbegino = "crtbegin.o";
+ }
+ lj->args.append(get_libc_file(g, crt1o));
+ lj->args.append(get_libc_file(g, "crti.o"));
+ lj->args.append(get_libc_static_file(g, crtbegino));
+ }
+
+ for (int 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);
+ }
+
+ if (g->link_libc) {
+ lj->args.append("-L");
+ lj->args.append(buf_ptr(g->libc_lib_dir));
+
+ lj->args.append("-L");
+ lj->args.append(buf_ptr(g->libc_static_lib_dir));
+ }
+
+ if (g->dynamic_linker && buf_len(g->dynamic_linker) > 0) {
+ lj->args.append("-dynamic-linker");
+ lj->args.append(buf_ptr(g->dynamic_linker));
+ } else {
+ lj->args.append("-dynamic-linker");
+ lj->args.append(buf_ptr(get_dynamic_linker(g->target_machine)));
+ }
+
+ if (g->out_type == OutTypeLib) {
+ buf_resize(&lj->out_file, 0);
+ buf_appendf(&lj->out_file, "lib%s.so.%d.%d.%d",
+ buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
+ Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->root_out_name), g->version_major);
+ lj->args.append("-shared");
+ lj->args.append("-soname");
+ lj->args.append(buf_ptr(soname));
+ }
+
+ // .o files
+ lj->args.append((const char *)buf_ptr(&lj->out_file_o));
+
+ if (g->is_test_build) {
+ const char *test_runner_name = g->link_libc ? "test_runner_libc" : "test_runner_nolibc";
+ Buf *test_runner_o_path = build_o(g, test_runner_name);
+ lj->args.append(buf_ptr(test_runner_o_path));
+ }
+
+ if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) {
+ Buf *builtin_o_path = build_o(g, "builtin");
+ lj->args.append(buf_ptr(builtin_o_path));
+ }
+
+ auto it = g->link_table.entry_iterator();
+ for (;;) {
+ auto *entry = it.next();
+ if (!entry)
+ break;
+
+ // we handle libc explicitly, don't do it here
+ if (!buf_eql_str(entry->key, "c")) {
+ Buf *arg = buf_sprintf("-l%s", buf_ptr(entry->key));
+ lj->args.append(buf_ptr(arg));
+ }
+ }
+
+
+ // libc dep
+ if (g->link_libc) {
+ if (g->is_static) {
+ lj->args.append("--start-group");
+ lj->args.append("-lgcc");
+ lj->args.append("-lgcc_eh");
+ lj->args.append("-lc");
+ lj->args.append("--end-group");
+ } else {
+ lj->args.append("-lgcc");
+ lj->args.append("--as-needed");
+ lj->args.append("-lgcc_s");
+ lj->args.append("--no-as-needed");
+ lj->args.append("-lc");
+ lj->args.append("-lgcc");
+ lj->args.append("--as-needed");
+ lj->args.append("-lgcc_s");
+ lj->args.append("--no-as-needed");
+ }
+ }
+
+ // crt end
+ if (lj->link_in_crt) {
+ lj->args.append(get_libc_static_file(g, "crtend.o"));
+ lj->args.append(get_libc_file(g, "crtn.o"));
+ }
+}
+
+static bool is_target_cyg_mingw(const ZigTarget *target) {
+ return (target->os == ZigLLVM_Win32 && target->environ == ZigLLVM_Cygnus) ||
+ (target->os == ZigLLVM_Win32 && target->environ == ZigLLVM_GNU);
+}
+
+static void construct_linker_job_mingw(LinkJob *lj) {
+ CodeGen *g = lj->codegen;
+
+ if (g->zig_target.arch.arch == ZigLLVM_x86) {
+ lj->args.append("-m");
+ lj->args.append("i386pe");
+ } else if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
+ lj->args.append("-m");
+ lj->args.append("i386pep");
+ } else if (g->zig_target.arch.arch == ZigLLVM_arm) {
+ lj->args.append("-m");
+ lj->args.append("thumb2pe");
+ }
+
+ if (g->windows_subsystem_windows) {
+ lj->args.append("--subsystem");
+ lj->args.append("windows");
+ } else if (g->windows_subsystem_console) {
+ lj->args.append("--subsystem");
+ lj->args.append("console");
+ }
+
+ bool dll = g->out_type == OutTypeLib;
+ bool shared = !g->is_static && dll;
+ if (g->is_static) {
+ lj->args.append("-Bstatic");
+ } else {
+ if (dll) {
+ lj->args.append("--dll");
+ } else if (shared) {
+ lj->args.append("--shared");
+ }
+ lj->args.append("-Bdynamic");
+ if (dll || shared) {
+ lj->args.append("-e");
+ if (g->zig_target.arch.arch == ZigLLVM_x86) {
+ lj->args.append("_DllMainCRTStartup@12");
+ } else {
+ lj->args.append("DllMainCRTStartup");
+ }
+ lj->args.append("--enable-auto-image-base");
+ }
+ }
+
+ lj->args.append("-o");
+ lj->args.append(buf_ptr(&lj->out_file));
+
+ if (lj->link_in_crt) {
+ if (shared || dll) {
+ lj->args.append(get_libc_file(g, "dllcrt2.o"));
+ } else {
+ if (g->windows_linker_unicode) {
+ lj->args.append(get_libc_file(g, "crt2u.o"));
+ } else {
+ lj->args.append(get_libc_file(g, "crt2.o"));
+ }
+ }
+ lj->args.append(get_libc_static_file(g, "crtbegin.o"));
+ }
+
+ for (int 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);
+ }
+
+ if (g->link_libc) {
+ lj->args.append("-L");
+ lj->args.append(buf_ptr(g->libc_lib_dir));
+
+ lj->args.append("-L");
+ lj->args.append(buf_ptr(g->libc_static_lib_dir));
+ }
+
+ lj->args.append((const char *)buf_ptr(&lj->out_file_o));
+
+ if (g->link_libc) {
+ if (g->is_static) {
+ lj->args.append("--start-group");
+ }
+
+ lj->args.append("-lmingw32");
+
+ lj->args.append("-lgcc");
+ bool is_android = (g->zig_target.environ == ZigLLVM_Android);
+ bool is_cyg_ming = is_target_cyg_mingw(&g->zig_target);
+ if (!g->is_static && !is_android) {
+ if (!is_cyg_ming) {
+ lj->args.append("--as-needed");
+ }
+ //lj->args.append("-lgcc_s");
+ if (!is_cyg_ming) {
+ lj->args.append("--no-as-needed");
+ }
+ }
+ if (g->is_static && !is_android) {
+ //lj->args.append("-lgcc_eh");
+ }
+ if (is_android && !g->is_static) {
+ lj->args.append("-ldl");
+ }
+
+ lj->args.append("-lmoldname");
+ lj->args.append("-lmingwex");
+ lj->args.append("-lmsvcrt");
+
+
+ if (g->windows_subsystem_windows) {
+ lj->args.append("-lgdi32");
+ lj->args.append("-lcomdlg32");
+ }
+ lj->args.append("-ladvapi32");
+ lj->args.append("-lshell32");
+ lj->args.append("-luser32");
+ lj->args.append("-lkernel32");
+
+ if (g->is_static) {
+ lj->args.append("--end-group");
+ }
+
+ if (lj->link_in_crt) {
+ lj->args.append(get_libc_static_file(g, "crtend.o"));
+ }
+ }
+}
+
+static void construct_linker_job(LinkJob *lj) {
+ switch (lj->codegen->zig_target.os) {
+ case ZigLLVM_UnknownOS:
+ zig_unreachable();
+ case ZigLLVM_Linux:
+ if (lj->codegen->zig_target.arch.arch == ZigLLVM_hexagon) {
+ zig_panic("TODO construct hexagon_TC linker job");
+ } else {
+ return construct_linker_job_linux(lj);
+ }
+ case ZigLLVM_CloudABI:
+ zig_panic("TODO construct CloudABI linker job");
+ case ZigLLVM_Darwin:
+ case ZigLLVM_MacOSX:
+ case ZigLLVM_IOS:
+ zig_panic("TODO construct DarwinClang linker job");
+ case ZigLLVM_DragonFly:
+ zig_panic("TODO construct DragonFly linker job");
+ case ZigLLVM_OpenBSD:
+ zig_panic("TODO construct OpenBSD linker job");
+ case ZigLLVM_Bitrig:
+ zig_panic("TODO construct Bitrig linker job");
+ case ZigLLVM_NetBSD:
+ zig_panic("TODO construct NetBSD linker job");
+ case ZigLLVM_FreeBSD:
+ zig_panic("TODO construct FreeBSD linker job");
+ case ZigLLVM_Minix:
+ zig_panic("TODO construct Minix linker job");
+ case ZigLLVM_NaCl:
+ zig_panic("TODO construct NaCl_TC linker job");
+ case ZigLLVM_Solaris:
+ zig_panic("TODO construct Solaris linker job");
+ case ZigLLVM_Win32:
+ switch (lj->codegen->zig_target.environ) {
+ default:
+ if (lj->codegen->zig_target.oformat == ZigLLVM_ELF) {
+ zig_panic("TODO construct Generic_ELF linker job");
+ } else if (lj->codegen->zig_target.oformat == ZigLLVM_MachO) {
+ zig_panic("TODO construct MachO linker job");
+ } else {
+ zig_panic("TODO construct Generic_GCC linker job");
+ }
+ case ZigLLVM_GNU:
+ return construct_linker_job_mingw(lj);
+ case ZigLLVM_Itanium:
+ zig_panic("TODO construct CrossWindowsToolChain linker job");
+ case ZigLLVM_MSVC:
+ case ZigLLVM_UnknownEnvironment:
+ zig_panic("TODO construct MSVC linker job");
+ }
+ case ZigLLVM_CUDA:
+ zig_panic("TODO construct Cuda linker job");
+ default:
+ // Of these targets, Hexagon is the only one that might have
+ // an OS of Linux, in which case it got handled above already.
+ if (lj->codegen->zig_target.arch.arch == ZigLLVM_tce) {
+ zig_panic("TODO construct TCE linker job");
+ } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_hexagon) {
+ zig_panic("TODO construct Hexagon_TC linker job");
+ } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_xcore) {
+ zig_panic("TODO construct XCore linker job");
+ } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_shave) {
+ zig_panic("TODO construct SHAVE linker job");
+ } else if (lj->codegen->zig_target.oformat == ZigLLVM_ELF) {
+ zig_panic("TODO construct Generic_ELF linker job");
+ } else if (lj->codegen->zig_target.oformat == ZigLLVM_MachO) {
+ zig_panic("TODO construct MachO linker job");
+ } else {
+ zig_panic("TODO construct Generic_GCC linker job");
+ }
+
+ }
+}
+
+static void ensure_we_have_linker_path(CodeGen *g) {
+ if (!g->linker_path || buf_len(g->linker_path) == 0) {
+ zig_panic("zig does not know the path to the linker");
+ }
+}
+
+void codegen_link(CodeGen *g, const char *out_file) {
+ LinkJob lj = {0};
+ lj.codegen = g;
+ if (out_file) {
+ buf_init_from_str(&lj.out_file, out_file);
+ } else {
+ buf_resize(&lj.out_file, 0);
+ }
+
+ bool is_optimized = g->is_release_build;
+ if (is_optimized) {
+ if (g->verbose) {
+ fprintf(stderr, "\nOptimization:\n");
+ fprintf(stderr, "---------------\n");
+ }
+
+ LLVMZigOptimizeModule(g->target_machine, g->module);
+
+ if (g->verbose) {
+ LLVMDumpModule(g->module);
+ }
+ }
+ if (g->verbose) {
+ fprintf(stderr, "\nLink:\n");
+ fprintf(stderr, "-------\n");
+ }
+
+ if (buf_len(&lj.out_file) == 0) {
+ assert(g->root_out_name);
+ buf_init_from_buf(&lj.out_file, g->root_out_name);
+ buf_append_str(&lj.out_file, get_exe_file_extension(g));
+ }
+
+ buf_init_from_buf(&lj.out_file_o, &lj.out_file);
+
+ if (g->out_type != OutTypeObj) {
+ const char *o_ext = get_o_file_extension(g);
+ buf_append_str(&lj.out_file_o, o_ext);
+ }
+
+ char *err_msg = nullptr;
+ if (LLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(&lj.out_file_o),
+ LLVMObjectFile, &err_msg))
+ {
+ zig_panic("unable to write object file: %s", err_msg);
+ }
+
+ if (g->out_type == OutTypeObj) {
+ if (g->verbose) {
+ fprintf(stderr, "OK\n");
+ }
+ return;
+ }
+
+ if (g->out_type == OutTypeLib && g->is_static) {
+ // invoke `ar`
+ // example:
+ // # static link into libfoo.a
+ // ar rcs libfoo.a foo1.o foo2.o
+ zig_panic("TODO invoke ar");
+ return;
+ }
+
+ lj.link_in_crt = (g->link_libc && g->out_type == OutTypeExe);
+ if (lj.link_in_crt) {
+ find_libc_path(g);
+ }
+
+
+
+ // invoke `ld`
+ ensure_we_have_linker_path(g);
+
+ construct_linker_job(&lj);
+
+
+ if (g->verbose) {
+ fprintf(stderr, "%s", buf_ptr(g->linker_path));
+ for (int i = 0; i < lj.args.length; i += 1) {
+ fprintf(stderr, " %s", lj.args.at(i));
+ }
+ fprintf(stderr, "\n");
+ }
+
+ int return_code;
+ Buf ld_stderr = BUF_INIT;
+ Buf ld_stdout = BUF_INIT;
+ os_exec_process(buf_ptr(g->linker_path), lj.args, &return_code, &ld_stderr, &ld_stdout);
+
+ if (return_code != 0) {
+ fprintf(stderr, "linker failed with return code %d\n", return_code);
+ fprintf(stderr, "%s ", buf_ptr(g->linker_path));
+ for (int i = 0; i < lj.args.length; i += 1) {
+ fprintf(stderr, "%s ", lj.args.at(i));
+ }
+ fprintf(stderr, "\n%s\n", buf_ptr(&ld_stderr));
+ exit(1);
+ } else if (buf_len(&ld_stderr)) {
+ fprintf(stderr, "%s\n", buf_ptr(&ld_stderr));
+ }
+
+ if (g->out_type == OutTypeLib) {
+ codegen_generate_h_file(g);
+ }
+
+ if (g->verbose) {
+ fprintf(stderr, "OK\n");
+ }
+}