[llvm] r348174 - [llvm-objcopy] Add --build-id-link-dir flag

Jake Ehrlich via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 3 11:49:23 PST 2018


Author: jakehehrlich
Date: Mon Dec  3 11:49:23 2018
New Revision: 348174

URL: http://llvm.org/viewvc/llvm-project?rev=348174&view=rev
Log:
[llvm-objcopy] Add --build-id-link-dir flag

This flag does not exist in GNU objcopy but has a major use case.
Debugging tools support the .build-id directory structure to find
debug binaries. There is no easy way to build this structure up
however. One way to do it is by using llvm-readelf and some crazy
shell magic. This implements the feature directly. It is most often
the case that you'll want to strip a file and send the original to
the .build-id directory but if you just want to send a file to the
.build-id directory you can copy to /dev/null instead.

Differential Revision: https://reviews.llvm.org/D54384

Added:
    llvm/trunk/test/tools/llvm-objcopy/ELF/bad-build-id.test
    llvm/trunk/test/tools/llvm-objcopy/ELF/build-id-link-dir.test
    llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test
    llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id.test
Modified:
    llvm/trunk/include/llvm/BinaryFormat/ELF.h
    llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp
    llvm/trunk/tools/llvm-objcopy/CopyConfig.h
    llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
    llvm/trunk/tools/llvm-objcopy/ELF/Object.h
    llvm/trunk/tools/llvm-objcopy/ObjcopyOpts.td

Modified: llvm/trunk/include/llvm/BinaryFormat/ELF.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/BinaryFormat/ELF.h?rev=348174&r1=348173&r2=348174&view=diff
==============================================================================
--- llvm/trunk/include/llvm/BinaryFormat/ELF.h (original)
+++ llvm/trunk/include/llvm/BinaryFormat/ELF.h Mon Dec  3 11:49:23 2018
@@ -1377,6 +1377,8 @@ enum {
   GNU_ABI_TAG_NACL = 6,
 };
 
+constexpr const char *ELF_NOTE_GNU = "GNU";
+
 // Android packed relocation group flags.
 enum {
   RELOCATION_GROUPED_BY_INFO_FLAG = 1,

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/bad-build-id.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/bad-build-id.test?rev=348174&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/bad-build-id.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/bad-build-id.test Mon Dec  3 11:49:23 2018
@@ -0,0 +1,21 @@
+# RUN: yaml2obj %s > %t
+# RUN: not llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-input=.debug %t 2>&1 >/dev/null | FileCheck %s
+
+# CHECK: build ID in file {{.*}} is smaller than two bytes.
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .note.gnu.build-id
+    Type:            SHT_NOTE
+    Flags:           [ SHF_ALLOC ]
+    Content:         040000000100000003000000474E55004F000000
+ProgramHeaders:
+  - Type: PT_NOTE
+    Flags: [ PF_R ]
+    Sections:
+      - Section: .note.gnu.build-id

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/build-id-link-dir.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/build-id-link-dir.test?rev=348174&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/build-id-link-dir.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/build-id-link-dir.test Mon Dec  3 11:49:23 2018
@@ -0,0 +1,56 @@
+# RUN: yaml2obj %s > %t
+# RUN: mkdir -p %t-dir
+
+# RUN: llvm-objcopy --build-id-link-dir=%t-dir %t %t2
+# RUN: not test -e %t-dir/4f/cb712aa6387724a9f465a32cd8c14b
+
+# RUN: llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-input=.debug %t %t3
+# RUN: cmp %t-dir/4f/cb712aa6387724a9f465a32cd8c14b.debug %t
+# RUN: rm %t-dir/4f/cb712aa6387724a9f465a32cd8c14b.debug
+
+# RUN: llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-output "" --strip-sections %t %t4
+# RUN: cmp %t-dir/4f/cb712aa6387724a9f465a32cd8c14b %t4
+# RUN: rm %t-dir/4f/cb712aa6387724a9f465a32cd8c14b
+
+# Linking the output of an inplace argument means that the file in the build-id
+# directory will be hard-linked to the resulting file.
+# RUN: cp %t %t5
+# RUN: llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-output "" --strip-sections %t5
+# RUN: cmp %t-dir/4f/cb712aa6387724a9f465a32cd8c14b %t5
+# RUN: rm %t-dir/4f/cb712aa6387724a9f465a32cd8c14b
+
+# Linking the input of an inplace argument means that the file in the build-id
+# directory will be hard-linked to the original file.
+# RUN: cp %t %t6
+# RUN: llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-input=.debug --strip-sections %t6
+# RUN: cmp %t-dir/4f/cb712aa6387724a9f465a32cd8c14b.debug %t
+# RUN: rm %t-dir/4f/cb712aa6387724a9f465a32cd8c14b.debug
+
+# You can use both at once.
+# RUN: llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-output "" --build-id-link-input=.debug --strip-sections %t %t7
+# RUN: cmp %t-dir/4f/cb712aa6387724a9f465a32cd8c14b.debug %t
+# RUN: cmp %t-dir/4f/cb712aa6387724a9f465a32cd8c14b %t7
+# RUN: rm %t-dir/4f/cb712aa6387724a9f465a32cd8c14b.debug
+# RUN: rm %t-dir/4f/cb712aa6387724a9f465a32cd8c14b
+
+# --build-id-link-output can have a suffix as well
+# RUN: llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-output=.debug --only-keep-debug %t %t8
+# RUN: cmp %t-dir/4f/cb712aa6387724a9f465a32cd8c14b.debug %t8
+# RUN: rm %t-dir/4f/cb712aa6387724a9f465a32cd8c14b.debug
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .note.gnu.build-id
+    Type:            SHT_NOTE
+    Flags:           [ SHF_ALLOC ]
+    Content:         040000001000000003000000474E55004FCB712AA6387724A9F465A32CD8C14B
+ProgramHeaders:
+  - Type: PT_NOTE
+    Flags: [ PF_R ]
+    Sections:
+      - Section: .note.gnu.build-id

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test?rev=348174&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test Mon Dec  3 11:49:23 2018
@@ -0,0 +1,11 @@
+# RUN: yaml2obj %s > %t
+# RUN: not llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-input=.debug %t 2>&1 >/dev/null | FileCheck %s
+
+# CHECK: Could not find build ID.
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id.test?rev=348174&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/no-build-id.test Mon Dec  3 11:49:23 2018
@@ -0,0 +1,21 @@
+# RUN: yaml2obj %s > %t
+# RUN: not llvm-objcopy --build-id-link-dir=%t-dir --build-id-link-input=.debug %t 2>&1 >/dev/null | FileCheck %s
+
+# CHECK: Could not find build ID.
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .note.foo
+    Type:            SHT_NOTE
+    Flags:           [ SHF_ALLOC ]
+    Content:         000000000000000000000000
+ProgramHeaders:
+  - Type: PT_NOTE
+    Flags: [ PF_R ]
+    Sections:
+      - Section: .note.gnu.foo

Modified: llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp?rev=348174&r1=348173&r2=348174&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp Mon Dec  3 11:49:23 2018
@@ -286,8 +286,15 @@ DriverConfig parseObjcopyOptions(ArrayRe
     }
   }
 
