diff --git a/INSTALL/INSTALL.vcxproj b/INSTALL/INSTALL.vcxproj index adff494..46039ae 100644 --- a/INSTALL/INSTALL.vcxproj +++ b/INSTALL/INSTALL.vcxproj @@ -127,12 +127,12 @@ call %(identity) "$(OutDir)" "$(Configuration)" "$(SolutionDir)" call %(identity) "$(OutDir)" "$(Configuration)" "$(SolutionDir)" call %(identity) "$(OutDir)" "$(Configuration)" "$(SolutionDir)" - $(SolutionDir)..\bdsx\bedrock_server\ChakraCore.dll;$(SolutionDir)..\bdsx\bedrock_server\Chakra.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.pdb;%(Outputs) - $(SolutionDir)..\bdsx\bedrock_server\ChakraCore.dll;$(SolutionDir)..\bdsx\bedrock_server\Chakra.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.pdb;%(Outputs) - $(SolutionDir)..\bdsx\bedrock_server\ChakraCore.dll;$(SolutionDir)..\bdsx\bedrock_server\Chakra.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.pdb;%(Outputs) - $(OutDir)VCRUNTIME140_1.dll;$(OutDir)VCRUNTIME140_1.pdb;$(OutDir)ChakraCore.dll - $(OutDir)VCRUNTIME140_1.dll;$(OutDir)VCRUNTIME140_1.pdb;$(OutDir)ChakraCore.dll - $(OutDir)VCRUNTIME140_1.dll;$(OutDir)VCRUNTIME140_1.pdb;$(OutDir)ChakraCore.dll + $(SolutionDir)..\bdsx\bedrock_server\ChakraCore.dll;$(SolutionDir)..\bdsx\bedrock_server\Chakra.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.pdb;$(SolutionDir)..\bdsx\bedrock_server\pdbcachegen.exe;%(Outputs) + $(SolutionDir)..\bdsx\bedrock_server\ChakraCore.dll;$(SolutionDir)..\bdsx\bedrock_server\Chakra.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.pdb;$(SolutionDir)..\bdsx\bedrock_server\pdbcachegen.exe;%(Outputs) + $(SolutionDir)..\bdsx\bedrock_server\ChakraCore.dll;$(SolutionDir)..\bdsx\bedrock_server\Chakra.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.dll;$(SolutionDir)..\bdsx\bedrock_server\VCRUNTIME140_1.pdb;$(SolutionDir)..\bdsx\bedrock_server\pdbcachegen.exe;%(Outputs) + $(OutDir)VCRUNTIME140_1.dll;$(OutDir)VCRUNTIME140_1.pdb;$(OutDir)ChakraCore.dll;$(OutDir)pdbcachegen.exe + $(OutDir)VCRUNTIME140_1.dll;$(OutDir)VCRUNTIME140_1.pdb;$(OutDir)ChakraCore.dll;$(OutDir)pdbcachegen.exe + $(OutDir)VCRUNTIME140_1.dll;$(OutDir)VCRUNTIME140_1.pdb;$(OutDir)ChakraCore.dll;$(OutDir)pdbcachegen.exe @@ -142,6 +142,9 @@ {77e07fd4-ed4b-4aaf-8b7a-d5751a742203} + + {0db97edf-3343-4575-af19-0ef056d395b2} + diff --git a/INSTALL/install.bat b/INSTALL/install.bat index 74e99ba..b075eb9 100644 --- a/INSTALL/install.bat +++ b/INSTALL/install.bat @@ -3,13 +3,19 @@ set config=%~2 set solutiondir=%~3 call "..\bdsx\version.bat" +set /p BDS_VERSION=<"../../bdsx/bdsx/version-bds.json" +set BDS_VERSION=%BDS_VERSION:"=% if "%config%" == "Debug" ( set _debug=_debug set d=d ) + call :copydll "%solutiondir%..\bdsx\bedrock_server" -if "%config%" == "Release" call :zip +if "%config%" == "Release" ( + call :copydll "%solutiondir%release\bdsx-core-%BDSX_CORE_VERSION%" + call :zip "%solutiondir%release\bdsx-core-%BDSX_CORE_VERSION%" +) goto :eof :copydll @@ -18,11 +24,11 @@ copy "%outdir%Chakra.dll" "%~1\Chakra.dll" copy "%outdir%VCRUNTIME140_1.dll" "%~1\VCRUNTIME140_1.dll" copy "%outdir%VCRUNTIME140_1.pdb" "%~1\VCRUNTIME140_1.pdb" copy "%outdir%ChakraCore.dll" "%~1\ChakraCore.dll" +copy "%outdir%pdbcachegen.exe" "%~1\pdbcachegen.exe" EXIT /B 0 :zip -call :copydll "%solutiondir%release\bdsx-core-%BDSX_CORE_VERSION%" echo Zipping... -if exist "%solutiondir%release\bdsx-core-%BDSX_CORE_VERSION%.zip" del "%solutiondir%release\bdsx-core-%BDSX_CORE_VERSION%.zip" -call zip "%solutiondir%release\bdsx-core-%BDSX_CORE_VERSION%" "%solutiondir%release\bdsx-core-%BDSX_CORE_VERSION%.zip" +if exist "%~1.zip" del "%~1.zip" +call zip "%~1" "%~1.zip" EXIT /B 0 \ No newline at end of file diff --git a/bdsx-core.sln b/bdsx-core.sln index 900ad46..8f89b9c 100644 --- a/bdsx-core.sln +++ b/bdsx-core.sln @@ -38,6 +38,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "INSTALL", "INSTALL\INSTALL. EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chakra2chakracore", "chakra2chakracore\chakra2chakracore.vcxproj", "{77E07FD4-ED4B-4AAF-8B7A-D5751A742203}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pdbcachegen", "pdbcachegen\pdbcachegen.vcxproj", "{0DB97EDF-3343-4575-AF19-0EF056D395B2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -123,6 +125,12 @@ Global {77E07FD4-ED4B-4AAF-8B7A-D5751A742203}.Release|x64.Build.0 = Release|x64 {77E07FD4-ED4B-4AAF-8B7A-D5751A742203}.ReleaseTest|x64.ActiveCfg = Release|x64 {77E07FD4-ED4B-4AAF-8B7A-D5751A742203}.ReleaseTest|x64.Build.0 = Release|x64 + {0DB97EDF-3343-4575-AF19-0EF056D395B2}.Debug|x64.ActiveCfg = Debug|x64 + {0DB97EDF-3343-4575-AF19-0EF056D395B2}.Debug|x64.Build.0 = Debug|x64 + {0DB97EDF-3343-4575-AF19-0EF056D395B2}.Release|x64.ActiveCfg = Release|x64 + {0DB97EDF-3343-4575-AF19-0EF056D395B2}.Release|x64.Build.0 = Release|x64 + {0DB97EDF-3343-4575-AF19-0EF056D395B2}.ReleaseTest|x64.ActiveCfg = Release|x64 + {0DB97EDF-3343-4575-AF19-0EF056D395B2}.ReleaseTest|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/bdsx/cachedpdb.cpp b/bdsx/cachedpdb.cpp index dacb8c8..3afaab3 100644 --- a/bdsx/cachedpdb.cpp +++ b/bdsx/cachedpdb.cpp @@ -13,839 +13,15 @@ using namespace kr; -CachedPdb g_pdb; -BText<32> g_md5; - -kr::BText16 CachedPdb::predefinedForCore; - -namespace -{ - uint32_t getSelIndex(Text16 *text) noexcept - { - const char16* namepos = text->find_r(u'#'); - if (namepos != nullptr) - { - uint32_t value = text->subarr(namepos + 1).to_uint(); - text->cut_self(namepos); - return value; - } - return 0; - } - void* getPointer(const JsValue& value) noexcept - { - VoidPointer* ptr = value.getNativeObject(); - if (ptr == nullptr) return nullptr; - return ptr->getAddressRaw(); - } - ATTR_NORETURN void throwAsJsException(FunctionError& err) throws(JsException) - { - TSZ16 tsz; - tsz << (AnsiToUtf16)(Text)err.getFunctionName() << u": failed, "; - err.getMessageTo(&tsz); - tsz << u"(0x" << hexf(err.getErrorCode(), 8) << u")\n"; - throw JsException(tsz); - } - - CriticalSection s_lock; - - enum class CacheState - { - Found, - New, - NotMatched - }; - struct SymbolMaskInfo - { - uint32_t counter; - uint32_t mask; - }; - - struct SymbolCache - { - byte* base; - Keep> fos; - Keep file; - bool endWithLine = true; - - SymbolCache(pcstr16 filepath) noexcept - :fos(nullptr) - { - base = (byte*)GetModuleHandleW(nullptr); - if (filepath != nullptr) - { - try - { - file = File::openRW(filepath); - } - catch (Error&) - { - } - } - } - - void startWriting() noexcept - { - if (file != nullptr && fos == nullptr) fos = _new io::FOStream(file); - } - - CacheState checkMd5(io::FIStream* fis, bool overwrite) noexcept - { - try - { - auto md5 = fis->readLine(); - if (md5.first.empty()) { - file->toBegin(); - file->truncate(); - throw EofException(); - } - if (md5.first != g_md5) - { - if (!overwrite) { - cerr << "[BDSX] MD5 Hash does not Matched" << endl; - cerr << "[BDSX] pdb.ini MD5 = " << md5.first << endl; - cerr << "[BDSX] current MD5 = " << g_md5 << endl; - cerr << "[BDSX] Please use 'npm i' to update it" << endl; - return CacheState::NotMatched; - } - file->toBegin(); - file->truncate(); - throw EofException(); - } - if (!md5.second) - { - endWithLine = false; - } - } - catch (EofException&) - { - startWriting(); - *fos << g_md5 << "\r\n"; - return CacheState::New; - } - return CacheState::Found; - } - }; - struct SymbolMap:SymbolCache - { - Map targets; - - SymbolMap(pcstr16 filepath) noexcept - :SymbolCache(filepath) - { - } - - void put(Text16 tx) noexcept - { - const char16* namepos = tx.find_r(u'#'); - uint32_t selbit = 1U << getSelIndex(&tx); - - auto res = targets.insert(tx, { 0, selbit }); - if (!res.second) - { - res.first->second.mask |= selbit; - } - } - - bool del(Text16 name) noexcept - { - uint32_t selbit = 1U << getSelIndex(&name); - auto iter = targets.find(name); - if (iter == targets.end()) return false; - - if ((iter->second.mask & selbit) == 0) return false; - iter->second.mask &= ~selbit; - if (iter->second.mask == 0) - { - targets.erase(iter); - } - return true; - } - - bool test(Text16 name, void* address, TText16* line, Text16* nameWithIndex) noexcept - { - auto iter = targets.find(name); - if (iter == targets.end()) return false; - SymbolMaskInfo& selects = iter->second; - uint32_t bit = (1 << selects.counter); - uint32_t index = selects.counter++; - - if ((selects.mask & bit) == 0) return false; - selects.mask &= ~bit; - if (selects.mask == 0) - { - targets.erase(iter); - } - - *line << name; - if (index != 0) - { - *line << u'#' << index; - } - *nameWithIndex = *line; - - *line << u" = 0x" << hexf((byte*)address - base); - if (fos != nullptr) *fos << (Utf16ToUtf8)*line << "\r\n"; - return true; - } - - bool empty() noexcept - { - return targets.empty(); - } - - uintptr_t readOffset(io::FIStream* fis, TText16* name) throws(EofException) - { - for (;;) - { - auto line = fis->readLine(); - if (!line.second) endWithLine = false; - pcstr equal = line.first.find_r('='); - if (equal == nullptr) continue; - - *name = (Utf8ToUtf16)line.first.cut(equal).trim(); - if (!del(*name)) { - name->truncate(); - continue; - } - - Text value = line.first.subarr(equal + 1).trim(); - - if (value.startsWith("0x")) - { - return value.subarr(2).to_uintp(16); - } - else - { - return value.to_uintp(); - } - } - } - - }; - - struct __StreamWithPad - { - void* padForAntiCorruptingByVisualStudioBug; // https://developercommunity2.visualstudio.com/t/An-invalid-handle-was-specified-a-weird/1322604 - io::FIStream* const fis; - - __StreamWithPad(io::FIStream* fis) noexcept - :fis(fis) - { - } - ~__StreamWithPad() noexcept - { - delete fis; - } - operator io::FIStream*() noexcept - { - return fis; - } - io::FIStream* operator ->() noexcept - { - return fis; - } - }; - - struct DeferField - { - bool inited = false; - JsPropertyId typeIndex; - JsPropertyId index; - JsPropertyId size; - JsPropertyId flags; - JsPropertyId value; - JsPropertyId address; - JsPropertyId _register; - JsPropertyId scope; - JsPropertyId tag; - JsPropertyId name; - }; - Deferred s_field(JsRuntime::initpack); -} - -CachedPdb::CachedPdb() noexcept -{ -} -CachedPdb::~CachedPdb() noexcept -{ -} - -void CachedPdb::close() noexcept -{ - if (s_lock.tryEnter()) - { - m_pdb.close(); - s_lock.leave(); - } -} -int CachedPdb::setOptions(int options) throws(JsException) -{ - if (!s_lock.tryEnter()) throw JsException(u"BUSY. It's using by async task"); - int out = PdbReader::setOptions(options); - s_lock.leave(); - return out; -} -int CachedPdb::getOptions() throws(JsException) -{ - if (!s_lock.tryEnter()) throw JsException(u"BUSY. It's using by async task"); - int out = PdbReader::getOptions(); - s_lock.leave(); - return out; -} - -TText16 CachedPdb::undecorate(Text16 text, int flags) noexcept { - TText16 undecorated; - undecorated << PdbReader::undecorate(text.data(), flags); - return move(undecorated); -} - -autoptr CachedPdb::getProcAddress(pcstr16 predefined, pcstr name) noexcept -{ - Text txname = (Text)name; - CsLock __lock = s_lock; - - SymbolCache targets(predefined); - void* foundptr = nullptr; - - if (predefined != nullptr) - { - try - { - Must> fis = _new io::FIStream((File*)targets.file); - CacheState state = targets.checkMd5(fis, false); - if (state == CacheState::New) - { - cout << "[BDSX] Generating " << (Utf16ToAnsi)(Text16)predefined << endl; - } - else if (state == CacheState::NotMatched) - { - return nullptr; - } - else - { - TText16 name; - bool endWithLine = true; - for (;;) - { - auto line = fis->readLine(); - if (!line.second) endWithLine = false; - pcstr equal = line.first.find_r('='); - if (equal == nullptr) continue; - if (line.first.cut(equal).trim() != txname) continue; - Text value = line.first.subarr(equal + 1).trim(); - - if (value.startsWith("0x")) - { - return targets.base + value.subarr(2).to_uintp(16); - } - else - { - return targets.base + value.to_uintp(); - } - } - } - } - catch (EofException&) - { - if (!targets.endWithLine) - { - targets.startWriting(); - targets.fos->write("\r\n"); - } - } - } - - // load from pdb - try - { - cout << "[BDSX] PdbReader: Search Symbols..." << endl; - if (m_pdb.base() == nullptr) - { - m_pdb.load(); - } - - TText line; - line << txname; - foundptr = m_pdb.getFunctionAddress(line.c_str()); - if (foundptr != nullptr) { - line << " = 0x" << hexf((byte*)foundptr - (byte*)m_pdb.base()); - cout << line << endl; - targets.startWriting(); - *targets.fos << line << "\r\n"; - targets.fos->flush(); - } - } - catch (FunctionError& err) - { - cerr << err.getFunctionName() << ": failed, "; - { - TSZ tsz; - err.getMessageTo(&tsz); - cerr << tsz; - } - cerr << "(0x" << hexf(err.getErrorCode(), 8) << ')' << endl; - } - - if (foundptr == nullptr) - { - cerr << txname << " not found" << endl; - } - - return foundptr; -} -JsValue CachedPdb::getProcAddresses(pcstr16 predefined, JsValue out, JsValue array, bool quiet, uint32_t undecorateOpts) throws(kr::JsException) -{ - int length = array.getArrayLength(); - if (length == 0) return out; - - if (!s_lock.tryEnter()) throw JsException(u"BUSY. It's using by the async task"); - finally { s_lock.leave(); }; - - SymbolMap targets(predefined); - - for (int i = 0; i < length; i++) - { - targets.put(array.get(i).cast()); - } - - if (predefined != nullptr) - { - if (targets.file == nullptr) - { - throw JsException(TSZ16() << u"Failed to open " << predefined); - } - try - { - Must> fis = _new io::FIStream((File*)targets.file); - - CacheState state = targets.checkMd5(fis, true); - if (state == CacheState::New) - { - if (!quiet) g_ctx->log(TSZ16() << u"[BDSX] Generating " << predefined); - } - else if (state == CacheState::NotMatched) - { - // does nothing - } - else - { - for (;;) - { - TText16 name; - uintptr_t offset = targets.readOffset(fis, &name); - NativePointer* ptr = NativePointer::newInstance(); - ptr->setAddressRaw(targets.base + offset); - out.set(name, ptr); - if (targets.empty()) return out; - } - } - } - catch (EofException&) - { - if (!targets.endWithLine) - { - targets.startWriting(); - targets.fos->write("\r\n"); - } - } - } - - // load from pdb - try - { - if (!quiet) - { - size_t size = targets.targets.size(); - if (size > 5) - { - g_ctx->log(TSZ16() << u"[BDSX] PdbReader: Cache not found, " << size << u" symbols"); - } - else - { - for (auto& pair : targets.targets) - { - g_ctx->log(TSZ16() << u"[BDSX] PdbReader: Cache not found, " << (Text16)pair.first); - } - } - g_ctx->log(u"[BDSX] PdbReader: Search Symbols..."); - } - if (m_pdb.base() == nullptr) - { - m_pdb.load(); - } - - struct Local - { - SymbolMap& targets; - bool quiet; - JsValue& out; - uint32_t undecorateOpts; - JsException exception; - } local = {targets, quiet, out, undecorateOpts }; - - targets.startWriting(); - - m_pdb.search16(nullptr, [&local](Text16 name, void* address, uint32_t typeId) { - TText16 undecorated; - if (local.undecorateOpts != -1) - { - undecorated << PdbReader::undecorate(name.data(), local.undecorateOpts); - name = undecorated; - } - Text16 nameWithIndex; - TText16 line; - if (!local.targets.test(name, address, &line, &nameWithIndex)) - { - return true; - } - if (!local.quiet) - { - g_ctx->log(line); - } - NativePointer* ptr = NativePointer::newInstance(); - ptr->setAddressRaw(address); - try - { - local.out.set(JsValue(nameWithIndex), ptr); - return !local.targets.empty(); - } - catch (JsException& ex) - { - local.exception = move(ex); - return false; - } - }); - if (!local.exception.isEmpty()) - { - throw move(local.exception); - } - if (targets.fos != nullptr) targets.fos->flush(); - } - catch (FunctionError& err) - { - throwAsJsException(err); - } - - if (!targets.empty()) - { - for (auto& item : targets.targets) - { - Text name = item.first; - if (!quiet) g_ctx->log(TSZ16() << Utf8ToUtf16(name) << u" not found"); - } - } - - return out; -} - -void CachedPdb::search(JsValue masks, JsValue cb) throws(kr::JsException) -{ - try - { - if (m_pdb.base() == nullptr) - { - m_pdb.load(); - } - - TText filter; - const char* filterstr = nullptr; - switch (masks.getType()) - { - case JsType::String: - filter = masks.cast(); - filter << '\0'; - filterstr = filter.data(); - break; - case JsType::Null: - break; - case JsType::Function: - cb = move(masks); - break; - case JsType::Object: { - // array - Map finder; - int length = masks.getArrayLength(); - finder.reserve((size_t)length * 2); - - for (int i = 0; i < length; i++) - { - TText text = masks.get(i).cast(); - finder.insert(text, i); - } - - m_pdb.search(nullptr, [&](Text name, void* address, uint32_t typeId) { - auto iter = finder.find(name); - if (finder.end() == iter) return true; - - NativePointer* ptr = NativePointer::newInstance(); - ptr->setAddressRaw(address); - - int index = iter->second; - return cb(masks.get(index), ptr, index).cast(); - }); - return; - } - } - JsException exception; - m_pdb.search(filterstr, [&](Text name, void* address, uint32_t typeId) { - NativePointer* ptr = NativePointer::newInstance(); - ptr->setAddressRaw(address); - try - { - return cb(TText16() << (Utf8ToUtf16)name, ptr).cast(); - } - catch (JsException& ex) - { - exception = move(ex); - return false; - } - }); - if (!exception.isEmpty()) - { - throw move(exception); - } - } - catch (FunctionError& err) - { - throwAsJsException(err); - } -} -JsValue CachedPdb::getAll(JsValue onprogress) throws(kr::JsException) -{ - try - { - bool callback = onprogress.getType() == JsType::Function; - if (!callback) g_ctx->log(u"[BDSX] PdbReader: Search Symbols..."); - - if (m_pdb.base() == nullptr) - { - m_pdb.load(); - } - - struct Local - { - JsValue out = JsNewObject; - timepoint now; - JsValue onprogress; - uintptr_t totalcount; - bool callback; - JsException exception; - - bool report() noexcept - { - if (callback) - { - try - { - JsValue res = onprogress.call(totalcount); - return res.getType() != JsType::Boolean || res.as(); - } - catch (JsException& ex) - { - exception = move(ex); - return false; - } - } - else - { - TSZ16 tsz; - tsz << u"[BDSX] PdbReader: Get symbols (" << totalcount << u')'; - g_ctx->log(tsz); - } - return true; - } - } local; - local.now = timepoint::now(); - local.callback = callback; - local.totalcount = 0; - if (callback) - { - local.onprogress = onprogress; - local.report(); - } - m_pdb.getAll16([&local](Text16 name, autoptr address, int typeId) { - ++local.totalcount; - timepoint newnow = timepoint::now(); - - NativePointer* ptr = NativePointer::newInstance(); - ptr->setAddressRaw(address); - local.out.set(name, ptr); - if (newnow - local.now > 500_ms) - { - local.now = newnow; - return local.report(); - } - return true; - }); - - local.report(); - if (!callback) - { - g_ctx->log(TSZ16() << u"[BDSX] PdbReader: done (" << local.totalcount << u")"); - } - else if (!local.exception.isEmpty()) - { - throw move(local.exception); - } - return local.out; - } - catch (FunctionError& err) - { - throwAsJsException(err); - } -} -void CachedPdb::getAllEx(JsValue cb) throws(kr::JsException) -{ - try - { - if (cb.getType() != JsType::Function) throw kr::JsException(u"function required"); - if (m_pdb.base() == nullptr) - { - m_pdb.load(); - } - - if (!s_field->inited) - { - s_field->inited = true; - s_field->typeIndex = JsPropertyId(u"typeIndex"); - s_field->index = JsPropertyId(u"index"); - s_field->size = JsPropertyId(u"size"); - s_field->flags = JsPropertyId(u"flags"); - s_field->value = JsPropertyId(u"value"); - s_field->address = JsPropertyId(u"address"); - s_field->_register = JsPropertyId(u"register"); - s_field->scope = JsPropertyId(u"scope"); - s_field->tag = JsPropertyId(u"tag"); - s_field->name = JsPropertyId(u"name"); - } - - struct Local - { - JsValue out = JsNewArray(); - timepoint now; - JsValue cb; - JsException exception; - - bool callback; - int counter = 0; - - bool flush() throws(JsException) - { - if (counter == 0) return true; - bool res = cb(out) != false; - out.setArrayLength(0); - counter = 0; - return res; - } - } local; - local.now = timepoint::now(); - local.cb = cb; - m_pdb.getAllEx([&local](Text name, SYMBOL_INFO* info) { - size_t size = name.size(); - JsValue tuple = JsNewObject; - tuple.set(s_field->typeIndex, (int)info->TypeIndex); - tuple.set(s_field->index, (int)info->Index); - tuple.set(s_field->size, (int)info->Size); - tuple.set(s_field->flags, (int)info->Flags); - - NativePointer* value = NativePointer::newInstance(); - value->setAddressRaw((void*)info->Value); - tuple.set(s_field->value, value); - - NativePointer* addr = NativePointer::newInstance(); - addr->setAddressRaw((void*)info->Address); - tuple.set(s_field->address, addr); - - tuple.set(s_field->_register, (int)info->Register); - tuple.set(s_field->scope, (int)info->Scope); - tuple.set(s_field->tag, (int)info->Tag); - tuple.set(s_field->name, TSZ16() << (NoneToUtf16)name); - local.out.set(local.counter++, tuple); - - timepoint newnow = timepoint::now(); - if (newnow - local.now > 100_ms) - { - local.now = newnow; - try - { - return local.flush(); - } - catch (JsException& ex) - { - local.exception = move(ex); - return false; - } - } - return true; - }); - - if (!local.exception.isEmpty()) - { - throw move(local.exception); - } - local.flush(); - } - catch (FunctionError& err) - { - throwAsJsException(err); - } -} JsValue getPdbNamespace() noexcept { JsValue pdb = JsNewObject; - pdb.set(u"coreCachePath", (Text16)CachedPdb::predefinedForCore); - pdb.setMethod(u"close", [] { return g_pdb.close(); }); - pdb.setMethod(u"setOptions", [](int options) { return g_pdb.setOptions(options); }); - pdb.setMethod(u"getOptions", []() { return g_pdb.getOptions(); }); - pdb.setMethod(u"undecorate", [](Text16 text, int flags)->TText16 { return g_pdb.undecorate(text, flags); }); - pdb.setMethod(u"search", [](JsValue masks, JsValue cb) { return g_pdb.search(masks, cb); }); - pdb.setMethod(u"getProcAddresses", [](JsValue out, JsValue array, bool quiet, bool undecorated) { return g_pdb.getProcAddresses(CachedPdb::predefinedForCore.data(), out, array, quiet, undecorated); }); - - JsValue getList = JsFunction::makeT([](Text16 predefined, JsValue out, JsValue array, bool quiet, JsValue undecorateOpts) { - return g_pdb.getProcAddresses(predefined.data(), out, array, quiet, undecorateOpts.abstractEquals(nullptr) ? -1 : undecorateOpts.as()); - }); - pdb.set(u"getList", getList); - pdb.setMethod(u"getAll", [](JsValue onprogress) { return g_pdb.getAll(onprogress); }); - pdb.setMethod(u"getAllEx", [](JsValue onprogress) { return g_pdb.getAllEx(onprogress); }); + pdb.setMethod(u"setOptions", [](int options) { return PdbReader::setOptions(options); }); + pdb.setMethod(u"getOptions", []() { return PdbReader::getOptions(); }); + pdb.setMethod(u"undecorate", [](Text16 text, int flags)->TText16 { + TText16 undecorated; + undecorated << PdbReader::undecorate(text.data(), flags); + return move(undecorated); + }); return pdb; } - -// async pdb, not completed -//getList.setMethod(u"async", [](Text16 predefined, JsValue out, JsValue array, bool quiet) { -// -// Array list; -// -// AText buffer; -// buffer.reserve(1024); -// -// int32_t len = array.getArrayLength(); -// for (int i = 0; i < len; i++) -// { -// size_t front = buffer.size(); -// buffer << (Utf16ToUtf8)array.get(i).cast(); -// size_t back = buffer.size(); -// buffer << '\0'; -// -// list.push(Text((pcstr)front, (pcstr)back)); -// } -// -// ThreadHandle::createLambda([predefined = AText16::concat(predefined, nullterm), quiet, buffer = move(buffer), list = move(list)]() mutable{ -// intptr_t off = (intptr_t)buffer.data(); -// for (Text& v : list) -// { -// v.addBegin(off); -// v.addEnd(off); -// } -// struct Local -// { -// AText out; -// } local; -// local.out.reserve(1024); -// g_pdb.getProcAddressesT(predefined.data(), list, [](Text name, void* fnptr, Local* param) { -// -// }, & local, quiet); -// AsyncTask::post([predefined = move(predefined), quiet, buffer = move(buffer), list = move(list)]() mutable{ -// -// }); -// }); -// }); \ No newline at end of file diff --git a/bdsx/cachedpdb.h b/bdsx/cachedpdb.h index cd2bc5a..9b2a3e9 100644 --- a/bdsx/cachedpdb.h +++ b/bdsx/cachedpdb.h @@ -3,36 +3,5 @@ #include #include -class CachedPdb -{ -private: - kr::PdbReader m_pdb; - -public: - static kr::BText16 predefinedForCore; - using Callback = void(*)(kr::Text16 name, void* fnptr, void* param); - - CachedPdb() noexcept; - ~CachedPdb() noexcept; - void close() noexcept; - int setOptions(int options) throws(kr::JsException); - int getOptions() throws(kr::JsException); - kr::TText16 undecorate(kr::Text16 text, int flags) noexcept; - kr::autoptr getProcAddress(kr::pcstr16 predefined, kr::pcstr name) noexcept; - template - bool getProcAddressesT(kr::pcstr16 predefined, kr::View text, void(*cb)(kr::Text16 name, void* fnptr, T* param), T* param, bool quiet) noexcept - { - static_assert(sizeof(T*) == sizeof(void*), "pointer size unmatched"); - return getProcAddresses(predefined, text, (Callback)cb, param, quiet); - } - kr::JsValue getProcAddresses(kr::pcstr16 predefined, kr::JsValue out, kr::JsValue array, bool quiet, uint32_t undecorateOpts) throws(kr::JsException); - void search(kr::JsValue masks, kr::JsValue cb) throws(kr::JsException); - kr::JsValue getAll(kr::JsValue onprogress) throws(kr::JsException); - void getAllEx(kr::JsValue cb) throws(kr::JsException); -}; - -extern CachedPdb g_pdb; -extern kr::BText<32> g_md5; - kr::JsValue getPdbNamespace() noexcept; diff --git a/bdsx/netfilter.cpp b/bdsx/netfilter.cpp index 4005990..7c89e30 100644 --- a/bdsx/netfilter.cpp +++ b/bdsx/netfilter.cpp @@ -490,7 +490,7 @@ JsValue getNetFilterNamespace() noexcept if (iptext.empty()) return false; return NetFilter::removeFilter(Ipv4Address(TSZ() << toNone(iptext))); }); - ipfilter.setMethod(u"entires", NetFilter::entries); + ipfilter.setMethod(u"entries", NetFilter::entries); ipfilter.setMethod(u"init", [](JsValue callbackOnExceeded) { NetFilter::init(callbackOnExceeded); }); diff --git a/bdsx/nodegate.cpp b/bdsx/nodegate.cpp index 54abb29..8e5be3f 100644 --- a/bdsx/nodegate.cpp +++ b/bdsx/nodegate.cpp @@ -58,6 +58,7 @@ namespace s_inited = true; v8::Isolate* isolate = context->GetIsolate(); node::AddEnvironmentCleanupHook(isolate, [](void*) { clear(); }, nullptr); + node::AtExit([](void*) { clear(); }); nodegate::initNativeModule(*exports); atexit(clear); } diff --git a/bdsx/sehandler.cpp b/bdsx/sehandler.cpp index 0bfc6f0..289f29c 100644 --- a/bdsx/sehandler.cpp +++ b/bdsx/sehandler.cpp @@ -7,6 +7,7 @@ #include "unwind.h" #include +#include #include #include #include @@ -61,7 +62,7 @@ namespace Array getStack(EXCEPTION_POINTERS* exptr, HANDLE thread) noexcept { - PdbReader::setOptions(0x00000002); + PdbReader::setOptions(SYMOPT_UNDNAME); try { for (int i = 0; i < 3; i++) { diff --git a/bdsx/version.bat b/bdsx/version.bat index 2153782..2a2ba31 100644 --- a/bdsx/version.bat +++ b/bdsx/version.bat @@ -1,2 +1,2 @@ -set BDSX_CORE_VERSION=1.0.12.2 +set BDSX_CORE_VERSION=1.0.13.1 diff --git a/pdbcachegen/pdbcachegen.aps b/pdbcachegen/pdbcachegen.aps new file mode 100644 index 0000000..0d7377d Binary files /dev/null and b/pdbcachegen/pdbcachegen.aps differ diff --git a/pdbcachegen/pdbcachegen.cpp b/pdbcachegen/pdbcachegen.cpp new file mode 100644 index 0000000..b17833b --- /dev/null +++ b/pdbcachegen/pdbcachegen.cpp @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../bdsx/gen/version.h" +#include "pdbcachegen.h" + +using namespace kr; + +uint32_t hashString(Text16 v) noexcept { + uint32_t out = 0; + uint32_t shift = 0; + uint32_t n = intact(v.size()); + for (uint32_t i = 0; i < n;i++) { + uint32_t chr = v[i] + i; + out += (chr << shift) | (chr >> (32 - shift)); + shift = (shift + 7) & 0x1f; + } + out += n; + return out; +} + +struct Symbol { + AText16 name; + void* address; +}; + +struct OffsetEntry { + uint32_t hash; + uint32_t nameOffset; + uint32_t rva; +}; + +int wmain(int argn, const wchar_t** args) { + byte md5[encoder::Md5Context::SIZE]; + filetime_t exeModifiedTime; + + bool force = false; + int argi = 0; + + pcstr16 exePath = nullptr, cachePath = nullptr; + + for (;;) { + pcstr16 arg = unwide(*args++); + if (arg == nullptr) break; + Text16 argtx = (Text16)arg; + if (argtx == u"-f") { + force = true; + } + else { + switch (argi) { + case 0: + exePath = arg; + break; + case 1: + cachePath = arg; + break; + default: + cerr << "Too many arguments" << endl; + return EINVAL; + } + argi++; + } + } + if (argi < 2) { + cerr << "Need two arguments at least" << endl; + cerr << "ex) pdbcachegen.exe (exe) (cachefile)" << endl; + return EINVAL; + } + try { + Must exe = File::open(exePath); + exeModifiedTime = exe->getLastModifiedTime(); + encoder::Md5Context ctx; + ctx.reset(); + ctx.update((TBuffer)exe->readAll()); + ctx.finish(md5); + } + catch (Error&) { + cerr << "exe file not found." << endl; + return ERROR_NOT_FOUND; + } + try { + Must cache = File::open(cachePath); + if (!force) { + if (exeModifiedTime < cache->getLastModifiedTime()) { + PdbCacheHeader header; + cache->read(&header, sizeof(header)); + if (header.version == PdbCacheHeader::VERSION) { + if (memcmp(header.md5, md5, encoder::Md5Context::SIZE) == 0) { + cout << "pdbcache.bin: latest" << endl; + return S_OK; + } + } + } + } + } + catch (Error&) { + } + Array symbols; + symbols.reserve(100000); + + cout << "pdbcache.bin: generating..." << endl; + cout.flush(); + + PdbReader pdb; + uint64_t base = 0x1000000; + pdb.load(base, exePath); + pdb.setOptions(SYMOPT_PUBLICS_ONLY); + pdb.getAll16([&symbols](Text16 name, autoptr64 address, uint32_t typeId) { + symbols.push({ name, address }); + return true; + }); + pdb.setOptions(SYMOPT_NO_PUBLICS); + + pdb.getAll16([&symbols](Text16 name, autoptr64 address, uint32_t typeId) { + symbols.push({ name, address }); + return true; + }); + + size_t capacity = symbols.size() * 2; + + Array keyTable; + keyTable.resize(capacity); + keyTable.zero(); + + size_t offsetBegin = capacity * sizeof(OffsetEntry) + sizeof(PdbCacheHeader); + Array names; + names.reserve(capacity*5); + + PdbCacheHeader header; + header.version = PdbCacheHeader::VERSION; + header.hashMapCapacity = intact(capacity); + memcpy(header.md5, md5, encoder::Md5Context::SIZE); + + Text16 mainText = u"main"; + uint32_t mainHash = hashString(mainText); + + size_t dupMax = 0; + for (Symbol& sym : symbols) { + uint32_t hash = hashString(sym.name); + if (sym.name == u"vsnprintf") debug(); + uint32_t rva = intact((uint64_t)sym.address - base); + if (hash == mainHash && sym.name == mainText) { + header.mainRva = rva; + } + + uint32_t index = hash; + size_t dupCount = 0; + for (;;) { + index %= capacity; + OffsetEntry& entry = keyTable[index]; + if (entry.nameOffset != 0) { + dupCount++; + index++; + continue; + } + entry.hash = hash; + entry.nameOffset = intact(names.size() + offsetBegin); + entry.rva = rva; + break; + } + + names << (Utf16ToUtf8)(sym.name); + names.write('\0'); + } + + io::FileStream* file = File::create(cachePath)->stream(); + file->writeas(header); + file->write((Buffer)keyTable); + file->write((Buffer)names); + delete file; + + cout << "pdbcache.bin: generated" << endl; + return S_OK; +} diff --git a/pdbcachegen/pdbcachegen.h b/pdbcachegen/pdbcachegen.h new file mode 100644 index 0000000..ecb46a7 --- /dev/null +++ b/pdbcachegen/pdbcachegen.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +struct PdbCacheHeader { + static constexpr uint32_t VERSION = 1; + + uint32_t version; + kr::byte md5[kr::encoder::Md5Context::SIZE]; + uint32_t mainRva; + uint32_t hashMapCapacity; +}; diff --git a/pdbcachegen/pdbcachegen.vcxproj b/pdbcachegen/pdbcachegen.vcxproj new file mode 100644 index 0000000..906bc3b --- /dev/null +++ b/pdbcachegen/pdbcachegen.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {0db97edf-3343-4575-af19-0ef056d395b2} + pdbcachegen + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + + + + + + {f5212e7b-4791-4488-a23a-57af3023e7d3} + + + + + + + + + \ No newline at end of file diff --git a/pdbcachegen/pdbcachegen.vcxproj.filters b/pdbcachegen/pdbcachegen.vcxproj.filters new file mode 100644 index 0000000..56f97a4 --- /dev/null +++ b/pdbcachegen/pdbcachegen.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/pdbcachegen/pdbcachegen.vcxproj.user b/pdbcachegen/pdbcachegen.vcxproj.user new file mode 100644 index 0000000..f8cfa17 --- /dev/null +++ b/pdbcachegen/pdbcachegen.vcxproj.user @@ -0,0 +1,22 @@ + + + + true + + + -f + WindowsLocalDebugger + + + -f + WindowsLocalDebugger + + + -f + WindowsLocalDebugger + + + -f + WindowsLocalDebugger + + \ No newline at end of file