From 5b8674d84af5bce937f2af0639c891988711193e Mon Sep 17 00:00:00 2001 From: Oleg Tolmatcev Date: Tue, 15 Sep 2026 21:42:24 +0200 Subject: [PATCH 1/3] add dwstModuleOpen and dwstModuleClose Split dwstOfFileExt() into dwstModuleOpen(), which builds the CU table, dwstOfModuleExt(), which resolves addresses with it, and dwstModuleClose(). Applications which already have a Dwarf_Debug of a module (e.g. drmingw) can build the CU table once and reuse it. The Dwarf_Debug is passed as void* so dwarfstack.h doesn't depend on libdwarf.h, and it is owned by the caller. --- include/dwarfstack.h | 16 +++++++ src/dwst-file.c | 103 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 97 insertions(+), 22 deletions(-) diff --git a/include/dwarfstack.h b/include/dwarfstack.h index 01fbbcf..8126030 100644 --- a/include/dwarfstack.h +++ b/include/dwarfstack.h @@ -95,6 +95,22 @@ EXPORT int dwstOfFileW( dwstCallbackW *callbackFunc,void *callbackContext ); +typedef struct dwst_module dwst_module; + +// dwstModuleOpen(): prepare debug information for repeated lookups +// dbg: Dwarf_Debug of the executable +// (has to stay valid until dwstModuleClose()) +// imageBase_dbg: preferred image base address of the executable +EXPORT dwst_module *dwstModuleOpen( + void *dbg,uint64_t imageBase_dbg ); + +// dwstModuleClose(): free module created by dwstModuleOpen() +// (dbg itself is not freed) +// module: module handle +EXPORT void dwstModuleClose( + dwst_module *module ); + + // dwstOfProcess(): stack information of current process // addr: stack addresses // count: number of addresses diff --git a/src/dwst-file.c b/src/dwst-file.c index 76d3f3c..53229ca 100644 --- a/src/dwst-file.c +++ b/src/dwst-file.c @@ -444,30 +444,21 @@ typedef struct cu_info int rangeCount; } cu_info; -int dwstOfFileExt( - const char *name,const wchar_t *nameW,uint64_t imageBase, - uint64_t *addr,int count, - dwstCallback *callbackFunc,dwstCallbackW *callbackFuncW, - void *callbackContext ) +struct dwst_module { - if( !nameW || !addr || !count || (!callbackFunc && !callbackFuncW) ) - return( 0 ); - - if( imageBase ) - dwarf_callback( callbackFunc,callbackFuncW,imageBase,name,nameW, - DWST_BASE_ADDR,NULL,callbackContext,0 ); - - Dwarf_Addr imageBase_dbg; Dwarf_Debug dbg; - if( dwarf_pe_init(nameW,&imageBase_dbg,0,0,&dbg,NULL)!=DW_DLV_OK ) - { - int i; - for( i=0; idbg = dbg; + module->imageBase_dbg = imageBase_dbg; + module->cuArr = cuArr; + module->cuQty = cuQty; + + return( module ); +} + +static int dwstOfModuleExt( + dwst_module *module,const char *name,const wchar_t *nameW, + uint64_t imageBase,uint64_t *addr,int count, + dwstCallback *callbackFunc,dwstCallbackW *callbackFuncW, + void *callbackContext ) +{ + Dwarf_Debug dbg = module->dbg; + Dwarf_Addr imageBase_dbg = module->imageBase_dbg; + cu_info *cuArr = module->cuArr; + int cuQty = module->cuQty; + uint64_t baseOffs = 0; if( imageBase ) { @@ -734,6 +744,18 @@ int dwstOfFileExt( DWST_NOT_FOUND,NULL,callbackContext,0 ); } + return( i ); +} + +void dwstModuleClose( + dwst_module *module ) +{ + if( !module ) return; + + Dwarf_Debug dbg = module->dbg; + cu_info *cuArr = module->cuArr; + int cuQty = module->cuQty; + int j; for( j=0; j Date: Tue, 15 Sep 2026 21:42:58 +0200 Subject: [PATCH 2/3] add dwstOfModule and dwstOfModuleW Resolve addresses with a module created by dwstModuleOpen(), without reading the debug information and the CU table again for every call. --- include/dwarfstack.h | 18 ++++++++++++++++++ src/dwst-file.c | 24 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/include/dwarfstack.h b/include/dwarfstack.h index 8126030..f3c5ef5 100644 --- a/include/dwarfstack.h +++ b/include/dwarfstack.h @@ -110,6 +110,24 @@ EXPORT dwst_module *dwstModuleOpen( EXPORT void dwstModuleClose( dwst_module *module ); +// dwstOfModule(): stack information of module +// module: module handle from dwstModuleOpen() +// name: executable location +// imageBase: used image base address +// addr: stack addresses +// count: number of addresses +// callbackFunc: callback function +// callbackContext: user-provided pointer (context) +EXPORT int dwstOfModule( + dwst_module *module,const char *name,uint64_t imageBase, + uint64_t *addr,int count, + dwstCallback *callbackFunc,void *callbackContext ); + +EXPORT int dwstOfModuleW( + dwst_module *module,const wchar_t *name,uint64_t imageBase, + uint64_t *addr,int count, + dwstCallbackW *callbackFunc,void *callbackContext ); + // dwstOfProcess(): stack information of current process // addr: stack addresses diff --git a/src/dwst-file.c b/src/dwst-file.c index 53229ca..8cb3dc4 100644 --- a/src/dwst-file.c +++ b/src/dwst-file.c @@ -613,6 +613,9 @@ static int dwstOfModuleExt( dwstCallback *callbackFunc,dwstCallbackW *callbackFuncW, void *callbackContext ) { + if( !module || !addr || !count || (!callbackFunc && !callbackFuncW) ) + return( 0 ); + Dwarf_Debug dbg = module->dbg; Dwarf_Addr imageBase_dbg = module->imageBase_dbg; cu_info *cuArr = module->cuArr; @@ -838,3 +841,24 @@ int dwstOfFileW( return( dwstOfFileExt(NULL,name,imageBase,addr,count, NULL,callbackFunc,callbackContext) ); } + +int dwstOfModule( + dwst_module *module,const char *name,uint64_t imageBase, + uint64_t *addr,int count, + dwstCallback *callbackFunc,void *callbackContext ) +{ + wchar_t *nameW = dwst_ansi2wide( name ); + int ret = dwstOfModuleExt( module,name,nameW,imageBase,addr,count, + callbackFunc,NULL,callbackContext ); + free( nameW ); + return( ret ); +} + +int dwstOfModuleW( + dwst_module *module,const wchar_t *name,uint64_t imageBase, + uint64_t *addr,int count, + dwstCallbackW *callbackFunc,void *callbackContext ) +{ + return( dwstOfModuleExt(module,NULL,name,imageBase,addr,count, + NULL,callbackFunc,callbackContext) ); +} From f11e83a18213eb5755e06d2b983558d6c7598511 Mon Sep 17 00:00:00 2001 From: Oleg Tolmatcev Date: Tue, 15 Sep 2026 21:44:08 +0200 Subject: [PATCH 3/3] report function and line start addresses in dwstOfModule dwstOfModule() and dwstOfModuleW() now call the callback with DWST_FUNC_ADDR and DWST_LINE_ADDR before each resolved frame, with the start address of the function and of the source line. This allows callers to compute the displacement of an address inside a function or line. For an address inside inlined code, e.g. inner() inlined into outer(), the callback is called for inner() and then for outer(), each preceded by its own DWST_FUNC_ADDR and DWST_LINE_ADDR. As with dwstOfFile(), addr is 0 for outer(), so callers compute the displacement from the address they looked up. DWST_LINE_ADDR is 0 for outer() here, since DWARF doesn't store where the code of the line calling inner() starts. dwstOfFile() and dwstOfProcess() don't report them, since existing callbacks don't expect a NULL filename. --- include/dwarfstack.h | 18 ++++++++++++++++++ src/dwst-file.c | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/include/dwarfstack.h b/include/dwarfstack.h index f3c5ef5..0565ce2 100644 --- a/include/dwarfstack.h +++ b/include/dwarfstack.h @@ -44,6 +44,9 @@ extern "C" { // funcname: function name // context: user-provided pointer (callbackContext) // columnno: column number +// For an address inside inlined code, the callback is called once per +// inlined function (innermost first) and finally for the containing +// function; addr is 0 for all but the first of these calls. typedef void dwstCallback( uint64_t addr,const char *filename,int lineno,const char *funcname, void *context,int columnno ); @@ -75,6 +78,19 @@ typedef void dwstCallbackW( // filename: executable location #define DWST_NOT_FOUND -3 +// DWST_LINE_ADDR: start address of the source line (only dwstOfModule()) +// addr: line address +// (0 for the outer frames of inlined code) +// filename: NULL +// funcname: function name +#define DWST_LINE_ADDR -4 + +// DWST_FUNC_ADDR: start address of the function (only dwstOfModule()) +// addr: function address +// filename: NULL +// funcname: function name +#define DWST_FUNC_ADDR -5 + // dwstOfFile(): stack information of file // name: executable location @@ -111,6 +127,8 @@ EXPORT void dwstModuleClose( dwst_module *module ); // dwstOfModule(): stack information of module +// (like dwstOfFile(), but additionally reports DWST_FUNC_ADDR and +// DWST_LINE_ADDR before each resolved frame) // module: module handle from dwstModuleOpen() // name: executable location // imageBase: used image base address diff --git a/src/dwst-file.c b/src/dwst-file.c index 8cb3dc4..c70792a 100644 --- a/src/dwst-file.c +++ b/src/dwst-file.c @@ -267,8 +267,29 @@ typedef struct inline_info uint64_t ptrOrig; int fileno,lineno,columnno; int fileno_offs; + Dwarf_Addr lineaddr; + uint64_t baseOffs; + int reportAddrs; } inline_info; +// report start addresses of function and source line +static void dwarf_callback_addrs( inline_info *cuInfo, + Dwarf_Addr low,const char *funcname ) +{ + if( !cuInfo->reportAddrs ) return; + + if( low ) + dwarf_callback( cuInfo->callbackFunc,cuInfo->callbackFuncW, + low - cuInfo->baseOffs,NULL,NULL, + DWST_FUNC_ADDR,funcname,cuInfo->callbackContext,cuInfo->columnno ); + + // lineaddr is 0 for the outer frames of inlined code, DWARF doesn't + // store where the code of the call line starts + dwarf_callback( cuInfo->callbackFunc,cuInfo->callbackFuncW, + cuInfo->lineaddr ? cuInfo->lineaddr - cuInfo->baseOffs : 0,NULL,NULL, + DWST_LINE_ADDR,funcname,cuInfo->callbackContext,cuInfo->columnno ); +} + // find the calling-location of inlined functions static int findInlined( Dwarf_Debug dbg,Dwarf_Die die,inline_info *cuInfo ) { @@ -353,6 +374,7 @@ static int findInlined( Dwarf_Debug dbg,Dwarf_Die die,inline_info *cuInfo ) if( cuInfo->ptrptr>=highpc ) continue; + low = lowpc; break; } @@ -366,6 +388,8 @@ static int findInlined( Dwarf_Debug dbg,Dwarf_Die die,inline_info *cuInfo ) { char *funcname = dwarf_name_of_func_linked( dbg,die ); + dwarf_callback_addrs( cuInfo,low,funcname ); + dwarf_callback( cuInfo->callbackFunc,cuInfo->callbackFuncW, cuInfo->ptrOrig,cuInfo->files[cuInfo->fileno],NULL, cuInfo->lineno,funcname,cuInfo->callbackContext,cuInfo->columnno ); @@ -394,6 +418,8 @@ static int findInlined( Dwarf_Debug dbg,Dwarf_Die die,inline_info *cuInfo ) { char *funcname = dwarf_name_of_func_linked( dbg,die ); + dwarf_callback_addrs( cuInfo,low,funcname ); + dwarf_callback( cuInfo->callbackFunc,cuInfo->callbackFuncW, cuInfo->ptrOrig,cuInfo->files[cuInfo->fileno],NULL, cuInfo->lineno,funcname,cuInfo->callbackContext,cuInfo->columnno ); @@ -402,6 +428,7 @@ static int findInlined( Dwarf_Debug dbg,Dwarf_Die die,inline_info *cuInfo ) cuInfo->lineno = lineno; cuInfo->columnno = 0; cuInfo->ptrOrig = 0; + cuInfo->lineaddr = 0; Dwarf_Attribute callcolumn; if( dwarf_attr(die,DW_AT_call_column,&callcolumn,NULL)==DW_DLV_OK ) @@ -611,7 +638,7 @@ static int dwstOfModuleExt( dwst_module *module,const char *name,const wchar_t *nameW, uint64_t imageBase,uint64_t *addr,int count, dwstCallback *callbackFunc,dwstCallbackW *callbackFuncW, - void *callbackContext ) + void *callbackContext,int reportAddrs ) { if( !module || !addr || !count || (!callbackFunc && !callbackFuncW) ) return( 0 ); @@ -677,6 +704,7 @@ static int dwstOfModuleExt( Dwarf_Unsigned srcfileno = 0; Dwarf_Unsigned lineno = 0; Dwarf_Unsigned columnno = 0; + Dwarf_Addr lineaddr = 0; if( lines ) { int c; @@ -699,6 +727,7 @@ static int dwstOfModuleExt( continue; } + dwarf_lineaddr( lines[c-1],&lineaddr,NULL ); dwarf_line_srcfileno( lines[c-1],&srcfileno,NULL ); dwarf_lineno( lines[c-1],&lineno,NULL ); dwarf_lineoff_b( lines[c-1],&columnno,NULL ); @@ -728,7 +757,7 @@ static int dwstOfModuleExt( inline_info ii = { ptr,cuInfo->base, files,fileCount,callbackFunc,callbackFuncW,callbackContext, ptrOrig,(int)srcfileno+cuInfo->fileno_offs,lineno,columnno, - cuInfo->fileno_offs }; + cuInfo->fileno_offs,lineaddr,baseOffs,reportAddrs }; walkChildren( dbg,die,(ChildWalker*)findInlined,&ii ); } else @@ -811,7 +840,7 @@ int dwstOfFileExt( if( module ) { ret = dwstOfModuleExt( module,name,nameW,imageBase,addr,count, - callbackFunc,callbackFuncW,callbackContext ); + callbackFunc,callbackFuncW,callbackContext,0 ); dwstModuleClose( module ); } @@ -849,7 +878,7 @@ int dwstOfModule( { wchar_t *nameW = dwst_ansi2wide( name ); int ret = dwstOfModuleExt( module,name,nameW,imageBase,addr,count, - callbackFunc,NULL,callbackContext ); + callbackFunc,NULL,callbackContext,1 ); free( nameW ); return( ret ); } @@ -860,5 +889,5 @@ int dwstOfModuleW( dwstCallbackW *callbackFunc,void *callbackContext ) { return( dwstOfModuleExt(module,NULL,name,imageBase,addr,count, - NULL,callbackFunc,callbackContext) ); + NULL,callbackFunc,callbackContext,1) ); }