-  Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
   Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink);
+  Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir);
+  if (InputArgs.hasArg(OBJCOPY_build_id_link_input))
+    Config.BuildIdLinkInput =
+        InputArgs.getLastArgValue(OBJCOPY_build_id_link_input);
+  if (InputArgs.hasArg(OBJCOPY_build_id_link_output))
+    Config.BuildIdLinkOutput =
+        InputArgs.getLastArgValue(OBJCOPY_build_id_link_output);
+  Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
   Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols);
 
   for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {

Modified: llvm/trunk/tools/llvm-objcopy/CopyConfig.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/CopyConfig.h?rev=348174&r1=348173&r2=348174&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/CopyConfig.h (original)
+++ llvm/trunk/tools/llvm-objcopy/CopyConfig.h Mon Dec  3 11:49:23 2018
@@ -51,6 +51,9 @@ struct CopyConfig {
 
   // Advanced options
   StringRef AddGnuDebugLink;
+  StringRef BuildIdLinkDir;
+  Optional<StringRef> BuildIdLinkInput;
+  Optional<StringRef> BuildIdLinkOutput;
   StringRef SplitDWO;
   StringRef SymbolsPrefix;
 

Modified: llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp?rev=348174&r1=348173&r2=348174&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp Mon Dec  3 11:49:23 2018
@@ -10,8 +10,8 @@
 #include "ELFObjcopy.h"
 #include "Buffer.h"
 #include "CopyConfig.h"
-#include "llvm-objcopy.h"
 #include "Object.h"
+#include "llvm-objcopy.h"
 
 #include "llvm/ADT/BitmaskEnum.h"
 #include "llvm/ADT/Optional.h"
@@ -28,6 +28,7 @@
 #include "llvm/Option/Option.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Compression.h"
+#include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/ErrorOr.h"
@@ -115,6 +116,63 @@ static std::unique_ptr<Writer> createWri
   llvm_unreachable("Invalid output format");
 }
 
+template <class ELFT>
+static Expected<ArrayRef<uint8_t>>
+findBuildID(const object::ELFFile<ELFT> &In) {
+  for (const auto &Phdr : unwrapOrError(In.program_headers())) {
+    if (Phdr.p_type != PT_NOTE)
+      continue;
+    Error Err = Error::success();
+    if (Err)
+      llvm_unreachable("Error::success() was an error.");
+    for (const auto &Note : In.notes(Phdr, Err)) {
+      if (Err)
+        return std::move(Err);
+      if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU)
+        return Note.getDesc();
+    }
+    if (Err)
+      return std::move(Err);
+  }
+  return createStringError(llvm::errc::invalid_argument,
+                           "Could not find build ID.");
+}
+
+static Expected<ArrayRef<uint8_t>>
+findBuildID(const object::ELFObjectFileBase &In) {
+  if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In))
+    return findBuildID(*O->getELFFile());
+  else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In))
+    return findBuildID(*O->getELFFile());
+  else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In))
+    return findBuildID(*O->getELFFile());
+  else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In))
+    return findBuildID(*O->getELFFile());
+
+  llvm_unreachable("Bad file format");
+}
+
+static void linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink,
+                             StringRef Suffix, ArrayRef<uint8_t> BuildIdBytes) {
+  SmallString<128> Path = Config.BuildIdLinkDir;
+  sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true));
+  if (auto EC = sys::fs::create_directories(Path))
+    error("cannot create build ID link directory " + Path + ": " +
+          EC.message());
+
+  sys::path::append(Path,
+                    llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true));
+  Path += Suffix;
+  if (auto EC = sys::fs::create_hard_link(ToLink, Path)) {
+    // Hard linking failed, try to remove the file first if it exists.
+    if (sys::fs::exists(Path))
+      sys::fs::remove(Path);
+    EC = sys::fs::create_hard_link(ToLink, Path);
+    if (EC)
+      error("cannot link " + ToLink + " to " + Path + ": " + EC.message());
+  }
+}
+
 static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
                            StringRef File, ElfType OutputElfType) {
   auto DWOFile = Reader.create();
@@ -488,11 +546,28 @@ void executeObjcopyOnBinary(const CopyCo
   ELFReader Reader(&In);
   std::unique_ptr<Object> Obj = Reader.create();
   const ElfType OutputElfType = getOutputElfType(In);
+  ArrayRef<uint8_t> BuildIdBytes;
+
+  if (!Config.BuildIdLinkDir.empty()) {
+    BuildIdBytes = unwrapOrError(findBuildID(In));
+    if (BuildIdBytes.size() < 2)
+      error("build ID in file '" + Config.InputFilename +
+            "' is smaller than two bytes");
+  }
+
+  if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) {
+    linkToBuildIdDir(Config, Config.InputFilename,
+                     Config.BuildIdLinkInput.getValue(), BuildIdBytes);
+  }
   handleArgs(Config, *Obj, Reader, OutputElfType);
   std::unique_ptr<Writer> Writer =
       createWriter(Config, *Obj, Out, OutputElfType);
   Writer->finalize();
   Writer->write();
+  if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) {
+    linkToBuildIdDir(Config, Config.OutputFilename,
+                     Config.BuildIdLinkOutput.getValue(), BuildIdBytes);
+  }
 }
 
 } // end namespace elf

