diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -114,6 +114,8 @@ ET_EXEC = 2, // Executable file ET_DYN = 3, // Shared object file ET_CORE = 4, // Core file + ET_LOOS = 0xfe00, // Beginning of OS-specific codes + ET_HIOS = 0xfeff, // OS-specific ET_LOPROC = 0xff00, // Beginning of processor-specific codes ET_HIPROC = 0xffff // Processor-specific }; 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,6 +574,7 @@ static Triple getHostArch(); bool isRelocatableObject() const override; + bool isExecutableObject() const override; StringRef mapDebugSectionName(StringRef Name) const override; 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 @@ -337,6 +337,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 /// return true. 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 + ObjectFileTest.cpp ) target_link_libraries(ObjectTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/Object/ObjectFileTest.cpp b/llvm/unittests/Object/ObjectFileTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Object/ObjectFileTest.cpp @@ -0,0 +1,161 @@ +//===- ObjectFileTest.cpp - Tests for object::ObjectFile ------------------===// +// +// 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/COFF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace object; + +template class TestBase : public ::testing::Test { +public: + void createObjectFile() { + StringRef Bytes(reinterpret_cast(&Header), sizeof(Header)); + auto ObjFileOrErr = + ObjectFile::createObjectFile(MemoryBufferRef(Bytes, ""), Magic); + ASSERT_TRUE(!!ObjFileOrErr) << "invalid object file format"; + ObjPtr = std::move(ObjFileOrErr.get()); + } + +protected: + TestBase(file_magic Magic = file_magic::unknown) : Magic(Magic) {} + + virtual void createGenericHeader() = 0; + + void SetUp() override { + createGenericHeader(); + createObjectFile(); + } + + HeaderType Header; + file_magic Magic; + std::unique_ptr ObjPtr; + std::vector Types; +}; + +class ELFTest : public TestBase { + void createGenericHeader() override { + 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_machine = ELF::EM_X86_64; + Header.e_version = ELF::EV_CURRENT; + Header.e_ehsize = sizeof(Header); + } + +protected: + ELFTest() { + Types = {ELF::ET_NONE, ELF::ET_REL, ELF::ET_DYN, ELF::ET_EXEC, ELF::ET_CORE, + ELF::ET_CORE + 1, + // This is arbitarily chosen between LOPROC and HIPROC. + ELF::ET_LOPROC + 20, + // Arbitraily chosen between LOOS and HIOS. + ELF::ET_LOOS + 17}; + } +}; + +class COFFTest : public TestBase { + void createGenericHeader() override { + coff_file_header Header; + memset(&Header, 0, sizeof(Header)); + Header.Machine = COFF::MachineTypes::IMAGE_FILE_MACHINE_AMD64; + } + +protected: + COFFTest() : TestBase(file_magic::coff_object) { + Types = {COFF::IMAGE_FILE_EXECUTABLE_IMAGE | + COFF::IMAGE_FILE_DEBUG_STRIPPED, + COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE | COFF::IMAGE_FILE_DLL | + COFF::IMAGE_FILE_LOCAL_SYMS_STRIPPED, + COFF::IMAGE_FILE_BYTES_REVERSED_HI | + COFF::IMAGE_FILE_LINE_NUMS_STRIPPED, + COFF::IMAGE_FILE_AGGRESSIVE_WS_TRIM}; + } +}; + +class MachOTest : public TestBase { + void createGenericHeader() override { + memset(&Header, 0, sizeof(Header)); + Header.magic = MachO::MH_MAGIC_64; + Header.cputype = MachO::CPU_TYPE_X86; + } + +protected: + MachOTest() : TestBase(file_magic::macho_object) { + Types = {MachO::MH_OBJECT, MachO::MH_EXECUTE, MachO::MH_FVMLIB, + MachO::MH_CORE, MachO::MH_PRELOAD, MachO::MH_DYLINKER, + MachO::MH_BUNDLE, MachO::MH_DSYM, MachO::MH_KEXT_BUNDLE}; + } +}; + +TEST_F(ELFTest, IsExecutable) { + for (auto CurrType : Types) { + Header.e_type = CurrType; + if (CurrType == ELF::ET_EXEC) + EXPECT_TRUE(ObjPtr->isExecutableObject()); + else + EXPECT_FALSE(ObjPtr->isExecutableObject()); + } +} + +TEST_F(ELFTest, IsRelocatable) { + for (auto CurrType : Types) { + Header.e_type = CurrType; + if (CurrType == ELF::ET_REL) + EXPECT_TRUE(ObjPtr->isRelocatableObject()); + else + EXPECT_FALSE(ObjPtr->isRelocatableObject()); + } +} + +TEST_F(COFFTest, IsExecutable) { + for (auto Characteristic : Types) { + Header.Characteristics = Characteristic; + if (Characteristic & COFF::IMAGE_FILE_EXECUTABLE_IMAGE) + EXPECT_TRUE(ObjPtr->isExecutableObject()); + else + EXPECT_FALSE(ObjPtr->isExecutableObject()); + } +} + +// A test for isRelocatableObject for COFF is notably missing because +// this isn't identified in the header for COFF and is more involved. + +TEST_F(MachOTest, IsExecutable) { + for (auto Type : Types) { + Header.filetype = Type; + createObjectFile(); + if (Type == MachO::MH_EXECUTE) + EXPECT_TRUE(ObjPtr->isExecutableObject()); + else + EXPECT_FALSE(ObjPtr->isExecutableObject()); + } +} + +TEST_F(MachOTest, IsRelocatable) { + for (auto Type : Types) { + Header.filetype = Type; + createObjectFile(); + if (Type == MachO::MH_OBJECT) + EXPECT_TRUE(ObjPtr->isRelocatableObject()); + else + EXPECT_FALSE(ObjPtr->isRelocatableObject()); + } +}