Commit 28bf768883

Andrew Kelley <andrew@ziglang.org>
2019-02-18 22:47:30
export _mh_execute_header with weak linkage
* also fix extern variables with initialiaztion values to generate runtime code * remove the workaround in example/shared_library/mathtest.zig * introduce the ability for global variables to have Weak and LinkOnce linkage * fix `@export` to work for non-functions. this code needs to be audited though. * fix comptime ptrcast not keeping bigger alignment * fix linker warnings when targeting darwin closes #1903
1 parent 74a335c
Changed files (10)
example/shared_library/mathtest.zig
@@ -1,12 +1,3 @@
-// TODO Remove this workaround
-comptime {
-    const builtin = @import("builtin");
-    if (builtin.os == builtin.Os.macosx) {
-        @export("__mh_execute_header", _mh_execute_header, builtin.GlobalLinkage.Weak);
-    }
-}
-var _mh_execute_header = extern struct {x: usize}{.x = 0};
-
 export fn add(a: i32, b: i32) i32 {
     return a + b;
 }
src/all_types.hpp
@@ -1853,7 +1853,9 @@ struct CodeGen {
 
 enum VarLinkage {
     VarLinkageInternal,
-    VarLinkageExport,
+    VarLinkageExportStrong,
+    VarLinkageExportWeak,
+    VarLinkageExportLinkOnce,
     VarLinkageExternal,
 };
 
src/analyze.cpp
@@ -3746,7 +3746,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
 
     VarLinkage linkage;
     if (is_export) {
-        linkage = VarLinkageExport;
+        linkage = VarLinkageExportStrong;
     } else if (is_extern) {
         linkage = VarLinkageExternal;
     } else {
src/codegen.cpp
@@ -6421,6 +6421,22 @@ static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
     }
 }
 
+static LLVMLinkage var_linkage_to_llvm(VarLinkage var_linkage) {
+    switch (var_linkage) {
+        case VarLinkageInternal:
+            return LLVMInternalLinkage;
+        case VarLinkageExportStrong:
+            return LLVMExternalLinkage;
+        case VarLinkageExportWeak:
+            return LLVMWeakODRLinkage;
+        case VarLinkageExportLinkOnce:
+            return LLVMLinkOnceODRLinkage;
+        case VarLinkageExternal:
+            return LLVMExternalLinkage;
+    }
+    zig_unreachable();
+}
+
 static void do_code_gen(CodeGen *g) {
     assert(!g->errors.length);
 
@@ -6501,21 +6517,21 @@ static void do_code_gen(CodeGen *g) {
                 global_value = LLVMAddGlobal(g->module, var->var_type->type_ref, buf_ptr(&var->name));
                 // TODO debug info for the extern variable
 
-                LLVMSetLinkage(global_value, LLVMExternalLinkage);
+                LLVMSetLinkage(global_value, var_linkage_to_llvm(var->linkage));
                 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
                 LLVMSetAlignment(global_value, var->align_bytes);
                 LLVMSetGlobalConstant(global_value, var->gen_is_const);
                 set_global_tls(g, var, global_value);
             }
         } else {
-            bool exported = (var->linkage == VarLinkageExport);
+            bool exported = (var->linkage != VarLinkageInternal);
             const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported));
             render_const_val(g, var->const_value, mangled_name);
             render_const_val_global(g, var->const_value, mangled_name);
             global_value = var->const_value->global_refs->llvm_global;
 
             if (exported) {
-                LLVMSetLinkage(global_value, LLVMExternalLinkage);
+                LLVMSetLinkage(global_value, var_linkage_to_llvm(var->linkage));
                 maybe_export_dll(g, global_value, GlobalLinkageIdStrong);
             }
             if (tld_var->section_name) {
src/ir.cpp
@@ -13133,6 +13133,20 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
     return ir_build_var_decl_gen(ira, &decl_var_instruction->base, var, casted_init_value);
 }
 
