diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt --- a/lld/MachO/CMakeLists.txt +++ b/lld/MachO/CMakeLists.txt @@ -27,6 +27,7 @@ LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat + BitReader Core DebugInfoDWARF LTO diff --git a/lld/MachO/Driver.h b/lld/MachO/Driver.h --- a/lld/MachO/Driver.h +++ b/lld/MachO/Driver.h @@ -46,6 +46,10 @@ llvm::Optional loadDylib(llvm::MemoryBufferRef mbref, DylibFile *umbrella = nullptr); +llvm::Optional loadArchiveMember(MemoryBufferRef, uint32_t modTime, + StringRef archiveName, + bool objCOnly); + uint32_t getModTime(llvm::StringRef path); void printArchiveMemberLoad(StringRef reason, const InputFile *); diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -276,10 +276,13 @@ if (config->allLoad || forceLoadArchive) { if (Optional buffer = readFile(path)) { for (const ArchiveMember &member : getArchiveMembers(*buffer)) { - inputFiles.insert(make(member.mbref, member.modTime, path)); - printArchiveMemberLoad( - (forceLoadArchive ? "-force_load" : "-all_load"), - inputFiles.back()); + if (Optional file = loadArchiveMember( + member.mbref, member.modTime, path, /*objCOnly=*/false)) { + inputFiles.insert(*file); + printArchiveMemberLoad( + (forceLoadArchive ? "-force_load" : "-all_load"), + inputFiles.back()); + } } } } else if (config->forceLoadObjC) { @@ -294,9 +297,9 @@ // these files here and below (as part of the ArchiveFile). if (Optional buffer = readFile(path)) { for (const ArchiveMember &member : getArchiveMembers(*buffer)) { - if (hasObjCSection(member.mbref)) { - inputFiles.insert( - make(member.mbref, member.modTime, path)); + if (Optional file = loadArchiveMember( + member.mbref, member.modTime, path, /*objCOnly=*/true)) { + inputFiles.insert(*file); printArchiveMemberLoad("-ObjC", inputFiles.back()); } } diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp --- a/lld/MachO/DriverUtils.cpp +++ b/lld/MachO/DriverUtils.cpp @@ -6,9 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "Driver.h" #include "Config.h" +#include "Driver.h" #include "InputFiles.h" +#include "ObjC.h" #include "lld/Common/Args.h" #include "lld/Common/ErrorHandler.h" @@ -16,6 +17,8 @@ #include "lld/Common/Reproduce.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/LTO/LTO.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -196,6 +199,26 @@ return file; } +Optional macho::loadArchiveMember(MemoryBufferRef mb, + uint32_t modTime, + StringRef archiveName, + bool objCOnly) { + switch (identify_magic(mb.getBuffer())) { + case file_magic::macho_object: + if (!objCOnly || hasObjCSection(mb)) + return make(mb, modTime, archiveName); + return None; + case file_magic::bitcode: + if (!objCOnly || check(isBitcodeContainingObjCCategory(mb))) + return make(mb); + return None; + default: + error(archiveName + ": archive member " + mb.getBufferIdentifier() + + " has unhandled file type"); + return None; + } +} + uint32_t macho::getModTime(StringRef path) { fs::file_status stat; if (!fs::status(path, stat)) diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -746,26 +746,13 @@ // to it later. const object::Archive::Symbol sym_copy = sym; - InputFile *file; - switch (identify_magic(mb.getBuffer())) { - case file_magic::macho_object: - file = make(mb, modTime, getName()); - break; - case file_magic::bitcode: - file = make(mb); - break; - default: - StringRef bufname = - CHECK(c.getName(), toString(this) + ": could not get buffer name"); - error(toString(this) + ": archive member " + bufname + - " has unhandled file type"); - return; + if (Optional file = + loadArchiveMember(mb, modTime, getName(), /*objCOnly=*/false)) { + inputFiles.insert(*file); + // ld64 doesn't demangle sym here even with -demangle. Match that, so + // intentionally no call to toMachOString() here. + printArchiveMemberLoad(sym_copy.getName(), *file); } - inputFiles.insert(file); - - // ld64 doesn't demangle sym here even with -demangle. Match that, so - // intentionally no call to toMachOString() here. - printArchiveMemberLoad(sym_copy.getName(), file); } BitcodeFile::BitcodeFile(MemoryBufferRef mbref) diff --git a/lld/test/MachO/invalid/bad-archive-member.s b/lld/test/MachO/invalid/bad-archive-member.s --- a/lld/test/MachO/invalid/bad-archive-member.s +++ b/lld/test/MachO/invalid/bad-archive-member.s @@ -5,6 +5,8 @@ # RUN: %lld -dylib -lSystem %t/foo.o -o %t/foo.dylib # RUN: llvm-ar rcs %t/foo.a %t/foo.dylib # RUN: not %lld %t/test.o %t/foo.a -o /dev/null 2>&1 | FileCheck %s -DFILE=%t/foo.a +# RUN: not %lld %t/test.o -force_load %t/foo.a -o /dev/null 2>&1 | FileCheck %s -DFILE=%t/foo.a +# RUN: not %lld %t/test.o -ObjC %t/foo.a -o /dev/null 2>&1 | FileCheck %s -DFILE=%t/foo.a # CHECK: error: [[FILE]]: archive member foo.dylib has unhandled file type #--- foo.s diff --git a/lld/test/MachO/invalid/bad-archive.s b/lld/test/MachO/invalid/bad-archive.s --- a/lld/test/MachO/invalid/bad-archive.s +++ b/lld/test/MachO/invalid/bad-archive.s @@ -5,6 +5,7 @@ # RUN: not %lld %t.o %t.a -o /dev/null 2>&1 | FileCheck -DFILE=%t.a %s # RUN: not %lld %t.o -force_load %t.a -o /dev/null 2>&1 | FileCheck -DFILE=%t.a %s +# RUN: not %lld %t.o -ObjC %t.a -o /dev/null 2>&1 | FileCheck -DFILE=%t.a %s # CHECK: error: [[FILE]]: failed to parse archive: truncated or malformed archive (remaining size of archive too small for next archive member header at offset 8) .global _main diff --git a/lld/test/MachO/lto-archive.ll b/lld/test/MachO/lto-archive.ll --- a/lld/test/MachO/lto-archive.ll +++ b/lld/test/MachO/lto-archive.ll @@ -1,14 +1,37 @@ ; REQUIRES: x86 ; RUN: rm -rf %t; split-file %s %t ; RUN: llvm-as %t/foo.ll -o %t/foo.o -; RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o +; RUN: llvm-as %t/objc.ll -o %t/objc.o ; RUN: llvm-ar rcs %t/foo.a %t/foo.o -; RUN: %lld -save-temps -lSystem %t/test.o %t/foo.a -o %t/test -; RUN: llvm-objdump -d --macho --no-show-raw-insn %t/test | FileCheck %s +; RUN: llvm-ar rcs %t/objc.a %t/objc.o -; CHECK: _main: -; CHECK-NEXT: callq _foo -; CHECK-NEXT: retq +; RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/main.s -o %t/main.o +; RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/references-foo.s -o %t/references-foo.o + +; RUN: %lld -lSystem %t/references-foo.o %t/foo.a -o %t/test +; RUN: llvm-objdump --macho --syms %t/test | FileCheck %s --check-prefix=FOO + +; RUN: %lld -lSystem -force_load %t/foo.a %t/main.o -o %t/force-load +; RUN: llvm-objdump --macho --syms %t/force-load | FileCheck %s --check-prefix=FOO + +; RUN: %lld -lSystem %t/foo.a %t/main.o -o %t/no-force-load +; RUN: llvm-objdump --macho --syms %t/no-force-load | FileCheck %s --check-prefix=NO-FOO + +; RUN: %lld -lSystem -ObjC -framework CoreFoundation %t/objc.a %t/main.o -o %t/objc +; RUN: llvm-objdump --macho --syms %t/objc | FileCheck %s --check-prefix=OBJC + +; RUN: %lld -lSystem -framework CoreFoundation %t/objc.a %t/main.o -o %t/no-objc +; RUN: llvm-objdump --macho --syms %t/no-objc | FileCheck %s --check-prefix=NO-OBJC + +; FOO: _foo + +; NO-FOO-NOT: _foo + +; OBJC-DAG: _OBJC_CLASS_$_Foo +; OBJC-DAG: _OBJC_METACLASS_$_Foo + +; NO-OBJC-NOT: _OBJC_CLASS_$_Foo +; NO-OBJC-NOT: _OBJC_METACLASS_$_Foo ;--- foo.ll @@ -19,7 +42,46 @@ ret void } -;--- test.s +;--- objc.ll + +target triple = "x86_64-apple-macosx10.15.0" +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +%struct._class_t = type { %struct._class_t*, %struct._class_t*, %struct._class_ro_t* } +%struct._class_ro_t = type { i8* } + +@"OBJC_METACLASS_$_NSObject" = external global %struct._class_t +@OBJC_CLASS_NAME_ = private unnamed_addr constant [4 x i8] c"Foo\00", + section "__TEXT,__objc_classname,cstring_literals" +@"_OBJC_METACLASS_RO_$_Foo" = internal global %struct._class_ro_t { + i8* getelementptr inbounds ([4 x i8], [4 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0) + }, + section "__DATA, __objc_const" +@"OBJC_METACLASS_$_Foo" = global %struct._class_t { + %struct._class_t* @"OBJC_METACLASS_$_NSObject", + %struct._class_t* @"OBJC_METACLASS_$_NSObject", + %struct._class_ro_t* @"_OBJC_METACLASS_RO_$_Foo" + }, + section "__DATA, __objc_data" +@"OBJC_CLASS_$_NSObject" = external global %struct._class_t +@"_OBJC_CLASS_RO_$_Foo" = internal global %struct._class_ro_t { + i8* getelementptr inbounds ([4 x i8], [4 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0) + }, + section "__DATA, __objc_const" +@"OBJC_CLASS_$_Foo" = global %struct._class_t { + %struct._class_t* @"OBJC_METACLASS_$_Foo", + %struct._class_t* @"OBJC_CLASS_$_NSObject", + %struct._class_ro_t* @"_OBJC_CLASS_RO_$_Foo" + }, + section "__DATA, __objc_data" + +;--- main.s + +.globl _main +_main: + ret + +;--- references-foo.s .globl _main _main: