Skip to content

Commit

Permalink
feat: fix path handling using utf-16
Browse files Browse the repository at this point in the history
  • Loading branch information
alandtse committed Oct 12, 2024
1 parent eca24b3 commit d5c0951
Showing 1 changed file with 90 additions and 49 deletions.
139 changes: 90 additions & 49 deletions src/Crash/PDB/PdbHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
#pragma once
#include "PdbHandler.h"
#include "Settings.h"
#include <comdef.h>
#include <DbgHelp.h>
#include <atlcomcli.h>
#include <codecvt> // For string conversions
#include <comdef.h>
#include <regex>
#include <unordered_set>

namespace Crash
{
Expand Down Expand Up @@ -46,6 +50,19 @@ namespace Crash
return wsdata;
}

std::wstring utf8_to_utf16(const std::string& utf8)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(utf8);
}

std::string utf16_to_utf8(const std::wstring& utf16)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(utf16);
}


std::string processSymbol(IDiaSymbol* a_symbol, IDiaSession* a_session, const DWORD& a_rva, std::string_view& a_name, uintptr_t& a_offset, std::string& a_result)
{
BSTR name;
Expand Down Expand Up @@ -129,38 +146,35 @@ namespace Crash
std::string pdb_details(std::string_view a_name, uintptr_t a_offset)
{
static std::mutex sync;
const std::lock_guard l{ sync };
std::string result = "";
//if (a_name.ends_with("exe")) //ignore exe since pdbs not readily available for bethesda exes
// return result;
std::lock_guard l{ sync };
std::string result;

std::filesystem::path dllPath{ a_name };
std::string dll_path = a_name.data();
if (!dllPath.has_parent_path())
if (!dllPath.has_parent_path()) {
dll_path = Crash::PDB::sPluginPath.data() + dllPath.filename().string();
auto rva = (DWORD)a_offset;
CComPtr<IDiaDataSource> pSource;
if (!SUCCEEDED(hr)) {
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
}
if (FAILED(hr)) {

auto rva = static_cast<DWORD>(a_offset);
CComPtr<IDiaDataSource> pSource;
HRESULT hr = S_OK;

// Initialize COM
if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED))) {
auto error = print_hr_failure(hr);
logger::info("Failed to initalize COM library for dll {}+{:07X}\t{}", a_name, a_offset, error);
CoUninitialize();
logger::info("Failed to initialize COM library for dll {}+{:07X}\t{}", a_name, a_offset, error);
return result;
}

// try to load local copy
// Attempt to load msdia140.dll
auto* msdia_dll = L"Data/SKSE/Plugins/msdia140.dll";
hr = NoRegCoCreate(msdia_dll, CLSID_DiaSource, __uuidof(IDiaDataSource), (void**)&pSource);
if (FAILED(hr)) {
auto error = print_hr_failure(hr);
logger::info("Failed to manually load msdia140.dll and create object for CLSID for dll {}+{:07X}\t{}", a_name, a_offset, error);
// msdia*.dll try registered copy
if (FAILED(hr = CoCreateInstance(CLSID_DiaSource,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IDiaDataSource),
(void**)&pSource))) {
logger::info("Failed to manually load msdia140.dll for dll {}+{:07X}\t{}", a_name, a_offset, error);

// Try registered copy
if (FAILED(hr = CoCreateInstance(CLSID_DiaSource, NULL, CLSCTX_INPROC_SERVER, __uuidof(IDiaDataSource), (void**)&pSource))) {
auto error = print_hr_failure(hr);
logger::info("Failed to load registered msdia140.dll for dll {}+{:07X}\t{}", a_name, a_offset, error);
CoUninitialize();
Expand All @@ -170,26 +184,40 @@ namespace Crash

wchar_t wszFilename[_MAX_PATH];
wchar_t wszPath[_MAX_PATH];
std::vector<std::string> searchPaths = { Crash::PDB::sPluginPath.data() };
mbstowcs(wszFilename, dll_path.c_str(), sizeof(wszFilename) / sizeof(wszFilename[0]));

// Convert UTF-8 dll_path to UTF-16
std::wstring dll_path_w = utf8_to_utf16(dll_path);
wcsncpy(wszFilename, dll_path_w.c_str(), sizeof(wszFilename) / sizeof(wchar_t));

const auto& debugConfig = Settings::GetSingleton()->GetDebug();
std::string symcache = debugConfig.symcache;

// Symcache handling (already UTF-8)
static bool symcacheChecked = false;
static bool symcacheValid = false;

if (!symcacheChecked) {
if (!symcache.empty() && std::filesystem::exists(symcache) && std::filesystem::is_directory(symcache)) {
logger::info("Symcache found at {}", symcache);
symcacheValid = true;
} else if (!symcache.empty()) {
logger::info("Symcache not found at {}", symcache);
} else
logger::info("Symcache not defined");
} else {
logger::info("Symcache not found at {}", symcache.empty() ? "not defined" : symcache);
}
symcacheChecked = true;
}

std::vector<std::string> searchPaths = { Crash::PDB::sPluginPath.data() };

if (symcacheValid) {
searchPaths.push_back(fmt::format(fmt::runtime("cache*{}"s), symcache.c_str()));
}
auto foundPDB = false;

bool foundPDB = false;
for (const auto& path : searchPaths) {
mbstowcs(wszPath, path.c_str(), sizeof(wszPath) / sizeof(wszPath[0]));
// Convert UTF-8 path to UTF-16
std::wstring path_w = utf8_to_utf16(path);
wcsncpy(wszPath, path_w.c_str(), sizeof(wszPath) / sizeof(wchar_t));

logger::info("Attempting to find pdb for {}+{:07X} with path {}", a_name, a_offset, path);
hr = pSource->loadDataForExe(wszFilename, wszPath, NULL);
if (FAILED(hr)) {
Expand All @@ -200,67 +228,80 @@ namespace Crash
foundPDB = true;
break;
}

if (!foundPDB) {
CoUninitialize();
return result;
}

logger::info("Successfully opened pdb for dll {}+{:07X}", a_name, a_offset);

IDiaSession* pSession;
IDiaSymbol* globalSymbol = nullptr;
IDiaEnumTables* enumTables = nullptr;
IDiaEnumSymbolsByAddr* enumSymbolsByAddr = nullptr;
hr = pSource->openSession(&pSession);
if (FAILED(hr)) {
// Rest of the PDB processing logic remains the same
// No other changes needed for UTF-8 handling
CComPtr<IDiaSession> pSession;
CComPtr<IDiaSymbol> globalSymbol;
CComPtr<IDiaEnumTables> enumTables;
CComPtr<IDiaEnumSymbolsByAddr> enumSymbolsByAddr;

if (FAILED(hr = pSource->openSession(&pSession))) {
auto error = print_hr_failure(hr);
logger::info("Failed to open IDiaSession for pdb for dll {}+{:07X}\t{}", a_name, a_offset, error);
CoUninitialize();
return result;
}
hr = pSession->get_globalScope(&globalSymbol);
if (FAILED(hr)) {

if (FAILED(hr = pSession->get_globalScope(&globalSymbol))) {
auto error = print_hr_failure(hr);
logger::info("Failed to getGlobalScope for pdb for dll {}+{:07X}\t{}", a_name, a_offset, error);
CoUninitialize();
return result;
}

hr = pSession->getEnumTables(&enumTables);
if (FAILED(hr)) {
if (FAILED(hr = pSession->getEnumTables(&enumTables))) {
auto error = print_hr_failure(hr);
logger::info("Failed to getEnumTables for pdb for dll {}+{:07X}\t{}", a_name, a_offset, error);
CoUninitialize();
return result;
}
hr = pSession->getSymbolsByAddr(&enumSymbolsByAddr);
if (FAILED(hr)) {

if (FAILED(hr = pSession->getSymbolsByAddr(&enumSymbolsByAddr))) {
auto error = print_hr_failure(hr);
logger::info("Failed to getSymbolsByAddr for pdb for dll {}+{:07X}\t{}", a_name, a_offset, error);
CoUninitialize();
return result;
}
IDiaSymbol* publicSymbol;

CComPtr<IDiaSymbol> publicSymbol;
if (pSession->findSymbolByRVA(rva, SymTagEnum::SymTagPublicSymbol, &publicSymbol) == S_OK) {
auto publicResult = processSymbol(publicSymbol, pSession, rva, a_name, a_offset, result);

// Log the public result (already demangled in processSymbol)
logger::info("Public symbol found for {}+{:07X}: {}", a_name, a_offset, publicResult);

DWORD privateRva;
IDiaSymbol* privateSymbol;
if (
publicSymbol->get_targetRelativeVirtualAddress(&privateRva) == S_OK &&
CComPtr<IDiaSymbol> privateSymbol;
if (publicSymbol->get_targetRelativeVirtualAddress(&privateRva) == S_OK &&
pSession->findSymbolByRVA(privateRva, SymTagEnum::SymTagFunction, &privateSymbol) == S_OK) {
// Do stuff with private symbol
auto privateResult = processSymbol(privateSymbol, pSession, privateRva, a_name, a_offset, result);

// Log the private result (already demangled in processSymbol)
logger::info("Private symbol found for {}+{:07X}: {}", a_name, a_offset, privateResult);

// Combine results
if (!privateResult.empty() && !publicResult.empty()) {
result = fmt::format("{}\t{}", privateResult, publicResult);
} else if (!privateResult.empty()) {
result = privateResult;
} else {
result = publicResult;
}
privateSymbol->Release();
} else {
result = publicResult;
}
} else {
logger::info("No public symbol found for {}+{:07X}", a_name, a_offset);
}
publicSymbol->Release();
pSession->Release();

CoUninitialize();
return result;
}
Expand Down

0 comments on commit d5c0951

Please sign in to comment.