Repository: dyninst/dyninst
Pull request #1908: Initial implementation of PE files parsing
Author: login: batuzovk
Labels: name: enhancement
name: WindowsPE
description: Windows PE file format
Created: 2025-03-10T12:42:05Z | Merged: 2025-03-19T19:47:16Z

Header and sections are parsed. Entry point is detected. Some parsing of .EXE files can be done.

Not supported yet: symbols, relocations.


Diff:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1e72ec4a9c..4026d78248 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,6 +47,7 @@ include(DyninstLibIberty)
 include(DyninstThread_DB)
 include(DyninstValgrind)
 include(DyninstOpenMP)
+include(DyninstPeParse)
 
 add_subdirectory(common)
 add_subdirectory(elf)
diff --git a/cmake/DyninstOptions.cmake b/cmake/DyninstOptions.cmake
index 504341c02a..4142b226f4 100644
--- a/cmake/DyninstOptions.cmake
+++ b/cmake/DyninstOptions.cmake
@@ -48,3 +48,5 @@ option(DYNINST_FORCE_RUNPATH "Require the use of RUNPATH instead of compiler's d
        OFF)
 
 option(DYNINST_ENABLE_TESTS "Build tests" OFF)
+
+option(DYNINST_ENABLE_FILEFORMAT_PE "Enable PE (Windows .exe/.dll) parsing" OFF)
diff --git a/cmake/tpls/DyninstPeParse.cmake b/cmake/tpls/DyninstPeParse.cmake
new file mode 100644
index 0000000000..9132b8e132
--- /dev/null
+++ b/cmake/tpls/DyninstPeParse.cmake
@@ -0,0 +1,47 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
+if(PeParse_ROOT_DIR)
+  set(pe-parse_ROOT "${PeParse_ROOT_DIR}")
+  mark_as_advanced(pe-parse_ROOT)
+  set(_find_path_args NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
+endif()
+
+if(DYNINST_ENABLE_FILEFORMAT_PE)
+  find_package(pe-parse REQUIRED ${_find_path_args})
+else()
+  set(pe-parse_FOUND FALSE)
+endif()
+
+if(NOT TARGET Dyninst::PeParse)
+  add_library(Dyninst::PeParse INTERFACE IMPORTED)
+  if(pe-parse_FOUND)
+    target_include_directories(
+      Dyninst::PeParse SYSTEM
+      INTERFACE $<TARGET_PROPERTY:pe-parse::pe-parse,INTERFACE_INCLUDE_DIRECTORIES>)
+    target_link_libraries(Dyninst::PeParse INTERFACE pe-parse::pe-parse)
+  endif()
+endif()
diff --git a/dyninstAPI/CMakeLists.txt b/dyninstAPI/CMakeLists.txt
index b169286704..4a46567e94 100644
--- a/dyninstAPI/CMakeLists.txt
+++ b/dyninstAPI/CMakeLists.txt
@@ -296,7 +296,7 @@ dyninst_library(
   DYNINST_DEPS common instructionAPI stackwalk pcontrol patchAPI parseAPI symtabAPI
   DYNINST_INTERNAL_DEPS dynDwarf dynElf
   PUBLIC_DEPS Dyninst::Boost_headers
-  PRIVATE_DEPS Threads::Threads
+  PRIVATE_DEPS Threads::Threads Dyninst::PeParse
 )
 # cmake-format: on
 
diff --git a/symtabAPI/CMakeLists.txt b/symtabAPI/CMakeLists.txt
index c49cfc1c9a..cbb3ac837d 100644
--- a/symtabAPI/CMakeLists.txt
+++ b/symtabAPI/CMakeLists.txt
@@ -73,8 +73,7 @@ if(DYNINST_OS_UNIX)
     src/LinkMap.C
     src/emitElf.C
     src/emitElfStatic.C
-    src/dwarfWalker.C
-    src/Object-pe.C)
+    src/dwarfWalker.C)
 
   if(DYNINST_HOST_ARCH_X86_64 OR DYNINST_HOST_ARCH_I386)
     list(APPEND _sources src/emitElfStatic-x86.C src/relocationEntry-elf-x86.C)
@@ -85,6 +84,12 @@ if(DYNINST_OS_UNIX)
   else()
     list(APPEND _sources src/emitElfStatic-stub.C src/relocationEntry-stub.C)
   endif()
+
+  if(pe-parse_FOUND)
+    list(APPEND _sources src/Object-pe.C)
+  else()
+    list(APPEND _sources src/Object-pe-stub.C)
+  endif()
 elseif(DYNINST_OS_Windows)
   list(APPEND _sources src/Object-nt.C src/emitWin.C src/relocationEntry-stub.C)
 endif()
@@ -98,7 +103,7 @@ dyninst_library(
   DEFINES SYMTAB_LIB
   DYNINST_DEPS common
   DYNINST_INTERNAL_DEPS dynDwarf dynElf
-  PRIVATE_DEPS OpenMP::OpenMP_CXX Dyninst::Boost Dyninst::ElfUtils
+  PRIVATE_DEPS OpenMP::OpenMP_CXX Dyninst::Boost Dyninst::ElfUtils Dyninst::PeParse
 )
 # cmake-format: on
 
diff --git a/symtabAPI/src/Object-pe-stub.C b/symtabAPI/src/Object-pe-stub.C
new file mode 100644
index 0000000000..4642e1fa82
--- /dev/null
+++ b/symtabAPI/src/Object-pe-stub.C
@@ -0,0 +1,114 @@
+/*
+ * See the dyninst/COPYRIGHT file for copyright information.
+ * 
+ * We provide the Paradyn Tools (below described as "Paradyn")
+ * on an AS IS basis, and do not warrant its validity or performance.
+ * We reserve the right to update, modify, or discontinue this
+ * software at any time.  We shall have no obligation to supply such
+ * updates or modifications or any other form of support to you.
+ * 
+ * By your use of Paradyn, you understand and agree that we (or any
+ * other person or entity with proprietary rights in Paradyn) are
+ * under no obligation to provide either maintenance services,
+ * update services, notices of latent defects, or correction of
+ * defects for Paradyn.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "symtabAPI/src/Object-pe.h"
+
+using namespace Dyninst;
+using namespace Dyninst::SymtabAPI;
+
+void ObjectPE::log_error(const std::string &msg)
+{
+    if (err_func_) {
+        std::string full_msg = msg + std::string("\n");
+        err_func_(full_msg.c_str());
+    }
+}
+
+ObjectPE::ObjectPE(MappedFile *mf_,
+                   bool,
+                   void (*err_func)(const char *),
+                   bool,
+                   Symtab *st) :
+    Object(mf_, err_func, st)
+{
+    file_format_ = FileFormat::PE;
+
+    has_error = true;
+    log_error("Parsing of PE files is not implemented yet");
+}
+
+ObjectPE::~ObjectPE()
+{
+}
+
+Offset ObjectPE::getPreferedBase() const
+{
+    return preferedBase_;
+}
+
+void ObjectPE::getDependencies(std::vector<std::string> &deps)
+{
+    deps.clear();
+    /* TODO */
+}
+
+Offset ObjectPE::getBaseAddress() const
+{
+    return 0;
+}
+
+Offset ObjectPE::getEntryAddress() const
+{
+    return entryAddress_;
+}
+
+Offset ObjectPE::getLoadAddress() const
+{
+    return loadAddress_;
+}
+
+bool ObjectPE::isOnlyExecutable() const
+{
+    return is_aout_;
+}
+
+bool ObjectPE::isExecutable() const
+{
+    /**
+     * This method is used only to check whether we should consider an entry point
+     * as a function hint. A DLL may have a valid entry point that is not covered by
+     * a symbol, so we want to always consider it as a hint.
+     */
+    return true;
+}
+
+bool ObjectPE::isOnlySharedLibrary() const
+{
+    return !is_aout_;
+}
+
+bool ObjectPE::isSharedLibrary() const
+{
+    return !is_aout_;
+}
+
+Dyninst::Architecture ObjectPE::getArch() const
+{
+    return Arch_none;
+}
diff --git a/symtabAPI/src/Object-pe.C b/symtabAPI/src/Object-pe.C
index 0609f39d0f..84ae63618e 100644
--- a/symtabAPI/src/Object-pe.C
+++ b/symtabAPI/src/Object-pe.C
@@ -28,32 +28,134 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 #include "symtabAPI/src/Object-pe.h"
+#include <pe-parse/parse.h>
 
 using namespace Dyninst;
 using namespace Dyninst::SymtabAPI;
 
+Region::perm_t ObjectPE::getRegionPerms(std::uint32_t flags)
+{
+    if((flags & peparse::IMAGE_SCN_MEM_EXECUTE) && (flags & peparse::IMAGE_SCN_MEM_WRITE))
+        return Region::RP_RWX;
+    else if(flags & peparse::IMAGE_SCN_MEM_EXECUTE)
+        return Region::RP_RX;
+    else if(flags & peparse::IMAGE_SCN_MEM_WRITE)
+        return Region::RP_RW;
+    else
+        return Region::RP_R;
+}
+
+Region::RegionType ObjectPE::getRegionType(std::uint32_t flags){
+    /**
+     * It's possible for a PE sections to contain an actually executed code
+     * without being labeled as a code section. So we check for both permission and section type
+     */
+    std::uint32_t exec_flags = ( peparse::IMAGE_SCN_CNT_CODE | peparse::IMAGE_SCN_MEM_EXECUTE );
+    std::uint32_t data_flags = ( peparse::IMAGE_SCN_CNT_INITIALIZED_DATA | peparse::IMAGE_SCN_CNT_UNINITIALIZED_DATA );
+    if((flags & exec_flags) && (flags & peparse::IMAGE_SCN_CNT_INITIALIZED_DATA))
+        return Region::RT_TEXTDATA;
+    else if(flags & exec_flags)
+        return Region::RT_TEXT;
+    else if (flags & data_flags)
+        return Region::RT_DATA;
+    else
+        return Region::RT_OTHER;
+}
+
 void ObjectPE::log_error(const std::string &msg)
 {
-	if (err_func_) {
-		std::string full_msg = msg + std::string("\n");
-		err_func_(full_msg.c_str());
-	}
+    if (err_func_) {
+        std::string full_msg = msg + std::string(": ") + peparse::GetPEErrString() + std::string("\n");
+        err_func_(full_msg.c_str());
+    }
 }
 
 ObjectPE::ObjectPE(MappedFile *mf_,
-                   bool defensive,
+                   bool,
                    void (*err_func)(const char *),
-                   bool alloc_syms,
+                   bool,
                    Symtab *st) :
     Object(mf_, err_func, st)
 {
-    (void)defensive;
-    (void)alloc_syms;
+    parsed_object_ = peparse::ParsePEFromPointer((unsigned char *)mf_->base_addr(), mf_->size());
+    if (!parsed_object_) {
+        has_error = true;
+        log_error("Error while parsing PE file");
+        return;
+    }
 
     file_format_ = FileFormat::PE;
 
-    has_error = true;
-    log_error("Parsing of PE files is not implemented yet");
+    parse_object();
+}
+
+ObjectPE::~ObjectPE()
+{
+    if (parsed_object_) {
+        peparse::DestructParsedPE((peparse::parsed_pe *)parsed_object_);
+    }
+}
+
+void ObjectPE::parse_object()
+{
+    peparse::parsed_pe *parsed_pe_ = (peparse::parsed_pe *)parsed_object_;
+    no_of_sections_ = parsed_pe_->peHeader.nt.FileHeader.NumberOfSections;
+    no_of_symbols_ = parsed_pe_->peHeader.nt.FileHeader.NumberOfSymbols;
+    addressWidth_nbytes = getArchWidth();
+
+    peparse::VA entry_addr = -1;
+    if (!peparse::GetEntryPoint(parsed_pe_, entry_addr)) {
+        log_error("Error while getting entry point");
+    }
+    entryAddress_ = entry_addr;
+    if (parsed_pe_->peHeader.nt.FileHeader.Machine == peparse::IMAGE_FILE_MACHINE_AMD64) {
+        preferedBase_ = loadAddress_ = parsed_pe_->peHeader.nt.OptionalHeader64.ImageBase;
+    } else {
+        preferedBase_ = loadAddress_ = parsed_pe_->peHeader.nt.OptionalHeader.ImageBase;
+    }
+
+    if (parsed_pe_->peHeader.nt.FileHeader.Characteristics & peparse::IMAGE_FILE_DLL) {
+        is_aout_ = false;
+    } else {
+        is_aout_ = true;
+    }
+
+    /* Iterate over section */
+    peparse::iterSec section_iterator =
+        [](void *N, const peparse::VA &, const std::string &name,
+           const peparse::image_section_header &header,
+           const peparse::bounded_buffer *) -> int {
+            ObjectPE *obj = static_cast<ObjectPE*>(N);
+            int reg_num = obj->regions_.size();
+            char *sec_ptr = (char *)obj->mf->base_addr() + header.PointerToRawData;
+            Offset sec_off = header.PointerToRawData;
+            Offset sec_len = std::max(header.SizeOfRawData, header.Misc.VirtualSize);
+            if (header.Characteristics & peparse::IMAGE_SCN_CNT_CODE) {
+                obj->code_ptr_ = sec_ptr;
+                obj->code_off_ = sec_off;
+                obj->code_len_ = sec_len;
+            } else if (header.Characteristics & peparse::IMAGE_SCN_CNT_INITIALIZED_DATA) {
+                obj->data_ptr_ = sec_ptr;
+                obj->data_off_ = sec_off;
+                obj->data_len_ = sec_len;
+            }
+            obj->regions_.push_back(new Region(reg_num, name, header.PointerToRawData,
+                                    header.SizeOfRawData, obj->loadAddress_ + header.VirtualAddress, sec_len,
+                                    sec_ptr, getRegionPerms(header.Characteristics),
+                                    getRegionType(header.Characteristics)));
+            return 0;
+        };
+    peparse::IterSec(parsed_pe_, section_iterator, this);
+
+    struct region_less {
+        bool operator()(const Region *r1, const Region *r2)
+        {
+            return r1->memOff_ < r2->memOff_;
+        }
+    } cmp;
+    std::sort(regions_.begin(), regions_.end(), cmp);
+
+    no_of_symbols_ = 0;
 }
 
 Offset ObjectPE::getPreferedBase() const
