Add call_conv to tool
This commit is contained in:
parent
3ad0cb5bd1
commit
15ec7bd1f1
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue