diff --git a/llvm/test/tools/llvm-objcopy/ELF/execute-permissions.test b/llvm/test/tools/llvm-objcopy/ELF/execute-permissions.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/execute-permissions.test @@ -0,0 +1,26 @@ +# UNSUPPORTED: windows +# REQUIRES: shell + +# RUN: umask 0 +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy %t %t1 +# RUN: test -x %t1 + +# RUN: chmod 0666 %t +# RUN: llvm-objcopy %t %t1 +# RUN: test -x %t1 + +# RUN: cp %p/Inputs/alloc-symtab.o %t +# RUN: llvm-objcopy %t %t1 +# RUN: test ! -x %t1 + +# RUN: chmod 0777 %t +# RUN: llvm-objcopy %t %t1 +# RUN: test ! -x %t1 + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 diff --git a/llvm/test/tools/llvm-objcopy/ELF/invalid-preserve-dates.test b/llvm/test/tools/llvm-objcopy/ELF/invalid-preserve-dates.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/invalid-preserve-dates.test @@ -0,0 +1,5 @@ +# RUN: llvm-objcopy --preserve-dates %p/Inputs/alloc-symtab.o - +# CHECK: error: --preserve-dates requires a file + +# RUN: llvm-objcopy --preserve-dates - $t < %p/Inputs/alloc-symtab.o +# CHECK: error: --preserve-dates requires a file diff --git a/llvm/test/tools/llvm-objcopy/ELF/respect-umask.test b/llvm/test/tools/llvm-objcopy/ELF/respect-umask.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/respect-umask.test @@ -0,0 +1,28 @@ +# UNSUPPORTED: windows + +# RUN: touch test +# RUN: chmod 0777 test +# RUN: ls -l test | cut -f 1 -d ' ' > %t.0777 +# RUN: chmod 0666 test +# RUN: ls -l test | cut -f 1 -d ' ' > %t.0666 +# RUN: chmod 0640 test +# RUN: ls -l test | cut -f 1 -d ' ' > %t.0640 +# RUN: chmod 0750 test +# RUN: ls -l test | cut -f 1 -d ' ' > %t.0750 +# RUN: rm test + +# RUN: umask 0 +# RUN: llvm-objcopy %p/Inputs/alloc-symtab.o %t +# RUN: ls -l %t | cut -f 1 -d ' ' > %t.perm +# RUN: cmp %t.0666 %t.perm +# RUN: llvm-objcopy %p/Inputs/dynrel.elf %t +# RUN: ls -l %t | cut -f 1 -d ' ' > %t.perm +# RUN: cmp %t.0777 %t.perm + +# RUN: umask 027 +# RUN: llvm-objcopy %p/Inputs/alloc-symtab.o %t +# RUN: ls -l %t | cut -f 1 -d ' ' > %t.perm +# RUN: cmp %t.0640 %t.perm +# RUN: llvm-objcopy %p/Inputs/dynrel.elf %t +# RUN: ls -l %t | cut -f 1 -d ' ' > %t.perm +# RUN: cmp %t.0750 %t.perm diff --git a/llvm/tools/llvm-objcopy/Buffer.cpp b/llvm/tools/llvm-objcopy/Buffer.cpp --- a/llvm/tools/llvm-objcopy/Buffer.cpp +++ b/llvm/tools/llvm-objcopy/Buffer.cpp @@ -36,7 +36,7 @@ } Expected> BufferOrErr = - FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); + FileOutputBuffer::create(getName(), Size); // 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) 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 @@ -29,6 +29,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" @@ -45,6 +46,10 @@ #include #include #include +#ifndef _WIN32 +#include +#include +#endif namespace llvm { namespace objcopy { @@ -149,6 +154,17 @@ "Unsupported object file format"); } +static bool isExecutableObject(object::Binary &Binary) { + if (auto *ELFObj = dyn_cast(&Binary)) + return ELFObj->getEType() & ELF::ET_EXEC; + else if (auto *COFFObj = dyn_cast(&Binary)) + return COFFObj->getCharacteristics() & COFF::IMAGE_FILE_EXECUTABLE_IMAGE; + else if (auto *MachOObj = dyn_cast(&Binary)) + return MachOObj->getHeader().filetype == MachO::MH_EXECUTE; + + return false; +} + static Error executeObjcopyOnArchive(const CopyConfig &Config, const Archive &Ar) { std::vector NewArchiveMembers; @@ -164,7 +180,8 @@ ChildOrErr.takeError()); MemBuffer MB(ChildNameOrErr.get()); - if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) + object::Binary &Binary = *ChildOrErr->get(); + if (Error E = executeObjcopyOnBinary(Config, Binary, MB)) return E; Expected Member = @@ -173,6 +190,7 @@ return createFileError(Ar.getFileName(), Member.takeError()); Member->Buf = MB.releaseMemoryBuffer(); Member->MemberName = Member->Buf->getBufferIdentifier(); + Member->Perms = isExecutableObject(Binary) ? 0777 : 0666; NewArchiveMembers.push_back(std::move(*Member)); } if (Err) @@ -183,7 +201,26 @@ Config.DeterministicArchives, Ar.isThin()); } -static Error restoreDateOnFile(StringRef Filename, +static std::error_code updatePermissions(StringRef Filename, bool Exec) { + if (Filename == "-") + return {}; + + // TODO: add wrapper around umask(2) in sys::fs +#ifndef _WIN32 + // umask(2) never fails so we ignore the return of the second call + // it is not thread safe as it sets the creation mask for the entire + // process + mode_t Mask = ::umask(0); + (void)::umask(Mask); +#else + int Mask = 0; +#endif + + return sys::fs::setPermissions( + Filename, static_cast((Exec ? 0777 : 0666) & ~Mask)); +} + +static Error restoreStatOnFile(StringRef Filename, bool RestoreDate, bool Exec, const sys::fs::file_status &Stat) { int FD; @@ -191,8 +228,12 @@ sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) return createFileError(Filename, EC); - if (auto EC = sys::fs::setLastAccessAndModificationTime( - FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) + if (RestoreDate) + if (auto EC = sys::fs::setLastAccessAndModificationTime( + FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) + return createFileError(Filename, EC); + + if (auto EC = updatePermissions(Filename, Exec)) return createFileError(Filename, EC); if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) @@ -206,10 +247,15 @@ /// format-agnostic modifications, i.e. preserving dates. static Error executeObjcopy(const CopyConfig &Config) { sys::fs::file_status Stat; - if (Config.PreserveDates) + if (Config.PreserveDates) { + if (Config.InputFilename == "-" || Config.OutputFilename == "-") + return createStringError(errc::invalid_argument, + "--preserve-dates requires a file"); if (auto EC = sys::fs::status(Config.InputFilename, Stat)) return createFileError(Config.InputFilename, EC); + } + bool Exec = false; if (Config.InputFormat == "binary") { auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename); if (!BufOrErr) @@ -228,19 +274,22 @@ return E; } else { FileBuffer FB(Config.OutputFilename); - if (Error E = executeObjcopyOnBinary(Config, - *BinaryOrErr.get().getBinary(), FB)) + object::Binary &Binary = *BinaryOrErr.get().getBinary(); + if (Error E = executeObjcopyOnBinary(Config, Binary, FB)) return E; + Exec = isExecutableObject(Binary); } } - if (Config.PreserveDates) { - if (Error E = restoreDateOnFile(Config.OutputFilename, Stat)) + if (Config.OutputFilename != "-") + if (Error E = restoreStatOnFile(Config.OutputFilename, Config.PreserveDates, + Exec, Stat)) + return E; + + if (!Config.SplitDWO.empty()) + if (Error E = restoreStatOnFile(Config.SplitDWO, Config.PreserveDates, + /*Exec=*/false, Stat)) return E; - if (!Config.SplitDWO.empty()) - if (Error E = restoreDateOnFile(Config.SplitDWO, Stat)) - return E; - } return Error::success(); }