From e75aa6f674ce90570fba9e5a3600535cba12a016 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Mon, 11 Jul 2016 23:10:18 +0000 Subject: [PATCH] Add a libLTO API to query a memory buffer and check if it contains ObjC categories The linker supports a feature to force load an object from a static archive if it defines an Objective-C category. This API supports this feature by looking at every section in the module to find if a category is defined in the module. llvm-svn: 275125 --- llvm/include/llvm-c/lto.h | 18 +++++-- llvm/include/llvm/Bitcode/ReaderWriter.h | 5 ++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 90 +++++++++++++++++++++++++++++++ llvm/test/LTO/X86/objc-detection-i386.ll | 59 ++++++++++++++++++++ llvm/test/LTO/X86/objc-detection.ll | 52 ++++++++++++++++++ llvm/tools/llvm-lto/llvm-lto.cpp | 19 +++++++ llvm/tools/lto/lto.cpp | 9 ++++ llvm/tools/lto/lto.exports | 1 + 8 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 llvm/test/LTO/X86/objc-detection-i386.ll create mode 100644 llvm/test/LTO/X86/objc-detection.ll diff --git a/llvm/include/llvm-c/lto.h b/llvm/include/llvm-c/lto.h index 39acdc7..655aedc 100644 --- a/llvm/include/llvm-c/lto.h +++ b/llvm/include/llvm-c/lto.h @@ -44,7 +44,7 @@ typedef bool lto_bool_t; * @{ */ -#define LTO_API_VERSION 19 +#define LTO_API_VERSION 20 /** * \since prior to LTO_API_VERSION=3 @@ -136,12 +136,20 @@ lto_module_is_object_file_for_target(const char* path, const char* target_triple_prefix); /** - * Checks if a buffer is a loadable object file. + * Return true if \p Buffer contains a bitcode file with ObjC code (category + * or class) in it. * - * \since prior to LTO_API_VERSION=3 + * \since LTO_API_VERSION=20 */ -extern lto_bool_t -lto_module_is_object_file_in_memory(const void* mem, size_t length); +bool lto_module_has_objc_category(const void *mem, size_t length); + +/** +* Checks if a buffer is a loadable object file. +* +* \since prior to LTO_API_VERSION=3 +*/ +extern lto_bool_t lto_module_is_object_file_in_memory(const void *mem, + size_t length); /** * Checks if a buffer is a loadable object compiled for requested target. diff --git a/llvm/include/llvm/Bitcode/ReaderWriter.h b/llvm/include/llvm/Bitcode/ReaderWriter.h index 2913f87..76a60a0 100644 --- a/llvm/include/llvm/Bitcode/ReaderWriter.h +++ b/llvm/include/llvm/Bitcode/ReaderWriter.h @@ -60,6 +60,11 @@ namespace llvm { std::string getBitcodeTargetTriple(MemoryBufferRef Buffer, LLVMContext &Context); + /// Return true if \p Buffer contains a bitcode file with ObjC code (category + /// or class) in it. + bool isBitcodeContainingObjCCategory(MemoryBufferRef Buffer, + LLVMContext &Context); + /// Read the header of the specified bitcode buffer and extract just the /// producer string information. If successful, this returns a string. On /// error, this returns "". diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 9aaf350..73a30c6 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -313,6 +313,10 @@ public: /// Cheap mechanism to just extract the identification block out of bitcode. ErrorOr parseIdentificationBlock(); + /// Peak at the module content and return true if any ObjC category or class + /// is found. + ErrorOr hasObjCCategory(); + static uint64_t decodeSignRotatedValue(uint64_t V); /// Materialize any deferred Metadata block. @@ -450,6 +454,7 @@ private: ArrayRef Record); std::error_code parseMetadataAttachment(Function &F); ErrorOr parseModuleTriple(); + ErrorOr hasObjCCategoryInModule(); std::error_code parseUseLists(); std::error_code initStream(std::unique_ptr Streamer); std::error_code initStreamFromBuffer(); @@ -4195,6 +4200,81 @@ std::error_code BitcodeReader::parseGlobalObjectAttachment( return std::error_code(); } +ErrorOr BitcodeReader::hasObjCCategory() { + if (std::error_code EC = initStream(nullptr)) + return EC; + + // Sniff for the signature. + if (!hasValidBitcodeHeader(Stream)) + return error("Invalid bitcode signature"); + + // We expect a number of well-defined blocks, though we don't necessarily + // need to understand them all. + while (1) { + BitstreamEntry Entry = Stream.advance(); + + switch (Entry.Kind) { + case BitstreamEntry::Error: + return error("Malformed block"); + case BitstreamEntry::EndBlock: + return std::error_code(); + + case BitstreamEntry::SubBlock: + if (Entry.ID == bitc::MODULE_BLOCK_ID) + return hasObjCCategoryInModule(); + + // Ignore other sub-blocks. + if (Stream.SkipBlock()) + return error("Malformed block"); + continue; + + case BitstreamEntry::Record: + Stream.skipRecord(Entry.ID); + continue; + } + } +} + +ErrorOr BitcodeReader::hasObjCCategoryInModule() { + if (Stream.EnterSubBlock(bitc::MODULE_BLOCK_ID)) + return error("Invalid record"); + + SmallVector Record; + // Read all the records for this module. + while (1) { + BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); + + switch (Entry.Kind) { + case BitstreamEntry::SubBlock: // Handled for us already. + case BitstreamEntry::Error: + return error("Malformed block"); + case BitstreamEntry::EndBlock: + return false; + case BitstreamEntry::Record: + // The interesting case. + break; + } + + // Read a record. + switch (Stream.readRecord(Entry.ID, Record)) { + default: + break; // Default behavior, ignore unknown content. + case bitc::MODULE_CODE_SECTIONNAME: { // SECTIONNAME: [strchr x N] + std::string S; + if (convertToString(Record, 0, S)) + return error("Invalid record"); + // Check for the i386 and other (x86_64, ARM) conventions + if (S.find("__DATA, __objc_catlist") != std::string::npos || + S.find("__OBJC,__category") != std::string::npos) + return true; + break; + } + } + Record.clear(); + } + llvm_unreachable("Exit infinite loop"); +} + /// Parse metadata attachments. std::error_code BitcodeReader::parseMetadataAttachment(Function &F) { if (Stream.EnterSubBlock(bitc::METADATA_ATTACHMENT_ID)) @@ -6548,6 +6628,16 @@ std::string llvm::getBitcodeTargetTriple(MemoryBufferRef Buffer, return Triple.get(); } +bool llvm::isBitcodeContainingObjCCategory(MemoryBufferRef Buffer, + LLVMContext &Context) { + std::unique_ptr Buf = MemoryBuffer::getMemBuffer(Buffer, false); + auto R = llvm::make_unique(Buf.release(), Context); + ErrorOr hasObjCCategory = R->hasObjCCategory(); + if (hasObjCCategory.getError()) + return false; + return hasObjCCategory.get(); +} + std::string llvm::getBitcodeProducerString(MemoryBufferRef Buffer, LLVMContext &Context) { std::unique_ptr Buf = MemoryBuffer::getMemBuffer(Buffer, false); diff --git a/llvm/test/LTO/X86/objc-detection-i386.ll b/llvm/test/LTO/X86/objc-detection-i386.ll new file mode 100644 index 0000000..d5da146 --- /dev/null +++ b/llvm/test/LTO/X86/objc-detection-i386.ll @@ -0,0 +1,59 @@ +; RUN: llvm-as < %s -o %t +; RUN: llvm-lto -check-for-objc %t | FileCheck %s + +; CHECK: contains ObjC + + +target datalayout = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128" +target triple = "i386-apple-macosx10.12.0" + +module asm "\09.lazy_reference .objc_class_name_A" +module asm "\09.objc_category_name_A_foo=0" +module asm "\09.globl .objc_category_name_A_foo" + +%0 = type opaque +%struct._objc_method = type { i8*, i8*, i8* } +%struct._objc_category = type { i8*, i8*, %struct._objc_method_list*, %struct._objc_method_list*, %struct._objc_protocol_list*, i32, %struct._prop_list_t*, %struct._prop_list_t* } +%struct._objc_method_list = type opaque +%struct._objc_protocol_list = type { %struct._objc_protocol_list*, i32, [0 x %struct._objc_protocol] } +%struct._objc_protocol = type { %struct._objc_protocol_extension*, i8*, %struct._objc_protocol_list*, %struct._objc_method_description_list*, %struct._objc_method_description_list* } +%struct._objc_protocol_extension = type { i32, %struct._objc_method_description_list*, %struct._objc_method_description_list*, %struct._prop_list_t*, i8**, %struct._prop_list_t* } +%struct._objc_method_description_list = type { i32, [0 x %struct._objc_method_description] } +%struct._objc_method_description = type { i8*, i8* } +%struct._prop_list_t = type { i32, i32, [0 x %struct._prop_t] } +%struct._prop_t = type { i8*, i8* } +%struct._objc_module = type { i32, i32, i8*, %struct._objc_symtab* } +%struct._objc_symtab = type { i32, i8*, i16, i16, [0 x i8*] } + +@OBJC_METH_VAR_NAME_ = private global [12 x i8] c"foo_myStuff\00", section "__TEXT,__cstring,cstring_literals", align 1 +@OBJC_METH_VAR_TYPE_ = private global [7 x i8] c"v8@0:4\00", section "__TEXT,__cstring,cstring_literals", align 1 +@OBJC_CLASS_NAME_ = private global [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals", align 1 +@OBJC_CLASS_NAME_.1 = private global [2 x i8] c"A\00", section "__TEXT,__cstring,cstring_literals", align 1 +@OBJC_CATEGORY_INSTANCE_METHODS_A_foo = private global { i8*, i32, [1 x %struct._objc_method] } { i8* null, i32 1, [1 x %struct._objc_method] [%struct._objc_method { i8* getelementptr inbounds ([12 x i8], [12 x i8]* @OBJC_METH_VAR_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* bitcast (void (%0*, i8*)* @"\01-[A(foo) foo_myStuff]" to i8*) }] }, section "__OBJC,__cat_inst_meth,regular,no_dead_strip", align 4 +@OBJC_CATEGORY_A_foo = private global %struct._objc_category { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @OBJC_CLASS_NAME_.1, i32 0, i32 0), %struct._objc_method_list* bitcast ({ i8*, i32, [1 x %struct._objc_method] }* @OBJC_CATEGORY_INSTANCE_METHODS_A_foo to %struct._objc_method_list*), %struct._objc_method_list* null, %struct._objc_protocol_list* null, i32 32, %struct._prop_list_t* null, %struct._prop_list_t* null }, section "__OBJC,__category,regular,no_dead_strip", align 4 +@OBJC_CLASS_NAME_.2 = private global [1 x i8] zeroinitializer, section "__TEXT,__cstring,cstring_literals", align 1 +@OBJC_SYMBOLS = private global { i32, i8*, i16, i16, [1 x i8*] } { i32 0, i8* null, i16 0, i16 1, [1 x i8*] [i8* bitcast (%struct._objc_category* @OBJC_CATEGORY_A_foo to i8*)] }, section "__OBJC,__symbols,regular,no_dead_strip", align 4 +@OBJC_MODULES = private global %struct._objc_module { i32 7, i32 16, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @OBJC_CLASS_NAME_.2, i32 0, i32 0), %struct._objc_symtab* bitcast ({ i32, i8*, i16, i16, [1 x i8*] }* @OBJC_SYMBOLS to %struct._objc_symtab*) }, section "__OBJC,__module_info,regular,no_dead_strip", align 4 +@llvm.compiler.used = appending global [9 x i8*] [i8* getelementptr inbounds ([12 x i8], [12 x i8]* @OBJC_METH_VAR_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @OBJC_CLASS_NAME_.1, i32 0, i32 0), i8* bitcast ({ i8*, i32, [1 x %struct._objc_method] }* @OBJC_CATEGORY_INSTANCE_METHODS_A_foo to i8*), i8* bitcast (%struct._objc_category* @OBJC_CATEGORY_A_foo to i8*), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @OBJC_CLASS_NAME_.2, i32 0, i32 0), i8* bitcast ({ i32, i8*, i16, i16, [1 x i8*] }* @OBJC_SYMBOLS to i8*), i8* bitcast (%struct._objc_module* @OBJC_MODULES to i8*)], section "llvm.metadata" + +; Function Attrs: nounwind ssp +define internal void @"\01-[A(foo) foo_myStuff]"(%0*, i8*) #0 { + %3 = alloca %0*, align 4 + %4 = alloca i8*, align 4 + store %0* %0, %0** %3, align 4 + store i8* %1, i8** %4, align 4 + ret void +} + +attributes #0 = { nounwind ssp "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = !{i32 1, !"Objective-C Version", i32 1} +!1 = !{i32 1, !"Objective-C Image Info Version", i32 0} +!2 = !{i32 1, !"Objective-C Image Info Section", !"__OBJC, __image_info,regular"} +!3 = !{i32 4, !"Objective-C Garbage Collection", i32 0} +!4 = !{i32 1, !"Objective-C Class Properties", i32 64} +!5 = !{i32 1, !"PIC Level", i32 2} +!6 = !{!"Apple LLVM version 8.0.0 (clang-800.0.24.1)"} diff --git a/llvm/test/LTO/X86/objc-detection.ll b/llvm/test/LTO/X86/objc-detection.ll new file mode 100644 index 0000000..cdae8ab --- /dev/null +++ b/llvm/test/LTO/X86/objc-detection.ll @@ -0,0 +1,52 @@ +; RUN: llvm-as < %s -o %t +; RUN: llvm-lto -check-for-objc %t | FileCheck %s + +; CHECK: contains ObjC + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.12.0" + +%0 = type opaque +%struct._class_t = type { %struct._class_t*, %struct._class_t*, %struct._objc_cache*, i8* (i8*, i8*)**, %struct._class_ro_t* } +%struct._objc_cache = type opaque +%struct._class_ro_t = type { i32, i32, i32, i8*, i8*, %struct.__method_list_t*, %struct._objc_protocol_list*, %struct._ivar_list_t*, i8*, %struct._prop_list_t* } +%struct.__method_list_t = type { i32, i32, [0 x %struct._objc_method] } +%struct._objc_method = type { i8*, i8*, i8* } +%struct._objc_protocol_list = type { i64, [0 x %struct._protocol_t*] } +%struct._protocol_t = type { i8*, i8*, %struct._objc_protocol_list*, %struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct._prop_list_t*, i32, i32, i8**, i8*, %struct._prop_list_t* } +%struct._ivar_list_t = type { i32, i32, [0 x %struct._ivar_t] } +%struct._ivar_t = type { i64*, i8*, i8*, i32, i32 } +%struct._prop_list_t = type { i32, i32, [0 x %struct._prop_t] } +%struct._prop_t = type { i8*, i8* } +%struct._category_t = type { i8*, %struct._class_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct._objc_protocol_list*, %struct._prop_list_t*, %struct._prop_list_t*, i32 } + +@OBJC_CLASS_NAME_ = private global [4 x i8] c"foo\00", section "__TEXT,__objc_classname,cstring_literals", align 1 +@"OBJC_CLASS_$_A" = external global %struct._class_t +@OBJC_METH_VAR_NAME_ = private global [12 x i8] c"foo_myStuff\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +@OBJC_METH_VAR_TYPE_ = private global [8 x i8] c"v16@0:8\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +@"\01l_OBJC_$_CATEGORY_INSTANCE_METHODS_A_$_foo" = private global { i32, i32, [1 x %struct._objc_method] } { i32 24, i32 1, [1 x %struct._objc_method] [%struct._objc_method { i8* getelementptr inbounds ([12 x i8], [12 x i8]* @OBJC_METH_VAR_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* bitcast (void (%0*, i8*)* @"\01-[A(foo) foo_myStuff]" to i8*) }] }, section "__DATA, __objc_const", align 8 +@"\01l_OBJC_$_CATEGORY_A_$_foo" = private global %struct._category_t { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0), %struct._class_t* @"OBJC_CLASS_$_A", %struct.__method_list_t* bitcast ({ i32, i32, [1 x %struct._objc_method] }* @"\01l_OBJC_$_CATEGORY_INSTANCE_METHODS_A_$_foo" to %struct.__method_list_t*), %struct.__method_list_t* null, %struct._objc_protocol_list* null, %struct._prop_list_t* null, %struct._prop_list_t* null, i32 64 }, section "__DATA, __objc_const", align 8 +@"OBJC_LABEL_CATEGORY_$" = private global [1 x i8*] [i8* bitcast (%struct._category_t* @"\01l_OBJC_$_CATEGORY_A_$_foo" to i8*)], section "__DATA, __objc_catlist, regular, no_dead_strip", align 8 +@llvm.compiler.used = appending global [6 x i8*] [i8* getelementptr inbounds ([4 x i8], [4 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([12 x i8], [12 x i8]* @OBJC_METH_VAR_NAME_, i32 0, i32 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* bitcast ({ i32, i32, [1 x %struct._objc_method] }* @"\01l_OBJC_$_CATEGORY_INSTANCE_METHODS_A_$_foo" to i8*), i8* bitcast (%struct._category_t* @"\01l_OBJC_$_CATEGORY_A_$_foo" to i8*), i8* bitcast ([1 x i8*]* @"OBJC_LABEL_CATEGORY_$" to i8*)], section "llvm.metadata" + +; Function Attrs: ssp uwtable +define internal void @"\01-[A(foo) foo_myStuff]"(%0*, i8*) #0 { + %3 = alloca %0*, align 8 + %4 = alloca i8*, align 8 + store %0* %0, %0** %3, align 8 + store i8* %1, i8** %4, align 8 + ret void +} + +attributes #0 = { ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = !{i32 1, !"Objective-C Version", i32 2} +!1 = !{i32 1, !"Objective-C Image Info Version", i32 0} +!2 = !{i32 1, !"Objective-C Image Info Section", !"__DATA, __objc_imageinfo, regular, no_dead_strip"} +!3 = !{i32 4, !"Objective-C Garbage Collection", i32 0} +!4 = !{i32 1, !"Objective-C Class Properties", i32 64} +!5 = !{i32 1, !"PIC Level", i32 2} +!6 = !{!"Apple LLVM version 8.0.0 (clang-800.0.24.1)"} diff --git a/llvm/tools/llvm-lto/llvm-lto.cpp b/llvm/tools/llvm-lto/llvm-lto.cpp index f3c6c62..84dce23 100644 --- a/llvm/tools/llvm-lto/llvm-lto.cpp +++ b/llvm/tools/llvm-lto/llvm-lto.cpp @@ -156,6 +156,10 @@ static cl::opt RestoreGlobalsLinkage( "restore-linkage", cl::init(false), cl::desc("Restore original linkage of globals prior to CodeGen")); +static cl::opt CheckHasObjC( + "check-for-objc", cl::init(false), + cl::desc("Only check if the module has objective-C defined in it")); + namespace { struct ModuleInfo { std::vector CanBeHidden; @@ -714,6 +718,21 @@ int main(int argc, char **argv) { return 0; } + if (CheckHasObjC) { + for (auto &Filename : InputFilenames) { + ErrorOr> BufferOrErr = + MemoryBuffer::getFile(Filename); + error(BufferOrErr, "error loading file '" + Filename + "'"); + auto Buffer = std::move(BufferOrErr.get()); + LLVMContext Ctx; + if (llvm::isBitcodeContainingObjCCategory(*Buffer, Ctx)) + outs() << "Bitcode " << Filename << " contains ObjC\n"; + else + outs() << "Bitcode " << Filename << " does not contain ObjC\n"; + } + return 0; + } + if (ThinLTOMode.getNumOccurrences()) { if (ThinLTOMode.getNumOccurrences() > 1) report_fatal_error("You can't specify more than one -thinlto-action"); diff --git a/llvm/tools/lto/lto.cpp b/llvm/tools/lto/lto.cpp index e69f360..0c36c99 100644 --- a/llvm/tools/lto/lto.cpp +++ b/llvm/tools/lto/lto.cpp @@ -14,6 +14,7 @@ #include "llvm-c/lto.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Bitcode/ReaderWriter.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" @@ -180,6 +181,14 @@ bool lto_module_is_object_file_for_target(const char* path, return LTOModule::isBitcodeForTarget(Buffer->get(), target_triplet_prefix); } +bool lto_module_has_objc_category(const void *mem, size_t length) { + std::unique_ptr Buffer(LTOModule::makeBuffer(mem, length)); + if (!Buffer) + return false; + LLVMContext Ctx; + return llvm::isBitcodeContainingObjCCategory(*Buffer, Ctx); +} + bool lto_module_is_object_file_in_memory(const void* mem, size_t length) { return LTOModule::isBitcodeFile(mem, length); } diff --git a/llvm/tools/lto/lto.exports b/llvm/tools/lto/lto.exports index 83a5943..74091c2 100644 --- a/llvm/tools/lto/lto.exports +++ b/llvm/tools/lto/lto.exports @@ -18,6 +18,7 @@ lto_module_is_object_file lto_module_is_object_file_for_target lto_module_is_object_file_in_memory lto_module_is_object_file_in_memory_for_target +lto_module_has_objc_category lto_module_dispose lto_api_version lto_codegen_set_diagnostic_handler -- 2.7.4