diff --git a/llvm/include/llvm/Object/MachOUniversalWriter.h b/llvm/include/llvm/Object/MachOUniversalWriter.h --- a/llvm/include/llvm/Object/MachOUniversalWriter.h +++ b/llvm/include/llvm/Object/MachOUniversalWriter.h @@ -92,8 +92,7 @@ Error writeUniversalBinary(ArrayRef Slices, StringRef OutputFileName); -Expected> -writeUniversalBinaryToBuffer(ArrayRef Slices); +Error writeUniversalBinaryToStream(ArrayRef Slices, raw_ostream &Out); } // end namespace object diff --git a/llvm/lib/Object/MachOUniversalWriter.cpp b/llvm/lib/Object/MachOUniversalWriter.cpp --- a/llvm/lib/Object/MachOUniversalWriter.cpp +++ b/llvm/lib/Object/MachOUniversalWriter.cpp @@ -263,8 +263,8 @@ return FatArchList; } -static Error writeUniversalBinaryToStream(ArrayRef Slices, - raw_ostream &Out) { +Error object::writeUniversalBinaryToStream(ArrayRef Slices, + raw_ostream &Out) { MachO::fat_header FatHeader; FatHeader.magic = MachO::FAT_MAGIC; FatHeader.nfat_arch = Slices.size(); @@ -324,14 +324,3 @@ } return Temp->keep(OutputFileName); } - -Expected> -object::writeUniversalBinaryToBuffer(ArrayRef Slices) { - SmallVector Buffer; - raw_svector_ostream Out(Buffer); - - if (Error E = writeUniversalBinaryToStream(Slices, Out)) - return std::move(E); - - return std::make_unique(std::move(Buffer)); -} diff --git a/llvm/tools/llvm-objcopy/Buffer.h b/llvm/tools/llvm-objcopy/Buffer.h deleted file mode 100644 --- a/llvm/tools/llvm-objcopy/Buffer.h +++ /dev/null @@ -1,68 +0,0 @@ -//===- Buffer.h -------------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_BUFFER_H -#define LLVM_TOOLS_OBJCOPY_BUFFER_H - -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/MemoryBuffer.h" -#include - -namespace llvm { -namespace objcopy { - -// The class Buffer abstracts out the common interface of FileOutputBuffer and -// WritableMemoryBuffer so that the hierarchy of Writers depends on this -// abstract interface and doesn't depend on a particular implementation. -// TODO: refactor the buffer classes in LLVM to enable us to use them here -// directly. -class Buffer { - StringRef Name; - -public: - virtual ~Buffer(); - virtual Error allocate(size_t Size) = 0; - virtual uint8_t *getBufferStart() = 0; - virtual Error commit() = 0; - - explicit Buffer(StringRef Name) : Name(Name) {} - StringRef getName() const { return Name; } -}; - -class FileBuffer : public Buffer { - std::unique_ptr Buf; - // Indicates that allocate(0) was called, and commit() should create or - // truncate a file instead of using a FileOutputBuffer. - bool EmptyFile = false; - -public: - Error allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit FileBuffer(StringRef FileName) : Buffer(FileName) {} -}; - -class MemBuffer : public Buffer { - std::unique_ptr Buf; - -public: - Error allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit MemBuffer(StringRef Name) : Buffer(Name) {} - - std::unique_ptr releaseMemoryBuffer(); -}; - -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H diff --git a/llvm/tools/llvm-objcopy/Buffer.cpp b/llvm/tools/llvm-objcopy/Buffer.cpp deleted file mode 100644 --- a/llvm/tools/llvm-objcopy/Buffer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//===- Buffer.cpp ---------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Buffer.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Process.h" -#include - -namespace llvm { -namespace objcopy { - -Buffer::~Buffer() {} - -static Error createEmptyFile(StringRef FileName) { - // Create an empty tempfile and atomically swap it in place with the desired - // output file. - Expected Temp = - sys::fs::TempFile::create(FileName + ".temp-empty-%%%%%%%"); - return Temp ? Temp->keep(FileName) : Temp.takeError(); -} - -Error FileBuffer::allocate(size_t Size) { - // When a 0-sized file is requested, skip allocation but defer file - // creation/truncation until commit() to avoid side effects if something - // happens between allocate() and commit(). - if (Size == 0) { - EmptyFile = true; - return Error::success(); - } - - Expected> BufferOrErr = - FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); - // FileOutputBuffer::create() returns an Error that is just a wrapper around - // std::error_code. Wrap it in FileError to include the actual filename. - if (!BufferOrErr) - return createFileError(getName(), BufferOrErr.takeError()); - Buf = std::move(*BufferOrErr); - return Error::success(); -} - -Error FileBuffer::commit() { - if (EmptyFile) - return createEmptyFile(getName()); - - assert(Buf && "allocate() not called before commit()!"); - Error Err = Buf->commit(); - // FileOutputBuffer::commit() returns an Error that is just a wrapper around - // std::error_code. Wrap it in FileError to include the actual filename. - return Err ? createFileError(getName(), std::move(Err)) : std::move(Err); -} - -uint8_t *FileBuffer::getBufferStart() { - return reinterpret_cast(Buf->getBufferStart()); -} - -Error MemBuffer::allocate(size_t Size) { - Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName()); - return Error::success(); -} - -Error MemBuffer::commit() { return Error::success(); } - -uint8_t *MemBuffer::getBufferStart() { - return reinterpret_cast(Buf->getBufferStart()); -} - -std::unique_ptr MemBuffer::releaseMemoryBuffer() { - return std::move(Buf); -} - -} // end namespace objcopy -} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/CMakeLists.txt b/llvm/tools/llvm-objcopy/CMakeLists.txt --- a/llvm/tools/llvm-objcopy/CMakeLists.txt +++ b/llvm/tools/llvm-objcopy/CMakeLists.txt @@ -22,7 +22,6 @@ add_public_tablegen_target(StripOptsTableGen) add_llvm_tool(llvm-objcopy - Buffer.cpp CopyConfig.cpp llvm-objcopy.cpp COFF/COFFObjcopy.cpp diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h @@ -11,6 +11,7 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class COFFObjectFile; @@ -18,11 +19,10 @@ namespace objcopy { struct CopyConfig; -class Buffer; namespace coff { Error executeObjcopyOnBinary(const CopyConfig &Config, - object::COFFObjectFile &In, Buffer &Out); + object::COFFObjectFile &In, raw_ostream &Out); } // end namespace coff } // end namespace objcopy diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "COFFObjcopy.h" -#include "Buffer.h" #include "CopyConfig.h" #include "Object.h" #include "Reader.h" @@ -272,7 +271,7 @@ } Error executeObjcopyOnBinary(const CopyConfig &Config, COFFObjectFile &In, - Buffer &Out) { + raw_ostream &Out) { COFFReader Reader(In); Expected> ObjOrErr = Reader.create(); if (!ObjOrErr) diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.h b/llvm/tools/llvm-objcopy/COFF/Reader.h --- a/llvm/tools/llvm-objcopy/COFF/Reader.h +++ b/llvm/tools/llvm-objcopy/COFF/Reader.h @@ -9,7 +9,6 @@ #ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H #define LLVM_TOOLS_OBJCOPY_COFF_READER_H -#include "Buffer.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Error.h" diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h --- a/llvm/tools/llvm-objcopy/COFF/Writer.h +++ b/llvm/tools/llvm-objcopy/COFF/Writer.h @@ -9,9 +9,9 @@ #ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H #define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H -#include "Buffer.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" #include #include @@ -23,7 +23,8 @@ class COFFWriter { Object &Obj; - Buffer &Buf; + std::unique_ptr Buf; + raw_ostream &Out; size_t FileSize; size_t FileAlignment; @@ -51,8 +52,8 @@ virtual ~COFFWriter() {} Error write(); - COFFWriter(Object &Obj, Buffer &Buf) - : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {} + COFFWriter(Object &Obj, raw_ostream &Out) + : Obj(Obj), Out(Out), StrTabBuilder(StringTableBuilder::WinCOFF) {} }; } // end namespace coff diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp --- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp +++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include #include @@ -240,7 +241,7 @@ } void COFFWriter::writeHeaders(bool IsBigObj) { - uint8_t *Ptr = Buf.getBufferStart(); + uint8_t *Ptr = reinterpret_cast(Buf->getBufferStart()); if (Obj.IsPE) { memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader)); Ptr += sizeof(Obj.DosHeader); @@ -302,7 +303,8 @@ void COFFWriter::writeSections() { for (const auto &S : Obj.getSections()) { - uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; + uint8_t *Ptr = reinterpret_cast(Buf->getBufferStart()) + + S.Header.PointerToRawData; ArrayRef Contents = S.getContents(); std::copy(Contents.begin(), Contents.end(), Ptr); @@ -331,7 +333,8 @@ } template void COFFWriter::writeSymbolStringTables() { - uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable; + uint8_t *Ptr = reinterpret_cast(Buf->getBufferStart()) + + Obj.CoffFileHeader.PointerToSymbolTable; for (const auto &S : Obj.getSymbols()) { // Convert symbols back to the right size, from coff_symbol32. copySymbol(*reinterpret_cast(Ptr), @@ -366,8 +369,9 @@ if (Error E = finalize(IsBigObj)) return E; - if (Error E = Buf.allocate(FileSize)) - return E; + Buf = WritableMemoryBuffer::getNewMemBuffer(FileSize); + if (!Buf) + return createStringError(llvm::errc::not_enough_memory, Twine(FileSize)); writeHeaders(IsBigObj); writeSections(); @@ -380,7 +384,11 @@ if (Error E = patchDebugDirectory()) return E; - return Buf.commit(); + // TODO: Implement direct writing to the output stream + // TODO: (without intermediate memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + Out.flush(); + return Error::success(); } Expected COFFWriter::virtualAddressToFileAddress(uint32_t RVA) { @@ -412,7 +420,8 @@ "debug directory extends past end of section"); size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; - uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; + uint8_t *Ptr = reinterpret_cast(Buf->getBufferStart()) + + S.Header.PointerToRawData + Offset; uint8_t *End = Ptr + Dir->Size; while (Ptr < End) { debug_directory *Debug = reinterpret_cast(Ptr); diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -12,6 +12,7 @@ namespace llvm { class Error; class MemoryBuffer; +class raw_ostream; namespace object { class ELFObjectFileBase; @@ -19,15 +20,14 @@ namespace objcopy { struct CopyConfig; -class Buffer; namespace elf { Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); + raw_ostream &Out); Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); + raw_ostream &Out); Error executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out); + object::ELFObjectFileBase &In, raw_ostream &Out); } // end namespace elf } // end namespace objcopy diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "ELFObjcopy.h" -#include "Buffer.h" #include "CopyConfig.h" #include "Object.h" +#include "Util.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" @@ -133,136 +133,39 @@ } static std::unique_ptr createELFWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, + Object &Obj, raw_ostream &Out, ElfType OutputElfType) { // Depending on the initial ELFT and OutputFormat we need a different Writer. switch (OutputElfType) { case ELFT_ELF32LE: - return std::make_unique>(Obj, Buf, !Config.StripSections, + return std::make_unique>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF64LE: - return std::make_unique>(Obj, Buf, !Config.StripSections, + return std::make_unique>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF32BE: - return std::make_unique>(Obj, Buf, !Config.StripSections, + return std::make_unique>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF64BE: - return std::make_unique>(Obj, Buf, !Config.StripSections, + return std::make_unique>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); } llvm_unreachable("Invalid output format"); } static std::unique_ptr createWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, + Object &Obj, raw_ostream &Out, ElfType OutputElfType) { switch (Config.OutputFormat) { case FileFormat::Binary: - return std::make_unique(Obj, Buf); + return std::make_unique(Obj, Out); case FileFormat::IHex: - return std::make_unique(Obj, Buf); + return std::make_unique(Obj, Out); default: - return createELFWriter(Config, Obj, Buf, OutputElfType); + return createELFWriter(Config, Obj, Out, OutputElfType); } } -template -static Expected> -findBuildID(const CopyConfig &Config, const object::ELFFile &In) { - auto PhdrsOrErr = In.program_headers(); - if (auto Err = PhdrsOrErr.takeError()) - return createFileError(Config.InputFilename, std::move(Err)); - - for (const auto &Phdr : *PhdrsOrErr) { - if (Phdr.p_type != PT_NOTE) - continue; - Error Err = Error::success(); - for (auto Note : In.notes(Phdr, Err)) - if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) - return Note.getDesc(); - if (Err) - return createFileError(Config.InputFilename, std::move(Err)); - } - - return createFileError(Config.InputFilename, - createStringError(llvm::errc::invalid_argument, - "could not find build ID")); -} - -static Expected> -findBuildID(const CopyConfig &Config, const object::ELFObjectFileBase &In) { - if (auto *O = dyn_cast>(&In)) - return findBuildID(Config, *O->getELFFile()); - else if (auto *O = dyn_cast>(&In)) - return findBuildID(Config, *O->getELFFile()); - else if (auto *O = dyn_cast>(&In)) - return findBuildID(Config, *O->getELFFile()); - else if (auto *O = dyn_cast>(&In)) - return findBuildID(Config, *O->getELFFile()); - - llvm_unreachable("Bad file format"); -} - -template -static Error makeStringError(std::error_code EC, const Twine &Msg, - Ts &&... Args) { - std::string FullMsg = (EC.message() + ": " + Msg).str(); - return createStringError(EC, FullMsg.c_str(), std::forward(Args)...); -} - -#define MODEL_8 "%%%%%%%%" -#define MODEL_16 MODEL_8 MODEL_8 -#define MODEL_32 (MODEL_16 MODEL_16) - -static Error 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)) - return createFileError( - Path.str(), - makeStringError(EC, "cannot create build ID link directory")); - - sys::path::append(Path, - llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true)); - Path += Suffix; - SmallString<128> TmpPath; - // create_hard_link races so we need to link to a temporary path but - // we want to make sure that we choose a filename that does not exist. - // By using 32 model characters we get 128-bits of entropy. It is - // unlikely that this string has ever existed before much less exists - // on this disk or in the current working directory. - // Additionally we prepend the original Path for debugging but also - // because it ensures that we're linking within a directory on the same - // partition on the same device which is critical. It has the added - // win of yet further decreasing the odds of a conflict. - sys::fs::createUniquePath(Twine(Path) + "-" + MODEL_32 + ".tmp", TmpPath, - /*MakeAbsolute*/ false); - if (auto EC = sys::fs::create_hard_link(ToLink, TmpPath)) { - Path.push_back('\0'); - return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), - Path.data()); - } - // We then atomically rename the link into place which will just move the - // link. If rename fails something is more seriously wrong so just return - // an error. - if (auto EC = sys::fs::rename(TmpPath, Path)) { - Path.push_back('\0'); - return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), - Path.data()); - } - // If `Path` was already a hard-link to the same underlying file then the - // temp file will be left so we need to remove it. Remove will not cause - // an error by default if the file is already gone so just blindly remove - // it rather than checking. - if (auto EC = sys::fs::remove(TmpPath)) { - TmpPath.push_back('\0'); - return makeStringError(EC, "could not remove '%s'", TmpPath.data()); - } - return Error::success(); -} - static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader, StringRef File, ElfType OutputElfType) { Expected> DWOFile = Reader.create(false); @@ -279,12 +182,14 @@ (*DWOFile)->Machine = Config.OutputArch.getValue().EMachine; (*DWOFile)->OSABI = Config.OutputArch.getValue().OSABI; } - FileBuffer FB(File); - std::unique_ptr Writer = - createWriter(Config, **DWOFile, FB, OutputElfType); - if (Error E = Writer->finalize()) - return E; - return Writer->write(); + + return writeToFile(File, [&](raw_ostream &OutFile) -> Error { + std::unique_ptr Writer = + createWriter(Config, **DWOFile, OutFile, OutputElfType); + if (Error E = Writer->finalize()) + return E; + return Writer->write(); + }); } static Error dumpSectionToFile(StringRef SecName, StringRef Filename, @@ -776,8 +681,8 @@ return Error::success(); } -static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, - ElfType OutputElfType) { +static Error writeOutput(const CopyConfig &Config, Object &Obj, + raw_ostream &Out, ElfType OutputElfType) { std::unique_ptr Writer = createWriter(Config, Obj, Out, OutputElfType); if (Error E = Writer->finalize()) @@ -786,7 +691,7 @@ } Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { + raw_ostream &Out) { IHexReader Reader(&In); Expected> Obj = Reader.create(true); if (!Obj) @@ -800,7 +705,7 @@ } Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { + raw_ostream &Out) { uint8_t NewSymbolVisibility = Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); BinaryReader Reader(&In, NewSymbolVisibility); @@ -818,7 +723,7 @@ } Error executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out) { + object::ELFObjectFileBase &In, raw_ostream &Out) { ELFReader Reader(&In, Config.ExtractPartition); Expected> Obj = Reader.create(!Config.SymbolsToAdd.empty()); @@ -828,37 +733,12 @@ const ElfType OutputElfType = Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) : getOutputElfType(In); - ArrayRef BuildIdBytes; - - if (!Config.BuildIdLinkDir.empty()) { - auto BuildIdBytesOrErr = findBuildID(Config, In); - if (auto E = BuildIdBytesOrErr.takeError()) - return E; - BuildIdBytes = *BuildIdBytesOrErr; - - if (BuildIdBytes.size() < 2) - return createFileError( - Config.InputFilename, - createStringError(object_error::parse_failed, - "build ID is smaller than two bytes")); - } - - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) - if (Error E = - linkToBuildIdDir(Config, Config.InputFilename, - Config.BuildIdLinkInput.getValue(), BuildIdBytes)) - return E; if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) return createFileError(Config.InputFilename, std::move(E)); if (Error E = writeOutput(Config, **Obj, Out, OutputElfType)) return createFileError(Config.InputFilename, std::move(E)); - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) - if (Error E = - linkToBuildIdDir(Config, Config.OutputFilename, - Config.BuildIdLinkOutput.getValue(), BuildIdBytes)) - return createFileError(Config.OutputFilename, std::move(E)); return Error::success(); } diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h --- a/llvm/tools/llvm-objcopy/ELF/Object.h +++ b/llvm/tools/llvm-objcopy/ELF/Object.h @@ -9,7 +9,6 @@ #ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H #define LLVM_TOOLS_OBJCOPY_OBJECT_H -#include "Buffer.h" #include "CopyConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" @@ -19,6 +18,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" #include #include #include @@ -106,7 +106,7 @@ class SectionWriter : public SectionVisitor { protected: - Buffer &Out; + WritableMemoryBuffer &Out; public: virtual ~SectionWriter() = default; @@ -123,7 +123,7 @@ virtual Error visit(const CompressedSection &Sec) override = 0; virtual Error visit(const DecompressedSection &Sec) override = 0; - explicit SectionWriter(Buffer &Buf) : Out(Buf) {} + explicit SectionWriter(WritableMemoryBuffer &Buf) : Out(Buf) {} }; template class ELFSectionWriter : public SectionWriter { @@ -143,7 +143,7 @@ Error visit(const CompressedSection &Sec) override; Error visit(const DecompressedSection &Sec) override; - explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {} + explicit ELFSectionWriter(WritableMemoryBuffer &Buf) : SectionWriter(Buf) {} }; template class ELFSectionSizer : public MutableSectionVisitor { @@ -187,7 +187,8 @@ Error visit(const CompressedSection &Sec) override; Error visit(const DecompressedSection &Sec) override; - explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} + explicit BinarySectionWriter(WritableMemoryBuffer &Buf) + : SectionWriter(Buf) {} }; using IHexLineData = SmallVector; @@ -283,7 +284,8 @@ virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef Data); public: - explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {} + explicit IHexSectionWriterBase(WritableMemoryBuffer &Buf) + : BinarySectionWriter(Buf) {} uint64_t getBufferOffset() const { return Offset; } Error visit(const Section &Sec) final; @@ -296,7 +298,7 @@ // Real IHEX section writer class IHexSectionWriter : public IHexSectionWriterBase { public: - IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {} + IHexSectionWriter(WritableMemoryBuffer &Buf) : IHexSectionWriterBase(Buf) {} void writeData(uint8_t Type, uint16_t Addr, ArrayRef Data) override; Error visit(const StringTableSection &Sec) override; @@ -305,14 +307,15 @@ class Writer { protected: Object &Obj; - Buffer &Buf; + std::unique_ptr Buf; + raw_ostream &Out; public: virtual ~Writer(); virtual Error finalize() = 0; virtual Error write() = 0; - Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {} + Writer(Object &O, raw_ostream &Out) : Obj(O), Out(Out) {} }; template class ELFWriter : public Writer { @@ -349,7 +352,7 @@ Error finalize() override; Error write() override; - ELFWriter(Object &Obj, Buffer &Buf, bool WSH, bool OnlyKeepDebug); + ELFWriter(Object &Obj, raw_ostream &Out, bool WSH, bool OnlyKeepDebug); }; class BinaryWriter : public Writer { @@ -362,7 +365,7 @@ ~BinaryWriter() {} Error finalize() override; Error write() override; - BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} + BinaryWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {} }; class IHexWriter : public Writer { @@ -381,7 +384,7 @@ ~IHexWriter() {} Error finalize() override; Error write() override; - IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} + IHexWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {} }; class SectionBase { diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp --- a/llvm/tools/llvm-objcopy/ELF/Object.cpp +++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp @@ -37,8 +37,8 @@ using namespace ELF; template void ELFWriter::writePhdr(const Segment &Seg) { - uint8_t *B = Buf.getBufferStart() + Obj.ProgramHdrSegment.Offset + - Seg.Index * sizeof(Elf_Phdr); + uint8_t *B = reinterpret_cast(Buf->getBufferStart()) + + Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr); Elf_Phdr &Phdr = *reinterpret_cast(B); Phdr.p_type = Seg.Type; Phdr.p_flags = Seg.Flags; @@ -67,7 +67,8 @@ void SectionBase::onRemove() {} template void ELFWriter::writeShdr(const SectionBase &Sec) { - uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset; + uint8_t *B = + reinterpret_cast(Buf->getBufferStart()) + Sec.HeaderOffset; Elf_Shdr &Shdr = *reinterpret_cast(B); Shdr.sh_name = Sec.NameIndex; Shdr.sh_type = Sec.Type; @@ -474,7 +475,7 @@ return createStringError(errc::invalid_argument, "'" + Sec.Name + "': " + toString(std::move(Err))); - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast(Out.getBufferStart()) + Sec.Offset; std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); return Error::success(); @@ -519,7 +520,7 @@ template Error ELFSectionWriter::visit(const CompressedSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast(Out.getBufferStart()) + Sec.Offset; if (Sec.CompressionType == DebugCompressionType::None) { std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); return Error::success(); @@ -624,7 +625,8 @@ } Error SectionWriter::visit(const StringTableSection &Sec) { - Sec.StrTabBuilder.write(Out.getBufferStart() + Sec.Offset); + Sec.StrTabBuilder.write(reinterpret_cast(Out.getBufferStart()) + + Sec.Offset); return Error::success(); } @@ -638,7 +640,7 @@ template Error ELFSectionWriter::visit(const SectionIndexSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast(Out.getBufferStart()) + Sec.Offset; llvm::copy(Sec.Indexes, reinterpret_cast(Buf)); return Error::success(); } @@ -979,7 +981,7 @@ template Error ELFSectionWriter::visit(const RelocationSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast(Out.getBufferStart()) + Sec.Offset; if (Sec.Type == SHT_REL) writeRel(Sec.Relocations, reinterpret_cast(Buf)); else @@ -1160,7 +1162,8 @@ template Error ELFSectionWriter::visit(const GnuDebugLinkSection &Sec) { - unsigned char *Buf = Out.getBufferStart() + Sec.Offset; + unsigned char *Buf = + reinterpret_cast(Out.getBufferStart()) + Sec.Offset; Elf_Word *CRC = reinterpret_cast(Buf + Sec.Size - sizeof(Elf_Word)); *CRC = Sec.CRC32; @@ -1970,7 +1973,7 @@ } template void ELFWriter::writeEhdr() { - Elf_Ehdr &Ehdr = *reinterpret_cast(Buf.getBufferStart()); + Elf_Ehdr &Ehdr = *reinterpret_cast(Buf->getBufferStart()); std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0); Ehdr.e_ident[EI_MAG0] = 0x7f; Ehdr.e_ident[EI_MAG1] = 'E'; @@ -2035,7 +2038,7 @@ // This reference serves to write the dummy section header at the begining // of the file. It is not used for anything else Elf_Shdr &Shdr = - *reinterpret_cast(Buf.getBufferStart() + Obj.SHOff); + *reinterpret_cast(Buf->getBufferStart() + Obj.SHOff); Shdr.sh_name = 0; Shdr.sh_type = SHT_NULL; Shdr.sh_flags = 0; @@ -2075,7 +2078,7 @@ template void ELFWriter::writeSegmentData() { for (Segment &Seg : Obj.segments()) { size_t Size = std::min(Seg.FileSize, Seg.getContents().size()); - std::memcpy(Buf.getBufferStart() + Seg.Offset, Seg.getContents().data(), + std::memcpy(Buf->getBufferStart() + Seg.Offset, Seg.getContents().data(), Size); } @@ -2086,12 +2089,12 @@ continue; uint64_t Offset = Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset; - std::memset(Buf.getBufferStart() + Offset, 0, Sec.Size); + std::memset(Buf->getBufferStart() + Offset, 0, Sec.Size); } } template -ELFWriter::ELFWriter(Object &Obj, Buffer &Buf, bool WSH, +ELFWriter::ELFWriter(Object &Obj, raw_ostream &Buf, bool WSH, bool OnlyKeepDebug) : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs), OnlyKeepDebug(OnlyKeepDebug) {} @@ -2401,7 +2404,10 @@ return E; if (WriteSectionHeaders) writeShdrs(); - return Buf.commit(); + + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + Out.flush(); + return Error::success(); } static Error removeUnneededSections(Object &Obj) { @@ -2521,9 +2527,10 @@ Sec.finalize(); } - if (Error E = Buf.allocate(totalSize())) - return E; - SecWriter = std::make_unique>(Buf); + Buf = WritableMemoryBuffer::getNewMemBuffer(totalSize()); + if (!Buf) + return createStringError(llvm::errc::not_enough_memory, Twine(totalSize())); + SecWriter = std::make_unique>(*Buf); return Error::success(); } @@ -2532,7 +2539,11 @@ if (Error Err = Sec.accept(*SecWriter)) return Err; - return Buf.commit(); + // TODO: Implement direct writing to the output stream + // TODO: (without intermediate memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + Out.flush(); + return Error::success(); } Error BinaryWriter::finalize() { @@ -2560,9 +2571,10 @@ TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); } - if (Error E = Buf.allocate(TotalSize)) - return E; - SecWriter = std::make_unique(Buf); + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(llvm::errc::not_enough_memory, Twine(TotalSize)); + SecWriter = std::make_unique(*Buf); return Error::success(); } @@ -2600,7 +2612,7 @@ } Error IHexWriter::write() { - IHexSectionWriter Writer(Buf); + IHexSectionWriter Writer(*Buf); // Write sections. for (const SectionBase *Sec : Sections) if (Error Err = Sec->accept(Writer)) @@ -2608,11 +2620,16 @@ uint64_t Offset = Writer.getBufferOffset(); // Write entry point address. - Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset); + Offset += writeEntryPointRecord( + reinterpret_cast(Buf->getBufferStart()) + Offset); // Write EOF. - Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset); + Offset += writeEndOfFileRecord( + reinterpret_cast(Buf->getBufferStart()) + Offset); assert(Offset == TotalSize); - return Buf.commit(); + + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + Out.flush(); + return Error::success(); } Error IHexWriter::checkSection(const SectionBase &Sec) { @@ -2656,7 +2673,12 @@ Sections.insert(&Sec); } - IHexSectionWriterBase LengthCalc(Buf); + std::unique_ptr EmptyBuffer = + WritableMemoryBuffer::getNewMemBuffer(0); + if (!EmptyBuffer) + return createStringError(errc::not_enough_memory, Twine(0)); + + IHexSectionWriterBase LengthCalc(*EmptyBuffer); for (const SectionBase *Sec : Sections) if (Error Err = Sec->accept(LengthCalc)) return Err; @@ -2666,8 +2688,11 @@ TotalSize = LengthCalc.getBufferOffset() + (Obj.Entry ? IHexRecord::getLineLength(4) : 0) + IHexRecord::getLineLength(0); - if (Error E = Buf.allocate(TotalSize)) - return E; + + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(llvm::errc::not_enough_memory, Twine(TotalSize)); + return Error::success(); } diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h @@ -11,6 +11,7 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class MachOObjectFile; @@ -23,10 +24,11 @@ namespace macho { Error executeObjcopyOnBinary(const CopyConfig &Config, - object::MachOObjectFile &In, Buffer &Out); + object::MachOObjectFile &In, raw_ostream &Out); Error executeObjcopyOnMachOUniversalBinary( - CopyConfig &Config, const object::MachOUniversalBinary &In, Buffer &Out); + CopyConfig &Config, const object::MachOUniversalBinary &In, + raw_ostream &Out); } // end namespace macho } // end namespace objcopy diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -17,6 +17,8 @@ #include "llvm/Object/MachOUniversalWriter.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" namespace llvm { namespace objcopy { @@ -386,7 +388,7 @@ } Error executeObjcopyOnBinary(const CopyConfig &Config, - object::MachOObjectFile &In, Buffer &Out) { + object::MachOObjectFile &In, raw_ostream &Out) { MachOReader Reader(In); Expected> O = Reader.create(); if (!O) @@ -416,7 +418,7 @@ Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config, const MachOUniversalBinary &In, - Buffer &Out) { + raw_ostream &Out) { SmallVector, 2> Binaries; SmallVector Slices; for (const auto &O : In.objects()) { @@ -460,27 +462,29 @@ Config.InputFilename.str().c_str()); } std::string ArchFlagName = O.getArchFlagName(); - MemBuffer MB(ArchFlagName); - if (Error E = executeObjcopyOnBinary(Config, **ObjOrErr, MB)) + + SmallVector Buffer; + raw_svector_ostream MemStream(Buffer); + + if (Error E = executeObjcopyOnBinary(Config, **ObjOrErr, MemStream)) return E; - std::unique_ptr OutputBuffer = - MB.releaseMemoryBuffer(); - Expected> BinaryOrErr = - object::createBinary(*OutputBuffer); + + std::unique_ptr MB = + std::make_unique(std::move(Buffer), + ArchFlagName); + Expected> BinaryOrErr = object::createBinary(*MB); if (!BinaryOrErr) return BinaryOrErr.takeError(); - Binaries.emplace_back(std::move(*BinaryOrErr), std::move(OutputBuffer)); + Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB)); Slices.emplace_back(*cast(Binaries.back().getBinary()), O.getAlign()); } - Expected> B = - writeUniversalBinaryToBuffer(Slices); - if (!B) - return B.takeError(); - if (Error E = Out.allocate((*B)->getBufferSize())) - return E; - memcpy(Out.getBufferStart(), (*B)->getBufferStart(), (*B)->getBufferSize()); - return Out.commit(); + + if (Error Err = writeUniversalBinaryToStream(Slices, Out)) + return Err; + + Out.flush(); + return Error::success(); } } // end namespace macho diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// -#include "../Buffer.h" #include "MachOLayoutBuilder.h" #include "MachOObjcopy.h" #include "Object.h" @@ -24,7 +23,8 @@ bool Is64Bit; bool IsLittleEndian; uint64_t PageSize; - Buffer &B; + std::unique_ptr Buf; + raw_ostream &Out; MachOLayoutBuilder LayoutBuilder; size_t headerSize() const; @@ -53,9 +53,9 @@ public: MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize, - Buffer &B) + raw_ostream &Out) : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), - PageSize(PageSize), B(B), LayoutBuilder(O, Is64Bit, PageSize) {} + PageSize(PageSize), Out(Out), LayoutBuilder(O, Is64Bit, PageSize) {} size_t totalSize() const; Error finalize(); diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -159,11 +159,12 @@ auto HeaderSize = Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - memcpy(B.getBufferStart(), &Header, HeaderSize); + memcpy(Buf->getBufferStart(), &Header, HeaderSize); } void MachOWriter::writeLoadCommands() { - uint8_t *Begin = B.getBufferStart() + headerSize(); + uint8_t *Begin = + reinterpret_cast(Buf->getBufferStart()) + headerSize(); for (const LoadCommand &LC : O.LoadCommands) { // Construct a load command. MachO::macho_load_command MLC = LC.MachOLoadCommand; @@ -257,7 +258,7 @@ assert(Sec->Offset && "Section offset can not be zero"); assert((Sec->Size == Sec->Content.size()) && "Incorrect section size"); - memcpy(B.getBufferStart() + Sec->Offset, Sec->Content.data(), + memcpy(Buf->getBufferStart() + Sec->Offset, Sec->Content.data(), Sec->Content.size()); for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) { RelocationInfo RelocInfo = Sec->Relocations[Index]; @@ -270,7 +271,7 @@ if (IsLittleEndian != sys::IsLittleEndianHost) MachO::swapStruct( reinterpret_cast(RelocInfo.Info)); - memcpy(B.getBufferStart() + Sec->RelOff + + memcpy(Buf->getBufferStart() + Sec->RelOff + Index * sizeof(MachO::any_relocation_info), &RelocInfo.Info, sizeof(RelocInfo.Info)); } @@ -300,7 +301,7 @@ O.LoadCommands[*O.SymTabCommandIndex] .MachOLoadCommand.symtab_command_data; - uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff; + uint8_t *StrTable = (uint8_t *)Buf->getBufferStart() + SymTabCommand.stroff; LayoutBuilder.getStringTableBuilder().write(StrTable); } @@ -311,7 +312,7 @@ O.LoadCommands[*O.SymTabCommandIndex] .MachOLoadCommand.symtab_command_data; - char *SymTable = (char *)B.getBufferStart() + SymTabCommand.symoff; + char *SymTable = (char *)Buf->getBufferStart() + SymTabCommand.symoff; for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end(); Iter != End; Iter++) { SymbolEntry *Sym = Iter->get(); @@ -330,7 +331,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.rebase_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.rebase_off; assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && "Incorrect rebase opcodes size"); memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size()); @@ -342,7 +343,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.bind_off; assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && "Incorrect bind opcodes size"); memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size()); @@ -354,7 +355,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.weak_bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.weak_bind_off; assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && "Incorrect weak bind opcodes size"); memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size()); @@ -366,7 +367,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.lazy_bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.lazy_bind_off; assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && "Incorrect lazy bind opcodes size"); memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size()); @@ -378,7 +379,7 @@ const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.export_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.export_off; assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && "Incorrect export trie size"); memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size()); @@ -393,7 +394,7 @@ .MachOLoadCommand.dysymtab_command_data; uint32_t *Out = - (uint32_t *)(B.getBufferStart() + DySymTabCommand.indirectsymoff); + (uint32_t *)(Buf->getBufferStart() + DySymTabCommand.indirectsymoff); for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) { uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex; if (IsLittleEndian != sys::IsLittleEndianHost) @@ -407,7 +408,7 @@ return; const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; - char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff; + char *Out = (char *)Buf->getBufferStart() + LinkEditDataCommand.dataoff; assert((LinkEditDataCommand.datasize == LD.Data.size()) && "Incorrect data size"); memcpy(Out, LD.Data.data(), LD.Data.size()); @@ -511,14 +512,20 @@ Error MachOWriter::finalize() { return LayoutBuilder.layout(); } Error MachOWriter::write() { - if (Error E = B.allocate(totalSize())) - return E; - memset(B.getBufferStart(), 0, totalSize()); + Buf = WritableMemoryBuffer::getNewMemBuffer(totalSize()); + if (!Buf) + return createStringError(llvm::errc::not_enough_memory, Twine(totalSize())); + memset(Buf->getBufferStart(), 0, totalSize()); writeHeader(); writeLoadCommands(); writeSections(); writeTail(); - return B.commit(); + + // TODO: Implement direct writing to the output stream + // TODO: (without intermediate memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + Out.flush(); + return Error::success(); } } // end namespace macho diff --git a/llvm/tools/llvm-objcopy/Util.h b/llvm/tools/llvm-objcopy/Util.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-objcopy/Util.h @@ -0,0 +1,50 @@ +//===- Util.h ---------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_UTIL_H +#define LLVM_TOOLS_OBJCOPY_UTIL_H + +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace objcopy { + +inline Error writeToFile(StringRef OutputFileName, + std::function Write) { + if (OutputFileName == "-") { + return Write(outs()); + } else if (OutputFileName == "/dev/null") { + raw_null_ostream Out; + return Write(Out); + } else { + unsigned Mode = sys::fs::all_read | sys::fs::all_write | sys::fs::all_exe; + Expected Temp = sys::fs::TempFile::create( + OutputFileName + ".temp-objcopy-%%%%%%", Mode); + if (!Temp) + return createFileError(OutputFileName, Temp.takeError()); + + raw_fd_ostream Out(Temp->FD, false); + + if (Error E = Write(Out)) { + if (Error DiscardError = Temp->discard()) + return joinErrors(std::move(E), std::move(DiscardError)); + return E; + } + + return Temp->keep(OutputFileName); + } + + llvm_unreachable("Unknown File kind!"); +} + +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_UTIL_H diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -6,17 +6,18 @@ // //===----------------------------------------------------------------------===// -#include "Buffer.h" #include "COFF/COFFObjcopy.h" #include "CopyConfig.h" #include "ELF/ELFObjcopy.h" #include "MachO/MachOObjcopy.h" +#include "Util.h" #include "wasm/WasmObjcopy.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/Binary.h" @@ -32,6 +33,7 @@ #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" @@ -40,6 +42,7 @@ #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" @@ -87,6 +90,104 @@ return parseObjcopyOptions(Args, reportWarning); } +template +static Expected> +findBuildID(const CopyConfig &Config, const object::ELFFile &In) { + auto PhdrsOrErr = In.program_headers(); + if (auto Err = PhdrsOrErr.takeError()) + return createFileError(Config.InputFilename, std::move(Err)); + + for (const auto &Phdr : *PhdrsOrErr) { + if (Phdr.p_type != ELF::PT_NOTE) + continue; + Error Err = Error::success(); + for (auto Note : In.notes(Phdr, Err)) + if (Note.getType() == ELF::NT_GNU_BUILD_ID && + Note.getName() == ELF::ELF_NOTE_GNU) + return Note.getDesc(); + if (Err) + return createFileError(Config.InputFilename, std::move(Err)); + } + + return createFileError(Config.InputFilename, + createStringError(llvm::errc::invalid_argument, + "could not find build ID")); +} + +static Expected> +findBuildID(const CopyConfig &Config, const object::ELFObjectFileBase &In) { + if (auto *O = dyn_cast>(&In)) + return findBuildID(Config, *O->getELFFile()); + else if (auto *O = dyn_cast>(&In)) + return findBuildID(Config, *O->getELFFile()); + else if (auto *O = dyn_cast>(&In)) + return findBuildID(Config, *O->getELFFile()); + else if (auto *O = dyn_cast>(&In)) + return findBuildID(Config, *O->getELFFile()); + + llvm_unreachable("Bad file format"); +} + +template +static Error makeStringError(std::error_code EC, const Twine &Msg, + Ts &&... Args) { + std::string FullMsg = (EC.message() + ": " + Msg).str(); + return createStringError(EC, FullMsg.c_str(), std::forward(Args)...); +} + +#define MODEL_8 "%%%%%%%%" +#define MODEL_16 MODEL_8 MODEL_8 +#define MODEL_32 (MODEL_16 MODEL_16) + +static Error 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)) + return createFileError( + Path.str(), + makeStringError(EC, "cannot create build ID link directory")); + + sys::path::append(Path, + llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true)); + Path += Suffix; + SmallString<128> TmpPath; + // create_hard_link races so we need to link to a temporary path but + // we want to make sure that we choose a filename that does not exist. + // By using 32 model characters we get 128-bits of entropy. It is + // unlikely that this string has ever existed before much less exists + // on this disk or in the current working directory. + // Additionally we prepend the original Path for debugging but also + // because it ensures that we're linking within a directory on the same + // partition on the same device which is critical. It has the added + // win of yet further decreasing the odds of a conflict. + sys::fs::createUniquePath(Twine(Path) + "-" + MODEL_32 + ".tmp", TmpPath, + /*MakeAbsolute*/ false); + if (auto EC = sys::fs::create_hard_link(ToLink, TmpPath)) { + Path.push_back('\0'); + return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), + Path.data()); + } + // We then atomically rename the link into place which will just move the + // link. If rename fails something is more seriously wrong so just return + // an error. + if (auto EC = sys::fs::rename(TmpPath, Path)) { + Path.push_back('\0'); + return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), + Path.data()); + } + // If `Path` was already a hard-link to the same underlying file then the + // temp file will be left so we need to remove it. Remove will not cause + // an error by default if the file is already gone so just blindly remove + // it rather than checking. + if (auto EC = sys::fs::remove(TmpPath)) { + TmpPath.push_back('\0'); + return makeStringError(EC, "could not remove '%s'", TmpPath.data()); + } + return Error::success(); +} + } // end namespace objcopy } // end namespace llvm @@ -108,20 +209,21 @@ return Error::success(); for (const NewArchiveMember &Member : NewMembers) { - // Internally, FileBuffer will use the buffer created by - // FileOutputBuffer::create, for regular files (that is the case for - // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. + // FileOutputBuffer::create, for regular files(that is the case for + // deepWriteArchive) will return OnDiskBuffer. // OnDiskBuffer uses a temporary file and then renames it. So in reality // there is no inefficiency / duplicated in-memory buffers in this case. For // now in-memory buffers can not be completely avoided since // NewArchiveMember still requires them even though writeArchive does not // write them on disk. - FileBuffer FB(Member.MemberName); - if (Error E = FB.allocate(Member.Buf->getBufferSize())) - return E; + Expected> FB = + FileOutputBuffer::create(Member.MemberName, Member.Buf->getBufferSize(), + FileOutputBuffer::F_executable); + if (!FB) + return FB.takeError(); std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), - FB.getBufferStart()); - if (Error E = FB.commit()) + (*FB)->getBufferStart()); + if (Error E = (*FB)->commit()) return E; } return Error::success(); @@ -130,7 +232,7 @@ /// The function executeObjcopyOnIHex does the dispatch based on the format /// of the output specified by the command line options. static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { + raw_ostream &Out) { // TODO: support output formats other than ELF. if (Error E = Config.parseELFConfig()) return E; @@ -140,7 +242,7 @@ /// The function executeObjcopyOnRawBinary does the dispatch based on the format /// of the output specified by the command line options. static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { + raw_ostream &Out) { switch (Config.OutputFormat) { case FileFormat::ELF: // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the @@ -160,7 +262,7 @@ /// The function executeObjcopyOnBinary does the dispatch based on the format /// of the input binary (ELF, MachO or COFF). static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, - Buffer &Out) { + raw_ostream &Out) { if (auto *ELFBinary = dyn_cast(&In)) { if (Error E = Config.parseELFConfig()) return E; @@ -197,15 +299,19 @@ return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", ChildOrErr.takeError()); - MemBuffer MB(ChildNameOrErr.get()); - if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) + SmallVector Buffer; + raw_svector_ostream MemStream(Buffer); + + if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MemStream)) return std::move(E); Expected Member = NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); if (!Member) return createFileError(Ar.getFileName(), Member.takeError()); - Member->Buf = MB.releaseMemoryBuffer(); + + Member->Buf = std::make_unique( + std::move(Buffer), ChildNameOrErr.get()); Member->MemberName = Member->Buf->getBufferIdentifier(); NewArchiveMembers.push_back(std::move(*Member)); } @@ -280,7 +386,7 @@ Stat.permissions(static_cast(0777)); } - using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); + using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, raw_ostream &); ProcessRawFn ProcessRaw; switch (Config.InputFormat) { case FileFormat::Binary: @@ -297,8 +403,11 @@ auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); if (!BufOrErr) return createFileError(Config.InputFilename, BufOrErr.getError()); - FileBuffer FB(Config.OutputFilename); - if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) + + if (Error E = writeToFile( + Config.OutputFilename, [&](raw_ostream &OutFile) -> Error { + return ProcessRaw(Config, *BufOrErr->get(), OutFile); + })) return E; } else { Expected> BinaryOrErr = @@ -310,10 +419,44 @@ if (Error E = executeObjcopyOnArchive(Config, *Ar)) return E; } else { - FileBuffer FB(Config.OutputFilename); - if (Error E = executeObjcopyOnBinary(Config, - *BinaryOrErr.get().getBinary(), FB)) + ArrayRef BuildIdBytes; + if (auto *ELFBinary = dyn_cast( + BinaryOrErr.get().getBinary())) { + if (!Config.BuildIdLinkDir.empty()) { + auto BuildIdBytesOrErr = findBuildID(Config, *ELFBinary); + if (auto E = BuildIdBytesOrErr.takeError()) + return E; + BuildIdBytes = *BuildIdBytesOrErr; + + if (BuildIdBytes.size() < 2) + return createFileError( + Config.InputFilename, + createStringError(object_error::parse_failed, + "build ID is smaller than two bytes")); + } + + if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) + if (Error E = linkToBuildIdDir(Config, Config.InputFilename, + Config.BuildIdLinkInput.getValue(), + BuildIdBytes)) + return E; + } + + if (Error E = writeToFile( + Config.OutputFilename, [&](raw_ostream &OutFile) -> Error { + return executeObjcopyOnBinary( + Config, *BinaryOrErr.get().getBinary(), OutFile); + })) return E; + + if (auto *ELFBinary = dyn_cast( + BinaryOrErr.get().getBinary())) { + if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) + if (Error E = linkToBuildIdDir(Config, Config.OutputFilename, + Config.BuildIdLinkOutput.getValue(), + BuildIdBytes)) + return createFileError(Config.OutputFilename, std::move(E)); + } } } diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h --- a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h +++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h @@ -11,6 +11,7 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class WasmObjectFile; @@ -18,11 +19,10 @@ namespace objcopy { struct CopyConfig; -class Buffer; namespace wasm { Error executeObjcopyOnBinary(const CopyConfig &Config, - object::WasmObjectFile &In, Buffer &Out); + object::WasmObjectFile &In, raw_ostream &Out); } // end namespace wasm } // end namespace objcopy diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp --- a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp @@ -7,12 +7,12 @@ //===----------------------------------------------------------------------===// #include "WasmObjcopy.h" -#include "Buffer.h" #include "CopyConfig.h" #include "Object.h" #include "Reader.h" #include "Writer.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/FileOutputBuffer.h" namespace llvm { namespace objcopy { @@ -93,7 +93,7 @@ } Error executeObjcopyOnBinary(const CopyConfig &Config, - object::WasmObjectFile &In, Buffer &Out) { + object::WasmObjectFile &In, raw_ostream &Out) { Reader TheReader(In); Expected> ObjOrErr = TheReader.create(); if (!ObjOrErr) diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.h b/llvm/tools/llvm-objcopy/wasm/Writer.h --- a/llvm/tools/llvm-objcopy/wasm/Writer.h +++ b/llvm/tools/llvm-objcopy/wasm/Writer.h @@ -9,7 +9,6 @@ #ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H #define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H -#include "Buffer.h" #include "Object.h" #include #include @@ -20,13 +19,13 @@ class Writer { public: - Writer(Object &Obj, Buffer &Buf) : Obj(Obj), Buf(Buf) {} + Writer(Object &Obj, raw_ostream &Out) : Obj(Obj), Out(Out) {} Error write(); private: using SectionHeader = SmallVector; Object &Obj; - Buffer &Buf; + raw_ostream &Out; std::vector SectionHeaders; /// Generate a wasm section section header for S. diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.cpp b/llvm/tools/llvm-objcopy/wasm/Writer.cpp --- a/llvm/tools/llvm-objcopy/wasm/Writer.cpp +++ b/llvm/tools/llvm-objcopy/wasm/Writer.cpp @@ -9,6 +9,7 @@ #include "Writer.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" @@ -54,12 +55,14 @@ } Error Writer::write() { - size_t FileSize = finalize(); - if (Error E = Buf.allocate(FileSize)) - return E; + size_t TotalSize = finalize(); + std::unique_ptr Buf = + WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(llvm::errc::not_enough_memory, Twine(TotalSize)); // Write the header. - uint8_t *Ptr = Buf.getBufferStart(); + uint8_t *Ptr = reinterpret_cast(Buf->getBufferStart()); Ptr = std::copy(Obj.Header.Magic.begin(), Obj.Header.Magic.end(), Ptr); support::endian::write32le(Ptr, Obj.Header.Version); Ptr += sizeof(Obj.Header.Version); @@ -70,7 +73,12 @@ ArrayRef Contents = Obj.Sections[I].Contents; Ptr = std::copy(Contents.begin(), Contents.end(), Ptr); } - return Buf.commit(); + + // TODO: Implement direct writing to the output stream + // TODO: (without intermediate memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + Out.flush(); + return Error::success(); } } // end namespace wasm