mirror of
https://github.com/intel/llvm.git
synced 2026-02-05 13:21:04 +08:00
Previously we applied the LayoutPass to order atoms and then
apply elf::ArrayOrderPass to sort them again. The first pass is
basically supposed to sort atoms in the normal fashion (which
is to sort symbols in the same order as the input files).
The second pass sorts atoms in {init,fini}_array.<priority> by
priority.
The problem is that the LayoutPass is overkill. It analyzes
references between atoms to make a decision how to sort them.
It's slow, hard to understand, and above all, it doesn't seem
that we need its feature for ELF in the first place.
This patch remove the LayoutPass from ELF pass list. Now all
reordering is done in elf::OrderPass. That pass sorts atoms by
{init,fini}_array, and if they are not in the special section,
they are ordered as the same order as they appear in the command
line. The new code is far easier to understand, faster, and
still able to create valid executables.
Unlike the previous layout pass, elf::OrderPass doesn't count
any attributes of an atom (e.g. permissions) except its
position. It's OK because the writer takes care of them if we
have to.
This patch changes the order of final output, although that's
benign. Tests are updated.
http://reviews.llvm.org/D7278
llvm-svn: 227666
259 lines
8.3 KiB
C++
259 lines
8.3 KiB
C++
//===- lib/ReaderWriter/ELF/ELFLinkingContext.cpp -------------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lld/ReaderWriter/ELFLinkingContext.h"
|
|
#include "ELFFile.h"
|
|
#include "OrderPass.h"
|
|
#include "TargetHandler.h"
|
|
#include "lld/Core/Instrumentation.h"
|
|
#include "lld/Core/SharedLibraryFile.h"
|
|
#include "lld/Passes/RoundTripYAMLPass.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/Support/ELF.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#if defined(HAVE_CXXABI_H)
|
|
#include <cxxabi.h>
|
|
#endif
|
|
|
|
namespace lld {
|
|
|
|
class CommandLineAbsoluteAtom : public AbsoluteAtom {
|
|
public:
|
|
CommandLineAbsoluteAtom(const File &file, StringRef name, uint64_t value)
|
|
: _file(file), _name(name), _value(value) {}
|
|
|
|
const File &file() const override { return _file; }
|
|
StringRef name() const override { return _name; }
|
|
uint64_t value() const override { return _value; }
|
|
Scope scope() const override { return scopeGlobal; }
|
|
|
|
private:
|
|
const File &_file;
|
|
StringRef _name;
|
|
uint64_t _value;
|
|
};
|
|
|
|
class CommandLineUndefinedAtom : public SimpleUndefinedAtom {
|
|
public:
|
|
CommandLineUndefinedAtom(const File &f, StringRef name)
|
|
: SimpleUndefinedAtom(f, name) {}
|
|
|
|
CanBeNull canBeNull() const override {
|
|
return CanBeNull::canBeNullAtBuildtime;
|
|
}
|
|
};
|
|
|
|
ELFLinkingContext::ELFLinkingContext(
|
|
llvm::Triple triple, std::unique_ptr<TargetHandlerBase> targetHandler)
|
|
: _outputELFType(llvm::ELF::ET_EXEC), _triple(triple),
|
|
_targetHandler(std::move(targetHandler)), _baseAddress(0),
|
|
_isStaticExecutable(false), _noInhibitExec(false), _exportDynamic(false),
|
|
_mergeCommonStrings(false), _useShlibUndefines(true),
|
|
_dynamicLinkerArg(false), _noAllowDynamicLibraries(false),
|
|
_mergeRODataToTextSegment(true), _demangle(true), _alignSegments(true),
|
|
_outputMagic(OutputMagic::DEFAULT), _initFunction("_init"),
|
|
_finiFunction("_fini"), _sysrootPath("") {}
|
|
|
|
void ELFLinkingContext::addPasses(PassManager &pm) {
|
|
pm.add(std::unique_ptr<Pass>(new elf::OrderPass()));
|
|
}
|
|
|
|
uint16_t ELFLinkingContext::getOutputMachine() const {
|
|
switch (getTriple().getArch()) {
|
|
case llvm::Triple::x86:
|
|
return llvm::ELF::EM_386;
|
|
case llvm::Triple::x86_64:
|
|
return llvm::ELF::EM_X86_64;
|
|
case llvm::Triple::hexagon:
|
|
return llvm::ELF::EM_HEXAGON;
|
|
case llvm::Triple::mipsel:
|
|
case llvm::Triple::mips64el:
|
|
return llvm::ELF::EM_MIPS;
|
|
case llvm::Triple::aarch64:
|
|
return llvm::ELF::EM_AARCH64;
|
|
case llvm::Triple::arm:
|
|
return llvm::ELF::EM_ARM;
|
|
default:
|
|
llvm_unreachable("Unhandled arch");
|
|
}
|
|
}
|
|
|
|
StringRef ELFLinkingContext::entrySymbolName() const {
|
|
if (_outputELFType == llvm::ELF::ET_EXEC && _entrySymbolName.empty())
|
|
return "_start";
|
|
return _entrySymbolName;
|
|
}
|
|
|
|
bool ELFLinkingContext::validateImpl(raw_ostream &diagnostics) {
|
|
switch (outputFileType()) {
|
|
case LinkingContext::OutputFileType::YAML:
|
|
_writer = createWriterYAML(*this);
|
|
break;
|
|
case LinkingContext::OutputFileType::Native:
|
|
llvm_unreachable("Unimplemented");
|
|
break;
|
|
default:
|
|
_writer = createWriterELF(this->targetHandler());
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ELFLinkingContext::isDynamic() const {
|
|
switch (_outputELFType) {
|
|
case llvm::ELF::ET_EXEC:
|
|
return !_isStaticExecutable;
|
|
case llvm::ELF::ET_DYN:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ELFLinkingContext::isRelativeReloc(const Reference &) const {
|
|
return false;
|
|
}
|
|
|
|
Writer &ELFLinkingContext::writer() const { return *_writer; }
|
|
|
|
static void buildSearchPath(SmallString<128> &path, StringRef dir,
|
|
StringRef sysRoot) {
|
|
if (!dir.startswith("=/"))
|
|
path.assign(dir);
|
|
else {
|
|
path.assign(sysRoot);
|
|
path.append(dir.substr(1));
|
|
}
|
|
}
|
|
|
|
ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const {
|
|
bool hasColonPrefix = libName[0] == ':';
|
|
SmallString<128> path;
|
|
for (StringRef dir : _inputSearchPaths) {
|
|
// Search for dynamic library
|
|
if (!_isStaticExecutable) {
|
|
buildSearchPath(path, dir, _sysrootPath);
|
|
llvm::sys::path::append(path, hasColonPrefix
|
|
? libName.drop_front()
|
|
: Twine("lib", libName) + ".so");
|
|
if (llvm::sys::fs::exists(path.str()))
|
|
return StringRef(*new (_allocator) std::string(path.str()));
|
|
}
|
|
// Search for static libraries too
|
|
buildSearchPath(path, dir, _sysrootPath);
|
|
llvm::sys::path::append(path, hasColonPrefix
|
|
? libName.drop_front()
|
|
: Twine("lib", libName) + ".a");
|
|
if (llvm::sys::fs::exists(path.str()))
|
|
return StringRef(*new (_allocator) std::string(path.str()));
|
|
}
|
|
if (!llvm::sys::fs::exists(libName))
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
|
|
|
return libName;
|
|
}
|
|
|
|
ErrorOr<StringRef> ELFLinkingContext::searchFile(StringRef fileName,
|
|
bool isSysRooted) const {
|
|
SmallString<128> path;
|
|
if (llvm::sys::path::is_absolute(fileName) && isSysRooted) {
|
|
path.assign(_sysrootPath);
|
|
path.append(fileName);
|
|
if (llvm::sys::fs::exists(path.str()))
|
|
return StringRef(*new (_allocator) std::string(path.str()));
|
|
} else if (llvm::sys::fs::exists(fileName))
|
|
return fileName;
|
|
|
|
if (llvm::sys::path::is_absolute(fileName))
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
|
|
|
for (StringRef dir : _inputSearchPaths) {
|
|
buildSearchPath(path, dir, _sysrootPath);
|
|
llvm::sys::path::append(path, fileName);
|
|
if (llvm::sys::fs::exists(path.str()))
|
|
return StringRef(*new (_allocator) std::string(path.str()));
|
|
}
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
|
}
|
|
|
|
void ELFLinkingContext::createInternalFiles(
|
|
std::vector<std::unique_ptr<File>> &files) const {
|
|
std::unique_ptr<SimpleFile> file(
|
|
new SimpleFile("<internal file for --defsym>"));
|
|
for (auto &i : getAbsoluteSymbols()) {
|
|
StringRef sym = i.first;
|
|
uint64_t val = i.second;
|
|
file->addAtom(*(new (_allocator) CommandLineAbsoluteAtom(*file, sym, val)));
|
|
}
|
|
files.push_back(std::move(file));
|
|
LinkingContext::createInternalFiles(files);
|
|
}
|
|
|
|
std::unique_ptr<File> ELFLinkingContext::createUndefinedSymbolFile() const {
|
|
if (_initialUndefinedSymbols.empty())
|
|
return nullptr;
|
|
std::unique_ptr<SimpleFile> undefinedSymFile(
|
|
new SimpleFile("command line option -u"));
|
|
for (auto undefSymStr : _initialUndefinedSymbols)
|
|
undefinedSymFile->addAtom(*(new (_allocator) CommandLineUndefinedAtom(
|
|
*undefinedSymFile, undefSymStr)));
|
|
return std::move(undefinedSymFile);
|
|
}
|
|
|
|
void ELFLinkingContext::notifySymbolTableCoalesce(const Atom *existingAtom,
|
|
const Atom *newAtom,
|
|
bool &useNew) {
|
|
// First suppose that the `existingAtom` is defined
|
|
// and the `newAtom` is undefined.
|
|
auto *da = dyn_cast<DefinedAtom>(existingAtom);
|
|
auto *ua = dyn_cast<UndefinedAtom>(newAtom);
|
|
if (!da && !ua) {
|
|
// Then try to reverse the assumption.
|
|
da = dyn_cast<DefinedAtom>(newAtom);
|
|
ua = dyn_cast<UndefinedAtom>(existingAtom);
|
|
}
|
|
|
|
if (da && ua && da->scope() == Atom::scopeGlobal &&
|
|
isa<SharedLibraryFile>(ua->file()))
|
|
// If strong defined atom coalesces away an atom declared
|
|
// in the shared object the strong atom needs to be dynamically exported.
|
|
// Save its name.
|
|
_dynamicallyExportedSymbols.insert(ua->name());
|
|
}
|
|
|
|
std::string ELFLinkingContext::demangle(StringRef symbolName) const {
|
|
if (!_demangle)
|
|
return symbolName;
|
|
|
|
// Only try to demangle symbols that look like C++ symbols
|
|
if (!symbolName.startswith("_Z"))
|
|
return symbolName;
|
|
|
|
#if defined(HAVE_CXXABI_H)
|
|
SmallString<256> symBuff;
|
|
StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff);
|
|
const char *cstr = nullTermSym.data();
|
|
int status;
|
|
char *demangled = abi::__cxa_demangle(cstr, nullptr, nullptr, &status);
|
|
if (demangled != NULL) {
|
|
std::string result(demangled);
|
|
// __cxa_demangle() always uses a malloc'ed buffer to return the result.
|
|
free(demangled);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
return symbolName;
|
|
}
|
|
|
|
} // end namespace lld
|