Hi ! I was looking for here and there for portable (windows & linux) call stack determination functionality - but could not find any. I have some experience with call stack determination, but only on windows - made memory leak detection utility for windows once upon a time. But all API's with which I have played around are windows based. Now wanted to go deeper with call stack determination and moreover make functionality portable - to start from windows & linux, later on expanding to android. Brief analysis showed a lot of complexity without any clear / good / portable library. Analysis came upon dbghelp.dll which is implemented in Wine - I have noticed that besides windows PE file format it contains also support for loading debug information for DWARF / ELF file format. I've got curious, and started to prototype. I've made windows project where I have dragged some parts of wine functionality. (LGPL licensed) Something similar was done once in here: https://github.com/vtjnash/dbghelp2 but he have used cross compilation toolchain, I wanted to use Visual studio + microsoft compiler. My own repository is located here:https://github.com/tapika/dbghelp2
My prime target is 64-bit windows, 32-bit is nice-to-have. Well - dll itself is nice, but I needed test application. Quick analysis came upon https://github.com/KjellKod/g3log%C2%A0that logger was using dbghelp - official microsoft distribution. After some fighting with compilation and linking errors I've managed to detach dbghelp from wine into separate .dll, I've called it dbghelp2.dll. But at run-time library refused to work. I'll try to list here problems upon which I've came accross: 32-bit compilation: A lot of unsupported traces in base functionality: 976c:fixme:dbghelp_msc:c:\prototyping\dbghelp2\dlls\dbghelp\msc.c 725: Unsupported type 1404 in ENUM field list976c:fixme:dbghelp_msc:c:\prototyping\dbghelp2\dlls\dbghelp\msc.c 2038: Unsupported symbol id 1124976c:fixme:dbghelp_msc:c:\prototyping\dbghelp2\dlls\dbghelp\msc.c 123: 00000000: 06 00 24 11 73 74 64 00 ..$.std. 976c:fixme:dbghelp_msc:c:\prototyping\dbghelp2\dlls\dbghelp\msc.c 2038: Unsupported symbol id 1124976c:fixme:dbghelp_msc:c:\prototyping\dbghelp2\dlls\dbghelp\msc.c 123: 00000000: 1a 00 24 11 5f 48 61 73 5f 41 44 4c 5f 73 77 61 ..$._Has_ADL_swa976c:fixme:dbghelp_msc:c:\prototyping\dbghelp2\dlls\dbghelp\msc.c 123: 00000010: 70 5f 64 65 74 61 69 6c 00 00 00 00 p_detail.... 976c:fixme:dbghelp_msc:c:\prototyping\dbghelp2\dlls\dbghelp\msc.c 2038: Unsupported symbol id 1124976c:fixme:dbghelp_msc:c:\prototyping\dbghelp2\dlls\dbghelp\msc.c 123: 00000000: 0a 00 24 11 72 65 6c 5f 6f 70 73 00 ..$.rel_ops. ... same problems occurs in 64-bit compilation. Finally call stack determination refused to work:976c:fixme:dbghelp:c:\prototyping\dbghelp2\dlls\dbghelp\stack.c 59: Failed to linearize address 771f:7000 (mode 0) 64-bit compilation: I've noticed that addressing uses 32-bit unsigned long, while 64-bit addressing mode requires uint64_t. Besides this g3log also was using incorrect API's - I've ended up replacing SymGetSymFromAddr64 with SymFromAddr. call stack determination seems to work, also function resolving, but not source code position or line information, what Microsoft dbghelp.dll can do out of box. So now dbghelp2.dll can determine stack like this: ******* STACKDUMP *******stack using dbghelp2.dll stack dump [0] std::basic_string<char,std::char_traits<char>,std::allocator<char> >::appendstack dump [1] std::basic_string<char,std::char_traits<char>,std::allocator<char> >::appendstack dump [2] example_fatal::tryToKillWithAccessingIllegalPointerstack dump [3] mainstack dump [4] invoke_mainstack dump [5] __scrt_common_main_sehstack dump [6] __scrt_common_mainstack dump [7] mainCRTStartupstack dump [8] stack dump [9] while Microsoft's implementation determines it like this:stack dump [0] c:\program files (x86)\microsoft visual studio\2017\enterprise\vc\tools\msvc\14.16.27023\include\xstring L: 2571 std::basic_string<char,std::char_traits<char>,std::allocator<char> >::appendstack dump [1] c:\program files (x86)\microsoft visual studio\2017\enterprise\vc\tools\msvc\14.16.27023\include\xstring L: 2593 std::basic_string<char,std::char_traits<char>,std::allocator<char> >::appendstack dump [2] c:\prototyping\dbghelp2\g3log\example\main_sigsegv.cpp L: 52 example_fatal::tryToKillWithAccessingIllegalPointerstack dump [3] c:\prototyping\dbghelp2\g3log\example\main_sigsegv.cpp L: 115 mainstack dump [4] d:\agent_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl L: 79 invoke_mainstack dump [5] d:\agent_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl L: 288 __scrt_common_main_sehstack dump [6] d:\agent_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl L: 331 __scrt_common_mainstack dump [7] d:\agent_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp L: 17 mainCRTStartupstack dump [8] BaseThreadInitThunkstack dump [9] RtlUserThreadStart Noticed stack frames [8] & [9] contains additional functions, not available in wine port. Here is 64-bit enabling commit: https://github.com/tapika/dbghelp2/commit/3a3e9daacae14c4f73585aeff4ef53a207...
Basically my intention was to prototype that it's possible to determine call stack using wine dbghelp parts. - Does it makes any sense to try to make my dbghelp prototype be more or less official / or create such version which would be suitable for wine and for standalone compilation ? - I would be interested in further development of dbghelp - as standalone dll / so independently from wine. At the moment dbghelp serves like read / parse of existing file formats (PE & ELF), has anyone considered of expanding support towards generation of same file formats ? See also CV2PDB: https://github.com/rainers/cv2pdbclang pdb generation support: http://blog.llvm.org/2017/08/llvm-on-windows-now-supports-pdb-debug.html
-- Have a nice day! Tarmo.
Tarmo Pikaro tapika@yahoo.com writes:
Here is 64-bit enabling commit:
https://github.com/tapika/dbghelp2/commit/3a3e9daacae14c4f73585aeff4ef53a207...
Basically my intention was to prototype that it's possible to determine call stack using wine dbghelp parts.
- Does it makes any sense to try to make my dbghelp prototype be more or less official / or create such version which would be suitable for wine and for standalone compilation ?
For what you want, you should be able to use Wine's dbghelp unmodified. Things that are legitimate bug fixes (like getting rid of 'long') can be merged upstream. Other stuff like missing WideCharToMultiByte can be addressed by providing the missing functions in your own separate object file, without changing the source.
- I would be interested in further development of dbghelp - as standalone dll / so independently from wine.
At the moment dbghelp serves like read / parse of existing file formats (PE & ELF), has anyone considered of expanding support towards generation of same file formats ?
There was an effort a long time ago to generate PDB files from the Unix data, but it never got good enough for real use. Doing this at the compiler level is probably more realistic.
Tarmo Pikaro tapika@yahoo.com writes:
Here is 64-bit enabling commit:
https://github.com/tapika/dbghelp2/commit/3a3e9daacae14c4f73585aeff4ef53a207...
Basically my intention was to prototype that it's possible to determine call stack using wine dbghelp parts.
- Does it makes any sense to try to make my dbghelp prototype be more or less official / or create such version which would be suitable for wine and for standalone compilation ?
For what you want, you should be able to use Wine's dbghelp unmodified.
Hmm... Basically use wine, and run windows executable, but load elf file instead ? Hmm... That sounds like a plan, will try this later on.I have already tried to cross compile wine in linux subsystem for windows.
Things that are legitimate bug fixes (like getting rid of 'long') can be merged upstream.
Ok, but I still bit suspicion on potential regressions, I don't fully know dbghelp area where it should work and where it should not.
Other stuff like missing WideCharToMultiByte can be addressed by providing the missing functions in your own separate object file, without changing the source.
I've scanned quite many unicode libraries and concluded of bringing up cutf library alive - that is basically a replacement forWideCharToMultiByte & windows api - uses less call arguments, but can achieve stuff. I've posted one message to reddit about cutf library in here:https://www.reddit.com/r/cpp/comments/bwdjjq/utf8_wide_character_conversion_... and already prototyped patching dbghelp library, see:https://github.com/tapika/dbghelp2/commit/b516e1ca2f47055c994a3ede9fc8c28dfa...
One thing which I'm not so sure about - is whether or not you actually use code pages (and which) in dbghelp.dll.
- I would be interested in further development of dbghelp - as standalone dll / so independently from wine.
At the moment dbghelp serves like read / parse of existing file formats (PE & ELF), has anyone considered of expanding support towards generation of same file formats ? There was an effort a long time ago to generate PDB files from the Unix data, but it never got good enough for real use. Doing this at the compiler level is probably more realistic.
I would like to head into direction of having only one compilation instead of multiple -https://www.reddit.com/r/cpp/comments/bjgt0j/make_portable_dll_without_secon...
basically replace .so & .dll file format with one centralized file format, which can be run on multiple platforms. (e.g. linux and windows) Please don't tell me it will be a long way to get there, I know that - but there already exists prototypeswho managed to make this happen. I think wine's dbghelp is good place to start from, butwe might indeed require native compilers support, need to locate good guys for this job. C# .net already compiles only one compilation .net assembly, why same cannot be done with C++ ? Suspect that need to set up "discussion" bridges to Microsoft compiler, gcc and clang teams. I had already some experience with Android platform development and know about linker limitations on that platform. Suspect might need to take some development effort to make dll loader, file format, call stack to be fully platform agnostic.
Tarmo Pikaro tapika@yahoo.com writes:
Other stuff like missing WideCharToMultiByte can be addressed by providing the missing functions in your own separate object file, without changing the source.
I've scanned quite many unicode libraries and concluded of bringing up cutf library alive - that is basically a replacement for WideCharToMultiByte & windows api - uses less call arguments, but can achieve stuff.
My point is that you can write a simple WideCharToMultiByte wrapper on top of your cutf library, and then you don't need to patch dbghelp at all, only to link it against your wrapper.
I would like to head into direction of having only one compilation instead of multiple - https://www.reddit.com/r/cpp/comments/bjgt0j/make_portable_dll_without_secon...
basically replace .so & .dll file format with one centralized file format, which can be run on multiple platforms. (e.g. linux and windows)
Please don't tell me it will be a long way to get there, I know that - but there already exists prototypes who managed to make this happen. I think wine's dbghelp is good place to start from, but we might indeed require native compilers support, need to locate good guys for this job.
The file format is not the interesting part, it's easy to do a PE loader on Linux, or an ELF loader on Windows. You could probably also write a PE<->ELF converter without too much trouble.
The thing is once you have loaded the binary, it's going to call APIs, and these are completely different between platforms. So you need either an abstraction layer to wrap all the APIs, or something like Wine to make the APIs compatible. That's where the real work is.
On Wednesday, June 5, 2019, 10:44:25 AM GMT+3, Alexandre Julliard julliard@winehq.org wrote:
Tarmo Pikaro tapika@yahoo.com writes:
Other stuff like missing WideCharToMultiByte can be addressed by providing the missing functions in your own separate object file, without changing the source.
I've scanned quite many unicode libraries and concluded of bringing up cutf library alive - that is basically a replacement for WideCharToMultiByte & windows api - uses less call arguments, but can achieve stuff. My point is that you can write a simple WideCharToMultiByte wrapper on top of your cutf library, and then you don't need to patch dbghelp at all, only to link it against your wrapper.
I think wrapping can go in either direction, but you must admit that writing: len = utf8zestimate(searchPath);
is shorter than: len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
by 6-1 = 5 parameters, and this form: utf8ztowchar(searchPath, sp, len);
is also shorter than: MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len); by 6 - 3 = 3 parameters.
What I'm afraid is usage of active code page (CP_ACP), which might be run-time configured via some locale command. I have tried to search some documentation on active PE structures, but could not find any - suspect it's relatively ancient structures without decent documentation. Made once upon a time as ascii, and never were designed from unicode perspective. Btw - windows C++ provides such classes as CA2W(), CW2A() - it can give even shorter form - wondering if dbghelp can be upgraded to C++ ? (pros / cons?)
I would like to head into direction of having only one compilation instead of multiple - https://www.reddit.com/r/cpp/comments/bjgt0j/make_portable_dll_without_secon...
basically replace .so & .dll file format with one centralized file format, which can be run on multiple platforms. (e.g. linux and windows)
Please don't tell me it will be a long way to get there, I know that - but there already exists prototypes who managed to make this happen. I think wine's dbghelp is good place to start from, but we might indeed require native compilers support, need to locate good guys for this job.
The file format is not the interesting part, it's easy to do a PE loader on Linux, or an ELF loader on Windows. You could probably also write a PE<->ELF converter without too much trouble.
The thing is once you have loaded the binary, it's going to call APIs, and these are completely different between platforms. So you need either an abstraction layer to wrap all the APIs, or something like Wine to make the APIs compatible. That's where the real work is.
Yes, but I would like to start from basics - enable functionality initially, and worry about which dllexports and what later on. I think we could have even our own (application) specific "standard" API.
Btw - I have tried to load from wine / LoadLibrary linux shared object, but could not. Tried something like this:
void stackCallbacker() { printf("%s", g3::internal::stackdump().c_str()); } typedef void(*func)(void); typedef void(*fcallFunc)(func);
int main(int argc, char **argv) { //HMODULE h = LoadLibraryA("dll.dll"); const char* dllName = "dll"; //const char* dllName = "/mnt/c/Prototyping/dbghelp2_dev2/bin/Release_x86/dll.so"; HMODULE h = LoadLibraryA(dllName); if (h == 0) { printf("Failed to load '%s'\n", dllName); return 0; } fcallFunc fc; *((FARPROC*)&fc) = GetProcAddress(h, "DoTestCall"); printf("Calling dll -> DoTestCall ...\n"); fc(stackCallbacker); printf("call ok."); But wine refuses to load dll.so by relative or absolute path. Does wine supports this ?
Tarmo Pikaro tapika@yahoo.com writes:
On Wednesday, June 5, 2019, 10:44:25 AM GMT+3, Alexandre Julliard julliard@winehq.org wrote:
Tarmo Pikaro tapika@yahoo.com writes:
Other stuff like missing WideCharToMultiByte can be addressed by providing the missing functions in your own separate object file, without changing the source.
I've scanned quite many unicode libraries and concluded of bringing up cutf library alive - that is basically a replacement for WideCharToMultiByte & windows api - uses less call arguments, but can achieve stuff.
My point is that you can write a simple WideCharToMultiByte wrapper on top of your cutf library, and then you don't need to patch dbghelp at all, only to link it against your wrapper.
I think wrapping can go in either direction, but you must admit that writing:
len = utf8zestimate(searchPath);
is shorter than:
len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
by 6-1 = 5 parameters,
It's also completely non-standard. In Wine, we use Windows APIs wherever possible.
Yes, but I would like to start from basics - enable functionality initially, and worry about which dll exports and what later on. I think we could have even our own (application) specific "standard" API.
Btw - I have tried to load from wine / LoadLibrary linux shared object, but could not. Tried something like this:
void stackCallbacker() { printf("%s", g3::internal::stackdump().c_str()); } typedef void(*func)(void); typedef void(*fcallFunc)(func); int main(int argc, char **argv) { //HMODULE h = LoadLibraryA("dll.dll"); const char* dllName = "dll"; //const char* dllName = "/mnt/c/Prototyping/dbghelp2_dev2/bin/Release_x86/dll.so"; HMODULE h = LoadLibraryA(dllName); if (h == 0) { printf("Failed to load '%s'\n", dllName); return 0; } fcallFunc fc; *((FARPROC*)&fc) = GetProcAddress(h, "DoTestCall"); printf("Calling dll -> DoTestCall ...\n"); fc(stackCallbacker); printf("call ok.");
But wine refuses to load dll.so by relative or absolute path. Does wine supports this ?
It works if the .so file was built as a Wine library, with an embedded PE header. It's not possible to load a plain .so lib, since there's no way to return a valid HMODULE for it.
Hi !
I think wrapping can go in either direction, but you must admit that writing:
len = utf8zestimate(searchPath);
is shorter than:
len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
by 6-1 = 5 parameters,
It's also completely non-standard. In Wine, we use Windows APIs wherever possible.
I think we could handle standardization through providing necessary functionality and covering that functionality by testing. Do you have testing for windows api's MultiByteToWideChar/... ? Btw - noticed that wine is using 2 = sizeof(wchar_t), like it's in windows at the moment, butin linux sizeof(wchar_t) == 4, so suspect my code won't work out of box. Need to check deeper later on.
Yes, but I would like to start from basics - enable functionality initially, and worry about which dll exports and what later on. I think we could have even our own (application) specific "standard" API.
Btw - I have tried to load from wine / LoadLibrary linux shared object, but could not. Tried something like this:
void stackCallbacker() { printf("%s", g3::internal::stackdump().c_str()); }
typedef void(*func)(void); typedef void(*fcallFunc)(func);
int main(int argc, char **argv) { //HMODULE h = LoadLibraryA("dll.dll"); const char* dllName = "dll"; //const char* dllName = "/mnt/c/Prototyping/dbghelp2_dev2/bin/Release_x86/dll.so"; HMODULE h = LoadLibraryA(dllName); if (h == 0) { printf("Failed to load '%s'\n", dllName); return 0; }
fcallFunc fc; *((FARPROC*)&fc) = GetProcAddress(h, "DoTestCall");
printf("Calling dll -> DoTestCall ...\n"); fc(stackCallbacker); printf("call ok.");
But wine refuses to load dll.so by relative or absolute path. Does wine supports this ?
It works if the .so file was built as a Wine library, with an embedded PE header. It's not possible to load a plain .so lib, since there's no way to return a valid HMODULE for it.
Do I need to use wine specific compiler or linker for that purpose ?
I have almost compiled dbghelp to linux (standalone), have now ~ 10 linking errors, but now also started to wonderhow do I test overall system. At the moment whole stack all resoving procedure can be divided into two steps:1. Resolve call stack (ip's of execution stack).2. Resolve symbol information on call stack. 1 at the moment is done using following for loop: for (size_t index = 0; index < frame_pointers.size(); ++index) { if (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &frame, context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { frame_pointers[index] = frame.AddrPC.Offset; } else { break; } } I've started to wonder how step 1 can be implemented for linux, and one approach is indeed to use same call sequence, but then I will need to wrap up ReadProcessMemory for linux, or I could use some existing library to do the same thing. I went to github and made a search for "unw_get_proc_name SymGetLineFromAddr64 callstack"and find out that there already exists portable version of call stack determination, residing in OVR_DebugHelp.cpp. For example this one: https://github.com/Magnusnorrby/MolyRift/blob/master/Kernel/OVR_DebugHelp.cp... where step 1 is implemented in here: https://github.com/Magnusnorrby/MolyRift/blob/master/Kernel/OVR_DebugHelp.cp... Generally for linux they use libunwind library. Quickly googling for libunwind gave me 3 different libraries:1. libunwind / llvm (c++ based)2. original libunwind/gnu: https://www.nongnu.org/libunwind/ (C based)3. Unity specific / android libunwind: https://github.com/Unity-Technologies/android-libunwind Uff... That's heavy, so many alternatives, and of course tightly bound to compiler which is used (gcc / clang). I suspect that get call stack can be generic function in itself, and even dbghelp implementation can be used - but I do care about it's functionality and it's performance. In my own implementation (long time ago):https://sourceforge.net/projects/diagnostic/http://diagnostic.sourceforge.ne... https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.cpp / see CaptureStackBackTracePro I've concluded to use RtlVirtualUnwind (64 bit) simply because StackWalk64 (native windows implementation)was too slow, also did not work out of box for managed code. I've noticed that in wine's dbghelp implementation there is also some disassembly approach when resolving call stack, might slow down call stack query, but might also bring more reliable call stack determination. Do you have any unit tests for StackWalk64 - I suspect they are not so trivial to implement, since depends on executing program. In diagnostic I can "hook" external application to monitor all memory allocation and free requests, also can use it also forperformance measuring of application slow down / call stack reliability determination. But this approach of course is applicable for windows only. Haven't investigated using hooks on linux. Btw - I've raised issue for dotnetclr on c++ mixed mode call stack determination: https://github.com/dotnet/coreclr/issues/23681