diff --git a/llvm/include/llvm/BinaryFormat/XCOFF.h b/llvm/include/llvm/BinaryFormat/XCOFF.h --- a/llvm/include/llvm/BinaryFormat/XCOFF.h +++ b/llvm/include/llvm/BinaryFormat/XCOFF.h @@ -22,6 +22,20 @@ enum { SectionNameSize = 8, SymbolNameSize = 8 }; enum ReservedSectionNum { N_DEBUG = -2, N_ABS = -1, N_UNDEF = 0 }; +// Flags for the headers f_flag field. +enum HeaderFlags { + F_RELFLG = 0x0001, + F_EXEC = 0x0002, + F_LNNO = 0x0004, + F_FDPR_PROF = 0x0010, + F_FDPR_OPTI = 0x0020, + F_DSA = 0x0040, + F_VARPG = 0x0100, + F_DYNLOAD = 0x1000, + F_SHROBJ = 0x2000, + F_LOADONLY = 0x4000 +}; + // Flags for defining the section type. Used for the s_flags field of // the section header structure. Defined in the system header `scnhdr.h`. enum SectionTypeFlags { diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h --- a/llvm/include/llvm/Object/COFF.h +++ b/llvm/include/llvm/Object/COFF.h @@ -1063,6 +1063,7 @@ StringRef &PDBFileName) const; bool isRelocatableObject() const override; + bool isExecutableObject() const override; bool is64() const { return PE32PlusHeader; } StringRef mapDebugSectionName(StringRef Name) const override; diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -433,6 +433,7 @@ elf_symbol_iterator_range getDynamicSymbolIterators() const override; bool isRelocatableObject() const override; + bool isExecutableObject() const override; }; using ELF32LEObjectFile = ELFObjectFile; @@ -1198,6 +1199,10 @@ return EF.getHeader()->e_type == ELF::ET_REL; } +template bool ELFObjectFile::isExecutableObject() const { + return EF.getHeader()->e_type == ELF::ET_EXEC; +} + } // end namespace object } // end namespace llvm diff --git a/llvm/include/llvm/Object/MachO.h b/llvm/include/llvm/Object/MachO.h --- a/llvm/include/llvm/Object/MachO.h +++ b/llvm/include/llvm/Object/MachO.h @@ -574,7 +574,8 @@ static Triple getHostArch(); bool isRelocatableObject() const override; - + bool isExecutableObject() const override; + StringRef mapDebugSectionName(StringRef Name) const override; bool hasPageZeroSegment() const { return HasPageZeroSegment; } diff --git a/llvm/include/llvm/Object/ObjectFile.h b/llvm/include/llvm/Object/ObjectFile.h --- a/llvm/include/llvm/Object/ObjectFile.h +++ b/llvm/include/llvm/Object/ObjectFile.h @@ -336,6 +336,9 @@ /// True if this is a relocatable object (.o/.obj). virtual bool isRelocatableObject() const = 0; + + /// True if this is an executable object. + virtual bool isExecutableObject() const = 0; /// @returns Pointer to ObjectFile subclass to handle this type of object. /// @param ObjectPath The path to the object file. ObjectPath.isObject must diff --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h --- a/llvm/include/llvm/Object/Wasm.h +++ b/llvm/include/llvm/Object/Wasm.h @@ -202,6 +202,7 @@ Triple::ArchType getArch() const override; SubtargetFeatures getFeatures() const override; bool isRelocatableObject() const override; + bool isExecutableObject() const override; bool isSharedObject() const; struct ReadContext { diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -155,6 +155,7 @@ SubtargetFeatures getFeatures() const override; Expected getStartAddress() const override; bool isRelocatableObject() const override; + bool isExecutableObject() const override; XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC); diff --git a/llvm/lib/Object/COFFObjectFile.cpp b/llvm/lib/Object/COFFObjectFile.cpp --- a/llvm/lib/Object/COFFObjectFile.cpp +++ b/llvm/lib/Object/COFFObjectFile.cpp @@ -1307,6 +1307,10 @@ return !DataDirectory; } +bool COFFObjectFile::isExecutableObject() const { + return getCharacteristics() & COFF::IMAGE_FILE_EXECUTABLE_IMAGE; +} + StringRef COFFObjectFile::mapDebugSectionName(StringRef Name) const { return StringSwitch(Name) .Case("eh_fram", "eh_frame") diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -4631,6 +4631,10 @@ return getHeader().filetype == MachO::MH_OBJECT; } +bool MachOObjectFile::isExecutableObject() const { + return getHeader().filetype == MachO::MH_EXECUTE; +} + Expected> ObjectFile::createMachOObjectFile(MemoryBufferRef Buffer, uint32_t UniversalCputype, diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -1537,6 +1537,10 @@ bool WasmObjectFile::isRelocatableObject() const { return HasLinkingSection; } +bool WasmObjectFile::isExecutableObject() const { + return !isRelocatableObject() && !isSharedObject(); +} + bool WasmObjectFile::isSharedObject() const { return HasDylinkSection; } const WasmSection &WasmObjectFile::getWasmSection(DataRefImpl Ref) const { diff --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp --- a/llvm/lib/Object/XCOFFObjectFile.cpp +++ b/llvm/lib/Object/XCOFFObjectFile.cpp @@ -329,6 +329,10 @@ return Result; } +bool XCOFFObjectFile::isExecutableObject() const { + return getFileHeader()->Flags == XCOFF::F_EXEC; +} + Expected XCOFFObjectFile::getStartAddress() const { // TODO FIXME Should get from auxiliary_header->o_entry when support for the // auxiliary_header is added. diff --git a/llvm/unittests/Object/CMakeLists.txt b/llvm/unittests/Object/CMakeLists.txt --- a/llvm/unittests/Object/CMakeLists.txt +++ b/llvm/unittests/Object/CMakeLists.txt @@ -7,6 +7,7 @@ MinidumpTest.cpp SymbolSizeTest.cpp SymbolicFileTest.cpp + IsExecutableTest.cpp ) target_link_libraries(ObjectTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/Object/IsExecutableTest.cpp b/llvm/unittests/Object/IsExecutableTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Object/IsExecutableTest.cpp @@ -0,0 +1,45 @@ +//===- IsExecutableTest.cpp - Tests for ObjectFile::isExecutableObject() --===// +// +// 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 "llvm/Object/ObjectFile.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace object; + +TEST(ELF, IsExec) { + ELF64LE::Ehdr Header; + memset(&Header, 0, sizeof(Header)); + const char e_ident[16] = { + '\177', 'E', 'L', 'F', + ELF::ELFCLASS64, + ELF::ELFDATA2LSB, + ELF::EV_CURRENT, + 0, + 0, + 9, 0, 0, 0, 0, 0, + 16 + }; + memcpy(&Header.e_ident, e_ident, 16); + Header.e_type = ELF::ET_REL; + Header.e_machine = ELF::EM_X86_64; + Header.e_version = ELF::EV_CURRENT; + Header.e_ehsize = sizeof(Header); + + StringRef Bytes(reinterpret_cast(&Header), sizeof(Header)); + auto ObjFileOrErr = ObjectFile::createObjectFile(MemoryBufferRef(Bytes, "")); + ASSERT_TRUE(!!ObjFileOrErr); + ASSERT_FALSE(ObjFileOrErr.get()->isExecutableObject()); + + Header.e_type = ELF::ET_EXEC; + ObjFileOrErr = ObjectFile::createObjectFile(MemoryBufferRef(Bytes, "")); + ASSERT_TRUE(!!ObjFileOrErr); + ASSERT_TRUE(ObjFileOrErr.get()->isExecutableObject()); +}