forked from OSchip/llvm-project
[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:
parent
4f61749e16
commit
0aae2bf373
|
@ -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)) {
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue