[ELF] Support --defsym option to define an absolute symbol.
authorRui Ueyama <ruiu@google.com>
Fri, 28 Mar 2014 19:02:06 +0000 (19:02 +0000)
committerRui Ueyama <ruiu@google.com>
Fri, 28 Mar 2014 19:02:06 +0000 (19:02 +0000)
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

lld/include/lld/ReaderWriter/ELFLinkingContext.h
lld/lib/Core/LinkingContext.cpp
lld/lib/Driver/GnuLdDriver.cpp
lld/lib/Driver/GnuLdOptions.td
lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp
lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp
lld/test/elf/defsym.objtxt [new file with mode: 0644]
lld/test/pecoff/include.test
lld/unittests/DriverTests/GnuLdDriverTest.cpp

index 031396c..9594903 100644 (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
 
index 361bff2..d92ef27 100644 (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>
index 1631c82..fb57706 100644 (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());
index 81f26b5..844327e 100644 (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">;
-
index 7bb8dc5..ebe2fb0 100644 (file)
 
 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);
 }
 
index 9b91cfa..849fc65 100644 (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(
diff --git a/lld/test/elf/defsym.objtxt b/lld/test/elf/defsym.objtxt
new file mode 100644 (file)
index 0000000..fa99c5e
--- /dev/null
@@ -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
index 514497a..bee1f48 100644 (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
index 63b898d..8e2fadc 100644 (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));
+}