Automatically Obtaining Source and Line Number from Symbol Name and Offset

June 25, 2013

one comment

Once upon a time, unmanaged code developers had to work very hard to correlate code offsets back to source file names and line numbers. One approach involved generating .cod files (assembly listings) for each module, and then painstakingly comparing the instruction offsets with the contents of the .cod file.

For example, if you received a faulting stack trace from a client machine that had the frame BatteryMeter!TemperatureAndBatteryUpdaterThread+0xd0, you could go back to the .cod file for BatteryMeter.exe, look up the code listing for TemperatureAndBatteryUpdaterThread, and then look for the source line located at (or near) offset 0xd0.

This process can be automated. I was asked a few days ago if it’s still necessary to use .cod files for this, and the answer is no. If you fire up WinDbg, you can simply File > Open Dump File, pass in your .exe or .dll as the dump file name, and then issue the ln command, as follows:

0:000> ln BatteryMeter!TemperatureAndBatteryUpdaterThread+0xd0
d:\dev\batterymeter\batterymeterdlg.cpp(58)

If you don’t have WinDbg handy and/or want to perform this automatically (maybe from a script), you can use the DbgHelp API to load the symbols for the appropriate module and then look up the symbol name and source information. The functions involved are SymLoadModule64, SymFromName, and SymGetLineFromAddr64, and the resulting program is not much more than 100 lines of code. Here’s a highlight:

DWORD displacement;
IMAGEHLP_LINE64 line;
RtlZeroMemory(&line, sizeof(line));
line.SizeOfStruct = sizeof(line);
if (!SymGetLineFromAddr64(hProcess, symbolAddress, &displacement, &line))
{
    printf("*** Error retrieving source line for %s: 0x%x\n",
        argv[1], GetLastError());
    return 1;
}
printf("%s [0x%I64x] = %s line %d (+0x%x)\n", argv[1], symbolAddress,
    line.FileName, line.LineNumber, displacement);

If you’re interested in the source code, you can find it on GitHub here. When you compile the app, it’s ready to run. You can specify the full symbol and offset on the command line, or just the offset from the base of the module (this is handy if you’re debugging a stack trace from a production machine where you didn’t have symbols).

> OffsetToSource BatteryMeter+0x12ecd
BatteryMeter+0x12ecd [0x412ecd] = d:\dev\batterymeter\batterymeterdlg.cpp line 46 (+0x0)

> OffsetToSource BatteryMeter!TemperatureAndBatteryUpdaterThread+0xd0
BatteryMeter!TemperatureAndBatteryUpdaterThread+0xd0 [0x412f60] = d:\dev\batterymeter\batterymeterdlg.cpp line 58 (+0x0)

(Note that the module has to be in the current directory, in the %PATH%, or in the symbol path. If the file does not have an extension, the program tries to add the .dll extension first, and then tries .exe before failing.)


I am posting short links and updates on Twitter as well as on this blog. You can follow me: @goldshtn

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. Marc ShermanJune 26, 2013 ב 10:18 AM

    Great write up, thanks!

    Reply