From b60b908fa2cdf7b1504c0aaa5ba3b5b936db786f Mon Sep 17 00:00:00 2001
From: Alessandro Gario <alessandrogario@users.noreply.github.com>
Date: Sat, 25 Nov 2017 22:01:53 +0100
Subject: [PATCH] Install public headers, add Arch package, build pepy under
 Travis and more (#57)

* CMake: Added install directives

* CMake: Added support for find_package(pe-parse)

* Fixed a compilation error on Linux

* CMake: Fix cmake module installation

* Added ArchLinux package

* Finished implementing the address converted example

* peaddrconv: Print the image base address.

* peaddrconv: Enable more warnings.

* Update travis to also build the examples

* Fix a compilation warning on Ubuntu 14.04

* Travis: Add macOS support.

* Better output for Travis, fix a compilation error on macOS.

* Travis: Do not build examples under macOS.

* Travis: Also compile the python module (pepy)

* Readme: Add a section to show how to use the library.

* Windows: Fix a compilation error, enable /analyze (see details).

The nt-headers.h include file is defining several constexpr values
using reserved (by windows.h) names.

These names (i.e.: IMAGE_FILE_MACHINE_UNKNOWN) are in fact macros
defined inside the Windows header files, and causes the preprocessor
to break definitions such as the following one:

constexpr std::uint16_t IMAGE_FILE_MACHINE_UNKNOWN = 0x0;

The fix (for now) consists in including the nt-headers.h file before
windows.h, but we should probably choose whether to use different
names or avoid defining those values (since they are inside the
system header anyway).
---
 .gitignore                                    |   1 +
 .travis.yml                                   |  15 +-
 CMakeLists.txt                                |  11 +-
 README.md                                     |  13 +
 cmake/compilation_flags.cmake                 |   6 +-
 {dump-prog => dump-pe}/CMakeLists.txt         |   6 +-
 dump-prog/dump.cpp => dump-pe/main.cpp        | 202 +++++-----
 examples/peaddrconv/CMakeLists.txt            |  39 ++
 examples/peaddrconv/README.md                 |   1 +
 examples/peaddrconv/main.cpp                  | 374 ++++++++++++++++++
 packages/archlinux/.gitignore                 |   5 +
 packages/archlinux/PKGBUILD                   |  18 +
 parser-library/CMakeLists.txt                 |   6 -
 pe-parser-library/CMakeLists.txt              |  20 +
 pe-parser-library/cmake/peparse-config.cmake  |   5 +
 .../include/parser-library}/nt-headers.h      |   1 +
 .../include/parser-library}/parse.h           |   0
 .../include/parser-library}/to_string.h       |   0
 .../src}/buffer.cpp                           |   9 +-
 .../src}/parse.cpp                            |   8 +-
 python/pepy.cpp                               |  69 ++--
 python/setup.py                               |   8 +-
 travis.sh                                     | 239 +++++++++++
 23 files changed, 897 insertions(+), 159 deletions(-)
 rename {dump-prog => dump-pe}/CMakeLists.txt (60%)
 rename dump-prog/dump.cpp => dump-pe/main.cpp (63%)
 create mode 100644 examples/peaddrconv/CMakeLists.txt
 create mode 100644 examples/peaddrconv/README.md
 create mode 100644 examples/peaddrconv/main.cpp
 create mode 100644 packages/archlinux/.gitignore
 create mode 100644 packages/archlinux/PKGBUILD
 delete mode 100644 parser-library/CMakeLists.txt
 create mode 100644 pe-parser-library/CMakeLists.txt
 create mode 100644 pe-parser-library/cmake/peparse-config.cmake
 rename {parser-library => pe-parser-library/include/parser-library}/nt-headers.h (99%)
 rename {parser-library => pe-parser-library/include/parser-library}/parse.h (100%)
 rename {parser-library => pe-parser-library/include/parser-library}/to_string.h (100%)
 rename {parser-library => pe-parser-library/src}/buffer.cpp (97%)
 rename {parser-library => pe-parser-library/src}/parse.cpp (99%)
 create mode 100755 travis.sh

diff --git a/.gitignore b/.gitignore
index 53b0920..1d6caeb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,5 @@ cmake-build-debug
 cmake-build-release
 build
 .vscode
+examples_build
 
diff --git a/.travis.yml b/.travis.yml
index 4d78ce8..ff11b28 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,27 +1,32 @@
 language: cpp
+
 os:
 - linux
 - osx
+
 compiler:
 - clang
 - gcc
+
 before_install:
-- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq ; fi
-- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update ; fi
-script: cmake . && make
+- ./travis.sh "$TRAVIS_OS_NAME" initialize
+script:
+- ./travis.sh "$TRAVIS_OS_NAME" build
+
 matrix:
   exclude:
   - compiler: gcc
     os: osx
+
 env:
   global:
   - secure: "O+BGqz4ugoVIJbQTh0dJjKRrsSVzkCYSe0WpRzEWK3l8Mw7hqX300g81TxRwTzN2zfUsROMzaeGaXWfGzYakgW59K1WIioaczxtv2MzzUQTbqzJPa+qQoP9bk/b2wJ5jcOL965/rudRju4UiIwuIgzDAMN3nAfIEJgV/2zANLIg="
+
 addons:
   coverity_scan:
     project:
       name: "trailofbits/pe-parse"
       description: "Principled, lightweight C/C++ PE parser"
     notification_email: dan@trailofbits.com
-    build_command_prepend: "cmake ."
-    build_command: "make"
+    build_command: "./travis.sh linux build"
     branch_pattern: master
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fc7f267..0298add 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,10 @@
 cmake_minimum_required(VERSION 3.1)
 project(pe-parse)
 
+if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+  set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Default install directory" FORCE)
+endif ()
+
 set(CMAKE_VERBOSE_MAKEFILE True)
 if (NOT CMAKE_BUILD_TYPE)
   set(CMAKE_BUILD_TYPE "RelWithDebInfo")
@@ -9,5 +13,8 @@ endif ()
 include(cmake/compilation_flags.cmake)
 list(APPEND GLOBAL_CXXFLAGS ${DEFAULT_CXX_FLAGS})
 
-add_subdirectory(parser-library)
-add_subdirectory(dump-prog)
+add_subdirectory(pe-parser-library)
+add_subdirectory(dump-pe)
+
+message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
+message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}")
diff --git a/README.md b/README.md
index e1fd6ac..ed45bc6 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,19 @@ pe-parse is built using `cmake` and has no major dependencies.
 2. `cmake .`
 3. `make`
 
+Using the library
+=======
+Once the library is installed, linking to it is easy! Add the following lines in your CMake project:
+
+```
+find_package(peparse REQUIRED)
+
+target_link_libraries(your_target_name ${PEPARSE_LIBRARIES})
+target_include_directories(your_target_name PRIVATE ${PEPARSE_INCLUDE_DIRS})
+```
+
+You can see a full example in the examples/peaddrconv folder.
+
 Authors
 =======
 pe-parse was designed and implemented by Andrew Ruef (andrew@trailofbits.com), with significant contributions from [Wesley Shields](https://github.com/wxsBSD).
diff --git a/cmake/compilation_flags.cmake b/cmake/compilation_flags.cmake
index d22807c..ef193ca 100644
--- a/cmake/compilation_flags.cmake
+++ b/cmake/compilation_flags.cmake
@@ -1,5 +1,5 @@
 if (WIN32)
-  list(APPEND DEFAULT_CXX_FLAGS /W4)
+  list(APPEND DEFAULT_CXX_FLAGS /W4 /analyze)
 
   if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
     list(APPEND DEFAULT_CXX_FLAGS /Zi)
@@ -24,13 +24,13 @@ else ()
   )
 
   if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
