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,50 @@ +# RUN: yaml2obj %s > %t +# RUN: mkdir -p %t-dir +# RUN: llvm-objcopy --build-id-link-dir=%t-dir %t %t2 +# RUN: cmp %t-dir/4f/cb712aa6387724a9f465a32cd8c14b %t + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + - Name: .dynsym + Type: SHT_DYNSYM + - Name: .dynstr + Type: SHT_STRTAB + - Name: .note.gnu.build-id + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: 040000001000000003000000474E55004FCB712AA6387724A9F465A32CD8C14B +DynamicSymbols: + Global: + - Name: atan2 + Type: STT_FUNC + Section: .text + - Name: pow + Type: STT_FUNC + Section: .text + - Name: memcpy + Type: STT_FUNC + Section: .text +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [ PF_R ] + Sections: + - Section: .dynsym + - Section: .dynstr + - Section: .note.gnu.build-id + - Type: PT_NOTE + Flags: [ PF_R ] + Sections: + - Section: .note.gnu.build-id Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -51,6 +51,7 @@ // Advanced options StringRef AddGnuDebugLink; + StringRef BuildIdLinkDir; 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,9 @@ } } - Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); + Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); + 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 @@ -12,6 +12,7 @@ #include "CopyConfig.h" #include "llvm-objcopy.h" #include "Object.h" +#include "Note.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/Optional.h" @@ -115,6 +116,42 @@ llvm_unreachable("Invalid output format"); } +static void linkToBuildIdDir(const CopyConfig &Config, const Object &Obj) { + Optional> BuildIdBytes; + for (const auto &Seg : Obj.segments()) { + if (Seg.Type != PT_NOTE) + continue; + BuildIdBytes = findBuildID(Seg.Contents); + if (BuildIdBytes) + break; + } + if (!BuildIdBytes) + error("cannot find build ID"); + + SmallString<128> Path(Config.BuildIdLinkDir); + sys::path::append(Path, llvm::toHex(BuildIdBytes.getValue()[0], true)); + if (auto EC = sys::fs::create_directories(Path)) { + error("cannot create build ID link directory " + Path + ": " + + EC.message()); + return; + } + + sys::path::append(Path, + llvm::toHex(ArrayRef(BuildIdBytes->begin() + 1, + BuildIdBytes->end()), + true)); + outs() << "out: " << Config.OutputFilename << " Path: " << Path << "\n"; + auto EC = sys::fs::create_hard_link(Config.InputFilename, Path); + if (EC) { + // Hard linking failed, try to remove the file first if it exists. + if (sys::fs::exists(Path)) + sys::fs::remove(Path, true); + EC = sys::fs::create_hard_link(Config.InputFilename, Path); + if (EC) + error("cannot link " + Path + ": " + EC.message()); + } +} + static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader, StringRef File, ElfType OutputElfType) { auto DWOFile = Reader.create(); @@ -208,6 +245,9 @@ if (!Config.SplitDWO.empty()) { splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); } + if (!Config.BuildIdLinkDir.empty()) { + linkToBuildIdDir(Config, Obj); + } // TODO: update or remove symbols only if there is an option that affects // them. Index: llvm/tools/llvm-objcopy/ELF/Note.h =================================================================== --- /dev/null +++ llvm/tools/llvm-objcopy/ELF/Note.h @@ -0,0 +1,102 @@ +//===- Notes.h ------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_NOTES_H +#define LLVM_TOOLS_LLVM_OBJCOPY_NOTES_H + +#include "llvm/BinaryFormat/ELF.h" + +namespace llvm { +namespace objcopy { +namespace elf { +namespace { + +using namespace ELF; + +static const char *ELF_NOTE_GNU = "GNU"; + +// Notes always have the same format. +class Note { + static uint32_t noteAlign(uint32_t x) { return (x + 3) & -4; } + +public: + uint32_t NameSize; + uint32_t DescSize; + uint32_t Type; + + size_t size() const { + return sizeof(Note) + noteAlign(NameSize) + noteAlign(DescSize); + } + + StringRef name() const { + const uint8_t *buf = reinterpret_cast(this); + buf += sizeof(Note); + if (buf[NameSize - 1] == 0) + return StringRef{reinterpret_cast(buf)}; + else + return StringRef{reinterpret_cast(buf), NameSize}; + } + + ArrayRef desc() const { + const uint8_t *buf = reinterpret_cast(this); + buf += sizeof(Note) + noteAlign(NameSize); + return ArrayRef{buf, DescSize}; + } +}; + +class NoteIterator { + const uint8_t *Begin; + const uint8_t *End; + const Note *Cur; + +public: + explicit NoteIterator() : Begin(nullptr), End(nullptr), Cur(nullptr) {} + explicit NoteIterator(const uint8_t *Begin, const uint8_t *End) + : Begin(Begin), End(End) { + Cur = reinterpret_cast(Begin); + } + NoteIterator &operator++() { + Begin += Cur->size(); + if (Begin >= End) { + Cur = nullptr; + } else { + Cur = reinterpret_cast(Begin); + } + return *this; + } + bool operator==(const NoteIterator &Iter) const { return Cur == Iter.Cur; } + bool operator!=(const NoteIterator &Iter) const { return Cur != Iter.Cur; } + const Note &operator*() { return *Cur; } +}; + +class NoteRange { + ArrayRef NoteData; + +public: + explicit NoteRange(ArrayRef Data) : NoteData(Data) {} + const NoteIterator begin() const { + return NoteIterator{NoteData.begin(), NoteData.end()}; + } + const NoteIterator end() const { return NoteIterator{}; } +}; + +Optional> findBuildID(ArrayRef NoteData) { + for (const auto &Note : NoteRange{NoteData}) { + if (Note.Type == NT_GNU_BUILD_ID && Note.name() == "GNU") { + return Note.desc(); + } + } + return {}; +} +} +} +} +} + +#endif 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 @@ -164,3 +164,5 @@ def version : Flag<["-", "--"], "version">, HelpText<"Print the version and exit.">; +defm build_id_link_dir : Eq<"build-id-link-dir", "Hard-link output to /xx/xxx name derived from hex build ID">, + MetaVarName<"dir">;