[ELF] Support --defsym option to define an absolute symbol.

This patch is to support --defsym option for ELF file format/GNU-compatible
driver. Currently it takes a symbol name followed by '=' and a number. If such
option is given, the driver sets up an absolute symbol with the specified
address. You can specify multiple --defsym options to define multiple symbols.

GNU LD's --defsym provides many more features. For example, it allows users to
specify another symbol name instead of a number to define a symbol alias, or it
even allows a symbol plus an offset (e.g. --defsym=foo+3) to define symbol-
relative alias. This patch does not support that, but will be supported in
subsequent patches.

Differential Revision: http://llvm-reviews.chandlerc.com/D3208

llvm-svn: 205029
This commit is contained in:
Rui Ueyama
2014-03-28 19:02:06 +00:00
parent b59fb7347a
commit 3907f2a802
9 changed files with 124 additions and 9 deletions

View File

@@ -151,9 +151,10 @@ public:
void setIsStaticExecutable(bool v) { _isStaticExecutable = v; }
void setMergeCommonStrings(bool v) { _mergeCommonStrings = v; }
void setUseShlibUndefines(bool use) { _useShlibUndefines = use; }
void setOutputELFType(uint32_t type) { _outputELFType = type; }
void createInternalFiles(std::vector<std::unique_ptr<File>> &) const override;
/// \brief Set the dynamic linker path
void setInterpreter(StringRef dynamicLinker) {
_dynamicLinkerArg = true;
@@ -181,6 +182,11 @@ public:
/// add to the list of finalizer functions
void addFiniFunction(StringRef name) { _finiFunctions.push_back(name); }
/// Add an absolute symbol. Used for --defsym.
void addInitialAbsoluteSymbol(StringRef name, uint64_t addr) {
_absoluteSymbols[name] = addr;
}
/// Return the list of initializer symbols that are specified in the
/// linker command line, using the -init option.
range<const StringRef *> initFunctions() const {
@@ -225,6 +231,10 @@ public:
return true;
}
const std::map<std::string, uint64_t> &getAbsoluteSymbols() const {
return _absoluteSymbols;
}
/// \brief Helper function to allocate strings.
StringRef allocateString(StringRef ref) const {
char *x = _allocator.Allocate<char>(ref.size() + 1);
@@ -272,6 +282,7 @@ protected:
StringRefVector _rpathList;
StringRefVector _rpathLinkList;
std::map<const SharedLibraryFile *, bool> _undefinedAtomsFromFile;
std::map<std::string, uint64_t> _absoluteSymbols;
};
} // end namespace lld

View File

@@ -44,7 +44,7 @@ bool LinkingContext::createImplicitFiles(
}
std::unique_ptr<File> LinkingContext::createEntrySymbolFile() const {
return createEntrySymbolFile("command line option -e");
return createEntrySymbolFile("<command line option -e>");
}
std::unique_ptr<File>
@@ -58,7 +58,7 @@ LinkingContext::createEntrySymbolFile(StringRef filename) const {
}
std::unique_ptr<File> LinkingContext::createUndefinedSymbolFile() const {
return createUndefinedSymbolFile("command line option -u");
return createUndefinedSymbolFile("<command line option -u or --defsym>");
}
std::unique_ptr<File>

View File

@@ -88,6 +88,19 @@ static error_code getFileMagic(ELFLinkingContext &ctx, StringRef path,
return make_error_code(ReaderError::unknown_file_format);
}
// Parses an argument of --defsym. A given string must be in the form
// of <symbol>=<number>. Note that we don't support symbol-relative
// aliases yet.
static bool parseDefsymOption(StringRef opt, StringRef &sym, uint64_t &addr) {
size_t equalPos = opt.find('=');
if (equalPos == StringRef::npos)
return false;
sym = opt.substr(0, equalPos);
if (opt.substr(equalPos + 1).getAsInteger(0, addr))
return false;
return true;
}
llvm::ErrorOr<StringRef> ELFFileNode::getPath(const LinkingContext &) const {
if (!_isDashlPrefix)
return _path;
@@ -363,6 +376,17 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
asNeeded = false;
break;
case OPT_defsym: {
StringRef sym;
uint64_t addr;
if (!parseDefsymOption(inputArg->getValue(), sym, addr)) {
diagnostics << "invalid --defsym: " << inputArg->getValue() << "\n";
return false;
}
ctx->addInitialAbsoluteSymbol(sym, addr);
break;
}
case OPT_start_group: {
std::unique_ptr<InputElement> controlStart(new ELFGroup(*ctx, index++));
controlNodeStack.push(controlStart.get());

View File

@@ -190,6 +190,9 @@ def use_shlib_undefs: Flag<["--"], "use-shlib-undefines">,
def allow_multiple_definition: Flag<["--"], "allow-multiple-definition">,
HelpText<"Allow multiple definitions">,
Group<grp_resolveropt>;
def defsym : Joined<["--"], "defsym=">,
HelpText<"Create a defined symbol">,
Group<grp_resolveropt>;
//===----------------------------------------------------------------------===//
/// Custom Options
@@ -243,4 +246,3 @@ def alias_output_filetype: Joined<["--"], "output-filetype=">,
//===----------------------------------------------------------------------===//
def help : Flag<["--"], "help">,
HelpText<"Display this help message">;

View File

@@ -25,6 +25,22 @@
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)
@@ -179,6 +195,19 @@ ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const {
return libName;
}
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;
@@ -186,7 +215,7 @@ std::unique_ptr<File> ELFLinkingContext::createUndefinedSymbolFile() const {
new SimpleFile("command line option -u"));
for (auto undefSymStr : _initialUndefinedSymbols)
undefinedSymFile->addAtom(*(new (_allocator) CommandLineUndefinedAtom(
*undefinedSymFile, undefSymStr)));
*undefinedSymFile, undefSymStr)));
return std::move(undefinedSymFile);
}

