Map File Breakpoints

While debugging, I am often faced with trying to set breakpoints in module code that I do not control. Said modules rarely have current symbols and access to source files either not possible or practical. I usually face these issues armed only with a call stack and a [possibly ancient] map file in which to determine the root cause of the error. I like to convince myself that single-stepping through code would be too easy and debugging ASM to find the problem makes things more interesting. Sometimes it works.

To get started, you need to guess where it is you would like to go – if you have a call stack, this is the function name at the top of the list. First try to set this breakpoint in the breakpoint window using the following format:

{,,ModuleName.dll} FunctionName

Please note that the FunctionName is case sensitive, but the ModuleName is not. If you run through your repro and the debugger did not break, we need to dig a little deeper and make sure we are actually using the correct module name. First, load the Platform Builder “Modules” window (View->Debug->Modules) and look for the module name in the list – remember that during makeimg, modules will often be renamed as they are added to the ROM. If you are certain of the module name, lets find the “base” of this module by breaking at DllMain (or WinMain for EXE’s).

{,,ModuleName.DLL} DllMain

Or

{,,ModuleName.EXE} WinMain

DllMain is often a convenient breakpoint since it is called often, not just on load/unload. Once we are stopped at DllMain, the ease of finding other breakpoints within this module will depend on how old your map file is. If the module has undergone significant changes, the below may not work, but in my experience map files are usually “close enough” to get what you need. Open the map file and find the function you are trying to break at:

...

0001:0002fab4 <?ParseGetCallerIdSettings@@YAJPBDAAPAXAAI@Z> 10030ab4 f supsvc.obj
0001:0002fdd4 <?ParseGetHideIdSettings@@YAJPBDAAPAXAAI@Z> 10030dd4 f supsvc.obj
0001:00030138 <?ParseGetDialedIdSettings@@YAJPBDAAPAXAAI@Z> 10031138 f supsvc.obj
0001:000303f8 <?ParseGetClosedGroupSettings@@YAJPBDAAPAXAAI@Z> 100313f8 f supsvc.obj
0001:00030810 <?ParseGetCallForwardingSettings@@YAJPBDAAPAXAAI@Z> 10031820 << HERE HERE HERE
0001:00031338 <?ParseGetCallWaitingSettings@@YAJPBDAAPAXAAI@Z> 10032338 f supsvc.obj
0001:00031920 <?BSCompareChars@@YAHPBX0@Z> 10032920 f util.obj
0001:00033018 <?ParseGetAudioGain@@YAJPBDAAPAXAAI@Z> 10034018 f voice.obj

...

The function I want to stop at is the RIL component ParseGetCallForwardingSettings() and is located at: 10031820. This number is not an address, but rather an offset that we can use to calculate how much code lies between this function and DllMain. Looking at the same map file, locate DllMain:

 

 


 0001:00029914       <?StartReadyStateQuery@CRilHandle@@QAAXXZ> 1002a914 f   rilhand.obj
 0001:00029960       <?Register@CRilEventLog@@QAAJXZ> 1002a960 f i rilmain.obj
 0001:000299c0       DllMain                    1002a9c0 f   rilmain.obj
 0001:00029a10       <?RIL_Deinit@@YAHK@Z>        1002aa10 f   rilmain.obj
 0001:00029aec       <?RIL_Read@@YAKKPAXK@Z>      1002aaec f   rilmain.obj
 0001:00029af4       <?RIL_Write@@YAKKPBXK@Z>     1002aaf4 f   rilmain.obj

Using calculator, find the amount of code that exists between these two functions. In this case, my function is later in the map file than DllMain so I Subtract:

10031820 - 1002a9c0 = 0x6E60

To get the code offset between these two functions. Going back to the debugger, locate the instruction address of DllMain and drag this into a watch window.

…0102A9B8 bx lr0102A9BC andhi r4, r0, r5DllMain:0102A9C0 stmdb sp!, {r4, lr} <<< HERE$M46300:0102A9C4 cmp r1, #10102A9C8 bne |$M46300+18h (0102a9dc)|0102A9CC ldr r0, [pc, #0x38]0102A9D0 bl |CRilEventLog::Register (0102a960)|…

 

 

 

Next, use the watch window to help your function by adding your offset value to the instruction address of DllMain. Note: if your function was less than DllMain, you would actually subtract:

 

 

 

Click and drag this resulting value back to the ASM window to find code close to your function. Looking at the ASM, you will see that we are very close to the top of a function and should set a breakpoint here.

 

Hint: Look for stmdb instructions and SP register manipulation to show you the beginning and end of functions.

 

...

010317EC ldmia sp!, {r4 - r8, lr}010317F0 bx lr010317F4 andhi r4, r0, r5010317F8 ???010317FC smlabbeq r0, r12, r3, r201031800 ldreqd r8, [r0, -r12]01031804 smlatteq r0, r12, r2, r801031808 andhi r0, r7, r7, asr r00103180C ???01031810 mov r12, sp << Func begins here01031814 stmdb sp!, {r0 - r3}01031818 stmdb sp!, {r4 - r12, lr}0103181C sub sp, sp, #0x86, 3001031820 mov r7, r2 << HERE01031824 mov r8, r101031828 str r0, [sp, #0x240]0103182C ldr r3, [pc, #0x3C4]01031830 ldr r3, [r3]01031834 str r3, [sp, #0x214]01031838 mov r1, #00103183C str r1, [r8]01031840 str r1, [r7]

...

 

 

The specific case above puts us very close to the top of a function – but is it the function we are looking for? It is hard to say without setting breakpoints and continuing to see if we stop. Don’t be shy about scrolling through the ASM looking for other functions nearby and setting multiply breakpoints (they are free!) on those as well. Function prologs have a certain “look” about them and your eye will train to identify them quickly.

The above steps will not work under all conditions and significant module changes can render older map files useless. But, as the device matures and code churn is reduced – I have found this method very useful. Good Luck!