Index: llvm/test/tools/llvm-objcopy/bad-build-id.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/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 Index: llvm/test/tools/llvm-objcopy/build-id-link-dir.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/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 Index: llvm/test/tools/llvm-objcopy/no-build-id.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/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 Index: llvm/test/tools/llvm-objcopy/no-build-id2.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/no-build-id2.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 Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -51,6 +51,9 @@ // Advanced options StringRef AddGnuDebugLink; + StringRef BuildIdLinkDir; + Optional BuildIdLinkInput; + Optional BuildIdLinkOutput; StringRef SplitDWO; StringRef SymbolsPrefix; Index: llvm/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -285,8 +285,15 @@ } } - 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)) { Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ 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,64 @@ llvm_unreachable("Invalid output format"); } +template +static Expected> +findBuildID(const object::ELFFile &In) { + constexpr const char *ELF_NOTE_GNU = "GNU"; + 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> +findBuildID(const object::ELFObjectFileBase &In) { + if (auto *O = dyn_cast>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast>(&In)) + return findBuildID(*O->getELFFile()); + + llvm_unreachable("Bad file format"); +} + +static void linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, + StringRef Suffix, ArrayRef 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(); @@ -494,11 +553,28 @@ ELFReader Reader(&In); std::unique_ptr Obj = Reader.create(); const ElfType OutputElfType = getOutputElfType(In); + ArrayRef 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 = 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 Index: llvm/tools/llvm-objcopy/ELF/Object.h =================================================================== --- llvm/tools/llvm-objcopy/ELF/Object.h +++ llvm/tools/llvm-objcopy/ELF/Object.h @@ -254,7 +254,6 @@ }; std::set Sections; - ArrayRef Contents; public: uint64_t Align; @@ -269,6 +268,7 @@ uint64_t OriginalOffset; Segment *ParentSegment = nullptr; + ArrayRef Contents; explicit Segment(ArrayRef Data) : Contents(Data) {} Segment() {} Index: llvm/tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -165,3 +165,15 @@ def version : Flag<["-", "--"], "version">, HelpText<"Print the version and exit.">; +defm build_id_link_dir + : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and " + "--build-id-link-output to ">, + MetaVarName<"dir">; +defm build_id_link_input + : Eq<"build-id-link-input", "Hard-link the input to /xx/xxx " + "name derived from hex build ID">, + MetaVarName<"suffix">; +defm build_id_link_output + : Eq<"build-id-link-output", "Hard-link the output to /xx/xxx " + "name derived from hex build ID">, + MetaVarName<"suffix">;