+static VarLinkage global_linkage_to_var_linkage(GlobalLinkageId id) {
+    switch (id) {
+        case GlobalLinkageIdStrong:
+            return VarLinkageExportStrong;
+        case GlobalLinkageIdWeak:
+            return VarLinkageExportWeak;
+        case GlobalLinkageIdLinkOnce:
+            return VarLinkageExportLinkOnce;
+        case GlobalLinkageIdInternal:
+            return VarLinkageInternal;
+    }
+    zig_unreachable();
+}
+
 static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) {
     IrInstruction *name = instruction->name->child;
     Buf *symbol_name = ir_resolve_str(ira, name);
@@ -13161,6 +13175,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
         add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here"));
     }
 
+    bool want_var_export = false;
     switch (target->value.type->id) {
         case ZigTypeIdInvalid:
         case ZigTypeIdUnreachable:
@@ -13196,6 +13211,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
                 ErrorMsg *msg = ir_add_error(ira, target,
                     buf_sprintf("exported struct value must be declared extern"));
                 add_error_note(ira->codegen, msg, target->value.type->data.structure.decl_node, buf_sprintf("declared here"));
+            } else {
+                want_var_export = true;
             }
             break;
         case ZigTypeIdUnion:
@@ -13203,6 +13220,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
                 ErrorMsg *msg = ir_add_error(ira, target,
                     buf_sprintf("exported union value must be declared extern"));
                 add_error_note(ira->codegen, msg, target->value.type->data.unionation.decl_node, buf_sprintf("declared here"));
+            } else {
+                want_var_export = true;
             }
             break;
         case ZigTypeIdEnum:
@@ -13210,6 +13229,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
                 ErrorMsg *msg = ir_add_error(ira, target,
                     buf_sprintf("exported enum value must be declared extern"));
                 add_error_note(ira->codegen, msg, target->value.type->data.enumeration.decl_node, buf_sprintf("declared here"));
+            } else {
+                want_var_export = true;
             }
             break;
         case ZigTypeIdMetaType: {
@@ -13299,6 +13320,16 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
             break;
     }
 
+    // TODO audit the various ways to use @export
+    if (want_var_export && target->id == IrInstructionIdLoadPtr) {
+        IrInstructionLoadPtr *load_ptr = reinterpret_cast<IrInstructionLoadPtr *>(target);
+        if (load_ptr->ptr->id == IrInstructionIdVarPtr) {
+            IrInstructionVarPtr *var_ptr = reinterpret_cast<IrInstructionVarPtr *>(load_ptr->ptr);
+            ZigVar *var = var_ptr->var;
+            var->linkage = global_linkage_to_var_linkage(global_linkage_id);
+        }
+    }
+
     return ir_const_void(ira, &instruction->base);
 }
 
@@ -13586,9 +13617,16 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
     if (var->var_type == nullptr || type_is_invalid(var->var_type))
         return ira->codegen->invalid_instruction;
 
+    ConstExprValue *mem_slot = nullptr;
+
     bool comptime_var_mem = ir_get_var_is_comptime(var);
+    bool linkage_makes_it_runtime = var->linkage == VarLinkageExternal;
+    bool is_const = var->src_is_const;
+    bool is_volatile = false;
+
+    if (linkage_makes_it_runtime)
+        goto no_mem_slot;
 
-    ConstExprValue *mem_slot = nullptr;
     if (var->const_value->special == ConstValSpecialStatic) {
         mem_slot = var->const_value;
     } else {
@@ -13602,8 +13640,6 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
         }
     }
 
-    bool is_const = var->src_is_const;
-    bool is_volatile = false;
     if (mem_slot != nullptr) {
         switch (mem_slot->special) {
             case ConstValSpecialRuntime:
@@ -20679,6 +20715,13 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
         ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier"));
         return ira->codegen->invalid_instruction;
     }
+    uint32_t src_align_bytes;
+    if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes)))
+        return ira->codegen->invalid_instruction;
+
+    uint32_t dest_align_bytes;
+    if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes)))
+        return ira->codegen->invalid_instruction;
 
     if (instr_is_comptime(ptr)) {
         bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type);