@@ -109,5 +211,58 @@ bool ObjectPE::isSharedLibrary() const
 
 Dyninst::Architecture ObjectPE::getArch() const
 {
-    return Arch_none;
+    if (!parsed_object_) return Arch_none;
+    switch (((peparse::parsed_pe *)parsed_object_)->peHeader.nt.FileHeader.Machine) {
+        case peparse::IMAGE_FILE_MACHINE_AMD64:
+        case peparse::IMAGE_FILE_MACHINE_EBC:
+            return Arch_x86_64;
+        case peparse::IMAGE_FILE_MACHINE_I386:
+            return Arch_x86;
+        case peparse::IMAGE_FILE_MACHINE_ARM:
+        case peparse::IMAGE_FILE_MACHINE_ARMNT:
+            return Arch_aarch32;
+        case peparse::IMAGE_FILE_MACHINE_ARM64:
+            return Arch_aarch64;
+        case peparse::IMAGE_FILE_MACHINE_POWERPC:
+        case peparse::IMAGE_FILE_MACHINE_POWERPCFP:
+            return Arch_ppc64;
+        case peparse::IMAGE_FILE_MACHINE_UNKNOWN:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA64:
+        case peparse::IMAGE_FILE_MACHINE_CEE:
+        case peparse::IMAGE_FILE_MACHINE_CEF:
+        case peparse::IMAGE_FILE_MACHINE_IA64:
+        case peparse::IMAGE_FILE_MACHINE_M32R:
+        case peparse::IMAGE_FILE_MACHINE_MIPS16:
+        case peparse::IMAGE_FILE_MACHINE_MIPSFPU:
+        case peparse::IMAGE_FILE_MACHINE_MIPSFPU16:
+        case peparse::IMAGE_FILE_MACHINE_R10000:
+        case peparse::IMAGE_FILE_MACHINE_RISCV32:
+        case peparse::IMAGE_FILE_MACHINE_RISCV64:
+        case peparse::IMAGE_FILE_MACHINE_RISCV128:
+        case peparse::IMAGE_FILE_MACHINE_SH3:
+        case peparse::IMAGE_FILE_MACHINE_SH3DSP:
+        case peparse::IMAGE_FILE_MACHINE_SH4:
+        case peparse::IMAGE_FILE_MACHINE_SH5:
+        case peparse::IMAGE_FILE_MACHINE_THUMB:
+        case peparse::IMAGE_FILE_MACHINE_TRICORE:
+        case peparse::IMAGE_FILE_MACHINE_WCEMIPSV2:
+        default:
+            return Arch_none;
+    }
+}
+
+int ObjectPE::getArchWidth() const
+{
+    switch (getArch()) {
+        case Arch_x86:
+        case Arch_aarch32:
+            return 4;
+        case Arch_x86_64:
+        case Arch_aarch64:
+            return 8;
+        case Arch_none:
+        default:
+            return -1;
+    }
 }
