Commit 5d2edac12d

Andrew Kelley <andrew@ziglang.org>
2019-03-14 00:33:19
breaking: remove --static; add -dynamic
`--static` is no longer an option. Instead, Zig makes things as static as possible by default. `-dynamic` can be used to choose a dynamic library rather than a static one. `--enable-pic` is a new option. Usually it will be enabled automatically, but in the case of build-exe with no dynamic libraries on Linux or freestanding, Zig chooses off by default. closes #1703 closes #1828
1 parent 85d0f0d
src/all_types.hpp
@@ -1616,6 +1616,12 @@ enum ValgrindSupport {
     ValgrindSupportEnabled,
 };
 
+enum WantPIC {
+    WantPICAuto,
+    WantPICDisabled,
+    WantPICEnabled,
+};
+
 struct CFile {
     ZigList<const char *> args;
     const char *source_path;
@@ -1791,6 +1797,8 @@ struct CodeGen {
     bool have_dllmain_crt_startup;
     bool have_pub_panic;
     bool have_err_ret_tracing;
+    bool have_pic;
+    bool have_dynamic_link; // this is whether the final thing will be dynamically linked. see also is_dynamic
     bool c_want_stdint;
     bool c_want_stdbool;
     bool verbose_tokenize;
@@ -1834,13 +1842,13 @@ struct CodeGen {
     const ZigTarget *zig_target;
     TargetSubsystem subsystem;
     ValgrindSupport valgrind_support;
-    bool is_static;
+    WantPIC want_pic;
+    bool is_dynamic; // shared library rather than static library. dynamic musl rather than static musl.
     bool strip_debug_symbols;
     bool is_test_build;
     bool is_single_threaded;
     bool linker_rdynamic;
     bool each_lib_rpath;
-    bool disable_pic;
     bool is_dummy_so;
     bool disable_gen_h;
 
src/codegen.cpp
@@ -182,13 +182,13 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
     } else {
         g->each_lib_rpath = true;
 
-        if (target_is_darwin(g->zig_target)) {
+        if (target_os_is_darwin(g->zig_target->os)) {
             init_darwin_native(g);
         }
 
     }
 
-    if (target_requires_libc(g->zig_target)) {
+    if (target_os_requires_libc(g->zig_target->os)) {
         g->libc_link_lib = create_link_lib(buf_create_from_str("c"));
         g->link_libs_list.append(g->libc_link_lib);
     }
@@ -3370,7 +3370,7 @@ static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default
     bool asm_has_side_effects = true;
     bool asm_is_alignstack = false;
     if (g->zig_target->arch == ZigLLVM_x86_64) {
-        if (g->zig_target->os == OsLinux || target_is_darwin(g->zig_target) || g->zig_target->os == OsSolaris ||
+        if (g->zig_target->os == OsLinux || target_os_is_darwin(g->zig_target->os) || g->zig_target->os == OsSolaris ||
             (g->zig_target->os == OsWindows && g->zig_target->abi != ZigLLVM_MSVC))
         {
             if (g->cur_fn->valgrind_client_request_array == nullptr) {
@@ -7283,7 +7283,44 @@ static const char *build_mode_to_str(BuildMode build_mode) {
     zig_unreachable();
 }
 
+static bool detect_dynamic_link(CodeGen *g) {
+    if (g->is_dynamic)
+        return true;
+    if (g->zig_target->os == OsFreestanding)
+        return false;
+    if (target_requires_pic(g->zig_target))
+        return true;
+    if (g->out_type == OutTypeExe) {
+        // If there are no dynamic libraries then we can disable PIC
+        for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
+            LinkLib *link_lib = g->link_libs_list.at(i);
+            if (target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name)))
+                continue;
+            return true;
+        }
+        return false;
+    }
+    return true;
+}
+
+static bool detect_pic(CodeGen *g) {
+    if (target_requires_pic(g->zig_target))
+        return true;
+    switch (g->want_pic) {
+        case WantPICDisabled:
+            return false;
+        case WantPICEnabled:
+            return true;
+        case WantPICAuto:
+            return g->have_dynamic_link;
+    }
+    zig_unreachable();
+}
+
 Buf *codegen_generate_builtin_source(CodeGen *g) {
+    g->have_dynamic_link = detect_dynamic_link(g);
+    g->have_pic = detect_pic(g);
+
     Buf *contents = buf_alloc();
 
     // NOTE: when editing this file, you may need to make modifications to the
@@ -7683,6 +7720,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
     buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
     buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
     buf_appendf(contents, "pub const valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g)));
+    buf_appendf(contents, "pub const position_independent_code = %s;\n", bool_to_str(g->have_pic));
 
     buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
 
@@ -7777,6 +7815,9 @@ static void init(CodeGen *g) {
     if (g->module)
         return;
 
+    g->have_dynamic_link = detect_dynamic_link(g);
+    g->have_pic = detect_pic(g);
+
     if (g->is_test_build) {
         g->subsystem = TargetSubsystemConsole;
     }
@@ -7807,10 +7848,7 @@ static void init(CodeGen *g) {
     bool is_optimized = g->build_mode != BuildModeDebug;
     LLVMCodeGenOptLevel opt_level = is_optimized ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone;
 
-    if (g->out_type == OutTypeExe && g->is_static) {
-        g->disable_pic = true;
-    }
-    LLVMRelocMode reloc_mode = g->disable_pic ? LLVMRelocStatic : LLVMRelocPIC;
+    LLVMRelocMode reloc_mode = g->have_pic ? LLVMRelocPIC: LLVMRelocStatic;
 
     const char *target_specific_cpu_args;
     const char *target_specific_features;
@@ -7892,8 +7930,13 @@ static void init(CodeGen *g) {
 }
 
 static void detect_dynamic_linker(CodeGen *g) {
-    if (g->dynamic_linker_path != nullptr || g->is_static)
+    if (g->dynamic_linker_path != nullptr)
         return;
+    if (!g->have_dynamic_link)
+        return;
+    if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))
+        return;
+
     const char *standard_ld_path = target_dynamic_linker(g->zig_target);
     if (standard_ld_path == nullptr)
         return;
@@ -7944,13 +7987,6 @@ static void detect_libc(CodeGen *g) {
     if (g->libc != nullptr || g->libc_link_lib == nullptr)
         return;
 
-    if (g->zig_target->os == OsLinux && target_abi_is_gnu(g->zig_target->abi) &&
-        g->is_static && g->out_type == OutTypeExe)
-    {
-        fprintf(stderr, "glibc does not support static linking\n");
-        exit(1);
-    }
-
     if (target_can_build_libc(g->zig_target)) {
         const char *generic_name = target_libc_generic_name(g->zig_target);
 
@@ -8010,17 +8046,16 @@ static void detect_libc(CodeGen *g) {
         if (want_sys_dir) {
             g->libc_include_dir_list[1] = &g->libc->sys_include_dir;
         }
-    } else if ((g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) &&
-        !target_is_darwin(g->zig_target))
+    } else if ((g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) &&
+        !target_os_is_darwin(g->zig_target->os))
     {
+        Buf triple_buf = BUF_INIT;
+        get_target_triple(&triple_buf, g->zig_target);
         fprintf(stderr,
-            "Zig is unable to provide a libc for the chosen target '%s-%s-%s'.\n"
+            "Zig is unable to provide a libc for the chosen target '%s'.\n"
             "The target is non-native, so Zig also cannot use the native libc installation.\n"
             "Choose a target which has a libc available, or provide a libc installation text file.\n"
-            "See `zig libc --help` for more details.\n",
-            target_arch_name(g->zig_target->arch),
-            target_os_name(g->zig_target->os),
-            target_abi_name(g->zig_target->abi));
+            "See `zig libc --help` for more details.\n", buf_ptr(&triple_buf));
         exit(1);
     }
 }
@@ -8203,7 +8238,7 @@ static void gen_root_source(CodeGen *g) {
         g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap.zig");
     }
     if (g->zig_target->os == OsWindows && !g->have_dllmain_crt_startup &&
-            g->out_type == OutTypeLib && !g->is_static)
+            g->out_type == OutTypeLib && g->is_dynamic)
     {
         g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap_lib.zig");
     }
@@ -8297,7 +8332,8 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose
     cache_int(cache_hash, g->zig_target->abi);
     cache_bool(cache_hash, g->strip_debug_symbols);
     cache_int(cache_hash, g->build_mode);
-    cache_bool(cache_hash, g->disable_pic);
+    cache_bool(cache_hash, g->have_pic);
+    cache_bool(cache_hash, want_valgrind_support(g));
     for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) {
         cache_str(cache_hash, g->clang_argv[arg_i]);
     }
@@ -8451,7 +8487,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
         args.append("-c");
         args.append(buf_ptr(c_source_file));
 
-        if (target_supports_fpic(g->zig_target) && !g->disable_pic) {
+        if (target_supports_fpic(g->zig_target) && g->have_pic) {
             args.append("-fPIC");
         }
 
@@ -9064,7 +9100,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
     cache_int(ch, g->zig_target->os);
     cache_int(ch, g->zig_target->abi);
     cache_int(ch, g->subsystem);
-    cache_bool(ch, g->is_static);
     cache_bool(ch, g->strip_debug_symbols);
     cache_bool(ch, g->is_test_build);
     if (g->is_test_build) {
@@ -9074,9 +9109,10 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
     cache_bool(ch, g->is_single_threaded);
     cache_bool(ch, g->linker_rdynamic);
     cache_bool(ch, g->each_lib_rpath);
-    cache_bool(ch, g->disable_pic);
     cache_bool(ch, g->disable_gen_h);
-    cache_bool(ch, g->valgrind_support);
+    cache_bool(ch, want_valgrind_support(g));
+    cache_bool(ch, g->have_pic);
+    cache_bool(ch, g->have_dynamic_link);
     cache_bool(ch, g->is_dummy_so);
     cache_buf_opt(ch, g->mmacosx_version_min);
     cache_buf_opt(ch, g->mios_version_min);
@@ -9150,7 +9186,7 @@ static void resolve_out_paths(CodeGen *g) {
                     buf_resize(out_basename, 0);
                     buf_append_str(out_basename, target_lib_file_prefix(g->zig_target));
                     buf_append_buf(out_basename, g->root_out_name);
-                    buf_append_str(out_basename, target_lib_file_ext(g->zig_target, g->is_static,
+                    buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic,
                                 g->version_major, g->version_minor, g->version_patch));
                     break;
             }
@@ -9182,6 +9218,8 @@ void codegen_build_and_link(CodeGen *g) {
         g->output_dir = buf_create_from_str(".");
     }
 
+    g->have_dynamic_link = detect_dynamic_link(g);
+    g->have_pic = detect_pic(g);
     detect_libc(g);
     detect_dynamic_linker(g);
 
src/ir.cpp
@@ -15543,9 +15543,8 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
 }
 
 static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) {
-    if (buf_eql_str(lib_name, "c") && ira->codegen->libc_link_lib == nullptr &&
-        !ira->codegen->reported_bad_link_libc_error)
-    {
+    bool is_libc = target_is_libc_lib_name(ira->codegen->zig_target, buf_ptr(lib_name));
+    if (is_libc && ira->codegen->libc_link_lib == nullptr && !ira->codegen->reported_bad_link_libc_error) {
         ir_add_error_node(ira, source_node,
             buf_sprintf("dependency on library c must be explicitly specified in the build command"));
         ira->codegen->reported_bad_link_libc_error = true;
@@ -15558,6 +15557,16 @@ static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name,
             return;
         }
     }
+
+    if (!is_libc && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) {
+        ErrorMsg *msg = ir_add_error_node(ira, source_node,
+            buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",
+                buf_ptr(lib_name)));
+        add_error_note(ira->codegen, msg, source_node,
+                buf_sprintf("fixed by `--library %s` or `--enable-pic`", buf_ptr(lib_name)));
+        ira->codegen->reported_bad_link_libc_error = true;
+    }
+
     for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) {
         Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i);
         if (buf_eql_buf(lib_name, forbidden_lib_name)) {
src/libc_installation.cpp
@@ -102,7 +102,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
     }
 
     if (buf_len(&libc->crt_dir) == 0) {
-        if (!target_is_darwin(target)) {
+        if (!target_os_is_darwin(target->os)) {
             if (verbose) {
                 fprintf(stderr, "crt_dir may not be empty for %s\n", target_os_name(target->os));
             }
src/link.cpp
@@ -35,7 +35,7 @@ static CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, Ou
     child_gen->llvm_argv = parent_gen->llvm_argv;
 
     codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
-    child_gen->disable_pic = parent_gen->disable_pic;
+    child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled;
     child_gen->valgrind_support = ValgrindSupportDisabled;
 
     codegen_set_errmsg_color(child_gen, parent_gen->err_color);
@@ -48,15 +48,6 @@ static CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, Ou
     return child_gen;
 }
 
-
-static bool target_is_glibc(CodeGen *g) {
-    return g->zig_target->os == OsLinux && target_abi_is_gnu(g->zig_target->abi);
-}
-
-static bool target_is_musl(CodeGen *g) {
-    return g->zig_target->os == OsLinux && target_abi_is_musl(g->zig_target->abi);
-}
-
 static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFile *c_file) {
     CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr);
     codegen_set_out_name(child_gen, buf_create_from_str(name));
@@ -97,7 +88,7 @@ static const char *build_dummy_so(CodeGen *parent, const char *name, size_t majo
     CodeGen *child_gen = create_child_codegen(parent, glibc_dummy_root_src, OutTypeLib, nullptr);
     codegen_set_out_name(child_gen, buf_create_from_str(name));
     codegen_set_lib_version(child_gen, major_version, 0, 0);
-    child_gen->is_static = false;
+    child_gen->is_dynamic = true;
     child_gen->is_dummy_so = true;
     codegen_build_and_link(child_gen);
     return buf_ptr(&child_gen->output_file_path);
@@ -106,7 +97,6 @@ static const char *build_dummy_so(CodeGen *parent, const char *name, size_t majo
 static const char *build_libunwind(CodeGen *parent) {
     CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr);
     codegen_set_out_name(child_gen, buf_create_from_str("unwind"));
-    child_gen->is_static = true;
     LinkLib *new_link_lib = codegen_add_link_lib(child_gen, buf_create_from_str("c"));
     new_link_lib->provided_explicitly = false;
     enum SrcKind {
@@ -490,7 +480,6 @@ static bool is_musl_arch_name(const char *name) {
 static const char *build_musl(CodeGen *parent) {
     CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr);
     codegen_set_out_name(child_gen, buf_create_from_str("c"));
-    child_gen->is_static = true;
 
     // When there is a src/<arch>/foo.* then it should substitute for src/foo.*
     // Even a .s file can substitute for a .c file.
@@ -608,7 +597,7 @@ static const char *build_musl(CodeGen *parent) {
 
 
 static const char *get_libc_crt_file(CodeGen *parent, const char *file) {
-    if (parent->libc == nullptr && target_is_glibc(parent)) {
+    if (parent->libc == nullptr && target_is_glibc(parent->zig_target)) {
         if (strcmp(file, "crti.o") == 0) {
             CFile *c_file = allocate<CFile>(1);
             c_file->source_path = glibc_start_asm_path(parent, "crti.S");
@@ -677,7 +666,6 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) {
         } else if (strcmp(file, "libc_nonshared.a") == 0) {
             CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr);
             codegen_set_out_name(child_gen, buf_create_from_str("c_nonshared"));
-            child_gen->is_static = true;
             {
                 CFile *c_file = allocate<CFile>(1);
                 c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "elf-init.c");
@@ -755,7 +743,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) {
         } else {
             zig_unreachable();
         }
-    } else if (parent->libc == nullptr && target_is_musl(parent)) {
+    } else if (parent->libc == nullptr && target_is_musl(parent->zig_target)) {
         if (strcmp(file, "crti.o") == 0) {
             return build_asm_object(parent, "crti", musl_start_asm_path(parent, "crti.s"));
         } else if (strcmp(file, "crtn.o") == 0) {
@@ -799,7 +787,6 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)
 
     CodeGen *child_gen = create_child_codegen(parent_gen, full_path, child_out_type,
             parent_gen->libc);
-    child_gen->is_static = true;
     codegen_set_out_name(child_gen, buf_create_from_str(aname));
 
     // This is so that compiler_rt and builtin libraries know whether they
@@ -924,9 +911,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
     lj->args.append(getLDMOption(g->zig_target));
 
     bool is_lib = g->out_type == OutTypeLib;
-    bool is_dyn_lib = !g->is_static && is_lib;
+    bool is_dyn_lib = g->is_dynamic && is_lib;
     Buf *soname = nullptr;
-    if (g->is_static) {
+    if (!g->have_dynamic_link) {
         if (g->zig_target->arch == ZigLLVM_arm || g->zig_target->arch == ZigLLVM_armeb ||
             g->zig_target->arch == ZigLLVM_thumb || g->zig_target->arch == ZigLLVM_thumbeb)
         {
@@ -948,7 +935,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
         const char *crt1o;
         if (g->zig_target->os == OsNetBSD) {
             crt1o = "crt0.o";
-        } else if (g->is_static) {
+        } else if (!g->have_dynamic_link) {
             crt1o = "crt1.o";
         } else {
             crt1o = "Scrt1.o";
@@ -994,12 +981,11 @@ static void construct_linker_job_elf(LinkJob *lj) {
             lj->args.append(buf_ptr(&g->libc->crt_dir));
         }
 
-        if (!g->is_static) {
+        if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) {
             assert(g->dynamic_linker_path != nullptr);
             lj->args.append("-dynamic-linker");
             lj->args.append(buf_ptr(g->dynamic_linker_path));
         }
-
     }
 
     if (is_dyn_lib) {
@@ -1012,16 +998,14 @@ static void construct_linker_job_elf(LinkJob *lj) {
         lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
     }
 
-    if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) {
-        if (g->libc_link_lib == nullptr && !g->is_dummy_so) {
+    if (!g->is_dummy_so && (g->out_type == OutTypeExe || is_dyn_lib)) {
+        if (g->libc_link_lib == nullptr) {
             Buf *builtin_a_path = build_a(g, "builtin");
             lj->args.append(buf_ptr(builtin_a_path));
         }
 
-        if (!g->is_dummy_so) {
-            Buf *compiler_rt_o_path = build_compiler_rt(g);
-            lj->args.append(buf_ptr(compiler_rt_o_path));
-        }
+        Buf *compiler_rt_o_path = build_compiler_rt(g);
+        lj->args.append(buf_ptr(compiler_rt_o_path));
     }
 
     for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
@@ -1030,17 +1014,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
             // libc is linked specially
             continue;
         }
-        if (g->libc == nullptr && (target_is_glibc(g) || target_is_musl(g))) {
+        if (g->libc == nullptr && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) {
             // these libraries are always linked below when targeting glibc
-            if (buf_eql_str(link_lib->name, "m")) {
-                continue;
-            } else if (buf_eql_str(link_lib->name, "pthread")) {
-                continue;
-            } else if (buf_eql_str(link_lib->name, "dl")) {
-                continue;
-            } else if (buf_eql_str(link_lib->name, "rt")) {
-                continue;
-            }
+            continue;
         }
         Buf *arg;
         if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
@@ -1057,7 +1033,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
     // libc dep
     if (g->libc_link_lib != nullptr) {
         if (g->libc != nullptr) {
-            if (g->is_static) {
+            if (!g->have_dynamic_link) {
                 lj->args.append("--start-group");
                 lj->args.append("-lgcc");
                 lj->args.append("-lgcc_eh");
@@ -1076,7 +1052,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
                 lj->args.append("-lgcc_s");
                 lj->args.append("--no-as-needed");
             }
-        } else if (target_is_glibc(g)) {
+        } else if (target_is_glibc(g->zig_target)) {
             lj->args.append(build_libunwind(g));
             lj->args.append(build_dummy_so(g, "c", 6));
             lj->args.append(build_dummy_so(g, "m", 6));
@@ -1084,7 +1060,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
             lj->args.append(build_dummy_so(g, "dl", 2));
             lj->args.append(build_dummy_so(g, "rt", 1));
             lj->args.append(get_libc_crt_file(g, "libc_nonshared.a"));
-        } else if (target_is_musl(g)) {
+        } else if (target_is_musl(g->zig_target)) {
             lj->args.append(build_libunwind(g));
             lj->args.append(build_musl(g));
         } else {
@@ -1158,10 +1134,10 @@ static void add_nt_link_args(LinkJob *lj, bool is_library) {
     CodeGen *g = lj->codegen;
 
     if (lj->link_in_crt) {
-        const char *lib_str = g->is_static ? "lib" : "";
+        const char *lib_str = g->is_dynamic ? "" : "lib";
         const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : "";
 
-        if (g->is_static) {
+        if (!g->is_dynamic) {
             Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str);
             lj->args.append(buf_ptr(cmt_lib_name));
         } else {
@@ -1265,7 +1241,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
         lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->crt_dir))));
     }
 
-    if (is_library && !g->is_static) {
+    if (is_library && g->is_dynamic) {
         lj->args.append("-DLL");
     }
 
@@ -1278,7 +1254,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
         lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
     }
 
-    if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) {
+    if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) {
         if (g->libc_link_lib == nullptr && !g->is_dummy_so) {
             Buf *builtin_a_path = build_a(g, "builtin");
             lj->args.append(buf_ptr(builtin_a_path));
@@ -1457,8 +1433,8 @@ static void construct_linker_job_macho(LinkJob *lj) {
     }
 
     bool is_lib = g->out_type == OutTypeLib;
-    bool is_dyn_lib = !g->is_static && is_lib;
-    if (g->is_static) {
+    bool is_dyn_lib = g->is_dynamic && is_lib;
+    if (!g->is_dynamic) {
         lj->args.append("-static");
     } else {
         lj->args.append("-dynamic");
@@ -1509,11 +1485,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
 
 
     if (g->out_type == OutTypeExe) {
-        if (g->is_static) {
-            lj->args.append("-no_pie");
-        } else {
-            lj->args.append("-pie");
-        }
+        lj->args.append("-pie");
     }
 
     lj->args.append("-o");
@@ -1629,7 +1601,7 @@ void codegen_link(CodeGen *g) {
         lj.args.append("-r");
     }
 
-    if (g->out_type == OutTypeLib && g->is_static) {
+    if (g->out_type == OutTypeLib && !g->is_dynamic) {
         ZigList<const char *> file_names = {};
         for (size_t i = 0; i < g->link_objects.length; i += 1) {
             file_names.append((const char *)buf_ptr(g->link_objects.at(i)));
src/main.cpp
@@ -52,7 +52,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
         "  --cache [auto|off|on]        build in cache, print output path to stdout\n"
         "  --color [auto|off|on]        enable or disable colored error messages\n"
         "  --disable-gen-h              do not generate a C header file (.h)\n"
-        "  --disable-pic                disable Position Independent Code for libraries\n"
+        "  --disable-pic                disable Position Independent Code\n"
+        "  --enable-pic                 enable Position Independent Code\n"
         "  --disable-valgrind           omit valgrind client requests in debug builds\n"
         "  --enable-valgrind            include valgrind client requests release builds\n"
         "  --emit [asm|bin|llvm-ir]     emit a specific file format as compilation output\n"
@@ -67,7 +68,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
         "  --release-safe               build with optimizations on and safety on\n"
         "  --release-small              build with size optimizations on and safety off\n"
         "  --single-threaded            source may assume it is only used single-threaded\n"
-        "  --static                     output will be statically linked\n"
+        "  -dynamic                     create a shared library (.so; .dll; .dylib)\n"
         "  --strip                      exclude debug symbols\n"
         "  -target [name]               <arch><sub>-<os>-<abi> see the targets command\n"
         "  --verbose-tokenize           enable compiler debug output for tokenization\n"
@@ -397,7 +398,7 @@ int main(int argc, char **argv) {
     const char *in_file = nullptr;
     Buf *output_dir = nullptr;
     bool strip = false;
-    bool is_static = false;
+    bool is_dynamic = false;
     OutType out_type = OutTypeUnknown;
     const char *out_name = nullptr;
     bool verbose_tokenize = false;
@@ -432,7 +433,6 @@ int main(int argc, char **argv) {
     size_t ver_minor = 0;
     size_t ver_patch = 0;
     bool timing_info = false;
-    bool disable_pic = false;
     const char *cache_dir = nullptr;
     CliPkg *cur_pkg = allocate<CliPkg>(1);
     BuildMode build_mode = BuildModeDebug;
@@ -445,6 +445,7 @@ int main(int argc, char **argv) {
     Buf *override_std_dir = nullptr;
     Buf *main_pkg_path = nullptr;
     ValgrindSupport valgrind_support = ValgrindSupportAuto;
+    WantPIC want_pic = WantPICAuto;
 
     ZigList<const char *> llvm_argv = {0};
     llvm_argv.append("zig (LLVM option parsing)");
@@ -620,8 +621,8 @@ int main(int argc, char **argv) {
                 }
             } else if (strcmp(arg, "--strip") == 0) {
                 strip = true;
-            } else if (strcmp(arg, "--static") == 0) {
-                is_static = true;
+            } else if (strcmp(arg, "-dynamic") == 0) {
+                is_dynamic = true;
             } else if (strcmp(arg, "--verbose-tokenize") == 0) {
                 verbose_tokenize = true;
             } else if (strcmp(arg, "--verbose-ast") == 0) {
@@ -642,12 +643,14 @@ int main(int argc, char **argv) {
                 each_lib_rpath = true;
             } else if (strcmp(arg, "-ftime-report") == 0) {
                 timing_info = true;
-            } else if (strcmp(arg, "--disable-pic") == 0) {
-                disable_pic = true;
             } else if (strcmp(arg, "--enable-valgrind") == 0) {
                 valgrind_support = ValgrindSupportEnabled;
             } else if (strcmp(arg, "--disable-valgrind") == 0) {
                 valgrind_support = ValgrindSupportDisabled;
+            } else if (strcmp(arg, "--enable-pic") == 0) {
+                want_pic = WantPICEnabled;
+            } else if (strcmp(arg, "--disable-pic") == 0) {
+                want_pic = WantPICDisabled;
             } else if (strcmp(arg, "--system-linker-hack") == 0) {
                 system_linker_hack = true;
             } else if (strcmp(arg, "--single-threaded") == 0) {
@@ -904,12 +907,24 @@ int main(int argc, char **argv) {
     }
 
     if (output_dir != nullptr && enable_cache == CacheOptOn) {
-        fprintf(stderr, "The --output-dir argument is incompatible with --cache on.\n");
+        fprintf(stderr, "`--output-dir` is incompatible with --cache on.\n");
+        return print_error_usage(arg0);
+    }
+
+    if (target_requires_pic(&target) && want_pic == WantPICDisabled) {
+        Buf triple_buf = BUF_INIT;
+        get_target_triple(&triple_buf, &target);
+        fprintf(stderr, "`--disable-pic` is incompatible with target '%s'\n", buf_ptr(&triple_buf));
         return print_error_usage(arg0);
     }
 
     if (emit_file_type != EmitFileTypeBinary && in_file == nullptr) {
-        fprintf(stderr, "A root source file is required when using --emit asm or --emit llvm-ir");
+        fprintf(stderr, "A root source file is required when using `--emit asm` or `--emit llvm-ir`\n");
+        return print_error_usage(arg0);
+    }
+
+    if (out_type != OutTypeLib && is_dynamic) {
+        fprintf(stderr, "`-dynamic` may only be specified with `build-lib`.\n");
         return print_error_usage(arg0);
     }
 
@@ -936,6 +951,7 @@ int main(int argc, char **argv) {
         CodeGen *g = codegen_create(main_pkg_path, nullptr, &target,
                 out_type, build_mode, get_zig_lib_dir(), override_std_dir, nullptr, nullptr);
         g->valgrind_support = valgrind_support;
+        g->want_pic = want_pic;
         g->is_single_threaded = is_single_threaded;
         Buf *builtin_source = codegen_generate_builtin_source(g);
         if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
@@ -1027,16 +1043,9 @@ int main(int argc, char **argv) {
                     get_zig_lib_dir(), override_std_dir, libc, cache_dir_buf);
             if (llvm_argv.length >= 2) codegen_set_llvm_argv(g, llvm_argv.items + 1, llvm_argv.length - 2);
             g->valgrind_support = valgrind_support;
+            g->want_pic = want_pic;
             g->subsystem = subsystem;
 
-            if (disable_pic) {
-                if (out_type != OutTypeLib || !is_static) {
-                    fprintf(stderr, "--disable-pic only applies to static libraries");
-                    return EXIT_FAILURE;
-                }
-                g->disable_pic = true;
-            }
-
             g->enable_time_report = timing_info;
             codegen_set_out_name(g, buf_out_name);
             codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
@@ -1049,7 +1058,7 @@ int main(int argc, char **argv) {
             codegen_set_clang_argv(g, clang_argv.items, clang_argv.length);
 
             codegen_set_strip(g, strip);
-            g->is_static = is_static;
+            g->is_dynamic = is_dynamic;
             g->dynamic_linker_path = dynamic_linker;
             g->verbose_tokenize = verbose_tokenize;
             g->verbose_ast = verbose_ast;
src/target.cpp
@@ -693,8 +693,8 @@ void get_target_triple(Buf *triple, const ZigTarget *target) {
             ZigLLVMGetEnvironmentTypeName(target->abi));
 }
 
-bool target_is_darwin(const ZigTarget *target) {
-    switch (target->os) {
+bool target_os_is_darwin(Os os) {
+    switch (os) {
         case OsMacOSX:
         case OsIOS:
         case OsWatchOS:
@@ -708,7 +708,7 @@ bool target_is_darwin(const ZigTarget *target) {
 ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target) {
     if (target->os == OsUefi || target->os == OsWindows) {
         return ZigLLVM_COFF;
-    } else if (target_is_darwin(target)) {
+    } else if (target_os_is_darwin(target->os)) {
         return ZigLLVM_MachO;
     }
     if (target->arch == ZigLLVM_wasm32 ||
@@ -942,7 +942,7 @@ const char *target_lib_file_ext(const ZigTarget *target, bool is_static,
     } else {
         if (is_static) {
             return ".a";
-        } else if (target_is_darwin(target)) {
+        } else if (target_os_is_darwin(target->os)) {
             return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
                         version_major, version_minor, version_patch));
         } else {
@@ -1279,7 +1279,7 @@ bool target_has_valgrind_support(const ZigTarget *target) {
         case ZigLLVM_UnknownArch:
             zig_unreachable();
         case ZigLLVM_x86_64:
-            return (target->os == OsLinux || target_is_darwin(target) || target->os == OsSolaris ||
+            return (target->os == OsLinux || target_os_is_darwin(target->os) || target->os == OsSolaris ||
                 (target->os == OsWindows && target->abi != ZigLLVM_MSVC));
         default:
             return false;
@@ -1287,11 +1287,11 @@ bool target_has_valgrind_support(const ZigTarget *target) {
     zig_unreachable();
 }
 
-bool target_requires_libc(const ZigTarget *target) {
+bool target_os_requires_libc(Os os) {
     // On Darwin, we always link libSystem which contains libc.
     // Similarly on FreeBSD and NetBSD we always link system libc
     // since this is the stable syscall interface.
-    return (target_is_darwin(target) || target->os == OsFreeBSD || target->os == OsNetBSD);
+    return (target_os_is_darwin(os) || os == OsFreeBSD || os == OsNetBSD);
 }
 
 bool target_supports_fpic(const ZigTarget *target) {
@@ -1300,6 +1300,19 @@ bool target_supports_fpic(const ZigTarget *target) {
   return target->os != OsWindows;
 }
 
+bool target_requires_pic(const ZigTarget *target) {
+  // This function returns whether non-pic code is completely invalid on the given target.
+  return target->os == OsWindows || target_os_requires_libc(target->os) || target_is_glibc(target);
+}
+
+bool target_is_glibc(const ZigTarget *target) {
+    return target->os == OsLinux && target_abi_is_gnu(target->abi);
+}
+
+bool target_is_musl(const ZigTarget *target) {
+    return target->os == OsLinux && target_abi_is_musl(target->abi);
+}
+
 ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) {
     switch (os) {
         case OsFreestanding:
@@ -1458,3 +1471,29 @@ const char *target_libc_generic_name(const ZigTarget *target) {
     }
     zig_unreachable();
 }
+
+bool target_is_libc_lib_name(const ZigTarget *target, const char *name) {
+    if (strcmp(name, "c") == 0)
+        return true;
+
+    if (target_abi_is_gnu(target->abi) || target_abi_is_musl(target->abi)) {
+        if (strcmp(name, "m") == 0)
+            return true;
+        if (strcmp(name, "rt") == 0)
+            return true;
+        if (strcmp(name, "pthread") == 0)
+            return true;
+        if (strcmp(name, "crypt") == 0)
+            return true;
+        if (strcmp(name, "util") == 0)
+            return true;
+        if (strcmp(name, "xnet") == 0)
+            return true;
+        if (strcmp(name, "resolv") == 0)
+            return true;
+        if (strcmp(name, "dl") == 0)
+            return true;
+    }
+
+    return false;
+}
src/target.hpp
@@ -155,13 +155,17 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type);
 bool target_is_arm(const ZigTarget *target);
 bool target_allows_addr_zero(const ZigTarget *target);
 bool target_has_valgrind_support(const ZigTarget *target);
-bool target_is_darwin(const ZigTarget *target);
-bool target_requires_libc(const ZigTarget *target);
+bool target_os_is_darwin(Os os);
+bool target_os_requires_libc(Os os);
 bool target_can_build_libc(const ZigTarget *target);
 const char *target_libc_generic_name(const ZigTarget *target);
+bool target_is_libc_lib_name(const ZigTarget *target, const char *name);
 bool target_supports_fpic(const ZigTarget *target);
+bool target_requires_pic(const ZigTarget *target);
 bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi);
 bool target_abi_is_musl(ZigLLVM_EnvironmentType abi);
+bool target_is_glibc(const ZigTarget *target);
+bool target_is_musl(const ZigTarget *target);
 
 uint32_t target_arch_pointer_bit_width(ZigLLVM_ArchType arch);
 
std/build.zig
@@ -157,10 +157,6 @@ pub const Builder = struct {
         return LibExeObjStep.createExecutable(self, name, root_src, false);
     }
 
-    pub fn addStaticExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
-        return LibExeObjStep.createExecutable(self, name, root_src, true);
-    }
-
     pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
         return LibExeObjStep.createObject(self, name, root_src);
     }
@@ -920,7 +916,7 @@ pub const LibExeObjStep = struct {
     target: Target,
     linker_script: ?[]const u8,
     out_filename: []const u8,
-    static: bool,
+    is_dynamic: bool,
     version: Version,
     build_mode: builtin.Mode,
     kind: Kind,
@@ -975,13 +971,13 @@ pub const LibExeObjStep = struct {
 
     pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep {
         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
-        self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver);
+        self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver);
         return self;
     }
 
     pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
-        self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0));
+        self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0));
         return self;
     }
 
@@ -991,9 +987,9 @@ pub const LibExeObjStep = struct {
         return self;
     }
 
-    pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, static: bool) *LibExeObjStep {
+    pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, is_dynamic: bool) *LibExeObjStep {
         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
-        self.* = initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0));
+        self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0));
         return self;
     }
 
@@ -1003,14 +999,14 @@ pub const LibExeObjStep = struct {
         return self;
     }
 
-    fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: Version) LibExeObjStep {
+    fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, is_dynamic: bool, ver: Version) LibExeObjStep {
         var self = LibExeObjStep{
             .strip = false,
             .builder = builder,
             .verbose_link = false,
             .verbose_cc = false,
             .build_mode = builtin.Mode.Debug,
-            .static = static,
+            .is_dynamic = is_dynamic,
             .kind = kind,
             .root_src = root_src,
             .name = name,
@@ -1057,7 +1053,7 @@ pub const LibExeObjStep = struct {
                 self.out_filename = self.builder.fmt("test{}", self.target.exeFileExt());
             },
             Kind.Lib => {
-                if (self.static) {
+                if (!self.is_dynamic) {
                     switch (self.target.getOs()) {
                         builtin.Os.windows => {
                             self.out_filename = self.builder.fmt("{}.lib", self.name);
@@ -1147,7 +1143,7 @@ pub const LibExeObjStep = struct {
     }
 
     pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
-        return self.kind == Kind.Lib and !self.static;
+        return self.kind == Kind.Lib and self.is_dynamic;
     }
 
     pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
@@ -1333,7 +1329,7 @@ pub const LibExeObjStep = struct {
                         try zig_args.append(other.getOutputPath());
                     },
                     LibExeObjStep.Kind.Lib => {
-                        if (other.static or self.target.isWindows()) {
+                        if (!other.is_dynamic or self.target.isWindows()) {
                             try zig_args.append("--object");
                             try zig_args.append(other.getOutputLibPath());
                         } else {
@@ -1413,7 +1409,7 @@ pub const LibExeObjStep = struct {
         zig_args.append("--name") catch unreachable;
         zig_args.append(self.name) catch unreachable;
 
-        if (self.kind == Kind.Lib and !self.static) {
+        if (self.kind == Kind.Lib and self.is_dynamic) {
             zig_args.append("--ver-major") catch unreachable;
             zig_args.append(builder.fmt("{}", self.version.major)) catch unreachable;
 
@@ -1423,8 +1419,8 @@ pub const LibExeObjStep = struct {
             zig_args.append("--ver-patch") catch unreachable;
             zig_args.append(builder.fmt("{}", self.version.patch)) catch unreachable;
         }
-        if (self.static) {
-            zig_args.append("--static") catch unreachable;
+        if (self.is_dynamic) {
+            try zig_args.append("-dynamic");
         }
 
         switch (self.target) {
@@ -1531,7 +1527,7 @@ pub const LibExeObjStep = struct {
             self.output_dir = os.path.dirname(output_path).?;
         }
 
-        if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) {
+        if (self.kind == Kind.Lib and self.is_dynamic and self.target.wantSharedLibSymLinks()) {
             try doAtomicSymLinks(builder.allocator, self.getOutputPath(), self.major_only_filename, self.name_only_filename);
         }
     }
@@ -1677,7 +1673,7 @@ const InstallArtifactStep = struct {
         };
         self.step.dependOn(&artifact.step);
         builder.pushInstalledFile(self.dest_file);
-        if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
+        if (self.artifact.kind == LibExeObjStep.Kind.Lib and self.artifact.is_dynamic) {
             builder.pushInstalledFile(os.path.join(
                 builder.allocator,
                 [][]const u8{ builder.lib_dir, artifact.major_only_filename },
@@ -1700,11 +1696,11 @@ const InstallArtifactStep = struct {
                 LibExeObjStep.Kind.Obj => unreachable,
                 LibExeObjStep.Kind.Test => unreachable,
                 LibExeObjStep.Kind.Exe => u32(0o755),
-                LibExeObjStep.Kind.Lib => if (self.artifact.static) u32(0o666) else u32(0o755),
+                LibExeObjStep.Kind.Lib => if (!self.artifact.is_dynamic) u32(0o666) else u32(0o755),
             },
         };
         try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode);
-        if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
+        if (self.artifact.isDynamicLibrary()) {
             try doAtomicSymLinks(builder.allocator, self.dest_file, self.artifact.major_only_filename, self.artifact.name_only_filename);
         }
     }