Add call_conv to tool

This commit is contained in:
Guus Waals 2025-06-01 22:24:56 +08:00
parent 3ad0cb5bd1
commit 15ec7bd1f1
7 changed files with 100 additions and 15 deletions

View File

@ -78,9 +78,8 @@ bool dumpTreeFile(const std::string &filepath) {
void register_cmd_dump(CLI::App &app) { void register_cmd_dump(CLI::App &app) {
auto cmd = auto cmd =
app.add_subcommand("dump-tree", "Dump the tree-sitter AST for a file"); app.add_subcommand("dump-tree", "Dump the tree-sitter AST for a file");
cmd->add_option("-f,--filepath", filepath, cmd->add_option("file", filepath,
"File to dump the tree-sitter AST for") "Input C++ file to parse")->required();
->required();
cmd->final_callback([]() { cmd->final_callback([]() {
spdlog::info("=== Processing: {} ===", filepath); spdlog::info("=== Processing: {} ===", filepath);
dumpTreeFile(filepath); dumpTreeFile(filepath);

View File

@ -24,10 +24,11 @@ bool generateHooksFile(const std::string &output_filepath) {
for (const auto &func : fix_functions) { for (const auto &func : fix_functions) {
// Extract just the filename from the full path // Extract just the filename from the full path
std::string filename = std::filesystem::path(func.filepath).filename().string(); std::string filename = std::filesystem::path(func.filepath).filename().string();
std::string conv_str = callingConventionToString(func.calling_convention);
output_stream << "HOOK(0x" << func.address << ", " << func.name << ") // " << filename << std::endl; output_stream << "HOOK(0x" << func.address << ", " << func.name << ", " << conv_str << ") // " << filename << std::endl;
spdlog::debug("Added hook: {} {} from {}", func.address, func.name, filename); spdlog::debug("Added hook: {} {} {} from {}", func.address, func.name, conv_str, filename);
} }
output_stream.close(); output_stream.close();

View File

@ -27,11 +27,11 @@ public:
&delete_imports_stmt, &delete_imports_stmt,
"Failed to prepare delete imports statement"); "Failed to prepare delete imports statement");
prepareStatement("INSERT OR REPLACE INTO Functions (filepath, name, " prepareStatement("INSERT OR REPLACE INTO Functions (filepath, name, "
"address, type) VALUES (?, ?, ?, ?)", "address, type, calling_convention) VALUES (?, ?, ?, ?, ?)",
&insert_functions_stmt, &insert_functions_stmt,
"Failed to prepare insert functions statement"); "Failed to prepare insert functions statement");
prepareStatement("INSERT OR REPLACE INTO Imports (filepath, name, address, " prepareStatement("INSERT OR REPLACE INTO Imports (filepath, name, address, "
"type) VALUES (?, ?, ?, ?)", "type, calling_convention) VALUES (?, ?, ?, ?, ?)",
&insert_imports_stmt, &insert_imports_stmt,
"Failed to prepare insert imports statement"); "Failed to prepare insert imports statement");
prepareStatement("DELETE FROM Globals WHERE filepath = ?", prepareStatement("DELETE FROM Globals WHERE filepath = ?",
@ -80,8 +80,8 @@ DatabaseManager::DatabaseManager(const std::string &db_path) : db(nullptr) {
} }
const char *create_tables = R"( const char *create_tables = R"(
CREATE TABLE IF NOT EXISTS Functions (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, PRIMARY KEY (name, filepath)); CREATE TABLE IF NOT EXISTS Functions (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, calling_convention INTEGER DEFAULT 0, PRIMARY KEY (name, filepath));
CREATE TABLE IF NOT EXISTS Imports (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, PRIMARY KEY (name, filepath)); CREATE TABLE IF NOT EXISTS Imports (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, calling_convention INTEGER DEFAULT 0, PRIMARY KEY (name, filepath));
CREATE TABLE IF NOT EXISTS Globals (filepath TEXT, name TEXT, address TEXT); CREATE TABLE IF NOT EXISTS Globals (filepath TEXT, name TEXT, address TEXT);
)"; )";
@ -118,6 +118,7 @@ void DatabaseManager::insertFunction(const FunctionInfo &func) {
sqlite3_bind_text(stmt, 2, func.name.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, func.name.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, func.address.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 3, func.address.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 4, static_cast<int>(func.type)); sqlite3_bind_int(stmt, 4, static_cast<int>(func.type));
sqlite3_bind_int(stmt, 5, static_cast<int>(func.calling_convention));
sqlite3_step(stmt); sqlite3_step(stmt);
} }
@ -240,7 +241,7 @@ std::vector<FunctionInfo> DatabaseManager::getFunctionsByType(FileType type) {
std::vector<FunctionInfo> functions; std::vector<FunctionInfo> functions;
const char *sql = R"( const char *sql = R"(
SELECT name, address, filepath SELECT name, address, filepath, calling_convention
FROM Functions FROM Functions
WHERE type = ? AND address != '' WHERE type = ? AND address != ''
ORDER BY address; ORDER BY address;
@ -259,6 +260,7 @@ std::vector<FunctionInfo> DatabaseManager::getFunctionsByType(FileType type) {
func.name = (const char *)sqlite3_column_text(stmt, 0); func.name = (const char *)sqlite3_column_text(stmt, 0);
func.address = (const char *)sqlite3_column_text(stmt, 1); func.address = (const char *)sqlite3_column_text(stmt, 1);
func.filepath = (const char *)sqlite3_column_text(stmt, 2); func.filepath = (const char *)sqlite3_column_text(stmt, 2);
func.calling_convention = static_cast<CallingConvention>(sqlite3_column_int(stmt, 3));
func.type = type; func.type = type;
func.is_import = false; // Functions table contains non-imports func.is_import = false; // Functions table contains non-imports

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
set -e set -e
tool=build/clang-x86_64-pc-windows-msvc/Release/r3_gh_tool tool=build/clang-x86_64-pc-windows-msvc/Release/gh_tool
cmake --build build/clang-x86_64-pc-windows-msvc/Release --target r3_gh_tool cmake --build build/clang-x86_64-pc-windows-msvc/Release --target gh_tool
types=(auto ref fix stub) types=(auto ref fix stub)
for type in "${types[@]}"; do for type in "${types[@]}"; do
@ -12,9 +12,9 @@ for type in "${types[@]}"; do
for file in "tmps/gh_${type}"/*.cxx; do for file in "tmps/gh_${type}"/*.cxx; do
echo "$file" >>"$file_list" echo "$file" >>"$file_list"
done done
$tool "@$file_list" -v --type=$type --log-file=log-functions.txt $tool -v --log-file=log-functions.txt functions "@$file_list" --type=$type
fi fi
done done
$tool tmps/gh_global.h -mglobals -v --log-file=log-globals.txt $tool -v --log-file=log-globals.txt globals tmps/gh_global.h
$tool -mduplicates -v --log-file=log-duplicates.txt $tool -v --log-file=log-duplicates.txt verify

View File

@ -209,6 +209,47 @@ bool hasFunctionBody(TSNode node) {
return false; return false;
} }
// Add this helper function to detect calling convention from a function declaration
CallingConvention getCallingConvention(TSNode node, const char *source_code) {
uint32_t child_count = ts_node_child_count(node);
for (uint32_t i = 0; i < child_count; i++) {
TSNode child = ts_node_child(node, i);
const char *type = ts_node_type(child);
// Look for identifiers that might be calling conventions
if (strcmp(type, "identifier") == 0) {
std::string text = extractNodeText(child, source_code);
if (text == "__fastcall" || text == "__FASTCALL") {
return CallingConvention::Fastcall;
} else if (text == "__stdcall" || text == "__STDCALL") {
return CallingConvention::Stdcall;
} else if (text == "__cdecl" || text == "__CDECL") {
return CallingConvention::Cdecl;
}
}
// Also check in type specifiers and declaration specifiers
else if (strcmp(type, "type_specifier") == 0 ||
strcmp(type, "declaration_specifier") == 0 ||
strcmp(type, "specifier_qualifier_list") == 0) {
CallingConvention conv = getCallingConvention(child, source_code);
if (conv != CallingConvention::Cdecl) {
return conv;
}
}
// Check in declarators as well (calling convention can appear in various positions)
else if (strcmp(type, "function_declarator") == 0 ||
strcmp(type, "pointer_declarator") == 0) {
CallingConvention conv = getCallingConvention(child, source_code);
if (conv != CallingConvention::Cdecl) {
return conv;
}
}
}
return CallingConvention::Cdecl; // Default
}
void findFunctions(TSNode node, const char *source_code, uint32_t source_length, void findFunctions(TSNode node, const char *source_code, uint32_t source_length,
std::vector<FunctionInfo> &functions, FileType file_type) { std::vector<FunctionInfo> &functions, FileType file_type) {
const char *type = ts_node_type(node); const char *type = ts_node_type(node);
@ -226,11 +267,19 @@ void findFunctions(TSNode node, const char *source_code, uint32_t source_length,
} }
if (!address.empty()) { if (!address.empty()) {
// Detect calling convention
CallingConvention calling_conv = getCallingConvention(node, source_code);
FunctionInfo func{func_name, address, "", FunctionInfo func{func_name, address, "",
strcmp(type, "function_definition") == 0 strcmp(type, "function_definition") == 0
? !hasFunctionBody(node) ? !hasFunctionBody(node)
: true, : true,
file_type}; // Add file_type parameter file_type}; // Add file_type parameter
func.calling_convention = calling_conv; // Set the calling convention
spdlog::debug("Found function: {} at {} with calling convention: {}",
func_name, address, callingConventionToString(calling_conv));
functions.push_back(func); functions.push_back(func);
} }
// We'll never nest function declarations // We'll never nest function declarations

View File

@ -1,5 +1,6 @@
#include "tool.hpp" #include "tool.hpp"
#include <filesystem> #include <filesystem>
#include <algorithm>
#include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h> #include <spdlog/sinks/basic_file_sink.h>
#include <CLI11.hpp> #include <CLI11.hpp>
@ -45,3 +46,28 @@ int main(int argc, char *argv[]) {
return 0; return 0;
} }
CallingConvention stringToCallingConvention(const std::string &conv_str) {
std::string lower_conv = conv_str;
std::transform(lower_conv.begin(), lower_conv.end(), lower_conv.begin(), ::tolower);
if (lower_conv == "fastcall" || lower_conv == "__fastcall") {
return CallingConvention::Fastcall;
} else if (lower_conv == "stdcall" || lower_conv == "__stdcall") {
return CallingConvention::Stdcall;
} else {
return CallingConvention::Cdecl; // Default
}
}
std::string callingConventionToString(CallingConvention conv) {
switch (conv) {
case CallingConvention::Fastcall:
return "fastcall";
case CallingConvention::Stdcall:
return "stdcall";
case CallingConvention::Cdecl:
default:
return "cdecl";
}
}

View File

@ -14,6 +14,9 @@ extern const std::regex ADDRESS_REGEX;
// Enums // Enums
enum class FileType { Auto, Fix, Stub, Ref }; enum class FileType { Auto, Fix, Stub, Ref };
// Add calling convention enum
enum class CallingConvention { Cdecl, Stdcall, Fastcall };
// Data structures // Data structures
struct FunctionInfo { struct FunctionInfo {
std::string name; std::string name;
@ -21,6 +24,7 @@ struct FunctionInfo {
std::string filepath; std::string filepath;
bool is_import; bool is_import;
FileType type; FileType type;
CallingConvention calling_convention = CallingConvention::Cdecl; // Default to cdecl
}; };
struct GlobalInfo { struct GlobalInfo {
@ -35,6 +39,10 @@ std::string fileTypeToString(FileType type);
bool hasAddressPattern(const std::string &comment); bool hasAddressPattern(const std::string &comment);
std::string extractAddress(const std::string &comment); std::string extractAddress(const std::string &comment);
// Add utility functions for calling convention
CallingConvention stringToCallingConvention(const std::string &conv_str);
std::string callingConventionToString(CallingConvention conv);
// Tree-sitter parsing functions // Tree-sitter parsing functions
std::string extractNodeText(TSNode node, const char *source_code); std::string extractNodeText(TSNode node, const char *source_code);
std::string findIdentifierInNode(TSNode node, const char *source_code); std::string findIdentifierInNode(TSNode node, const char *source_code);