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,16 @@ +# RUN: umask 0 +# RUN: echo 'int main(){}' | clang -x c - -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: echo 'int a;' | clang -x c - -o %t -c +# RUN: llvm-objcopy %t %t1 +# RUN: test ! -x %t1 + +# RUN: chmod 0777 %t1 +# RUN: llvm-objcopy %t %t1 +# RUN: test ! -x %t1 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,26 @@ +# 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: diff %t.0666 %t.perm +# RUN: llvm-objcopy %p/Inputs/dynrel.elf %t +# RUN: ls -l %t | cut -f 1 -d ' ' > %t.perm +# RUN: diff %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: diff %t.0640 %t.perm +# RUN: llvm-objcopy %p/Inputs/dynrel.elf %t +# RUN: ls -l %t | cut -f 1 -d ' ' > %t.perm +# RUN: diff %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" @@ -43,6 +44,7 @@ #include #include #include +#include #include #include @@ -183,6 +185,17 @@ Config.DeterministicArchives, Ar.isThin()); } +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 restoreDateOnFile(StringRef Filename, const sys::fs::file_status &Stat) { int FD; @@ -201,16 +214,45 @@ return Error::success(); } +static Error restoreStatOnFile(const CopyConfig &Config, bool Exec, + const sys::fs::file_status &Stat) { + if (Config.PreserveDates) { + if (Error E = restoreDateOnFile(Config.OutputFilename, Stat)) + return E; + if (!Config.SplitDWO.empty()) + if (Error E = restoreDateOnFile(Config.SplitDWO, Stat)) + return E; + } + + if (Config.OutputFilename == "-") + return Error::success(); + + mode_t Mask = ::umask(0); + (void) ::umask(Mask); + if (std::error_code E = sys::fs::setPermissions( + Config.OutputFilename, + static_cast((Exec ? 0777 : 0666) & ~Mask))) + return createFileError(Config.OutputFilename, E); + + return Error::success(); +} + /// The function executeObjcopy does the higher level dispatch based on the type /// of input (raw binary, archive or single object file) and takes care of the /// 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") { + Exec = true; auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename); if (!BufOrErr) return createFileError(Config.InputFilename, BufOrErr.getError()); @@ -228,21 +270,14 @@ 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)) - return E; - if (!Config.SplitDWO.empty()) - if (Error E = restoreDateOnFile(Config.SplitDWO, Stat)) - return E; - } - - return Error::success(); + return restoreStatOnFile(Config, Exec, Stat); } int main(int argc, char **argv) {