[lld-macho] Add --start-lib --end-lib

In ld.lld, when an ObjFile/BitcodeFile is read in --start-lib state, the file is
given archive semantics. --end-lib closes the previous --start-lib. A build
system can use this feature as an alternative to archives. This patch ports
the feature to lld-macho.

--start-lib and --end-lib are positional, unlike usual ld64 options.
I think the slight drawback does not matter as (a) reusing option names
make build systems convenient (b) `--start-lib a.o b.o --end-lib` conveys more
information than an alternative design: `-objlib a.o -objlib b.o` because
--start-lib makes it clear which objects are in the same conceptual archive.
This provides flexibility (c) `-objlib`/`-filelist` interaction may be weird.

Close https://github.com/llvm/llvm-project/issues/52931

Reviewed By: #lld-macho, Jez Ng, oontvoo

Differential Revision: https://reviews.llvm.org/D116913
This commit is contained in:
Fangrui Song 2022-01-19 10:14:49 -08:00
parent 4f61749e16
commit 0aae2bf373
12 changed files with 338 additions and 31 deletions

View File

@ -249,7 +249,8 @@ static llvm::CachePruningPolicy getLTOCachePolicy(InputArgList &args) {
static DenseMap<StringRef, ArchiveFile *> loadedArchives;
static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive,
bool isExplicit = true, bool isBundleLoader = false) {
bool isLazy = false, bool isExplicit = true,
bool isBundleLoader = false) {
Optional<MemoryBufferRef> buffer = readFile(path);
if (!buffer)
return nullptr;
@ -319,7 +320,7 @@ static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive,
break;
}
case file_magic::macho_object:
newFile = make<ObjFile>(mbref, getModTime(path), "");
newFile = make<ObjFile>(mbref, getModTime(path), "", isLazy);
break;
case file_magic::macho_dynamically_linked_shared_lib:
case file_magic::macho_dynamically_linked_shared_lib_stub:
@ -331,7 +332,7 @@ static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive,
}
break;
case file_magic::bitcode:
newFile = make<BitcodeFile>(mbref, "", 0);
newFile = make<BitcodeFile>(mbref, "", 0, isLazy);
break;
case file_magic::macho_executable:
case file_magic::macho_bundle:
@ -346,9 +347,20 @@ static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive,
error(path + ": unhandled file type");
}
if (newFile && !isa<DylibFile>(newFile)) {
if ((isa<ObjFile>(newFile) || isa<BitcodeFile>(newFile)) && newFile->lazy &&
config->forceLoadObjC) {
for (Symbol *sym : newFile->symbols)
if (sym && sym->getName().startswith(objc::klass)) {
extract(*newFile, "-ObjC");
break;
}
if (newFile->lazy && hasObjCSection(mbref))
extract(*newFile, "-ObjC");
}
// printArchiveMemberLoad() prints both .a and .o names, so no need to
// print the .a name here.
if (config->printEachFile && magic != file_magic::archive)
// print the .a name here. Similarly skip lazy files.
if (config->printEachFile && magic != file_magic::archive && !isLazy)
message(toString(newFile));
inputFiles.insert(newFile);
}
@ -360,7 +372,7 @@ static void addLibrary(StringRef name, bool isNeeded, bool isWeak,
ForceLoad forceLoadArchive) {
if (Optional<StringRef> path = findLibrary(name)) {
if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
addFile(*path, forceLoadArchive, isExplicit))) {
addFile(*path, forceLoadArchive, /*isLazy=*/false, isExplicit))) {
if (isNeeded)
dylibFile->forceNeeded = true;
if (isWeak)
@ -380,7 +392,7 @@ static void addFramework(StringRef name, bool isNeeded, bool isWeak,
ForceLoad forceLoadArchive) {
if (Optional<StringRef> path = findFramework(name)) {
if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
addFile(*path, forceLoadArchive, isExplicit))) {
addFile(*path, forceLoadArchive, /*isLazy=*/false, isExplicit))) {
if (isNeeded)
dylibFile->forceNeeded = true;
if (isWeak)
@ -425,13 +437,13 @@ void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) {
}
}
static void addFileList(StringRef path) {
static void addFileList(StringRef path, bool isLazy) {
Optional<MemoryBufferRef> buffer = readFile(path);
if (!buffer)
return;
MemoryBufferRef mbref = *buffer;
for (StringRef path : args::getLines(mbref))
addFile(rerootPath(path), ForceLoad::Default);
addFile(rerootPath(path), ForceLoad::Default, isLazy);
}
// An order file has one entry per line, in the following format:
@ -545,7 +557,8 @@ static void compileBitcodeFiles() {
auto *lto = make<BitcodeCompiler>();
for (InputFile *file : inputFiles)
if (auto *bitcodeFile = dyn_cast<BitcodeFile>(file))
lto->add(*bitcodeFile);
if (!file->lazy)
lto->add(*bitcodeFile);
for (ObjFile *file : lto->compile())
inputFiles.insert(file);
@ -962,6 +975,7 @@ static void createFiles(const InputArgList &args) {
TimeTraceScope timeScope("Load input files");
// This loop should be reserved for options whose exact ordering matters.
// Other options should be handled via filtered() and/or getLastArg().
bool isLazy = false;
for (const Arg *arg : args) {
const Option &opt = arg->getOption();
warnIfDeprecatedOption(opt);
@ -969,7 +983,7 @@ static void createFiles(const InputArgList &args) {
switch (opt.getID()) {
case OPT_INPUT:
addFile(rerootPath(arg->getValue()), ForceLoad::Default);
addFile(rerootPath(arg->getValue()), ForceLoad::Default, isLazy);
break;
case OPT_needed_library:
if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
@ -989,7 +1003,7 @@ static void createFiles(const InputArgList &args) {
dylibFile->forceWeakImport = true;
break;
case OPT_filelist:
addFileList(arg->getValue());
addFileList(arg->getValue(), isLazy);
break;
case OPT_force_load:
addFile(rerootPath(arg->getValue()), ForceLoad::Yes);
@ -1011,6 +1025,16 @@ static void createFiles(const InputArgList &args) {
opt.getID() == OPT_reexport_framework, /*isExplicit=*/true,
ForceLoad::Default);
break;
case OPT_start_lib:
if (isLazy)
error("nested --start-lib");
isLazy = true;
break;
case OPT_end_lib:
if (!isLazy)
error("stray --end-lib");
isLazy = false;
break;
default:
break;
}
@ -1247,7 +1271,8 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
if (const Arg *arg = args.getLastArg(OPT_bundle_loader)) {
if (config->outputType != MH_BUNDLE)
error("-bundle_loader can only be used with MachO bundle output");
addFile(arg->getValue(), ForceLoad::Default, /*isExplicit=*/false,
addFile(arg->getValue(), ForceLoad::Default, /*isLazy=*/false,
/*isExplicit=*/false,
/*isBundleLoader=*/true);
}
if (const Arg *arg = args.getLastArg(OPT_umbrella)) {

View File

@ -836,13 +836,21 @@ OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName,
sections.back().subsections.push_back({0, isec});
}
ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName)
: InputFile(ObjKind, mb), modTime(modTime) {
ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
bool lazy)
: InputFile(ObjKind, mb, lazy), modTime(modTime) {
this->archiveName = std::string(archiveName);
if (target->wordSize == 8)
parse<LP64>();
else
parse<ILP32>();
if (lazy) {
if (target->wordSize == 8)
parseLazy<LP64>();
else
parseLazy<ILP32>();
} else {
if (target->wordSize == 8)
parse<LP64>();
else
parse<ILP32>();
}
}
template <class LP> void ObjFile::parse() {
@ -904,6 +912,32 @@ template <class LP> void ObjFile::parse() {
registerCompactUnwind();
}
template <class LP> void ObjFile::parseLazy() {
using Header = typename LP::mach_header;
using NList = typename LP::nlist;
auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
auto *hdr = reinterpret_cast<const Header *>(mb.getBufferStart());
const load_command *cmd = findCommand(hdr, LC_SYMTAB);
if (!cmd)
return;
auto *c = reinterpret_cast<const symtab_command *>(cmd);
ArrayRef<NList> nList(reinterpret_cast<const NList *>(buf + c->symoff),
c->nsyms);
const char *strtab = reinterpret_cast<const char *>(buf) + c->stroff;
symbols.resize(nList.size());
for (auto it : llvm::enumerate(nList)) {
const NList &sym = it.value();
if ((sym.n_type & N_EXT) && !isUndef(sym)) {
// TODO: Bound checking
StringRef name = strtab + sym.n_strx;
symbols[it.index()] = symtab->addLazyObject(name, *this);
if (!lazy)
break;
}
}
}
void ObjFile::parseDebugInfo() {
std::unique_ptr<DwarfObject> dObj = DwarfObject::create(this);
if (!dObj)
@ -1548,8 +1582,8 @@ static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym,
}
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive)
: InputFile(BitcodeKind, mb) {
uint64_t offsetInArchive, bool lazy)
: InputFile(BitcodeKind, mb, lazy) {
this->archiveName = std::string(archiveName);
std::string path = mb.getBufferIdentifier().str();
// ThinLTO assumes that all MemoryBufferRefs given to it have a unique
@ -1565,12 +1599,47 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
utostr(offsetInArchive)));
obj = check(lto::InputFile::create(mbref));
if (lazy)
parseLazy();
else
parse();
}
void BitcodeFile::parse() {
// Convert LTO Symbols to LLD Symbols in order to perform resolution. The
// "winning" symbol will then be marked as Prevailing at LTO compilation
// time.
symbols.clear();
for (const lto::InputFile::Symbol &objSym : obj->symbols())
symbols.push_back(createBitcodeSymbol(objSym, *this));
}
void BitcodeFile::parseLazy() {
symbols.resize(obj->symbols().size());
for (auto it : llvm::enumerate(obj->symbols())) {
const lto::InputFile::Symbol &objSym = it.value();
if (!objSym.isUndefined()) {
symbols[it.index()] =
symtab->addLazyObject(saver.save(objSym.getName()), *this);
if (!lazy)
break;
}
}
}
void macho::extract(InputFile &file, StringRef reason) {
assert(file.lazy);
file.lazy = false;
printArchiveMemberLoad(reason, &file);
if (auto *bitcode = dyn_cast<BitcodeFile>(&file)) {
bitcode->parse();
} else {
auto &f = cast<ObjFile>(file);
if (target->wordSize == 8)
f.parse<LP64>();
else
f.parse<ILP32>();
}
}
template void ObjFile::parse<LP64>();

View File

@ -94,16 +94,21 @@ public:
std::vector<Symbol *> symbols;
std::vector<Section> sections;
// Provides an easy way to sort InputFiles deterministically.
const int id;
// If not empty, this stores the name of the archive containing this file.
// We use this string for creating error messages.
std::string archiveName;
// Provides an easy way to sort InputFiles deterministically.
const int id;
// True if this is a lazy ObjFile or BitcodeFile.
bool lazy = false;
protected:
InputFile(Kind kind, MemoryBufferRef mb)
: mb(mb), id(idCount++), fileKind(kind), name(mb.getBufferIdentifier()) {}
InputFile(Kind kind, MemoryBufferRef mb, bool lazy = false)
: mb(mb), id(idCount++), lazy(lazy), fileKind(kind),
name(mb.getBufferIdentifier()) {}
InputFile(Kind, const llvm::MachO::InterfaceFile &);
@ -117,8 +122,10 @@ private:
// .o file
class ObjFile final : public InputFile {
public:
ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName);
ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
bool lazy = false);
ArrayRef<llvm::MachO::data_in_code_entry> getDataInCode() const;
template <class LP> void parse();
static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
@ -130,7 +137,7 @@ public:
private:
Section *compactUnwindSection = nullptr;
template <class LP> void parse();
template <class LP> void parseLazy();
template <class SectionHeader> void parseSections(ArrayRef<SectionHeader>);
template <class LP>
void parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
@ -229,10 +236,14 @@ private:
class BitcodeFile final : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive);
uint64_t offsetInArchive, bool lazy = false);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
void parse();
std::unique_ptr<llvm::lto::InputFile> obj;
private:
void parseLazy();
};
extern llvm::SetVector<InputFile *> inputFiles;
@ -240,6 +251,8 @@ extern llvm::DenseMap<llvm::CachedHashStringRef, MemoryBufferRef> cachedReads;
llvm::Optional<MemoryBufferRef> readFile(StringRef path);
void extract(InputFile &file, StringRef reason);
namespace detail {
template <class CommandType, class... Types>

View File

@ -73,6 +73,10 @@ def thinlto_cache_policy: Joined<["--"], "thinlto-cache-policy=">,
Group<grp_lld>;
def O : JoinedOrSeparate<["-"], "O">,
HelpText<"Optimize output file size">;
def start_lib: Flag<["--"], "start-lib">,
HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
def end_lib: Flag<["--"], "end-lib">,
HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
def no_warn_dylib_install_name: Joined<["--"], "no-warn-dylib-install-name">,
HelpText<"Do not warn on -install-name if -dylib is not passed (default)">,
Group<grp_lld>;

View File

@ -115,6 +115,8 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *file,
replaceSymbol<Undefined>(s, name, file, refState);
else if (auto *lazy = dyn_cast<LazyArchive>(s))
lazy->fetchArchiveMember();
else if (isa<LazyObject>(s))
extract(*s->getFile(), s->getName());
else if (auto *dynsym = dyn_cast<DylibSymbol>(s))
dynsym->reference(refState);
else if (auto *undefined = dyn_cast<Undefined>(s))
@ -199,6 +201,26 @@ Symbol *SymbolTable::addLazyArchive(StringRef name, ArchiveFile *file,
return s;
}
Symbol *SymbolTable::addLazyObject(StringRef name, InputFile &file) {
Symbol *s;
bool wasInserted;
std::tie(s, wasInserted) = insert(name, &file);
if (wasInserted) {
replaceSymbol<LazyObject>(s, file, name);
} else if (isa<Undefined>(s)) {
extract(file, name);
} else if (auto *dysym = dyn_cast<DylibSymbol>(s)) {
if (dysym->isWeakDef()) {
if (dysym->getRefState() != RefState::Unreferenced)
extract(file, name);
else
replaceSymbol<LazyObject>(s, file, name);
}
}
return s;
}
Defined *SymbolTable::addSynthetic(StringRef name, InputSection *isec,
uint64_t value, bool isPrivateExtern,
bool includeInSymtab,

View File

@ -53,6 +53,7 @@ public:
Symbol *addLazyArchive(StringRef name, ArchiveFile *file,
const llvm::object::Archive::Symbol &sym);
Symbol *addLazyObject(StringRef name, InputFile &file);
Defined *addSynthetic(StringRef name, InputSection *, uint64_t value,
bool isPrivateExtern, bool includeInSymtab,

View File

@ -38,6 +38,7 @@ public:
CommonKind,
DylibKind,
LazyArchiveKind,
LazyObjectKind,
};
virtual ~Symbol() {}
@ -51,6 +52,9 @@ public:
}
bool isLive() const { return used; }
bool isLazy() const {
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
virtual uint64_t getVA() const { return 0; }
@ -294,12 +298,25 @@ private:
const llvm::object::Archive::Symbol sym;
};
// A defined symbol in an ObjFile/BitcodeFile surrounded by --start-lib and
// --end-lib.
class LazyObject : public Symbol {
public:
LazyObject(InputFile &file, StringRef name)
: Symbol(LazyObjectKind, name, &file) {
isUsedInRegularObj = false;
}
static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
};
union SymbolUnion {
alignas(Defined) char a[sizeof(Defined)];
alignas(Undefined) char b[sizeof(Undefined)];
alignas(CommonSymbol) char c[sizeof(CommonSymbol)];
alignas(DylibSymbol) char d[sizeof(DylibSymbol)];
alignas(LazyArchive) char e[sizeof(LazyArchive)];
alignas(LazyObject) char f[sizeof(LazyObject)];
};
template <typename T, typename... ArgT>

View File

@ -226,7 +226,7 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
// (See discussions/alternatives already considered on D107533)
if (!defined->isExternal())
if (Symbol *sym = symtab->find(defined->getName()))
if (sym->kind() != Symbol::LazyArchiveKind)
if (!sym->isLazy())
r.referent = s = sym;
}
if (auto *undefined = dyn_cast<Undefined>(s)) {

View File

@ -7,6 +7,8 @@
# RUN: llvm-ar r %t/pack.a %t/defined.o %t/combined.o
# RUN: %lld -dylib -arch x86_64 -platform_version ios-simulator 12.0.0 15.0 -ObjC %t/pack.a -o %t/a.dylib
# RUN: llvm-objdump --macho --syms %t/a.dylib | FileCheck %s
# RUN: %lld -dylib -arch x86_64 -platform_version ios-simulator 12.0.0 15.0 -ObjC --start-lib %t/defined.o %t/combined.o --end-lib -o %t/a.dylib
# RUN: llvm-objdump --macho --syms %t/a.dylib | FileCheck %s
# CHECK: SYMBOL TABLE:
# CHECK: {{.*}} l F __TEXT,__text _my_personality

View File

@ -19,6 +19,9 @@
# RUN: %lld -lSystem %t/test.o -o %t/test -L%t -lHasSomeObjC2 -ObjC
# RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s --check-prefix=OBJC
# RUN: %lld -lSystem %t/test.o -o %t/test --start-lib %t/no-objc.o %t/has-objc-symbol.o %t/has-objc-category.o %t/has-swift.o %t/wrong-arch.o --end-lib -ObjC
# RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s --check-prefix=OBJC
# OBJC: Sections:
# OBJC-NEXT: Idx Name Size VMA Type
# OBJC-NEXT: 0 __text {{.*}} TEXT
@ -27,8 +30,8 @@
# OBJC-NEXT: 3 has_objc_symbol {{.*}} DATA
# OBJC-EMPTY:
# OBJC-NEXT: SYMBOL TABLE:
# OBJC-NEXT: g F __TEXT,__text _main
# OBJC-NEXT: g F __TEXT,__text _OBJC_CLASS_$_MyObject
# OBJC-DAG: g F __TEXT,__text _main
# OBJC-DAG: g F __TEXT,__text _OBJC_CLASS_$_MyObject
# RUN: %lld -lSystem %t/test.o -o %t/test -L%t -lHasSomeObjC
# RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s --check-prefix=NO-OBJC
@ -58,6 +61,11 @@
# RUN: -lHasSomeObjC 2>&1 | FileCheck %s --check-prefix=DUP-ERROR
# DUP-ERROR: error: duplicate symbol: _has_dup
## TODO: Load has-objc-symbol.o prior to symbol resolution to match the archive behavior.
# RUN: not %lld -dylib %t/refs-dup.o %t/refs-objc.o -o %t/refs-dup --start-lib %t/no-objc.o \
# RUN: %t/has-objc-symbol.o %t/has-objc-category.o %t/has-swift.o %t/wrong-arch.o --end-lib \
# RUN: -ObjC --check-prefix=DUP-FROM-OBJC
#--- has-objc-symbol.s
.globl _OBJC_CLASS_$_MyObject, _has_dup
_OBJC_CLASS_$_MyObject:

129
lld/test/MachO/start-lib.s Normal file
View File

@ -0,0 +1,129 @@
# REQUIRES: x86
# RUN: rm -rf %t; split-file %s %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin main.s -o main.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin calls-foo.s -o calls-foo.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin 1.s -o 1.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin 2.s -o 2.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin common.s -o common.o
# RUN: llvm-as 1.ll -o 1.bc
# RUN: llvm-as 2.ll -o 2.bc
## Neither 1.o nor 2.o is loaded.
# RUN: %lld main.o --start-lib 1.o 2.o --end-lib -why_load | count 0
# RUN: %lld main.o --start-lib -filelist filelist --end-lib -why_load | count 0
# RUN: llvm-readobj -s a.out | FileCheck %s
# CHECK-NOT: Name: _foo
# CHECK-NOT: Name: _bar
## _bar loads 2.o. The last --end-lib can be omitted.
# RUN: %lld main.o -u _bar --start-lib 1.o 2.o -t -why_load | FileCheck %s --check-prefix=CHECK2WHY
# RUN: %lld main.o -u _bar --start-lib -filelist filelist -t -why_load | FileCheck %s --check-prefix=CHECK2WHY
# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=CHECK2 %s
# CHECK2WHY: main.o
# CHECK2WHY-NEXT: 2.o
# CHECK2WHY-NEXT: _bar forced load of 2.o
# CHECK2WHY-EMPTY:
# CHECK2-NOT: Name: _foo
# CHECK2: Name: _bar
# CHECK2-NOT: Name: _foo
## _foo loads 1.o. 1.o loads 2.o.
# RUN: %lld main.o -u _foo --start-lib 1.o 2.o -why_load | FileCheck %s --check-prefix=CHECK3WHY
# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=CHECK3 %s
# RUN: %lld main.o -u _foo --start-lib 2.o --end-lib --start-lib 1.o -why_load | FileCheck %s --check-prefix=CHECK3WHY
# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=CHECK3 %s
# CHECK3WHY: _foo forced load of 1.o
# CHECK3WHY-NEXT: _bar forced load of 2.o
# CHECK3WHY-EMPTY:
# CHECK3-DAG: Name: _foo
# CHECK3-DAG: Name: _bar
## Don't treat undefined _bar in 1.o as a lazy definition.
# RUN: not %lld main.o -u _bar --start-lib 1.o 2>&1 | FileCheck %s --check-prefix=CHECK4
# CHECK4: error: undefined symbol: _bar
# RUN: %lld main.o -u _common --start-lib common.o
# RUN: llvm-readobj -s a.out | FileCheck %s --check-prefix=COMMON1
# COMMON1: Name: _common
# RUN: %lld main.o --start-lib common.o
# RUN: llvm-readobj -s a.out | FileCheck %s --check-prefix=COMMON2
# COMMON2-NOT: Name: _common
## Neither 1.bc nor 2.bc is loaded.
# RUN: %lld main.o --start-lib 1.bc 2.bc -why_load | count 0
# RUN: llvm-readobj -s a.out | FileCheck %s --check-prefix=BITCODE
# BITCODE-NOT: Name: _foo
# BITCODE-NOT: Name: _bar
## _bar loads 2.bc.
# RUN: %lld main.o -u _bar --start-lib 1.bc 2.bc -why_load | FileCheck %s --check-prefix=BITCODE2WHY
# RUN: llvm-readobj -s a.out | FileCheck %s --check-prefix=BITCODE2
# BITCODE2WHY: _bar forced load of 2.bc
# BITCODE2WHY-EMPTY:
# BITCODE2-NOT: Name: _foo
# BITCODE2: Name: _bar
# BITCODE2-NOT: Name: _foo
## calls-foo.o loads 1.bc. 1.bc loads 2.bc.
# RUN: %lld calls-foo.o --start-lib 1.bc 2.bc -why_load | FileCheck %s --check-prefix=BITCODE3WHY
# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=BITCODE3 %s
# RUN: %lld calls-foo.o --start-lib 2.bc --end-lib --start-lib 1.bc -why_load | FileCheck %s --check-prefix=BITCODE3WHY
# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=BITCODE3 %s
# BITCODE3WHY: _foo forced load of 1.bc
# BITCODE3WHY-NEXT: _bar forced load of 2.bc
# BITCODE3WHY-EMPTY:
# BITCODE3-DAG: Name: _foo
# RUN: not %lld main.o --start-lib --start-lib 2>&1 | FileCheck -check-prefix=NESTED-LIB %s
# NESTED-LIB: error: nested --start-lib
# RUN: not %lld --end-lib 2>&1 | FileCheck %s --check-prefix=STRAY
# STRAY: error: stray --end-lib
#--- main.s
.globl _main
_main:
#--- calls-foo.s
.globl _main
_main:
call _foo
#--- 1.s
.globl _foo
_foo:
call _bar
#--- 2.s
.globl _bar
_bar:
ret
#--- common.s
.comm _common, 1
#--- 1.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"
define void @foo() {
tail call void () @bar()
ret void
}
declare void @bar()
#--- 2.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"
define void @bar() {
ret void
}
#--- filelist
1.o
2.o

View File

@ -65,6 +65,11 @@
# RUN: %lld -lSystem -o %t/weak-ar-weak-dylib -L%t %t/weakfoo.a -lweakfoo %t/refs-foo.o
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
# RUN: %lld -lSystem -o %t/weak-dylib-weak-ar -L%t -lweakfoo --start-lib %t/weakfoo.o --end-lib %t/refs-foo.o
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-dylib-weak-ar | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
# RUN: %lld -lSystem -o %t/weak-ar-weak-dylib -L%t --start-lib %t/weakfoo.o --end-lib -lweakfoo %t/refs-foo.o
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
## (Weak) archive symbols have the same precedence as dylib symbols.
# RUN: %lld -lSystem -o %t/weak-ar-nonweak-dylib -L%t %t/weakfoo.a -lfoo %t/refs-foo.o
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-nonweak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
@ -96,6 +101,11 @@
# RUN: %lld -dylib -lSystem -o %t/weak-unref-dylib-weak-ar -L%t -lweakfoo %t/weakfoo.a
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-unref-dylib-weak-ar | FileCheck %s --check-prefix=NO-SYM
# RUN: %lld -dylib -lSystem -o %t/weak-ar-weak-unref-dylib -L%t --start-lib %t/weakfoo.o --end-lib -lweakfoo
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-unref-dylib | FileCheck %s --check-prefix=NO-SYM
# RUN: %lld -dylib -lSystem -o %t/weak-unref-dylib-weak-ar -L%t -lweakfoo --start-lib %t/weakfoo.o --end-lib
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-unref-dylib-weak-ar | FileCheck %s --check-prefix=NO-SYM
## However, weak references are sufficient to cause the archive to be loaded.
# RUN: %lld -dylib -lSystem -o %t/weak-ar-weak-ref-weak-dylib -L%t %t/weakfoo.a -lweakfoo %t/weak-refs-foo.o
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-ref-weak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
@ -104,6 +114,13 @@
# RUN: %lld -dylib -lSystem -o %t/weak-ref-weak-dylib-weak-ar -L%t -lweakfoo %t/weak-refs-foo.o %t/weakfoo.a
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ref-weak-dylib-weak-ar | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
# RUN: %lld -dylib -lSystem -o %t/weak-ar-weak-ref-weak-dylib -L%t --start-lib %t/weakfoo.o --end-lib -lweakfoo %t/weak-refs-foo.o
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-ref-weak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
# RUN: %lld -dylib -lSystem -o %t/weak-ref-weak-dylib-weak-ar -L%t -lweakfoo --start-lib %t/weakfoo.o --end-lib %t/weak-refs-foo.o
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ref-weak-dylib-weak-ar | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
# RUN: %lld -dylib -lSystem -o %t/weak-ref-weak-dylib-weak-ar -L%t -lweakfoo %t/weak-refs-foo.o --start-lib %t/weakfoo.o --end-lib
# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ref-weak-dylib-weak-ar | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT
#--- foo.s
.globl _foo
.section __TEXT,nonweak