diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml deleted file mode 100644 index ae7bf54..0000000 --- a/.github/workflows/ccpp.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: CI Tests - -on: - push: - paths-ignore: ['**.md'] - pull_request: - paths-ignore: ['**.md'] - -jobs: - Clang_Format: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - - name: Setup - run: | - sudo apt-get update - sudo apt-get install -y clang-format - shell: bash - - name: Validate Source Formatting - run: | - find . -name '*.hh' -o -iname '*.cc' | xargs clang-format -i -style=file - if [[ -n $(git diff) ]]; then - echo "You must run make format before submitting a pull request" - echo "" - git diff - exit -1 - fi - shell: bash - - Release: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - - name: Setup - run: | - sudo apt-get update - sudo apt-get install -y cmake libcurl4-gnutls-dev libboost-filesystem-dev ninja-build - shell: bash - - name: Validate Release Build - run: | - cd build - cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. - ninja - ninja package - shell: bash - - name: Archive debian packages - uses: actions/upload-artifact@v3 - with: - name: libmspdb_release - path: build/*.deb - - Debug: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - - name: Setup - run: | - sudo apt-get update - sudo apt-get install -y cmake libcurl4-gnutls-dev libboost-filesystem-dev ninja-build - shell: bash - - name: Validate Debug Build - run: | - cd build - cmake -GNinja -DCMAKE_BUILD_TYPE=Debug .. - ninja - ninja package - shell: bash - - name: Archive debian packages - uses: actions/upload-artifact@v3 - with: - name: libmspdb_debug - path: build/*.deb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d1fa9d4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,78 @@ +name: CI Tests + +on: + pull_request: + paths-ignore: ['**.md'] + branches: + - main + +jobs: + # Validate code formatting + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup + run: | + sudo apt-get update + sudo apt-get install -y clang-format + shell: bash + - name: Validate Source Formatting + run: | + mapfile -t source < <(find . -iname "*.cc") + mapfile -t headers < <(find . -iname "*.hh") + code=("${source[@]}" "${headers[@]}") + if ! clang-format -style=file "${code[@]}" -i -Werror --dry-run; then + echo "You must run make format before submitting a pull request" + echo "" + git diff + exit 1 + fi + echo "Your source is formatted real good!" + shell: bash + + # Tests all build scenarios + build: + runs-on: ${{ matrix.os }} + + # Test builds for several Ubuntus with both release and debug + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04, ubuntu-24.04] + build_type: [Release, Debug] + + steps: + - uses: actions/checkout@v4 + + # Install build deps + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake libcurl4-gnutls-dev libboost-filesystem-dev ninja-build + shell: bash + + # Run the build + - name: Validate ${{ matrix.build_type }} Build + run: | + cd build + cmake -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} .. + ninja package + shell: bash + + # Test the build + - name: Run tests + run: | + cd build/tests + ./pdbfetch > /dev/null + ./pdbdump > /dev/null + ./pdbtypes > /dev/null + ./pdbtypetree > /dev/null + ./pdbwrapper > /dev/null + + # Upload build .debs + - name: Archive debian packages + uses: actions/upload-artifact@v4 + with: + name: libmspdb_${{ matrix.os }}_${{ matrix.build_type }} + path: build/*.deb diff --git a/.github/workflows/gh-release.yml b/.github/workflows/gh-release.yml deleted file mode 100644 index dd39d2c..0000000 --- a/.github/workflows/gh-release.yml +++ /dev/null @@ -1,48 +0,0 @@ -on: - push: - tags: - - 'v*' - -name: Upload Release Asset - -jobs: - build: - name: Upload Release Asset - runs-on: ubuntu-20.04 - steps: - - name: Checkout code - uses: actions/checkout@v2 - - name: Setup - run: | - sudo apt-get update - sudo apt-get install -y cmake libcurl4-gnutls-dev libboost-filesystem-dev ninja-build - shell: bash - - name: Build Release - run: | - cd build - cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. - ninja - ninja package - zip --verbose --junk-paths libmspdb.zip ./*.deb - ls -la libmspdb.zip - shell: bash - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./build/libmspdb.zip - asset_name: libmspdb.zip - asset_content_type: application/zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7a81efa --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,68 @@ +name: Create Release + +on: + push: + tags: + - 'v*' + +jobs: + # Build and publish release assets + build: + runs-on: ${{ matrix.os }} + + # Build releases for supported Ubuntu versions + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04, ubuntu-24.04] + + steps: + - uses: actions/checkout@v4 + + # Install build deps + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake libcurl4-gnutls-dev libboost-filesystem-dev ninja-build + shell: bash + + # Run the build + - name: ${{ matrix.os }} Build Release + run: | + cd build + cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. + ninja package + shell: bash + + # Upload build .debs + - name: Archive debian release packages + uses: actions/upload-artifact@v4 + with: + name: libmspdb_${{ matrix.os }}_Release + path: build/*.deb + + # Create the release + release: + needs: build + runs-on: ubuntu-latest + steps: + # Download the built debs from the build job + - name: Download Release Artifacts + uses: actions/download-artifact@v4 + with: + path: all-debs + pattern: libmspdb_*_Release + merge-multiple: true + + # Create and upload the debs to a new release + - name: Upload Release Asset + id: upload-release-asset + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + la -alh ./all-debs + gh release create ${{ github.ref_name }} \ + --title "Release ${{ github.ref_name }}" \ + --draft \ + ./all-debs/*.deb + shell: bash \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e15e3cf..19ebb97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ ADD_SUBDIRECTORY(tests) # Format target ADD_CUSTOM_TARGET( format - COMMAND find ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/include -name '*.hh' -o -iname '*.cc' | xargs clang-format -i -style=file + COMMAND find ${PROJECT_SOURCE_DIR}/tests ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/include -name '*.hh' -o -iname '*.cc' | xargs clang-format -i -style=file ) # Install rule for include headers diff --git a/tests/pdbdump.cc b/tests/pdbdump.cc index 41d9889..9490c4d 100644 --- a/tests/pdbdump.cc +++ b/tests/pdbdump.cc @@ -21,7 +21,7 @@ using namespace mspdb; using namespace std; int main() { - PDB pdb("/home/papes/git/libmspdb/ntkrnlmp.pdb"); + PDB pdb("./pdbstore/ntdll.pdb/08A413EE85E91D0377BA33DC3A2641941/ntdll.pdb"); cout << "BlockSize: " << pdb.block_size() << '\n'; cout << "NumBlocks: " << pdb.num_blocks() << '\n'; diff --git a/tests/pdbfetch.cc b/tests/pdbfetch.cc index 148b96b..be17bc3 100644 --- a/tests/pdbfetch.cc +++ b/tests/pdbfetch.cc @@ -21,5 +21,5 @@ using namespace mspdb; int main() { PDBStore store("./pdbstore/"); - auto pdb = store.open_pdb("win32u.pdb", "C0685FE74DEEA4BEC6F9CC52FEAC5D231"); + auto pdb = store.open_pdb("ntdll.pdb", "08A413EE85E91D0377BA33DC3A2641941"); } \ No newline at end of file diff --git a/tests/pdbtypes.cc b/tests/pdbtypes.cc index da566e4..ec6b194 100644 --- a/tests/pdbtypes.cc +++ b/tests/pdbtypes.cc @@ -291,7 +291,7 @@ void TypeContainer::write_member(const LF_MEMBER& lfMember) const { } int main() { - PDB pdb("/home/papes/git/libmspdb/ntkrnlmp.pdb"); + PDB pdb("./pdbstore/ntdll.pdb/08A413EE85E91D0377BA33DC3A2641941/ntdll.pdb"); const auto& tpi = pdb.stream_directory().type_info_stream(); for (const LF_STRUCTURE& lfStruct : tpi.structs()) { diff --git a/tests/pdbtypetree.cc b/tests/pdbtypetree.cc index 61484d4..66f88da 100644 --- a/tests/pdbtypetree.cc +++ b/tests/pdbtypetree.cc @@ -20,39 +20,74 @@ #include #include #include +#include using namespace mspdb; +// Used to prevent infinity. If the thing we're about to recurse into is already +// in the stack of things we're recursed into, then we don't want to do that. +static std::vector type_stack; + void recurse(const LF_TYPE& type, const std::string& prefix = "") { + if (std::find(type_stack.begin(), type_stack.end(), &type) == type_stack.end()) { + type_stack.push_back(&type); + } else { + std::cout << to_string(type.type()) << "(stopping here - infinite recursive type)\n"; + return; + } std::cout << to_string(type.type()); switch (type.type()) { case LEAF_TYPE::LF_ARRAY: std::cout << "->"; recurse(static_cast(type).element_type(), prefix); + type_stack.pop_back(); return; case LEAF_TYPE::LF_BITFIELD: std::cout << "->"; recurse(static_cast(type).base_type(), prefix); + type_stack.pop_back(); return; case LEAF_TYPE::LF_MEMBER: std::cout << "->"; recurse(static_cast(type).index(), prefix); + type_stack.pop_back(); return; case LEAF_TYPE::LF_MODIFIER: { const auto& lfModifier = static_cast(type); std::cout << std::hex; std::cout << "(0x" << lfModifier.modifiers() << ")->"; std::cout << std::dec; - recurse(lfModifier.modified_type(), prefix); + + const auto& lfType = lfModifier.modified_type(); + if (std::find(type_stack.begin(), type_stack.end(), &lfType) == type_stack.end()) { + type_stack.push_back(&lfType); + } else { + std::cout << to_string(type.type()) + << "(stopping here - infinite recursive modifier)\n"; + return; + } + + recurse(lfType, prefix); + type_stack.pop_back(); return; } case LEAF_TYPE::LF_POINTER: { const auto& lfPointer = static_cast(type); + std::cout << std::hex; std::cout << "(0x" << lfPointer.size() << ")->"; std::cout << std::dec; - recurse(lfPointer.underlying_type(), prefix); + + const auto& lfType = lfPointer.underlying_type(); + if (std::find(type_stack.begin(), type_stack.end(), &lfType) == type_stack.end()) { + type_stack.push_back(&lfType); + } else { + std::cout << to_string(type.type()) << "(stopping here - infinite recursive pointer)\n"; + return; + } + recurse(lfType, prefix); + type_stack.pop_back(); return; } case LEAF_TYPE::LF_PROCEDURE: { @@ -62,6 +97,7 @@ void recurse(const LF_TYPE& type, const std::string& prefix = "") { for (const LF_TYPE& member : lfProc.arg_list()) { std::cout << new_prefix; recurse(member, new_prefix); + type_stack.pop_back(); } return; } @@ -74,6 +110,7 @@ void recurse(const LF_TYPE& type, const std::string& prefix = "") { for (const LF_MEMBER& member : lfStruct.field_list()) { std::cout << new_prefix; recurse(member, new_prefix); + type_stack.pop_back(); } } return; @@ -86,6 +123,7 @@ void recurse(const LF_TYPE& type, const std::string& prefix = "") { for (const LF_MEMBER& member : lfStruct.field_list()) { std::cout << new_prefix; recurse(member, new_prefix); + type_stack.pop_back(); } } return; @@ -98,7 +136,7 @@ void recurse(const LF_TYPE& type, const std::string& prefix = "") { } int main() { - PDB pdb("/home/papes/git/libmspdb/ntkrnlmp.pdb"); + PDB pdb("./pdbstore/ntdll.pdb/08A413EE85E91D0377BA33DC3A2641941/ntdll.pdb"); const auto& tpi = pdb.stream_directory().type_info_stream(); for (const LF_CLASS& entry : tpi.classes()) { @@ -107,7 +145,6 @@ int main() { for (const LF_STRUCTURE& entry : tpi.structs()) { recurse(entry); } - for (const LF_UNION& entry : tpi.unions()) { recurse(entry); } diff --git a/tests/pdbwrapper.cc b/tests/pdbwrapper.cc index 108480c..f1b926f 100644 --- a/tests/pdbwrapper.cc +++ b/tests/pdbwrapper.cc @@ -40,6 +40,8 @@ std::string get_type(const LF_TYPE& lfType) { return get_type(static_cast(lfType).base_type()); case LEAF_TYPE::LF_ARRAY: return get_type(static_cast(lfType).element_type()); + case LEAF_TYPE::LF_UNION: + return static_cast(lfType).name(); default: throw pdb_exception("Unhandled type: " + to_string(lfType.type())); } @@ -51,22 +53,27 @@ void write_wrapper(const T& lfStruct) { std::cout << "public:\n"; for (const LF_MEMBER& member : lfStruct.field_list()) { - std::cout << " virtual " << get_type(member.index()) << " " << member.name() << "() {\n"; - std::cout << std::hex; - switch (member.index().type()) { - default: - std::cout << " return *reinterpret_cast(buffer + 0x" << member.offset() << ");\n"; + try { + auto type = get_type(member.index()); // May throw + std::cout << " virtual " << type << " " << member.name() << "() {\n"; + std::cout << std::hex; + switch (member.index().type()) { + default: + std::cout << " return *reinterpret_cast(buffer + 0x" + << member.offset() << ");\n"; + } + std::cout << std::dec; + std::cout << " };\n"; + } catch (const pdb_exception& e) { + std::cerr << "Skipping member " << member.name() << ": " << e.what() << "\n"; } - std::cout << std::dec; - std::cout << " };\n"; } std::cout << "};\n"; } int main() { - PDB pdb("/Users/papes/git/libmspdb/ntkrnlmp.pdb"); + PDB pdb("./pdbstore/ntdll.pdb/08A413EE85E91D0377BA33DC3A2641941/ntdll.pdb"); const auto& tpi = pdb.stream_directory().type_info_stream(); for (const LF_STRUCTURE& lfStruct : tpi.structs()) {