-    list(APPEND PROJECT_CXXFLAGS -gdwarf-2 -g3)
+  list(APPEND DEFAULT_CXX_FLAGS -gdwarf-2 -g3)
   endif ()
 
   if (CMAKE_BUILD_TYPE STREQUAL "Debug")
     message(STATUS "This is a debug build; enabling -Weverything...")
 
-    list(APPEND PROJECT_CXXFLAGS
+    list(APPEND DEFAULT_CXX_FLAGS
       -Weverything -Wno-c++98-compat -Wno-missing-prototypes
       -Wno-missing-variable-declarations -Wno-global-constructors
       -Wno-exit-time-destructors -Wno-padded -Wno-error
diff --git a/dump-prog/CMakeLists.txt b/dump-pe/CMakeLists.txt
similarity index 60%
rename from dump-prog/CMakeLists.txt
rename to dump-pe/CMakeLists.txt
index 5ecd67b..17bc535 100644
--- a/dump-prog/CMakeLists.txt
+++ b/dump-pe/CMakeLists.txt
@@ -1,6 +1,8 @@
 cmake_minimum_required(VERSION 3.1)
-project(dump-prog)
+project(dump-pe)
 
-add_executable(${PROJECT_NAME} dump.cpp)
+add_executable(${PROJECT_NAME} main.cpp)
 target_link_libraries(${PROJECT_NAME} PRIVATE pe-parser-library)
 target_compile_options(${PROJECT_NAME} PRIVATE ${GLOBAL_CXXFLAGS})
+
+install(TARGETS ${PROJECT_NAME} DESTINATION "bin")
diff --git a/dump-prog/dump.cpp b/dump-pe/main.cpp
similarity index 63%
rename from dump-prog/dump.cpp
rename to dump-pe/main.cpp
index 5897fd5..e6982fd 100644
--- a/dump-prog/dump.cpp
+++ b/dump-pe/main.cpp
@@ -22,9 +22,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */
 
-#include "parse.h"
 #include <iostream>
 #include <sstream>
+#include <cstring>
+
+#include <parser-library/parse.h>
 
 using namespace std;
 using namespace peparse;
@@ -273,112 +275,118 @@ int printSecs(void *N,
             << endl;
 
 int main(int argc, char *argv[]) {
-  if (argc == 2) {
-    parsed_pe *p = ParsePEFromFile(argv[1]);
+  if (argc != 2 || (argc == 2 && std::strcmp(argv[1], "--help") == 0)) {
+    std::cout << "dump-pe utility from Trail of Bits\n";
+    std::cout << "Repository: https://github.com/trailofbits/pe-parse\n\n";
+    std::cout << "Usage:\n\tdump-pe /path/to/executable.exe\n";
+    return 1;
+  }
 
-    if (p != NULL) {
-      // print out some things
-      DUMP_FIELD(Signature);
-      DUMP_FIELD(FileHeader.Machine);
-      DUMP_FIELD(FileHeader.NumberOfSections);
-      DUMP_DEC_FIELD(FileHeader.TimeDateStamp);
-      DUMP_FIELD(FileHeader.PointerToSymbolTable);
-      DUMP_DEC_FIELD(FileHeader.NumberOfSymbols);
-      DUMP_FIELD(FileHeader.SizeOfOptionalHeader);
-      DUMP_FIELD(FileHeader.Characteristics);
-      if (p->peHeader.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) {
-        DUMP_FIELD(OptionalHeader.Magic);
-        DUMP_DEC_FIELD(OptionalHeader.MajorLinkerVersion);
-        DUMP_DEC_FIELD(OptionalHeader.MinorLinkerVersion);
-        DUMP_FIELD(OptionalHeader.SizeOfCode);
-        DUMP_FIELD(OptionalHeader.SizeOfInitializedData);
-        DUMP_FIELD(OptionalHeader.SizeOfUninitializedData);
-        DUMP_FIELD(OptionalHeader.AddressOfEntryPoint);
-        DUMP_FIELD(OptionalHeader.BaseOfCode);
-        DUMP_FIELD(OptionalHeader.BaseOfData);
-        DUMP_FIELD(OptionalHeader.ImageBase);
-        DUMP_FIELD(OptionalHeader.SectionAlignment);
-        DUMP_FIELD(OptionalHeader.FileAlignment);
-        DUMP_DEC_FIELD(OptionalHeader.MajorOperatingSystemVersion);
-        DUMP_DEC_FIELD(OptionalHeader.MinorOperatingSystemVersion);
-        DUMP_DEC_FIELD(OptionalHeader.Win32VersionValue);
-        DUMP_FIELD(OptionalHeader.SizeOfImage);
-        DUMP_FIELD(OptionalHeader.SizeOfHeaders);
-        DUMP_FIELD(OptionalHeader.CheckSum);
-        DUMP_FIELD(OptionalHeader.Subsystem);
-        DUMP_FIELD(OptionalHeader.DllCharacteristics);
-        DUMP_FIELD(OptionalHeader.SizeOfStackReserve);
-        DUMP_FIELD(OptionalHeader.SizeOfStackCommit);
-        DUMP_FIELD(OptionalHeader.SizeOfHeapReserve);
-        DUMP_FIELD(OptionalHeader.SizeOfHeapCommit);
-        DUMP_FIELD(OptionalHeader.LoaderFlags);
-        DUMP_DEC_FIELD(OptionalHeader.NumberOfRvaAndSizes);
-      } else {
-        DUMP_FIELD(OptionalHeader64.Magic);
-        DUMP_DEC_FIELD(OptionalHeader64.MajorLinkerVersion);
-        DUMP_DEC_FIELD(OptionalHeader64.MinorLinkerVersion);
-        DUMP_FIELD(OptionalHeader64.SizeOfCode);
-        DUMP_FIELD(OptionalHeader64.SizeOfInitializedData);
-        DUMP_FIELD(OptionalHeader64.SizeOfUninitializedData);
-        DUMP_FIELD(OptionalHeader64.AddressOfEntryPoint);
-        DUMP_FIELD(OptionalHeader64.BaseOfCode);
-        DUMP_FIELD(OptionalHeader64.ImageBase);
-        DUMP_FIELD(OptionalHeader64.SectionAlignment);
-        DUMP_FIELD(OptionalHeader64.FileAlignment);
-        DUMP_DEC_FIELD(OptionalHeader64.MajorOperatingSystemVersion);
-        DUMP_DEC_FIELD(OptionalHeader64.MinorOperatingSystemVersion);
-        DUMP_DEC_FIELD(OptionalHeader64.Win32VersionValue);
-        DUMP_FIELD(OptionalHeader64.SizeOfImage);
-        DUMP_FIELD(OptionalHeader64.SizeOfHeaders);
-        DUMP_FIELD(OptionalHeader64.CheckSum);
-        DUMP_FIELD(OptionalHeader64.Subsystem);
-        DUMP_FIELD(OptionalHeader64.DllCharacteristics);
-        DUMP_FIELD(OptionalHeader64.SizeOfStackReserve);
-        DUMP_FIELD(OptionalHeader64.SizeOfStackCommit);
-        DUMP_FIELD(OptionalHeader64.SizeOfHeapReserve);
-        DUMP_FIELD(OptionalHeader64.SizeOfHeapCommit);
-        DUMP_FIELD(OptionalHeader64.LoaderFlags);
-        DUMP_DEC_FIELD(OptionalHeader64.NumberOfRvaAndSizes);
-      }
+  parsed_pe *p = ParsePEFromFile(argv[1]);
+
+  if (p != NULL) {
+    // print out some things
+    DUMP_FIELD(Signature);
+    DUMP_FIELD(FileHeader.Machine);
+    DUMP_FIELD(FileHeader.NumberOfSections);
+    DUMP_DEC_FIELD(FileHeader.TimeDateStamp);
+    DUMP_FIELD(FileHeader.PointerToSymbolTable);
+    DUMP_DEC_FIELD(FileHeader.NumberOfSymbols);
+    DUMP_FIELD(FileHeader.SizeOfOptionalHeader);
+    DUMP_FIELD(FileHeader.Characteristics);
+    if (p->peHeader.nt.OptionalMagic == NT_OPTIONAL_32_MAGIC) {
+      DUMP_FIELD(OptionalHeader.Magic);
+      DUMP_DEC_FIELD(OptionalHeader.MajorLinkerVersion);
+      DUMP_DEC_FIELD(OptionalHeader.MinorLinkerVersion);
+      DUMP_FIELD(OptionalHeader.SizeOfCode);
+      DUMP_FIELD(OptionalHeader.SizeOfInitializedData);
+      DUMP_FIELD(OptionalHeader.SizeOfUninitializedData);
+      DUMP_FIELD(OptionalHeader.AddressOfEntryPoint);
+      DUMP_FIELD(OptionalHeader.BaseOfCode);
+      DUMP_FIELD(OptionalHeader.BaseOfData);
+      DUMP_FIELD(OptionalHeader.ImageBase);
+      DUMP_FIELD(OptionalHeader.SectionAlignment);
+      DUMP_FIELD(OptionalHeader.FileAlignment);
+      DUMP_DEC_FIELD(OptionalHeader.MajorOperatingSystemVersion);
+      DUMP_DEC_FIELD(OptionalHeader.MinorOperatingSystemVersion);
+      DUMP_DEC_FIELD(OptionalHeader.Win32VersionValue);
+      DUMP_FIELD(OptionalHeader.SizeOfImage);
+      DUMP_FIELD(OptionalHeader.SizeOfHeaders);
+      DUMP_FIELD(OptionalHeader.CheckSum);
+      DUMP_FIELD(OptionalHeader.Subsystem);
+      DUMP_FIELD(OptionalHeader.DllCharacteristics);
+      DUMP_FIELD(OptionalHeader.SizeOfStackReserve);
+      DUMP_FIELD(OptionalHeader.SizeOfStackCommit);
+      DUMP_FIELD(OptionalHeader.SizeOfHeapReserve);
+      DUMP_FIELD(OptionalHeader.SizeOfHeapCommit);
+      DUMP_FIELD(OptionalHeader.LoaderFlags);
+      DUMP_DEC_FIELD(OptionalHeader.NumberOfRvaAndSizes);
+    } else {
+      DUMP_FIELD(OptionalHeader64.Magic);
+      DUMP_DEC_FIELD(OptionalHeader64.MajorLinkerVersion);
+      DUMP_DEC_FIELD(OptionalHeader64.MinorLinkerVersion);
+      DUMP_FIELD(OptionalHeader64.SizeOfCode);
+      DUMP_FIELD(OptionalHeader64.SizeOfInitializedData);
+      DUMP_FIELD(OptionalHeader64.SizeOfUninitializedData);
+      DUMP_FIELD(OptionalHeader64.AddressOfEntryPoint);
+      DUMP_FIELD(OptionalHeader64.BaseOfCode);
+      DUMP_FIELD(OptionalHeader64.ImageBase);
+      DUMP_FIELD(OptionalHeader64.SectionAlignment);
+      DUMP_FIELD(OptionalHeader64.FileAlignment);
+      DUMP_DEC_FIELD(OptionalHeader64.MajorOperatingSystemVersion);
+      DUMP_DEC_FIELD(OptionalHeader64.MinorOperatingSystemVersion);
+      DUMP_DEC_FIELD(OptionalHeader64.Win32VersionValue);
+      DUMP_FIELD(OptionalHeader64.SizeOfImage);
+      DUMP_FIELD(OptionalHeader64.SizeOfHeaders);
+      DUMP_FIELD(OptionalHeader64.CheckSum);
+      DUMP_FIELD(OptionalHeader64.Subsystem);
+      DUMP_FIELD(OptionalHeader64.DllCharacteristics);
+      DUMP_FIELD(OptionalHeader64.SizeOfStackReserve);
+      DUMP_FIELD(OptionalHeader64.SizeOfStackCommit);
+      DUMP_FIELD(OptionalHeader64.SizeOfHeapReserve);
+      DUMP_FIELD(OptionalHeader64.SizeOfHeapCommit);
+      DUMP_FIELD(OptionalHeader64.LoaderFlags);
+      DUMP_DEC_FIELD(OptionalHeader64.NumberOfRvaAndSizes);
+    }
 
 #undef DUMP_FIELD
 #undef DUMP_DEC_FIELD
 
-      std::cout << "Imports: " << endl;
-      IterImpVAString(p, printImports, NULL);
-      std::cout << "Relocations: " << endl;
-      IterRelocs(p, printRelocs, NULL);
-      std::cout << "Symbols (symbol table): " << endl;
-      IterSymbols(p, printSymbols, NULL);
-      std::cout << "Sections: " << endl;
-      IterSec(p, printSecs, NULL);
-      std::cout << "Exports: " << endl;
-      IterExpVA(p, printExps, NULL);
+    std::cout << "Imports: " << endl;
+    IterImpVAString(p, printImports, NULL);
+    std::cout << "Relocations: " << endl;
+    IterRelocs(p, printRelocs, NULL);
+    std::cout << "Symbols (symbol table): " << endl;
+    IterSymbols(p, printSymbols, NULL);
+    std::cout << "Sections: " << endl;
+    IterSec(p, printSecs, NULL);
+    std::cout << "Exports: " << endl;
+    IterExpVA(p, printExps, NULL);
 
-      // read the first 8 bytes from the entry point and print them
-      VA entryPoint;
-      if (GetEntryPoint(p, entryPoint)) {
-        std::cout << "First 8 bytes from entry point (0x";
+    // read the first 8 bytes from the entry point and print them
+    VA entryPoint;
+    if (GetEntryPoint(p, entryPoint)) {
+      std::cout << "First 8 bytes from entry point (0x";
 
-        std::cout << to_string<VA>(entryPoint, hex);
-        std::cout << "):" << endl;
-        for (std::size_t i = 0; i < 8; i++) {
-          ::uint8_t b;
-          ReadByteAtVA(p, i + entryPoint, b);
-          std::cout << " 0x" << to_string<uint32_t>(b, hex);
-        }
-
-        std::cout << endl;
+      std::cout << to_string<VA>(entryPoint, hex);
+      std::cout << "):" << endl;
+      for (std::size_t i = 0; i < 8; i++) {
+        ::uint8_t b;
+        ReadByteAtVA(p, i + entryPoint, b);
+        std::cout << " 0x" << to_string<uint32_t>(b, hex);
       }
 
-      std::cout << "Resources: " << endl;
-      IterRsrc(p, printRsrc, NULL);
-      DestructParsedPE(p);
-    } else {
-      std::cout << "Error: " << GetPEErr() << " (" << GetPEErrString() << ")"
-                << endl;
-      std::cout << "Location: " << GetPEErrLoc() << endl;
+      std::cout << endl;
     }
+
+    std::cout << "Resources: " << endl;
+    IterRsrc(p, printRsrc, NULL);
+    DestructParsedPE(p);
+  } else {
+    std::cout << "Error: " << GetPEErr() << " (" << GetPEErrString() << ")"
+              << endl;
+    std::cout << "Location: " << GetPEErrLoc() << endl;
   }
+
   return 0;
 }
diff --git a/examples/peaddrconv/CMakeLists.txt b/examples/peaddrconv/CMakeLists.txt
new file mode 100644
index 0000000..3577f5c
--- /dev/null
+++ b/examples/peaddrconv/CMakeLists.txt
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.1)
+project(peaddrconv)
+
+if (WIN32)
+  list(APPEND PEADDRCONV_CXXFLAGS /W4 /analyze)
+
+  if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
+    list(APPEND PEADDRCONV_CXXFLAGS /Zi)
+  endif ()
+
+  if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
+    list(APPEND PEADDRCONV_CXXFLAGS /WX)
+  endif ()
+
+else ()
+  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_EXTENSIONS OFF)
+
+  list(APPEND PEADDRCONV_CXXFLAGS
+    -pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization
+    -Wformat=2 -Winit-self -Wlong-long -Wmissing-declarations -Wmissing-include-dirs -Wcomment
+    -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion
+    -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wunused -Wuninitialized
+    -Wno-missing-declarations
+  )
+
+  if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
+    list(APPEND PEADDRCONV_CXXFLAGS -gdwarf-2 -g3)
+  endif ()
+endif ()
+
+find_package(peparse REQUIRED)
+
+add_executable(${PROJECT_NAME} main.cpp)
+target_link_libraries(${PROJECT_NAME} ${PEPARSE_LIBRARIES})
+target_include_directories(${PROJECT_NAME} PRIVATE ${PEPARSE_INCLUDE_DIRS})
+target_compile_options(${PROJECT_NAME} PRIVATE ${PEADDRCONV_CXXFLAGS})
+
+install(TARGETS ${PROJECT_NAME} DESTINATION "bin")
diff --git a/examples/peaddrconv/README.md b/examples/peaddrconv/README.md
new file mode 100644
index 0000000..479d5e0
--- /dev/null
+++ b/examples/peaddrconv/README.md
@@ -0,0 +1 @@
+Note that you have to install the library before you can build this example!
diff --git a/examples/peaddrconv/main.cpp b/examples/peaddrconv/main.cpp
new file mode 100644
index 0000000..daae916
--- /dev/null
+++ b/examples/peaddrconv/main.cpp
@@ -0,0 +1,374 @@
+#include <iostream>
+#include <limits>
+#include <memory>
+
+#include <climits>
+#include <cstring>
+
+#include <parser-library/parse.h>
+
+using ParsedPeRef =
+    std::unique_ptr<peparse::parsed_pe, void (*)(peparse::parsed_pe *)>;
+
+ParsedPeRef openExecutable(const std::string &path) noexcept {
+  // The factory function does not throw exceptions!
+  ParsedPeRef obj(peparse::ParsePEFromFile(path.data()),
+                  peparse::DestructParsedPE);
+  if (!obj) {
+    return ParsedPeRef(nullptr, peparse::DestructParsedPE);
+  }
+
+  return obj;
+}
+
+enum class AddressType {
+  PhysicalOffset,
+  RelativeVirtualAddress,
+  VirtualAddress
+};
+
+bool convertAddress(ParsedPeRef &pe,
+                    std::uintptr_t address,
+                    AddressType source_type,
+                    AddressType destination_type,
+                    std::uintptr_t &result) noexcept {
+  if (source_type == destination_type) {
+    result = address;
+    return true;
+  }
+
+  std::uint64_t image_base_address = 0U;
+  if (pe->peHeader.nt.FileHeader.Machine == peparse::IMAGE_FILE_MACHINE_AMD64) {
+    image_base_address = pe->peHeader.nt.OptionalHeader64.ImageBase;
+  } else {
+    image_base_address = pe->peHeader.nt.OptionalHeader.ImageBase;
+  }
+
+  struct SectionAddressLimits final {
+    std::uintptr_t lowest_rva;
+    std::uintptr_t lowest_offset;
+
+    std::uintptr_t highest_rva;
+    std::uintptr_t highest_offset;
+  };
+
+  auto L_getSectionAddressLimits = [](void *N,
+                                      peparse::VA secBase,
+                                      std::string &secName,
+                                      peparse::image_section_header s,
+                                      peparse::bounded_buffer *data) -> int {
+    static_cast<void>(secBase);
+    static_cast<void>(secName);
+    static_cast<void>(data);
+
+    SectionAddressLimits *section_address_limits =
+        static_cast<SectionAddressLimits *>(N);
+
+    section_address_limits->lowest_rva =
+        std::min(section_address_limits->lowest_rva,
+                 static_cast<std::uintptr_t>(s.VirtualAddress));
+
+    section_address_limits->lowest_offset =
+        std::min(section_address_limits->lowest_offset,
+                 static_cast<std::uintptr_t>(s.PointerToRawData));
+
+    std::uintptr_t sectionSize;
+    if (s.SizeOfRawData != 0) {
+      sectionSize = s.SizeOfRawData;
+    } else {
+      sectionSize = s.Misc.VirtualSize;
+    }
+
+    section_address_limits->highest_rva =
+        std::max(section_address_limits->highest_rva,
+                 static_cast<std::uintptr_t>(s.VirtualAddress + sectionSize));
+
+    section_address_limits->highest_offset =
+        std::max(section_address_limits->highest_offset,
+                 static_cast<std::uintptr_t>(s.PointerToRawData + sectionSize));
+
+    return 0;
+  };
+
+  SectionAddressLimits section_address_limits = {
+      std::numeric_limits<std::uintptr_t>::max(),
+      std::numeric_limits<std::uintptr_t>::max(),
+      std::numeric_limits<std::uintptr_t>::min(),
+      std::numeric_limits<std::uintptr_t>::min()};
+
+  IterSec(pe.get(), L_getSectionAddressLimits, &section_address_limits);
+
+  switch (source_type) {
+    case AddressType::PhysicalOffset: {
+      if (address >= section_address_limits.highest_offset) {
+        return false;
+      }
+
+      if (destination_type == AddressType::RelativeVirtualAddress) {
+        struct CallbackData final {
+          bool found;
+          std::uintptr_t address;
+          std::uintptr_t result;
+        };
+
+        auto L_inspectSection = [](void *N,
+                                   peparse::VA secBase,
+                                   std::string &secName,
+                                   peparse::image_section_header s,
+                                   peparse::bounded_buffer *data) -> int {
+          static_cast<void>(secBase);
+          static_cast<void>(secName);
+          static_cast<void>(data);
+
+          std::uintptr_t sectionBaseOffset = s.PointerToRawData;
+
+          std::uintptr_t sectionEndOffset = sectionBaseOffset;
+          if (s.SizeOfRawData != 0) {
+            sectionEndOffset += s.SizeOfRawData;
+          } else {
+            sectionEndOffset += s.Misc.VirtualSize;
+          }
+
+          auto callback_data = static_cast<CallbackData *>(N);
+          if (callback_data->address >= sectionBaseOffset &&
+              callback_data->address < sectionEndOffset) {
+            callback_data->result = s.VirtualAddress + (callback_data->address -
+                                                        s.PointerToRawData);
+
+            callback_data->found = true;
+            return 1;
+          }
+
+          return 0;
+        };
+
+        CallbackData callback_data = {false, address, 0U};
+        IterSec(pe.get(), L_inspectSection, &callback_data);
+
+        if (!callback_data.found) {
+          return false;
+        }
+
+        result = callback_data.result;
+        return true;
+
+      } else if (destination_type == AddressType::VirtualAddress) {
+        std::uintptr_t rva = 0U;
+        if (!convertAddress(pe,
+                            address,
+                            source_type,
+                            AddressType::RelativeVirtualAddress,
+                            rva)) {
+          return false;
+        }
+
+        result = image_base_address + rva;
+        return true;
+      }
+
+      return false;
+    }
+
+    case AddressType::RelativeVirtualAddress: {
+      if (address < section_address_limits.lowest_rva) {
+        result = address;
+        return true;
+      } else if (address >= section_address_limits.highest_rva) {
+        return false;
+      }
+
+      if (destination_type == AddressType::PhysicalOffset) {
+        struct CallbackData final {
+          bool found;
+          std::uintptr_t address;
+          std::uintptr_t result;
+        };
+
+        auto L_inspectSection = [](void *N,
+                                   peparse::VA secBase,
+                                   std::string &secName,
+                                   peparse::image_section_header s,
+                                   peparse::bounded_buffer *data) -> int {
+          static_cast<void>(secBase);
+          static_cast<void>(secName);
+          static_cast<void>(data);
+
+          std::uintptr_t sectionBaseAddress = s.VirtualAddress;
+          std::uintptr_t sectionEndAddress =
+              sectionBaseAddress + s.Misc.VirtualSize;
+
+          auto callback_data = static_cast<CallbackData *>(N);
+          if (callback_data->address >= sectionBaseAddress &&
+              callback_data->address < sectionEndAddress) {
+            callback_data->result =
+                s.PointerToRawData +
+                (callback_data->address - sectionBaseAddress);
+
+            callback_data->found = true;
+            return 1;
+          }
+
+          return 0;
+        };
+
+        CallbackData callback_data = {false, address, 0U};
+        IterSec(pe.get(), L_inspectSection, &callback_data);
+
+        if (!callback_data.found) {
+          return false;
+        }
+
+        result = callback_data.result;
+        return true;
+
+      } else if (destination_type == AddressType::VirtualAddress) {
+        result = image_base_address + address;
+        return true;
+      }
+
+      return false;
+    }
+
+    case AddressType::VirtualAddress: {
+      if (address < image_base_address) {
+        return false;
+      }
+
+      std::uintptr_t rva = address - image_base_address;
+      return convertAddress(pe,
+                            rva,
+                            AddressType::RelativeVirtualAddress,
+                            destination_type,
+                            result);
+    }
+
+    default: { return false; }
+  }
+}
+
+int main(int argc, char *argv[]) {
+  if (argc != 3 || (argc == 2 && std::strcmp(argv[1], "--help") == 0)) {
+    std::cout << "PE address conversion utility from Trail of Bits\n";
+    std::cout << "Usage:\n\tpeaddrconv /path/to/executable.exe address\n\n";
+    std::cout << "The <address> parameter is always interpreted as hex!\n";
+
+    return 1;
+  }
+
+  const char *executable_path = argv[1];
+  const char *address_as_string = argv[2];
+
+  char *last_parsed_char = nullptr;
+  errno = 0;
+
+  auto address = std::strtoull(address_as_string, &last_parsed_char, 16);
+  if (address == 0U && *last_parsed_char != 0) {
+    std::cout << "Invalid address specified\n";
+    return 1;
+
+  } else if (address == ULLONG_MAX && errno == ERANGE) {
+    std::cout << "The address you specified is too big\n";
+    return 1;
+  }
+
+  auto pe = openExecutable(executable_path);
+  if (!pe) {
+    std::cout << "Failed to open the executable\n\n";
+
+    std::cout << "Error: " << peparse::GetPEErr() << " ("
+              << peparse::GetPEErrString() << ")\n";
+
+    std::cout << "Location: " << peparse::GetPEErrLoc() << "\n";
+    return 1;
+  }
+
+  std::uint64_t image_base_address = 0U;
+  if (pe->peHeader.nt.FileHeader.Machine == peparse::IMAGE_FILE_MACHINE_AMD64) {
+    image_base_address = pe->peHeader.nt.OptionalHeader64.ImageBase;
+  } else {
+    image_base_address = pe->peHeader.nt.OptionalHeader.ImageBase;
+  }
+
+  std::cout << "Image base address: 0x" << std::hex << image_base_address
+            << "\n";
+  std::cout << "Converting address 0x" << std::hex << address << "...\n\n";
+
+  std::uintptr_t result = 0U;
+
+  std::cout << "as Physical offset (off)\n";
+  std::cout << "  to rva:\t";
+  if (convertAddress(pe,
+                     address,
+                     AddressType::PhysicalOffset,
+                     AddressType::RelativeVirtualAddress,
+                     result)) {
+    std::cout << "0x" << std::hex << result;
+  } else {
+    std::cout << "-";
+  }
+  std::cout << "\n";
+
+  std::cout << "  to va:\t";
+  if (convertAddress(pe,
+                     address,
+                     AddressType::PhysicalOffset,
+                     AddressType::VirtualAddress,
+                     result)) {
+    std::cout << "0x" << std::hex << result;
+  } else {
+    std::cout << "-";
+  }
+  std::cout << "\n\n";
+
+  std::cout << "as Relative virtual address (rva)\n";
+  std::cout << "  to off:\t";
+  if (convertAddress(pe,
+                     address,
+                     AddressType::RelativeVirtualAddress,
+                     AddressType::PhysicalOffset,
+                     result)) {
+    std::cout << "0x" << std::hex << result;
+  } else {
+    std::cout << "-";
+  }
+  std::cout << "\n";
+
+  std::cout << "  to va:\t";
+  if (convertAddress(pe,
+                     address,
+                     AddressType::RelativeVirtualAddress,
+                     AddressType::VirtualAddress,
+                     result)) {
+    std::cout << "0x" << std::hex << result;
+  } else {
+    std::cout << "-";
+  }
+  std::cout << "\n\n";
+
+  std::cout << "as Virtual address (va)\n";
+  std::cout << "  to off:\t";
+  if (convertAddress(pe,
+                     address,
+                     AddressType::VirtualAddress,
+                     AddressType::PhysicalOffset,
+                     result)) {
+    std::cout << "0x" << std::hex << result;
+  } else {
+    std::cout << "-";
+  }
+  std::cout << "\n";
+
+  std::cout << "  to rva:\t";
+  if (convertAddress(pe,
+                     address,
+                     AddressType::VirtualAddress,
+                     AddressType::RelativeVirtualAddress,
+                     result)) {
+    std::cout << "0x" << std::hex << result;
+  } else {
+    std::cout << "-";
+  }
+  std::cout << "\n";
+
+  return 0;
+}
diff --git a/packages/archlinux/.gitignore b/packages/archlinux/.gitignore
new file mode 100644
index 0000000..e402c05
--- /dev/null
+++ b/packages/archlinux/.gitignore
@@ -0,0 +1,5 @@
+pkg
+src
+*.pkg.tar
+*.pkg.tar.xz
+
diff --git a/packages/archlinux/PKGBUILD b/packages/archlinux/PKGBUILD
new file mode 100644
index 0000000..68f836d
--- /dev/null
+++ b/packages/archlinux/PKGBUILD
@@ -0,0 +1,18 @@
+pkgname=pe-parse
+pkgver=1.0
+pkgrel=1
+arch=("x86_64" "x86")
+pkgdesc="PE parsing library from Trail of Bits"
+url="https://github.com/trailofbits/pe-parse"
+license=('MIT')
+makedepends=("cmake")
+provides=("pe-parse")
+
+build() {
+  cmake ../../../
+  make -j `nproc`
+}
+
+package() {
+  make DESTDIR="$pkgdir/" install
+}
diff --git a/parser-library/CMakeLists.txt b/parser-library/CMakeLists.txt
deleted file mode 100644
index 24ec07d..0000000
--- a/parser-library/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-cmake_minimum_required(VERSION 3.1)
-project(pe-parser-library)
-
-add_library(${PROJECT_NAME} buffer.cpp parse.cpp)
-target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_compile_options(${PROJECT_NAME} PRIVATE ${GLOBAL_CXXFLAGS})
diff --git a/pe-parser-library/CMakeLists.txt b/pe-parser-library/CMakeLists.txt
new file mode 100644
index 0000000..aca02d5
--- /dev/null
+++ b/pe-parser-library/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.1)
+project(pe-parser-library)
+
+# List all files explicitly; this will make IDEs happy (i.e. QtCreator, CLion, ...)
+list(APPEND PEPARSERLIB_SOURCEFILES
+  include/parser-library/parse.h
+  include/parser-library/nt-headers.h
+  include/parser-library/to_string.h
+
+  src/buffer.cpp
+  src/parse.cpp
+)
+
+add_library(${PROJECT_NAME} STATIC ${PEPARSERLIB_SOURCEFILES})
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_compile_options(${PROJECT_NAME} PRIVATE ${GLOBAL_CXXFLAGS})
+
+install(TARGETS ${PROJECT_NAME} DESTINATION "lib")
+install(FILES "cmake/peparse-config.cmake" DESTINATION "lib/cmake/peparse")
+install(DIRECTORY "include/parser-library" DESTINATION "include")
diff --git a/pe-parser-library/cmake/peparse-config.cmake b/pe-parser-library/cmake/peparse-config.cmake
new file mode 100644
index 0000000..0435b8e
--- /dev/null
+++ b/pe-parser-library/cmake/peparse-config.cmake
@@ -0,0 +1,5 @@
+find_path(PEPARSE_INCLUDE_DIR "parser-library/parse.h")
+find_library(PEPARSE_LIBRARIES NAMES "libpe-parser-library.a")
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(libproxy DEFAULT_MSG PEPARSE_INCLUDE_DIR PEPARSE_LIBRARIES)
diff --git a/parser-library/nt-headers.h b/pe-parser-library/include/parser-library/nt-headers.h
similarity index 99%
rename from parser-library/nt-headers.h
rename to pe-parser-library/include/parser-library/nt-headers.h
index 5ce8d64..d3f744a 100644
--- a/parser-library/nt-headers.h
+++ b/pe-parser-library/include/parser-library/nt-headers.h
@@ -25,6 +25,7 @@ THE SOFTWARE.
 #pragma once
 
 #include <cstdint>
+#include <string>
 
 #define _offset(t, f)         \
   static_cast<std::uint32_t>( \
diff --git a/parser-library/parse.h b/pe-parser-library/include/parser-library/parse.h
similarity index 100%
rename from parser-library/parse.h
rename to pe-parser-library/include/parser-library/parse.h
diff --git a/parser-library/to_string.h b/pe-parser-library/include/parser-library/to_string.h
similarity index 100%
rename from parser-library/to_string.h
rename to pe-parser-library/include/parser-library/to_string.h
diff --git a/parser-library/buffer.cpp b/pe-parser-library/src/buffer.cpp
similarity index 97%
rename from parser-library/buffer.cpp
rename to pe-parser-library/src/buffer.cpp
index 1c0f477..b9b0f8f 100644
--- a/parser-library/buffer.cpp
+++ b/pe-parser-library/src/buffer.cpp
@@ -22,12 +22,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */
 
-#include "parse.h"
-
 #include <cstring>
 #include <fstream>
 
+// keep this header above "windows.h" because it contains many types
+#include <parser-library/parse.h>
+
 #ifdef WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#define VC_EXTRALEAN
+
 #include <intrin.h>
 #include <windows.h>
 #else
diff --git a/parser-library/parse.cpp b/pe-parser-library/src/parse.cpp
similarity index 99%
rename from parser-library/parse.cpp
rename to pe-parser-library/src/parse.cpp
index 3ff1460..19c72eb 100644
--- a/parser-library/parse.cpp
+++ b/pe-parser-library/src/parse.cpp
@@ -22,15 +22,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */
 
-#include "parse.h"
-#include "nt-headers.h"
-#include "to_string.h"
-
 #include <algorithm>
 #include <cstring>
 #include <list>
 #include <stdexcept>
 
+#include <parser-library/nt-headers.h>
+#include <parser-library/parse.h>
+#include <parser-library/to_string.h>
+
 using namespace std;
 
 namespace peparse {
diff --git a/python/pepy.cpp b/python/pepy.cpp
index e2bfdb3..7ffcc40 100644
--- a/python/pepy.cpp
+++ b/python/pepy.cpp
@@ -25,8 +25,8 @@
  * SUCH DAMAGE.
  */
 
-#include "parse.h"
 #include <Python.h>
+#include <parser-library/parse.h>
 #include <structmember.h>
 
 using namespace peparse;
@@ -37,9 +37,9 @@ using namespace peparse;
  * Add some definition for compatibility between python2 and python3
  */
 #if PY_MAJOR_VERSION >= 3
-    #define PyInt_FromLong PyLong_FromLong
-    #define PyInt_AsLong PyLong_AsLong
-    #define PyString_FromString PyUnicode_FromString
+#define PyInt_FromLong PyLong_FromLong
+#define PyInt_AsLong PyLong_AsLong
+#define PyString_FromString PyUnicode_FromString
 #endif
 
 /*
@@ -47,12 +47,11 @@ using namespace peparse;
  * Needed for compatibility with python3
  */
 #ifndef PyVarObject_HEAD_INIT
-    #define PyVarObject_HEAD_INIT(type, size) \
-        PyObject_HEAD_INIT(type) size,
+#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
 #endif
 
 #ifndef Py_TYPE
-    #define Py_TYPE(_ob_) (((PyObject*)(_ob_))->ob_type)
+#define Py_TYPE(_ob_) (((PyObject *) (_ob_))->ob_type)
 #endif
 
 /* These are used to across multiple objects. */
@@ -77,9 +76,13 @@ using namespace peparse;
 
 static PyObject *pepy_error;
 
-struct pepy { PyObject_HEAD };
+struct pepy {
+  PyObject_HEAD
+};
 
-struct pepy_parsed { PyObject_HEAD parsed_pe *pe; };
+struct pepy_parsed {
+  PyObject_HEAD parsed_pe *pe;
+};
 
 struct pepy_section {
   PyObject_HEAD PyObject *name;
@@ -164,7 +167,7 @@ static PyGetSetDef pepy_import_getseters[] = {
     {NULL}};
 
 static PyTypeObject pepy_import_type = {
-    PyVarObject_HEAD_INIT(NULL,0)       /* ob_size */
+    PyVarObject_HEAD_INIT(NULL, 0)    /* ob_size */
     "pepy.import",                    /* tp_name */
     sizeof(pepy_import),              /* tp_basicsize */
     0,                                /* tp_itemsize */
@@ -238,7 +241,7 @@ static PyGetSetDef pepy_export_getseters[] = {
     {NULL}};
 
 static PyTypeObject pepy_export_type = {
-    PyVarObject_HEAD_INIT(NULL,0)       /* ob_size */
+    PyVarObject_HEAD_INIT(NULL, 0)    /* ob_size */
     "pepy.export",                    /* tp_name */
     sizeof(pepy_export),              /* tp_basicsize */
     0,                                /* tp_itemsize */
@@ -310,7 +313,7 @@ static PyGetSetDef pepy_relocation_getseters[] = {
     {NULL}};
 
 static PyTypeObject pepy_relocation_type = {
-    PyVarObject_HEAD_INIT(NULL,0)           /* ob_size */
+    PyVarObject_HEAD_INIT(NULL, 0)        /* ob_size */
     "pepy.relocation",                    /* tp_name */
     sizeof(pepy_relocation),              /* tp_basicsize */
     0,                                    /* tp_itemsize */
@@ -412,7 +415,7 @@ static PyGetSetDef pepy_section_getseters[] = {
     {NULL}};
 
 static PyTypeObject pepy_section_type = {
-    PyVarObject_HEAD_INIT(NULL,0)        /* ob_size */
+    PyVarObject_HEAD_INIT(NULL, 0)     /* ob_size */
     "pepy.section",                    /* tp_name */
     sizeof(pepy_section),              /* tp_basicsize */
     0,                                 /* tp_itemsize */
@@ -616,7 +619,7 @@ static PyGetSetDef pepy_resource_getseters[] = {
     {NULL}};
 
 static PyTypeObject pepy_resource_type = {
-    PyVarObject_HEAD_INIT(NULL,0)         /* ob_size */
+    PyVarObject_HEAD_INIT(NULL, 0)      /* ob_size */
     "pepy.resource",                    /* tp_name */
     sizeof(pepy_resource),              /* tp_basicsize */
     0,                                  /* tp_itemsize */
@@ -716,20 +719,21 @@ static PyObject *pepy_parsed_get_bytes(PyObject *self, PyObject *args) {
    * PybyteArray_FromStringAndSize
    */
 
-  uint8_t *buf = new(std::nothrow) uint8_t[len];
+  uint8_t *buf = new (std::nothrow) uint8_t[len];
   if (!buf) {
     /* in case allocation failed */
-    PyErr_SetString(pepy_error, "Unable to create initial buffer (allocation failure).");
+    PyErr_SetString(pepy_error,
+                    "Unable to create initial buffer (allocation failure).");
     return NULL;
   }
 
-  for (idx = 0; idx < len; idx++)  {
+  for (idx = 0; idx < len; idx++) {
     if (!ReadByteAtVA(((pepy_parsed *) self)->pe, start + idx, buf[idx]))
       break;
   }
 
   /* use idx as content length, if we get less than asked for */
-  ret = PyByteArray_FromStringAndSize(reinterpret_cast<char*>(buf), idx);
+  ret = PyByteArray_FromStringAndSize(reinterpret_cast<char *>(buf), idx);
   if (!ret) {
     PyErr_SetString(pepy_error, "Unable to create new byte array.");
     return NULL;
@@ -1203,7 +1207,7 @@ static PyMethodDef pepy_parsed_methods[] = {
     {NULL}};
 
 static PyTypeObject pepy_parsed_type = {
-    PyVarObject_HEAD_INIT(NULL,0)               /* ob_size */
+    PyVarObject_HEAD_INIT(NULL, 0)            /* ob_size */
     "pepy.parsed",                            /* tp_name */
     sizeof(pepy_parsed),                      /* tp_basicsize */
     0,                                        /* tp_itemsize */
@@ -1279,8 +1283,7 @@ static PyObject *pepy_parse(PyObject *self, PyObject *args) {
 static PyMethodDef pepy_methods[] = {
     {"parse", pepy_parse, METH_VARARGS, "Parse PE from file."}, {NULL}};
 
-static
-PyObject* pepi_module_init(void) {
+static PyObject *pepi_module_init(void) {
   PyObject *m;
 
   if (PyType_Ready(&pepy_parsed_type) < 0 ||
@@ -1293,15 +1296,15 @@ PyObject* pepi_module_init(void) {
 
 #if PY_MAJOR_VERSION >= 3
   static struct PyModuleDef moduledef = {
-    PyModuleDef_HEAD_INIT,
-    "pepy",
-    "Python interface to pe-parse.",
-    -1,
-    pepy_methods,
-    NULL,
-    NULL,
-    NULL,
-    NULL,
+      PyModuleDef_HEAD_INIT,
+      "pepy",
+      "Python interface to pe-parse.",
+      -1,
+      pepy_methods,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
   };
 #endif
 
@@ -1401,13 +1404,11 @@ PyObject* pepi_module_init(void) {
 }
 
 #if PY_MAJOR_VERSION >= 3
-PyMODINIT_FUNC PyInit_pepy(void)
-{
+PyMODINIT_FUNC PyInit_pepy(void) {
   return pepi_module_init();
 }
 #else
-PyMODINIT_FUNC initpepy(void)
-{
+PyMODINIT_FUNC initpepy(void) {
   pepi_module_init();
 }
 #endif
diff --git a/python/setup.py b/python/setup.py
index 63c93fb..a30bac1 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -28,15 +28,15 @@ from distutils.core import setup, Extension
 INCLUDE_DIRS = ['/usr/local/include',
                 '/opt/local/include',
                 '/usr/include',
-                '../parser-library']
+                '../pe-parser-library/include']
 LIBRARY_DIRS = ['/usr/lib',
                 '/usr/local/lib']
 
 extension_mod = Extension('pepy',
                           sources = ['pepy.cpp',
-                                     '../parser-library/parse.cpp',
-                                     '../parser-library/buffer.cpp'],
-                          extra_compile_args = ["-g", "-O0"], # Debug only
+                                     '../pe-parser-library/src/parse.cpp',
+                                     '../pe-parser-library/src/buffer.cpp'],
+                          extra_compile_args = ["-std=c++11", "-g", "-O0"], # Debug only
                           include_dirs = INCLUDE_DIRS,
                           library_dirs = LIBRARY_DIRS)
 
diff --git a/travis.sh b/travis.sh
new file mode 100755
index 0000000..df9ddc1
--- /dev/null
+++ b/travis.sh
@@ -0,0 +1,239 @@
+#!/usr/bin/env bash
+
+main() {
+  if [ $# -ne 2 ] ; then
+    printf "Usage:\n\ttravis.sh <linux|osx> <initialize|build>\n"
+    return 1
+  fi
+
+  local platform_name="$1"
+  local operation_type="$2"
+
+  if [[ "${platform_name}" != "osx" && "${platform_name}" != "linux" ]] ; then
+    printf "Invalid platform: ${platform_name}\n"
+    return 1
+  fi
+
+  if [[ "${operation_type}" == "initialize" ]] ; then
+    "${platform_name}_initialize"
+    return $?
+
+  elif [[ "$operation_type" == "build" ]] ; then
+    "${platform_name}_build"
+    return $?
+  
+  else
+    printf "Invalid operation\n"
+    return 1
+  fi
+}
+
+get_processor_count() {
+  which nproc > /dev/null
+  if [ $? -eq 0 ] ; then
+    nproc
+    return 0
+  fi
+
+  which sysctl > /dev/null
+  if [ $? -eq 0 ] ; then
+    sysctl -n hw.ncpu
+    return 0
+  fi
+
+  return 1
+}
+
+linux_initialize() {
+  printf "Initializing platform: linux\n"
+  local log_file=`mktemp`
+
+  printf " > Updating the package database..\n"
+  sudo apt-get -qq update > "${log_file}" 2>&1
+  if [ $? -ne 0 ] ; then
+    printf " x The package database could not be updated\n\n\n"
+    cat "${log_file}"
+    return 1
+  fi
+
+  printf " > Installing the required packages...\n"
+  sudo apt-get install -qqy cmake python2.7 python-dev build-essential realpath > "${log_file}" 2>&1
+  if [ $? -ne 0 ] ; then
+    printf " x Could not install the required dependencies\n\n\n"
+    cat "${log_file}"
+    return 1
+  fi
+
+  printf " > The system has been successfully initialized\n"
+  return 0
+}
+
+osx_initialize() {
+  printf "Initializing platform: macOS\n"
+  local log_file=`mktemp`
+
+  printf " > Updating the package database..\n"
+  brew update > "${log_file}" 2>&1
+  if [ $? -ne 0 ] ; then
+    printf " x The package database could not be updated\n\n\n"
+    cat "${log_file}"
+    return 1
+  fi
+
+  printf " > Installing CMake...\n"
+  brew install cmake  > "${log_file}" 2>&1
+  if [ $? -ne 0 ] ; then
+    printf " x Failed to install CMake\n\n\n"
+    cat "${log_file}"
+  fi
+
+  printf " > The system has been successfully initialized\n"
+  return 0
+}
+
+common_build() {
+  printf "Gathering system information...\n"
+
+  which cmake > /dev/null
+  printf " > CMake version: "
+  if [ $? -eq 0 ] ; then
+    cmake --version | head -n 1
+  else
+    printf "not found\n"
+  fi
+
+  which gcc > /dev/null
+  printf " > GCC version: "
+  if [ $? -eq 0 ] ; then
+    gcc --version | head -n 1
+  else
+    printf "not found\n"
+  fi
+
+  which clang > /dev/null
+  printf " > Clang version: "
+  if [ $? -eq 0 ] ; then
+    clang --version | head -n 1
+  else
+    printf "not found\n"
+  fi
+
+  printf "\n"
+
+  printf "Library\n"
+  if [ ! -d "build" ] ; then
+    printf " > Creating the build directory...\n"
+    mkdir "build"
+    if [ $? -ne 0 ] ; then
+      printf " x Failed to create the build directory\n"
+      return 1
+    fi
+  fi
+
+  local log_file=`mktemp`
+  local processor_count=`get_processor_count`
+
+  printf " > Configuring...\n"
+  ( cd "build" && cmake .. ) > "$log_file" 2>&1
+  if [ $? -ne 0 ] ; then
+    printf " x Configure failed; CMake returned an error.\n\n\n"
+    cat "$log_file"
+    return 1
+  fi
+
+  printf " > Building...\n"
+  ( cd "build" && make -j "${processor_count}" ) > "$log_file" 2>&1
+  if [ $? -ne 0 ] ; then
+    printf " x The build has failed.\n\n\n"
+    cat "$log_file"
+    return 1
+  fi
+
+  printf " > Installing...\n"
+  sudo touch /usr/lib/test_file > /dev/null 2>&1
+  if [ $? -ne 0 ] ; then
+    printf " x Access denied to /usr/lib; examples will not be built\n"
+
+  else
+    ( cd "build" && sudo make install ) > "$log_file" 2>&1
+    if [ $? -ne 0 ] ; then
+      printf " x Failed to install the library.\n\n\n"
+      cat "$log_file"
+      return 1
+    fi
+
+    printf "\n"
+
+    printf "Examples\n"
+    if [ ! -d "examples_build" ] ; then
+      printf " > Creating the build directory...\n"
+      mkdir "examples_build"
+      if [ $? -ne 0 ] ; then
+        printf " x Failed to create the build directory\n\n\n"
+        cat "$log_file"
+        return 1
+      fi
+    fi
+
+    printf " > Configuring...\n"
+    ( cd "examples_build" && cmake "../examples/peaddrconv" ) > "$log_file" 2>&1
+    if [ $? -ne 0 ] ; then
+      printf " x Configure failed; CMake returned an error.\n\n\n"
+      cat "$log_file"
+      return 1
+    fi
+
+    printf " > Building...\n"
+    ( cd "examples_build" && make -j "${processor_count}" ) > "$log_file" 2>&1
+    if [ $? -ne 0 ] ; then
+      printf " x The build has failed.\n\n\n"
+      cat "$log_file"
+      return 1
+    fi
+  fi
+
+  printf "\n"
+
+  printf "pepy\n"
+
+  printf " > Building...\n"
+  ( cd python && python2 ./setup.py build ) > "$log_file" 2>&1
+  if [ $? -ne 0 ] ; then
+      printf " x Build failed.\n\n\n"
+      cat "$log_file"
+      return 1
+  fi
+
+  return 0
+}
+
+linux_build() {
+  printf "Building platform: linux\n"
+
+  source /etc/*-release
+  printf "Distribution: ${DISTRIB_DESCRIPTION}\n\n"
+
+  common_build
+  if [ $? -ne 0 ] ; then
+    return 1
+  fi
+
+  return 0
+}
+
+osx_build() {
+  printf "Building platform: macOS\n\n"
+
+  printf "macOS version: "
+  sw_vers -productVersion
+
+  common_build
+  if [ $? -ne 0 ] ; then
+    return 1
+  fi
+
+  return 0
+}
+
+main $@
+exit $?