Index: llvm/test/tools/llvm-objcopy/ELF/binary-output-empty.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/binary-output-empty.test @@ -0,0 +1,27 @@ +# RUN: yaml2obj %s -o %t.o + +# Writing an empty output to a non-existent file will still create it. +# RUN: rm -f %t-new.txt +# RUN: llvm-objcopy -R .text -O binary %t.o %t-new.txt +# RUN: wc -c %t-new.txt | FileCheck %s + +# Writing an empty output to an existing file will truncate it. +# RUN: echo abcd > %t-existing.txt +# RUN: llvm-objcopy -R .text -O binary %t.o %t-existing.txt +# RUN: wc -c %t-existing.txt | FileCheck %s + +# In both cases, the file should be empty. +# CHECK: 0 + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: "c3c3c3c3" + Size: 0x1000 Index: llvm/tools/llvm-objcopy/Buffer.cpp =================================================================== --- llvm/tools/llvm-objcopy/Buffer.cpp +++ llvm/tools/llvm-objcopy/Buffer.cpp @@ -10,7 +10,9 @@ #include "Buffer.h" #include "llvm-objcopy.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Process.h" #include namespace llvm { @@ -18,7 +20,30 @@ Buffer::~Buffer() {} +static Error CreateAndTruncateFile(StringRef FileName) { + int FD; + // Note: CD_CreateAlways will automatically truncate a file if it already + // exists. + if (std::error_code EC = sys::fs::openFileForWrite( + FileName, FD, sys::fs::CD_CreateAlways, sys::fs::OF_None)) + return createFileError(FileName, errorCodeToError(EC)); + if (std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD)) + return createFileError(FileName, errorCodeToError(EC)); + return Error::success(); +} + void FileBuffer::allocate(size_t Size) { + // Only create a buffer if we have a positive size. Otherwise, create (and + // truncate) the file. + if (Size == 0) { + // FIXME: Change allocate() signature from void to Error to avoid + // exiting via error() here. + if (Error Err = CreateAndTruncateFile(getName())) + handleAllErrors(std::move(Err), + [](const ErrorInfoBase &E) { error(E.message()); }); + return; + } + Expected> BufferOrErr = FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) { @@ -27,7 +52,7 @@ Buf = std::move(*BufferOrErr); } -Error FileBuffer::commit() { return Buf->commit(); } +Error FileBuffer::commit() { return Buf ? Buf->commit() : Error::success(); } uint8_t *FileBuffer::getBufferStart() { return reinterpret_cast(Buf->getBufferStart());