diff --git a/symtabAPI/src/Object-pe.h b/symtabAPI/src/Object-pe.h
index 9ccb12699e..2dfe23ec5a 100644
--- a/symtabAPI/src/Object-pe.h
+++ b/symtabAPI/src/Object-pe.h
@@ -41,6 +41,7 @@ class ObjectPE : public Object {
 
 public:
     ObjectPE(MappedFile *, bool, void(*)(const char *) = log_msg, bool = true, Symtab * = NULL);
+    ~ObjectPE();
 
     Offset getPreferedBase() const override;
     void getDependencies(std::vector<std::string> &deps) override;
@@ -63,6 +64,16 @@ class ObjectPE : public Object {
     const ObjectPE& operator=(const ObjectPE &);
 
     void log_error(const std::string &);
+    void parse_object();
+
+    static Region::perm_t getRegionPerms(std::uint32_t flags);
+    static Region::RegionType getRegionType(std::uint32_t flags);
+
+    int getArchWidth() const;
+
+    // Internal pe-parse structure. The pointer is kept as "void"
+    // to support compilation both with and without pr-parse library.
+    void *parsed_object_;
 
     Offset entryAddress_;
 


Reviews:
user: login: hainest
body: There are two big questions:

1. How do we test this

We don't currently have a way to reliably generate PE files on any of our test machines. GithubCI does have Windows runners, but Dyninst won't compile under Windows. We also (still) don't have a good solution for collecting binaries for doing regression tests.

2. What do we name the feature flag?

I propose `DYNINST_ENABLE_WINDOWSPE`.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T05:01:16Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T10:10:14Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T10:16:16Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T19:46:48Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T10:30:20Z
user: login: hainest
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T17:59:36Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T11:25:56Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T11:27:00Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T11:39:00Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T16:09:40Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T16:23:37Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T11:53:45Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T14:52:18Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T15:03:09Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T10:24:13Z
user: login: batuzovk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T10:29:22Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T17:05:19Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T17:06:08Z
user: login: hainest
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T19:47:07Z

Review comments:
diff_hunk: @@ -0,0 +1,45 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+# See Modules/FindPeParse.cmake for details
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+    return()
+endif()
+
+if(PeParse_ROOT_DIR)
+    set(PeParse_NO_SYSTEM_PATHS ON)
+    mark_as_advanced(PeParse_NO_SYSTEM_PATHS)
+    set(PeParse_ROOT "${PeParse_ROOT_DIR}")
+    mark_as_advanced(PeParse_ROOT)
+endif()
+
+find_package(PeParse REQUIRED)
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: We should double-check that the PeParse CMake package uses the variables set here.
created_at: 2025-03-11T04:48:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,45 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+# See Modules/FindPeParse.cmake for details
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: Once we decide on a name for the feature flags, we should add a check here to create dummy targets (see tpls/DyninstOpenMP.cmake for an example).
created_at: 2025-03-11T04:50:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,6 +64,16 @@ class ObjectPE : public Object {
     const ObjectPE& operator=(const ObjectPE &);
 
     void log_error(const std::string &);
+    void parse_object();
+
+    static Region::perm_t getRegionPerms(std::uint32_t flags);
+    static Region::RegionType getRegionType(std::uint32_t flags);
+
+    int getArchWidth() const;
+
+    // Internal pe-parse structure. The pointer is kept as "void"
+    // to support compilation both with and without pr-parse library.
+    void *parsed_object_;
path: symtabAPI/src/Object-pe.h
user: login: hainest
body: minor note: I have a strong dislike of using opaque pointers in C++, but it works well enough for now.
created_at: 2025-03-11T04:58:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,6 +64,16 @@ class ObjectPE : public Object {
     const ObjectPE& operator=(const ObjectPE &);
 
     void log_error(const std::string &);
+    void parse_object();
+
+    static Region::perm_t getRegionPerms(std::uint32_t flags);
+    static Region::RegionType getRegionType(std::uint32_t flags);
+
+    int getArchWidth() const;
+
+    // Internal pe-parse structure. The pointer is kept as "void"
+    // to support compilation both with and without pr-parse library.
+    void *parsed_object_;
path: symtabAPI/src/Object-pe.h
user: login: batuzovk
body: I don't like them either, but here it helps to avoid `#ifdef` completely, so I think it is worth it.
created_at: 2025-03-11T10:10:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,45 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+# See Modules/FindPeParse.cmake for details
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+    return()
+endif()
+
+if(PeParse_ROOT_DIR)
+    set(PeParse_NO_SYSTEM_PATHS ON)
+    mark_as_advanced(PeParse_NO_SYSTEM_PATHS)
+    set(PeParse_ROOT "${PeParse_ROOT_DIR}")
+    mark_as_advanced(PeParse_ROOT)
+endif()
+
+find_package(PeParse REQUIRED)
path: cmake/tpls/DyninstPeParse.cmake
user: login: batuzovk
body: I'm not sure I understood your concern (though I'm not too familiar with cmake). I think those variables are used in `cmake/Modules/FindPeParse.cmake`, and yes, they are handled there. PeParse itself provides the most basic config file, and that's it.
created_at: 2025-03-11T10:16:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,45 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+# See Modules/FindPeParse.cmake for details
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+    return()
+endif()
+
+if(PeParse_ROOT_DIR)
+    set(PeParse_NO_SYSTEM_PATHS ON)
+    mark_as_advanced(PeParse_NO_SYSTEM_PATHS)
+    set(PeParse_ROOT "${PeParse_ROOT_DIR}")
+    mark_as_advanced(PeParse_ROOT)
+endif()
+
+find_package(PeParse REQUIRED)
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: Oh, sorry. I somehow left off the other critical part of this comment: since pe-parse exports a CMake package, we don't need to make a `FindPeParse`. When I built pe-parse, the exported module doesn't use `NO_SYSTEM_PATHS`. We have a similar setup for DyninstTBB.
created_at: 2025-03-11T19:46:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,45 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+# See Modules/FindPeParse.cmake for details
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+    return()
+endif()
+
+if(PeParse_ROOT_DIR)
+    set(PeParse_NO_SYSTEM_PATHS ON)
+    mark_as_advanced(PeParse_NO_SYSTEM_PATHS)
+    set(PeParse_ROOT "${PeParse_ROOT_DIR}")
+    mark_as_advanced(PeParse_ROOT)
+endif()
+
+find_package(PeParse REQUIRED)
path: cmake/tpls/DyninstPeParse.cmake
user: login: batuzovk
body: Ah, I see. I've rewritten CMake part of the patch. I think it doesn't need feature flag any more. If there is pe-parse library - dyninst is compiled with PE support, if there isn't - it is compiled without it. I've remove `FindPeParse` module completely.
created_at: 2025-03-12T10:30:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -5,6 +5,7 @@ list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_LIST_DIR}/Modules"
 
 include(DyninstBoost)
 include(DyninstTBB)
+include(DyninstPeParse)
path: cmake/DyninstConfig.cmake.in
user: login: hainest
body: Like ELF, we want to keep these types of dependencies internal to Dyninst. I wish we didn't export any of our dependencies, but that would be a huge breaking change.

```suggestion

```


created_at: 2025-03-13T17:12:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,41 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
+if(PeParse_ROOT_DIR)
+  set(pe-parse_ROOT "${PeParse_ROOT_DIR}")
+  mark_as_advanced(pe-parse_ROOT)
+  set(_find_path_args NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
+endif()
+
+find_package(pe-parse CONFIG)
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: ```suggestion
find_package(pe-parse REQUIRED ${_find_path_args})
```
created_at: 2025-03-13T17:15:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,41 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
+if(PeParse_ROOT_DIR)
+  set(pe-parse_ROOT "${PeParse_ROOT_DIR}")
+  mark_as_advanced(pe-parse_ROOT)
+  set(_find_path_args NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
+endif()
+
+find_package(pe-parse CONFIG)
+
+if(NOT TARGET Dyninst::PeParse)
+  add_library(Dyninst::PeParse INTERFACE IMPORTED)
+  if(pe-parse_FOUND)
+    target_include_directories(Dyninst::PeParse INTERFACE "${PeParse_INCLUDE_DIRS}")
+    target_link_libraries(Dyninst::PeParse INTERFACE pe-parse::pe-parse)
+  endif()
+endif()
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: pe-parse only exports a single target with `INTERFACE_INCLUDE_DIRECTORIES` set. This is similar to how [TBB](https://github.com/dyninst/dyninst/blob/master/cmake/tpls/DyninstTBB.cmake#L41) works. 

```cmake
if(NOT TARGET Dyninst::PeParse)
  add_library(Dyninst::PeParse INTERFACE IMPORTED)
  target_link_libraries(Dyninst::PeParse INTERFACE pe-parse::pe-parse)
  target_include_directories(
    Dyninst::PeParse SYSTEM
    INTERFACE
      $<TARGET_PROPERTY:pe-parse::pe-parse,INTERFACE_INCLUDE_DIRECTORIES>)
endif()
```
created_at: 2025-03-13T17:28:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -98,7 +103,7 @@ dyninst_library(
   DEFINES SYMTAB_LIB
   DYNINST_DEPS common
   DYNINST_INTERNAL_DEPS dynDwarf dynElf
-  PRIVATE_DEPS OpenMP::OpenMP_CXX Dyninst::Boost Dyninst::ElfUtils
+  PRIVATE_DEPS OpenMP::OpenMP_CXX Dyninst::Boost Dyninst::ElfUtils Dyninst::PeParse
path: symtabAPI/CMakeLists.txt
user: login: hainest
body: @kupsch @bbiiggppiigg @wxrdnx @rochauha

This raises a point that I was thinking about the other day: should we extract PE parsing into its own library like dynElf? I think it might be useful. We can delve further into that once we get more PE parsing working rather than holding up @batuzovk's work . Ultimately, we don't want to do file parsing directly in the `Object` classes since that would create tight coupling between dyninstAPI and the third-party library (this is how we ended up with bad ROSE support in dataflowAPI).
created_at: 2025-03-13T17:38:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,117 @@
+/*
+ * See the dyninst/COPYRIGHT file for copyright information.
+ * 
+ * We provide the Paradyn Tools (below described as "Paradyn")
+ * on an AS IS basis, and do not warrant its validity or performance.
+ * We reserve the right to update, modify, or discontinue this
+ * software at any time.  We shall have no obligation to supply such
+ * updates or modifications or any other form of support to you.
+ * 
+ * By your use of Paradyn, you understand and agree that we (or any
+ * other person or entity with proprietary rights in Paradyn) are
+ * under no obligation to provide either maintenance services,
+ * update services, notices of latent defects, or correction of
+ * defects for Paradyn.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "symtabAPI/src/Object-pe.h"
+
+using namespace Dyninst;
+using namespace Dyninst::SymtabAPI;
+
+void ObjectPE::log_error(const std::string &msg)
+{
+    if (err_func_) {
+        std::string full_msg = msg + std::string("\n");
+        err_func_(full_msg.c_str());
+    }
+}
+
+ObjectPE::ObjectPE(MappedFile *mf_,
+                   bool defensive,
+                   void (*err_func)(const char *),
+                   bool alloc_syms,
+                   Symtab *st) :
+    Object(mf_, err_func, st)
+{
+    (void)defensive;
path: symtabAPI/src/Object-pe-stub.C
user: login: hainest
body: Let's remove the names for the unused parameters rather than just casting them to avoid compiler warnings.
created_at: 2025-03-13T17:39:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -49,11 +79,87 @@ ObjectPE::ObjectPE(MappedFile *mf_,
 {
     (void)defensive;
     (void)alloc_syms;
+    parsed_object_ = peparse::ParsePEFromPointer((unsigned char *)mf_->base_addr(), mf_->size());
+    if (!parsed_object_) {
+        has_error = true;
+        log_error("Error while parsing PE file");
+        return;
+    }
 
     file_format_ = FileFormat::PE;
 
-    has_error = true;
-    log_error("Parsing of PE files is not implemented yet");
+    parse_object();
+}
+
+ObjectPE::~ObjectPE()
+{
+    if (parsed_object_) {
+        peparse::DestructParsedPE((peparse::parsed_pe *)parsed_object_);
+    }
+}
+
+void ObjectPE::parse_object()
+{
+    peparse::parsed_pe *parsed_pe_ = (peparse::parsed_pe *)parsed_object_;
+    no_of_sections_ = parsed_pe_->peHeader.nt.FileHeader.NumberOfSections;
+    no_of_symbols_ = parsed_pe_->peHeader.nt.FileHeader.NumberOfSymbols;
+    addressWidth_nbytes = getArchWidth();
+
+    peparse::VA entry_addr = -1;
+    if (!peparse::GetEntryPoint(parsed_pe_, entry_addr)) {
+        log_error("Error while getting entry point");
+    }
+    entryAddress_ = entry_addr;
+    if (parsed_pe_->peHeader.nt.FileHeader.Machine == peparse::IMAGE_FILE_MACHINE_AMD64) {
+        preferedBase_ = loadAddress_ = parsed_pe_->peHeader.nt.OptionalHeader64.ImageBase;
+    } else {
+        preferedBase_ = loadAddress_ = parsed_pe_->peHeader.nt.OptionalHeader.ImageBase;
+    }
+
+    if (parsed_pe_->peHeader.nt.FileHeader.Characteristics & peparse::IMAGE_FILE_DLL) {
+        is_aout_ = false;
+    } else {
+        is_aout_ = true;
+    }
+
+    /* Iterate over section */
+    peparse::iterSec section_iterator =
+        [](void *N, const peparse::VA &, const std::string &name,
+           const peparse::image_section_header &header,
+           const peparse::bounded_buffer *) -> int {
+            ObjectPE *obj = static_cast<ObjectPE*>(N);
+            int reg_num = obj->regions_.size();
+            char *sec_ptr = (char *)obj->mf->base_addr() + header.PointerToRawData;
+            Offset sec_off = header.PointerToRawData;
+            Offset sec_len = std::max(header.SizeOfRawData, header.Misc.VirtualSize);
+            if (header.Characteristics & peparse::IMAGE_SCN_CNT_CODE) {
+                obj->code_ptr_ = sec_ptr;
+                obj->code_off_ = sec_off;
+                obj->code_len_ = sec_len;
+            } else if (header.Characteristics & peparse::IMAGE_SCN_CNT_INITIALIZED_DATA) {
+                obj->data_ptr_ = sec_ptr;
+                obj->data_off_ = sec_off;
+                obj->data_len_ = sec_len;
+            } else {
+                /* ... */
path: symtabAPI/src/Object-pe.C
user: login: hainest
body: Are there useful, but unhandled, cases here? If not, let's remove this.
created_at: 2025-03-13T17:53:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,5 +215,57 @@ bool ObjectPE::isSharedLibrary() const
 
 Dyninst::Architecture ObjectPE::getArch() const
 {
-    return Arch_none;
+    if (!parsed_object_) return Arch_none;
+    switch (((peparse::parsed_pe *)parsed_object_)->peHeader.nt.FileHeader.Machine) {
+        case peparse::IMAGE_FILE_MACHINE_AMD64:
+            return Arch_x86_64;
+        case peparse::IMAGE_FILE_MACHINE_I386:
+            return Arch_x86;
+        case peparse::IMAGE_FILE_MACHINE_ARM:
+        case peparse::IMAGE_FILE_MACHINE_ARMNT:
+            return Arch_aarch32;
+        case peparse::IMAGE_FILE_MACHINE_ARM64:
+            return Arch_aarch64;
+        case peparse::IMAGE_FILE_MACHINE_UNKNOWN:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA64:
+        case peparse::IMAGE_FILE_MACHINE_CEE:
+        case peparse::IMAGE_FILE_MACHINE_CEF:
+        case peparse::IMAGE_FILE_MACHINE_EBC:
+        case peparse::IMAGE_FILE_MACHINE_IA64:
path: symtabAPI/src/Object-pe.C
user: login: hainest
body: Itanium is dead (yay!), but there might be some win32 binaries still targeting it. Let's treat it as `Arch_x86_64` for now.
created_at: 2025-03-13T17:56:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,5 +215,57 @@ bool ObjectPE::isSharedLibrary() const
 
 Dyninst::Architecture ObjectPE::getArch() const
 {
-    return Arch_none;
+    if (!parsed_object_) return Arch_none;
+    switch (((peparse::parsed_pe *)parsed_object_)->peHeader.nt.FileHeader.Machine) {
+        case peparse::IMAGE_FILE_MACHINE_AMD64:
+            return Arch_x86_64;
+        case peparse::IMAGE_FILE_MACHINE_I386:
+            return Arch_x86;
+        case peparse::IMAGE_FILE_MACHINE_ARM:
+        case peparse::IMAGE_FILE_MACHINE_ARMNT:
+            return Arch_aarch32;
+        case peparse::IMAGE_FILE_MACHINE_ARM64:
+            return Arch_aarch64;
+        case peparse::IMAGE_FILE_MACHINE_UNKNOWN:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA64:
+        case peparse::IMAGE_FILE_MACHINE_CEE:
+        case peparse::IMAGE_FILE_MACHINE_CEF:
+        case peparse::IMAGE_FILE_MACHINE_EBC:
+        case peparse::IMAGE_FILE_MACHINE_IA64:
+        case peparse::IMAGE_FILE_MACHINE_M32R:
+        case peparse::IMAGE_FILE_MACHINE_MIPS16:
+        case peparse::IMAGE_FILE_MACHINE_MIPSFPU:
+        case peparse::IMAGE_FILE_MACHINE_MIPSFPU16:
+        case peparse::IMAGE_FILE_MACHINE_POWERPC:
path: symtabAPI/src/Object-pe.C
user: login: hainest
body: The pe-parse comment says this is little-endian ppc, so let's mark it as `Arch_ppc64`.
created_at: 2025-03-13T17:57:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,5 +215,57 @@ bool ObjectPE::isSharedLibrary() const
 
 Dyninst::Architecture ObjectPE::getArch() const
 {
-    return Arch_none;
+    if (!parsed_object_) return Arch_none;
+    switch (((peparse::parsed_pe *)parsed_object_)->peHeader.nt.FileHeader.Machine) {
+        case peparse::IMAGE_FILE_MACHINE_AMD64:
+            return Arch_x86_64;
+        case peparse::IMAGE_FILE_MACHINE_I386:
+            return Arch_x86;
+        case peparse::IMAGE_FILE_MACHINE_ARM:
+        case peparse::IMAGE_FILE_MACHINE_ARMNT:
+            return Arch_aarch32;
+        case peparse::IMAGE_FILE_MACHINE_ARM64:
+            return Arch_aarch64;
+        case peparse::IMAGE_FILE_MACHINE_UNKNOWN:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA64:
+        case peparse::IMAGE_FILE_MACHINE_CEE:
+        case peparse::IMAGE_FILE_MACHINE_CEF:
+        case peparse::IMAGE_FILE_MACHINE_EBC:
+        case peparse::IMAGE_FILE_MACHINE_IA64:
+        case peparse::IMAGE_FILE_MACHINE_M32R:
+        case peparse::IMAGE_FILE_MACHINE_MIPS16:
+        case peparse::IMAGE_FILE_MACHINE_MIPSFPU:
+        case peparse::IMAGE_FILE_MACHINE_MIPSFPU16:
+        case peparse::IMAGE_FILE_MACHINE_POWERPC:
+        case peparse::IMAGE_FILE_MACHINE_POWERPCFP:
path: symtabAPI/src/Object-pe.C
user: login: hainest
body: Same here.
created_at: 2025-03-13T17:58:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,5 +215,57 @@ bool ObjectPE::isSharedLibrary() const
 
 Dyninst::Architecture ObjectPE::getArch() const
 {
-    return Arch_none;
+    if (!parsed_object_) return Arch_none;
+    switch (((peparse::parsed_pe *)parsed_object_)->peHeader.nt.FileHeader.Machine) {
+        case peparse::IMAGE_FILE_MACHINE_AMD64:
+            return Arch_x86_64;
+        case peparse::IMAGE_FILE_MACHINE_I386:
+            return Arch_x86;
+        case peparse::IMAGE_FILE_MACHINE_ARM:
+        case peparse::IMAGE_FILE_MACHINE_ARMNT:
+            return Arch_aarch32;
+        case peparse::IMAGE_FILE_MACHINE_ARM64:
+            return Arch_aarch64;
+        case peparse::IMAGE_FILE_MACHINE_UNKNOWN:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA64:
+        case peparse::IMAGE_FILE_MACHINE_CEE:
+        case peparse::IMAGE_FILE_MACHINE_CEF:
+        case peparse::IMAGE_FILE_MACHINE_EBC:
+        case peparse::IMAGE_FILE_MACHINE_IA64:
path: symtabAPI/src/Object-pe.C
user: login: batuzovk
body: I'm confused about this suggestion. We don't have a decoder for it. We won't be able to parse those binaries any way. Reporting them as x86_64 will only confuse everyone.

`IMAGE_FILE_MACHINE_EBC` on the other hand is worth reporting as x86_64. It is EFI byte code apparently, and it seem to be just a 64-bit PE file. I've updated that part.
created_at: 2025-03-14T11:25:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,41 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
+if(PeParse_ROOT_DIR)
+  set(pe-parse_ROOT "${PeParse_ROOT_DIR}")
+  mark_as_advanced(pe-parse_ROOT)
+  set(_find_path_args NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
+endif()
+
+find_package(pe-parse CONFIG)
path: cmake/tpls/DyninstPeParse.cmake
user: login: batuzovk
body: I still think it should be `CONFIG` and not `REQUIRED` in this particular case. I've add `_find_path_args` part.
created_at: 2025-03-14T11:27:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,41 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
+if(PeParse_ROOT_DIR)
+  set(pe-parse_ROOT "${PeParse_ROOT_DIR}")
+  mark_as_advanced(pe-parse_ROOT)
+  set(_find_path_args NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
+endif()
+
+find_package(pe-parse CONFIG)
+
+if(NOT TARGET Dyninst::PeParse)
+  add_library(Dyninst::PeParse INTERFACE IMPORTED)
+  if(pe-parse_FOUND)
+    target_include_directories(Dyninst::PeParse INTERFACE "${PeParse_INCLUDE_DIRS}")
+    target_link_libraries(Dyninst::PeParse INTERFACE pe-parse::pe-parse)
+  endif()
+endif()
path: cmake/tpls/DyninstPeParse.cmake
user: login: batuzovk
body: Updated with slight adjustments to make pe-parse an optional dependency.
created_at: 2025-03-14T11:38:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,45 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+# See Modules/FindPeParse.cmake for details
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+    return()
+endif()
+
+if(PeParse_ROOT_DIR)
+    set(PeParse_NO_SYSTEM_PATHS ON)
+    mark_as_advanced(PeParse_NO_SYSTEM_PATHS)
+    set(PeParse_ROOT "${PeParse_ROOT_DIR}")
+    mark_as_advanced(PeParse_ROOT)
+endif()
+
+find_package(PeParse REQUIRED)
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: >I think it doesn't need feature flag any more. If there is pe-parse library - dyninst is compiled with PE support, if there isn't - it is compiled without it.

Historically, Dyninst has taken this approach. However, I'd like to move toward a model where functionality is explicitly enabled and will fail if it can't (e.g., a library can't be found). Otherwise, we end up in a situation where pe-parse silently fails to be found, then a user is left confused as to why Dyninst won't parse a PE file.
created_at: 2025-03-14T16:09:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,5 +215,57 @@ bool ObjectPE::isSharedLibrary() const
 
 Dyninst::Architecture ObjectPE::getArch() const
 {
-    return Arch_none;
+    if (!parsed_object_) return Arch_none;
+    switch (((peparse::parsed_pe *)parsed_object_)->peHeader.nt.FileHeader.Machine) {
+        case peparse::IMAGE_FILE_MACHINE_AMD64:
+            return Arch_x86_64;
+        case peparse::IMAGE_FILE_MACHINE_I386:
+            return Arch_x86;
+        case peparse::IMAGE_FILE_MACHINE_ARM:
+        case peparse::IMAGE_FILE_MACHINE_ARMNT:
+            return Arch_aarch32;
+        case peparse::IMAGE_FILE_MACHINE_ARM64:
+            return Arch_aarch64;
+        case peparse::IMAGE_FILE_MACHINE_UNKNOWN:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA:
+        case peparse::IMAGE_FILE_MACHINE_ALPHA64:
+        case peparse::IMAGE_FILE_MACHINE_CEE:
+        case peparse::IMAGE_FILE_MACHINE_CEF:
+        case peparse::IMAGE_FILE_MACHINE_EBC:
+        case peparse::IMAGE_FILE_MACHINE_IA64:
path: symtabAPI/src/Object-pe.C
user: login: hainest
body: Oh, you are quite right. I though the current x86 parser would handle Itanium, but I guess it doesn't. Thanks for the careful check.
created_at: 2025-03-14T16:23:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,45 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+# See Modules/FindPeParse.cmake for details
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+    return()
+endif()
+
+if(PeParse_ROOT_DIR)
+    set(PeParse_NO_SYSTEM_PATHS ON)
+    mark_as_advanced(PeParse_NO_SYSTEM_PATHS)
+    set(PeParse_ROOT "${PeParse_ROOT_DIR}")
+    mark_as_advanced(PeParse_ROOT)
+endif()
+
+find_package(PeParse REQUIRED)
path: cmake/tpls/DyninstPeParse.cmake
user: login: batuzovk
body: I've updated to use explicit feature enable. The flag is currently called `DYNINST_ENABLE_FILEFORMAT_PE`, but I can change it if this name sounds too long.
created_at: 2025-03-17T11:53:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,45 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+# See Modules/FindPeParse.cmake for details
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+    return()
+endif()
+
+if(PeParse_ROOT_DIR)
+    set(PeParse_NO_SYSTEM_PATHS ON)
+    mark_as_advanced(PeParse_NO_SYSTEM_PATHS)
+    set(PeParse_ROOT "${PeParse_ROOT_DIR}")
+    mark_as_advanced(PeParse_ROOT)
+endif()
+
+find_package(PeParse REQUIRED)
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: That works for me. Could you add an entry in DyninstOptions.cmake?
created_at: 2025-03-18T14:52:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: One item I forgot about is setting the minimum pe-parse version required, constraining `find_package` with it, and adding an entry in `docker/dependencies.versions`. I'm fine just making the minimum version the latest release which looks like v2.1.1. Does this work need your PRs (203/304)?
created_at: 2025-03-18T15:02:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
path: cmake/tpls/DyninstPeParse.cmake
user: login: batuzovk
body: Pe-parse doesn't export version information, unfortunately. So, version is considered as `unknown`, and any minimum requirements will fail the build.

```
  The following configuration files were considered but not accepted:

    /home/batuzovk/inst/lib/cmake/pe-parse/pe-parse-config.cmake, version: unknown
```
created_at: 2025-03-19T10:24:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
path: cmake/tpls/DyninstPeParse.cmake
user: login: batuzovk
body: As for PRs in pe-parse repository:

* 203 is very desirable, but not mandatory. Without it we won't be able to process COFF symbol table correctly. The files will be parsed, but some function might be missing, and we might get "junk" function somewhere else. COFF symbols aren't used much in `.exe`/`.dll` files. MS documentation even suggests they shouldn't be used at all, but they can be present nevertheless. It will be mandatory for supporting `.obj` files if we get there at some point.
* 204 does not affect current patch series. It will become mandatory should we decide to support `.obj` files.
created_at: 2025-03-19T10:29:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: > Pe-parse doesn't export version information, unfortunately. So, version is considered as `unknown`, and any minimum requirements will fail the build.
> 
> ```
>   The following configuration files were considered but not accepted:
> 
>     /home/batuzovk/inst/lib/cmake/pe-parse/pe-parse-config.cmake, version: unknown
> ```

Well, that's neat. I might submit a PR to them for that since they maintain a VERSION file.
created_at: 2025-03-19T17:05:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+#======================================================================================
+# PeParse.cmake
+#
+# Configure PeParse for Dyninst
+#
+#   ----------------------------------------
+#
+# Directly exports the following CMake variables
+#
+# PeParse_ROOT_DIR       - Computed base directory the of PeParse installation
+#
+# NOTE:
+# The exported PeParse_ROOT_DIR can be different from the value provided by the user
+# in the case that it is determined to build PeParse from source. In such a case,
+# PeParse_ROOT_DIR will contain the directory of the from-source installation.
+#
+#======================================================================================
+
+include_guard(GLOBAL)
+
+cmake_policy(SET CMP0074 NEW) # Use <Package>_ROOT
+
+if(NOT UNIX)
+  return()
+endif()
+
path: cmake/tpls/DyninstPeParse.cmake
user: login: hainest
body: > As for PRs in pe-parse repository:
> 
>     * 203 is very desirable, but not mandatory. Without it we won't be able to process COFF symbol table correctly. The files will be parsed, but some function might be missing, and we might get "junk" function somewhere else. COFF symbols aren't used much in `.exe`/`.dll` files. MS documentation even suggests they shouldn't be used at all, but they can be present nevertheless. It will be mandatory for supporting `.obj` files if we get there at some point.
> 
>     * 204 does not affect current patch series. It will become mandatory should we decide to support `.obj` files.

We barely support .o files, so those will likely not be needed for quite some time.
created_at: 2025-03-19T17:06:08Z
author_association: CONTRIBUTOR

PR comments:
user: login: batuzovk
created_at: 2025-03-10T12:44:47Z
body: Things that should work:

* Compilation without `libpe-parse.so`. Dyninst will be able to parse ELF files, but an attempt to parse a PE file will result in errors.
* Parsing of some EXE files, e.g. `CMD.EXE` with disassemble from the example repository.
author_association: CONTRIBUTOR
user: login: batuzovk
created_at: 2025-03-11T10:09:36Z
body: > We don't currently have a way to reliably generate PE files on any of our test machines. GithubCI does have Windows runners, but Dyninst won't compile under Windows.

I'm not familiar with specifics of github workflows, but if we talk in general...

There are cross-compilers. At least Debian and Ubuntu have `gcc-mingw-w64` and `gcc-mingw-w32` in their repositories. It can compile `.c`/`.cpp` files to `.exe` suitable for some basic testing.

Alternatively you can download pre-built binaries of some ope-source projects. For example, `cmake` and `llvm` have archives with Windows binaries.

> I propose DYNINST_ENABLE_WINDOWSPE

I'd prefer `DYNINST_ENABLE_FILEFORMAT_PE`. Because technically this is not about Windows, it is about PE file format. But I'm ok with `DYNINST_ENABLE_WINDOWSPE` as well.
author_association: CONTRIBUTOR
user: login: batuzovk
created_at: 2025-03-13T14:39:22Z
body: The latest push was a rebase, no actual changes.
author_association: CONTRIBUTOR
user: login: hainest
created_at: 2025-03-19T19:46:52Z
body: This looks good to me. Thanks so much for being patient as we figure out new features like this.
author_association: CONTRIBUTOR