diff --git a/releasenotes.md b/releasenotes.md index 822041b16..bfc4cc029 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -6,11 +6,11 @@ ### Fixes - Error with unsigned compare in `@ensure` when early returning 0 #1207. +- Prevent Mach-O from removing `@init` and `@dynamic` in a more reliable way #1200. ### Stdlib changes - Added `remove_first_item` `remove_last_item` and `remove_item` as aliases for the `match` functions. - ## 0.6.0 Change list ### Changes / improvements diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 6506fd4f9..b97420024 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -135,10 +135,27 @@ LLVMValueRef llvm_get_selector(GenContext *c, const char *name) } -void llvm_emit_macho_xtor(GenContext *c, LLVMValueRef *list, const char *name) +static LLVMValueRef llvm_emit_macho_xtor_use(GenContext *c, LLVMValueRef global, const char *name) +{ + LLVMValueRef initializer = LLVMAddFunction(c->module, name, c->xtor_func_type); + LLVMSetLinkage(initializer, LLVMInternalLinkage); + LLVMSetAlignment(initializer, 8); + LLVMValueRef vals_fn[3] = { llvm_const_int(c, type_int, 1), initializer, llvm_get_zero(c, type_voidptr) }; + LLVMValueRef result = LLVMConstNamedStruct(c->xtor_entry_type, vals_fn, 3); + + LLVMBasicBlockRef last_block; + LLVMBuilderRef builder = llvm_create_function_entry(c, initializer, &last_block); + LLVMValueRef load = LLVMBuildLoad2(builder, LLVMTypeOf(LLVMGetInitializer(global)), global, ".retain_global"); + LLVMSetVolatile(load, true); + LLVMBuildRetVoid(builder); + LLVMDisposeBuilder(builder); + return result; +} + +static LLVMValueRef llvm_emit_macho_xtor(GenContext *c, LLVMValueRef *list, const char *name, const char *retain_name) { unsigned len = vec_size(list); - if (!len) return; + if (!len) return NULL; scratch_buffer_clear(); scratch_buffer_append(".list$"); scratch_buffer_append(name); @@ -151,31 +168,46 @@ void llvm_emit_macho_xtor(GenContext *c, LLVMValueRef *list, const char *name) LLVMSetInitializer(global, array); LLVMSetSection(global, scratch_buffer_to_string()); LLVMSetAlignment(global, llvm_abi_alignment(c, c->xtor_entry_type)); + + return llvm_emit_macho_xtor_use(c, global, retain_name); } void llvm_emit_constructors_and_destructors(GenContext *c) { if (platform_target.object_format == OBJ_FORMAT_MACHO) { - llvm_emit_macho_xtor(c, c->constructors, "c3ctor"); - llvm_emit_macho_xtor(c, c->destructors, "c3dtor"); + + LLVMValueRef c3_dynamic = LLVMGetNamedGlobal(c->module, "$c3_dynamic"); + LLVMValueRef dtor_global = llvm_emit_macho_xtor(c, c->constructors, "c3ctor", ".c3_ctor_retain"); + LLVMValueRef ctor_global = llvm_emit_macho_xtor(c, c->destructors, "c3dtor", ".c3_dtor_retain"); LLVMValueRef runtime_start = LLVMGetNamedFunction(c->module, "__c3_runtime_startup"); - if (!runtime_start || !LLVMGetFirstBasicBlock(runtime_start)) return; - LLVMValueRef vals[3] = { llvm_const_int(c, type_int, 65535), runtime_start, llvm_get_zero(c, type_voidptr) }; - LLVMValueRef entry = LLVMConstNamedStruct(c->xtor_entry_type, vals, 3); - LLVMValueRef array = LLVMConstArray(c->xtor_entry_type, &entry, 1); - LLVMValueRef global_ctor = LLVMAddGlobal(c->module, LLVMTypeOf(array), "llvm.global_ctors"); - LLVMSetLinkage(global_ctor, LLVMAppendingLinkage); - LLVMSetInitializer(global_ctor, array); + int len = 0; + LLVMValueRef ctors[5]; + if (ctor_global) ctors[len++] = ctor_global; + if (dtor_global) ctors[len++] = dtor_global; + if (c3_dynamic) ctors[len++] = llvm_emit_macho_xtor_use(c, c3_dynamic, ".c3_dynamic_retain"); + if (!runtime_start || !LLVMGetFirstBasicBlock(runtime_start)) + { + goto EMIT_CTORS; + } LLVMValueRef runtime_end = LLVMGetNamedFunction(c->module, "__c3_runtime_finalize"); if (!runtime_end || !LLVMGetFirstBasicBlock(runtime_end)) error_exit("Failed to find __c3_runtime_finalize in the same module as __c3_runtime_startup."); - vals[1] = runtime_end; - entry = LLVMConstNamedStruct(c->xtor_entry_type, vals, 3); - array = LLVMConstArray(c->xtor_entry_type, &entry, 1); + LLVMValueRef vals[3] = { llvm_const_int(c, type_int, 65535), runtime_end, llvm_get_zero(c, type_voidptr) }; + LLVMValueRef entry = LLVMConstNamedStruct(c->xtor_entry_type, vals, 3); + LLVMValueRef array = LLVMConstArray(c->xtor_entry_type, &entry, 1); LLVMValueRef global_dtor = LLVMAddGlobal(c->module, LLVMTypeOf(array), "llvm.global_dtors"); LLVMSetLinkage(global_dtor, LLVMAppendingLinkage); LLVMSetInitializer(global_dtor, array); + vals[1] = runtime_start; + entry = LLVMConstNamedStruct(c->xtor_entry_type, vals, 3); + ctors[len++] = entry; +EMIT_CTORS: + if (len == 0) return; + array = LLVMConstArray(c->xtor_entry_type, ctors, len); + LLVMValueRef global_ctor = LLVMAddGlobal(c->module, LLVMTypeOf(array), "llvm.global_ctors"); + LLVMSetLinkage(global_ctor, LLVMAppendingLinkage); + LLVMSetInitializer(global_ctor, array); return; } llvm_emit_xtor(c, c->constructors, "llvm.global_ctors"); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 1acb62cad..631d33b7b 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -562,11 +562,6 @@ void llvm_emit_dynamic_functions(GenContext *c, Decl **funcs) LLVMSetInitializer(global, array); LLVMSetSection(global, "__DATA,__c3_dynamic"); LLVMSetAlignment(global, llvm_abi_alignment(c, c->xtor_entry_type)); - LLVMValueRef array_used = LLVMConstArray(LLVMTypeOf(global), &global, 1); - LLVMValueRef global_used = LLVMAddGlobal(c->module, LLVMTypeOf(array_used), "llvm.used"); - LLVMSetLinkage(global_used, LLVMAppendingLinkage); - LLVMSetInitializer(global_used, array_used); - LLVMSetSection(global_used, "llvm.metadata"); return; } diff --git a/test/unit/stdlib/collections/list.c3 b/test/unit/stdlib/collections/list.c3 index faaf23c34..977c3240a 100644 --- a/test/unit/stdlib/collections/list.c3 +++ b/test/unit/stdlib/collections/list.c3 @@ -32,7 +32,7 @@ fn void! delete_contains_index() assert(test.contains(3)); test[0] = 10; assert(test.contains(10)); - test.remove_all_matches(10); + test.remove_item(10); assert(test.array_view() == int[]{ 2, 3 }); assert(!test.contains(1)); assert(test.contains(2)); @@ -42,7 +42,7 @@ fn void! delete_contains_index() assert(test.array_view() == int[]{ 0, 2, 3, 0 }); assert(test.index_of(0)! == 0); assert(test.rindex_of(0)! == 3); - test.remove_all_matches(0); + test.remove_item(0); assert(test.len() == 2); assert(test.array_view() == int[]{ 2, 3 }); }