Modified: llvm/trunk/tools/llvm-objcopy/ELF/Object.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ELF/Object.h?rev=348174&r1=348173&r2=348174&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/ELF/Object.h (original)
+++ llvm/trunk/tools/llvm-objcopy/ELF/Object.h Mon Dec  3 11:49:23 2018
@@ -254,7 +254,6 @@ private:
   };
 
   std::set<const SectionBase *, SectionCompare> Sections;
-  ArrayRef<uint8_t> Contents;
 
 public:
   uint64_t Align;
@@ -269,6 +268,7 @@ public:
 
   uint64_t OriginalOffset;
   Segment *ParentSegment = nullptr;
+  ArrayRef<uint8_t> Contents;
 
   explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {}
   Segment() {}

Modified: llvm/trunk/tools/llvm-objcopy/ObjcopyOpts.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ObjcopyOpts.td?rev=348174&r1=348173&r2=348174&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/ObjcopyOpts.td (original)
+++ llvm/trunk/tools/llvm-objcopy/ObjcopyOpts.td Mon Dec  3 11:49:23 2018
@@ -166,3 +166,15 @@ defm prefix_symbols
 def version : Flag<["-", "--"], "version">,
               HelpText<"Print the version and exit.">;
 def V : Flag<["-"], "V">, Alias<version>;
+defm build_id_link_dir
+    : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and "
+                              "--build-id-link-output to <dir>">,
+      MetaVarName<"dir">;
+defm build_id_link_input
+    : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> "
+                                "name derived from hex build ID">,
+      MetaVarName<"suffix">;
+defm build_id_link_output
+    : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> "
+                                 "name derived from hex build ID">,
+      MetaVarName<"suffix">;




More information about the llvm-commits mailing list