@@ -20701,16 +20744,15 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
         IrInstruction *result = ir_const(ira, source_instr, dest_type);
         copy_const_val(&result->value, val, false);
         result->value.type = dest_type;
-        return result;
-    }
 
-    uint32_t src_align_bytes;
-    if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes)))
-        return ira->codegen->invalid_instruction;
+        // Keep the bigger alignment, it can only help-
+        // unless the target is zero bits.
+        if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) {
+            result =  ir_align_cast(ira, result, src_align_bytes, false);
+        }
 
-    uint32_t dest_align_bytes;
-    if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes)))
-        return ira->codegen->invalid_instruction;
+        return result;
+    }
 
     if (dest_align_bytes > src_align_bytes) {
         ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
src/link.cpp
@@ -899,7 +899,11 @@ static void construct_linker_job_macho(LinkJob *lj) {
             lj->args.append("-ios_simulator_version_min");
             break;
     }
-    lj->args.append(buf_ptr(buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro)));
+    Buf *version_string = buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro);
+    lj->args.append(buf_ptr(version_string));
+
+    lj->args.append("-sdk_version");
+    lj->args.append(buf_ptr(version_string));
 
 
     if (g->out_type == OutTypeExe) {
@@ -920,7 +924,9 @@ static void construct_linker_job_macho(LinkJob *lj) {
     add_rpath(lj, &g->output_file_path);
 
     if (shared) {
-        lj->args.append("-headerpad_max_install_names");
+        if (g->system_linker_hack) {
+            lj->args.append("-headerpad_max_install_names");
+        }
     } else if (g->is_static) {
         lj->args.append("-lcrt0.o");
     } else {
std/c/darwin.zig
@@ -36,11 +36,20 @@ pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usi
 pub extern "c" fn bind(socket: c_int, address: ?*const sockaddr, address_len: socklen_t) c_int;
 pub extern "c" fn socket(domain: c_int, type: c_int, protocol: c_int) c_int;
 
+const mach_hdr = if (@sizeOf(usize) == 8) mach_header_64 else mach_header;
+
 /// The value of the link editor defined symbol _MH_EXECUTE_SYM is the address
 /// of the mach header in a Mach-O executable file type.  It does not appear in
 /// any file type other than a MH_EXECUTE file type.  The type of the symbol is
 /// absolute as the header is not part of any section.
-pub extern "c" var _mh_execute_header: if (@sizeOf(usize) == 8) mach_header_64 else mach_header;
+/// This symbol is populated when linking the system's libc, which is guaranteed
+/// on this operating system. However when building object files or libraries,
+/// the system libc won't be linked until the final executable. So we
+/// export a weak symbol here, to be overridden by the real one.
+pub extern "c" var _mh_execute_header: mach_hdr = undefined;
+comptime {
+    @export("__mh_execute_header", _mh_execute_header, @import("builtin").GlobalLinkage.Weak);
+}
 
 pub const mach_header_64 = macho.mach_header_64;
 pub const mach_header = macho.mach_header;
std/debug/index.zig
@@ -574,7 +574,7 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach
 }
 
 fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
-    const base_addr = @ptrToInt(&std.c._mh_execute_header);
+    const base_addr = std.os.getBaseAddress();
     const adjusted_addr = 0x100000000 + (address - base_addr);
 
     const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse {
std/os/index.zig
@@ -701,7 +701,9 @@ pub fn getBaseAddress() usize {
             const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
             return phdr - @sizeOf(std.elf.Ehdr);
         },
-        builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => return @ptrToInt(&std.c._mh_execute_header),
+        builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => {
+            return @ptrToInt(&std.c._mh_execute_header);
+        },
         builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
         else => @compileError("Unsupported OS"),
     }
test/stage1/behavior/ptrcast.zig
@@ -50,3 +50,11 @@ const Bytes = struct {
         return res;
     }
 };
+
+test "comptime ptrcast keeps larger alignment" {
+    comptime {
+        const a: u32 = 1234;
+        const p = @ptrCast([*]const u8, &a);
+        std.debug.assert(@typeOf(p) == [*]align(@alignOf(u32)) const u8);
+    }
+}