diff options
| author | Jake Ehrlich <jakehehrlich@google.com> | 2018-12-03 19:49:23 +0000 |
|---|---|---|
| committer | Jake Ehrlich <jakehehrlich@google.com> | 2018-12-03 19:49:23 +0000 |
| commit | 8ad7779071718ebc07672dbd3c30a9617128a591 (patch) | |
| tree | 94ebc9124c68af3d752584bed3534fcaa137bc60 | |
| parent | 3e66d81ec660701607aaf393c8bd831b9ab1fd08 (diff) | |
| download | bcm5719-llvm-8ad7779071718ebc07672dbd3c30a9617128a591.tar.gz bcm5719-llvm-8ad7779071718ebc07672dbd3c30a9617128a591.zip | |
[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
llvm-svn: 348174
| -rw-r--r-- | llvm/include/llvm/BinaryFormat/ELF.h | 2 | ||||
| -rw-r--r-- | llvm/test/tools/llvm-objcopy/ELF/bad-build-id.test | 21 | ||||
| -rw-r--r-- | llvm/test/tools/llvm-objcopy/ELF/build-id-link-dir.test | 56 | ||||
| -rw-r--r-- | llvm/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test | 11 | ||||
| -rw-r--r-- | llvm/test/tools/llvm-objcopy/ELF/no-build-id.test | 21 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/CopyConfig.cpp | 9 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/CopyConfig.h | 3 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp | 77 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/ELF/Object.h | 2 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/ObjcopyOpts.td | 12 |
10 files changed, 211 insertions, 3 deletions
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index ebbf830a60e..3a999bcc6f0 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -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, diff --git a/llvm/test/tools/llvm-objcopy/ELF/bad-build-id.test b/llvm/test/tools/llvm-objcopy/ELF/bad-build-id.test new file mode 100644 index 00000000000..24b3cb697a0 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/bad-build-id.test @@ -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 diff --git a/llvm/test/tools/llvm-objcopy/ELF/build-id-link-dir.test b/llvm/test/tools/llvm-objcopy/ELF/build-id-link-dir.test new file mode 100644 index 00000000000..9fe9a68cfdb --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/build-id-link-dir.test @@ -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 diff --git a/llvm/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test b/llvm/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test new file mode 100644 index 00000000000..357e0cdba48 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/no-build-id-no-notes.test @@ -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 diff --git a/llvm/test/tools/llvm-objcopy/ELF/no-build-id.test b/llvm/test/tools/llvm-objcopy/ELF/no-build-id.test new file mode 100644 index 00000000000..d75f3be1780 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/no-build-id.test @@ -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 diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp index 0385782fa91..73611778c93 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -286,8 +286,15 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { } } - 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)) { diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h index 68cd2477126..f6585116cdf 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/llvm/tools/llvm-objcopy/CopyConfig.h @@ -51,6 +51,9 @@ struct CopyConfig { // Advanced options StringRef AddGnuDebugLink; + StringRef BuildIdLinkDir; + Optional<StringRef> BuildIdLinkInput; + Optional<StringRef> BuildIdLinkOutput; StringRef SplitDWO; StringRef SymbolsPrefix; diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp index 045b34f7715..7430b19c772 100644 --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -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> createWriter(const CopyConfig &Config, 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 CopyConfig &Config, 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 diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h index 91ff1cddac1..bac23fe2a45 100644 --- a/llvm/tools/llvm-objcopy/ELF/Object.h +++ b/llvm/tools/llvm-objcopy/ELF/Object.h @@ -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() {} diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td index 4217188bbec..73985ef8f62 100644 --- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -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">; |