View File

@@ -93,11 +93,12 @@ bool PECOFFLinkingContext::validateImpl(raw_ostream &diagnostics) {
}
std::unique_ptr<File> PECOFFLinkingContext::createEntrySymbolFile() const {
return LinkingContext::createEntrySymbolFile("command line option /entry");
return LinkingContext::createEntrySymbolFile("<command line option /entry>");
}
std::unique_ptr<File> PECOFFLinkingContext::createUndefinedSymbolFile() const {
return LinkingContext::createUndefinedSymbolFile("command line option /include");
return LinkingContext::createUndefinedSymbolFile(
"<command line option /include>");
}
bool PECOFFLinkingContext::createImplicitFiles(

View File

@@ -0,0 +1,9 @@
# RUN: lld -flavor gnu -target x86_64 --defsym=foo=0x1234 -r %s \
# RUN: --output-filetype=yaml | FileCheck %s
absolute-atoms:
# CHECK: absolute-atoms:
# CHECK: - name: foo
# CHECK: scope: global
# CHECK: value: 0x0000000000001234

View File

@@ -4,5 +4,5 @@
# RUN: /subsystem:console -- %t.obj 2> %t.log
# RUN: FileCheck %s < %t.log
CHECK: Undefined symbol: command line option /include: sym1
CHECK: Undefined symbol: command line option /include: sym2
CHECK: Undefined symbol: <command line option /include>: sym1
CHECK: Undefined symbol: <command line option /include>: sym2

View File

@@ -27,8 +27,47 @@ protected:
};
}
// All calls of parse() in this file has empty "--start-group" and "--end-group"
// options. This is a workaround for the current GNU-compatible driver. The
// driver complains if no input file is given, but if we give a file, it tries
// to read it to get magic bytes. It's not suitable for unit tests.
//
// TODO: Modify the driver to make it more test friendly.
TEST_F(GnuLdParserTest, Empty) {
EXPECT_FALSE(parse("ld", nullptr));
EXPECT_EQ(linkingContext(), nullptr);
EXPECT_EQ("No input files\n", errorMessage());
}
// Tests for --defsym
TEST_F(GnuLdParserTest, DefsymDecimal) {
EXPECT_TRUE(parse("ld", "--start-group", "--end-group", "--defsym=sym=1000",
nullptr));
assert(_context.get());
auto map = _context->getAbsoluteSymbols();
EXPECT_EQ((size_t)1, map.size());
EXPECT_EQ((uint64_t)1000, map["sym"]);
}
TEST_F(GnuLdParserTest, DefsymHexadecimal) {
EXPECT_TRUE(parse("ld", "--start-group", "--end-group", "--defsym=sym=0x1000",
nullptr));
auto map = _context->getAbsoluteSymbols();
EXPECT_EQ((size_t)1, map.size());
EXPECT_EQ((uint64_t)0x1000, map["sym"]);
}
TEST_F(GnuLdParserTest, DefsymOctal) {
EXPECT_TRUE(parse("ld", "--start-group", "--end-group", "--defsym=sym=0777",
nullptr));
auto map = _context->getAbsoluteSymbols();
EXPECT_EQ((size_t)1, map.size());
EXPECT_EQ((uint64_t)0777, map["sym"]);
}
TEST_F(GnuLdParserTest, DefsymFail) {
EXPECT_FALSE(
parse("ld", "--start-group", "--end-group", "--defsym=sym=abc", nullptr));
}