Debugging with MAP file - finding instruction address for a AV (advanced)

How can I find the instruction line in the program from the AV instruction address using the MAP file.

I am using Delphi 7, however I don't think it's relevant here.

I have created a small test project only for debug purposes that triggers a Access Violation error. From this error I would like to use the instruction address provided by the application error and the corresponding MAP file for the application to find the source code line generating the error.

It's basically one main form with a button that when pressed it tries to write to a pointer which was not allocated -> access violation.
I would also like to get the name of variable which was not initialized.

Source Code:
****************************(start source code)************************
unit Test;
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);

  TDummyType = record
      sBuffer: string;
      iLength: integer;
  PTDummyType = ^TDummyType;

  Form1: TForm1;


procedure TForm1.Button1Click(Sender: TObject);
   PTest: PTDummyType;
  PTest.sBuffer := 'string';  // line that gives the Acess Violation
  if (PTest.sBuffer = 'string') then

****************************(end source code)************************

The MAP file looks like this:
****************************(start map file )************************
 Start         Length     Name                   Class
 0001:00000000 00051C44H .text                   CODE
 0002:00000000 00001194H .data                   DATA
 0002:00001194 00000C05H .bss                    BSS


Line numbers for Test(Test.pas) segment .text

    36 0001:00051974    37 0001:00051975    38 0001:00051981    39 0001:0005198F
    40 0001:00051999    42 0001:000519EC    42 0001:000519F3

Line numbers for Project2(D:\Development\Project2.dpr) segment .text

     9 0001:00051BFC    10 0001:00051C0C    11 0001:00051C18    12 0001:00051C30
    13 0001:00051C3C


Program entry point at 0001:00051BFC
****************************(end map file )************************

The line {PTest.sBuffer := 'string' } generates an: Access Violation at address 004042A4 in module Project2.exe. Write of address 0042A820.

But this source code line in the MAP file has the address 0001:00051975 at line 37.
Pausing the program and using Search - Find Error with the 004042A4 address indeed gives me the address where the program breaks but that is not attached to a line in the source code.

I want to be able to find the instruction address corresponding to my source code in the map file that was the closest one next to the assembly instruction that generated the AV.

That is lets assume that I can't debug the source code (for example the AV appears on the customer's computer and I can't replicate it in the development environment). From the MAP file and AV instruction address how can I find the line number responsible for the error and maybe the name of the variable not initilized???

Long time ago I had some basic assembly knowlegde but now I can't find a specific guide on get to my instruction address in the MAP file.

Please don't point me to third party tools. I want the low level solution where you need to manually compute offsets and stuff like that.

A good online resource for such a guide would also be accepted as an aswer.

Thank you
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

TavirgilAuthor Commented:
I forgot to mention that I understand that the AV address 004042A4 reported, is in another segment call (I don't know if this is correct how I am stating it) from my Button1Click procedure which holds the source code line that is responsable for the error.

Also if I add a breakpoint on this exact source code line {PTest.sBuffer := 'string' } the CPU Window gives me the address of this instruction as 00452975 which does not match neither the AV error reported instruction 004042A4, nor the instruction address in the MAP file  37 0001:00051975.

Madshi knows all about it, but i am not sure if he has explanations online.
A few things...

Project/Options/Linker says your application should be loaded at offset $00400000 (Image base). So you need to add $00400000 to the .code addresses in the map file.

For reasons I've never understood, it also add an extra $00001000 offset... ?? Don't know why.

The problem is that the access violation doesn't actally occur within your code.. It will (probably) occur in the system function that does string assignment, for example. Let's see.. The address I get for the AV is 004042b0. Subtract $00400000 to get 000042b0. Subtract the magic $00001000 to get 32b0. Look that up in the .code segment in the map file to get @LStrAsg which starts at 3288.... Which sounds like it's probably a string assignment function to me :-) (Phew!)

So you need to unravel the stack when you get an AV. (Which is what Madshi does!) This means you need to compile with stack frames enabled, and you need to poke around within your application when it fails. When you get the AV you can open the CPU window (Delphi Pro) and look at the stack. (Typically at address 0012xxxx or so in the bottom right window).

Register ESP is the stack pointer. Register EBP is typically the stack frame pointer. The address just below that on the stack is probably the return address i.e. what's called this function.. Then you can unravel.. but it's not trivial...

Cloud Class® Course: Microsoft Windows 7 Basic

This introductory course to Windows 7 environment will teach you about working with the Windows operating system. You will learn about basic functions including start menu; the desktop; managing files, folders, and libraries.

As Andrew said, you need to substract the image base, which is most probably $400000. Furthermore you need to substract the "base of code" address, which is stored in the image nt headers of each module (exe/dll). It's usually $1000. You can check which value it has by using the freeware tool "PEProwse Pro". It's the field "Base Of Code" in the "Details" of the "Optional Header". You'll also find the image base address there.

In your specific situation "Access Violation at address 004042A4" this seems to be the address "32a4" in the map file. As you've noticed yourself, this address is not inside of your own unit. The reason for this simply is, that your code "PTest.sBuffer := 'string'" calls a Delphi system function, which tries to assign the string. This Delphi system function then crashes.

So, as you can see, having just the crashing address doesn't help much. Even if you find the name of the crashing function (which would be "System.@LStrAsg") you still have no clue what really happended.

The only way to get really helping information is to do a full stack trace and get the unit/function name of each stack item. This way the 2nd item in that stack trace should be the line of code where you did the crashing string assignment.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Always wondered where that $1000 came from :-)

I might be able to dig out my old stack trace code if it's interesting, if Madshi doesn't publish his source :-)
>> if Madshi doesn't publish his source :-)


Btw, the Delphi Jedi JCL has a full stack tracing logic. It's free and comes with full sources. It might not be the best available solution (especially not when being compared with madExcept :-), but it does do its job quite well.
TavirgilAuthor Commented:
I think I got I know.

So in my case, with the instruction address from the CPU window (for the "PTest.sBuffer := 'string'" line):

$00452975 - $400000 (image base) - $1000 (base code address) = $00051975 wich is exactly the address in the MAP file.

However the AV error gives me an instruction address from System.@LStrAsg function. Only by having a snapshot of the current stack when the AV occurs I can track from which function and at what point in that fucntion the call occured.

Am I on the right track here ?

Ok so let's just say that I create an application, send it to a customer which in turn gets an AV error. There is not way for me to track where the error occured only by the AV report and the MAP file.

I would need some sort of debug units (like madshi exception) compiled with my application in order to also get a snapshot of the stack which then can lead me to the bad instruction in the code.

So I definetly DO NEED a third party tool / units.
Correct - you need to log both the AV address and the call stack. Then you use all this to look stuff up in the map file.
(And you probably need to compile with stack frames and no optimisation, otherwise you'll miss bits of the stack trace)
> Am I on the right track here ?


However, sometimes having the access violation address *is* enough to show you the crashing source code line. E.g. if you don't do a string assignment, but an integer assignment, things will work out without needing a stack trace. But it's simply not reliably this way.

> So I definetly DO NEED a third party tool / units.

Well, you can do it on your own, too, of course. But why should you do that? It would cost you hundreds of hours to implement such a solution. Instead you should probably choose between the available 3rd party tools like JCL (free incl sources) and madExcept (free for non-commercial usage only, commercial usage costs USD 50, commercial version contains sources).

> And you probably need to compile with stack frames and no optimisation, otherwise you'll miss bits of the stack trace

When using madExcept you can leave all settings as they are. No stack frames needed. Optimization doesn't harm. Not sure what JCL needs, though.
TavirgilAuthor Commented:
Got in guys.

I will keep in mind madExcept and JCL.

Much appreciate your help. I would like to split the points to both of you because both of you helped me out. I hope this does not upset neither of you guys.

Thanks again for your time and help.
Madshi, do you know of bugs in the JCL code? If so then please report them.
Hi Robert!

I'm not aware of any bugs in JCL. I said that it's not the best solution, because it's missing some things which I find quite important for an exception tracking component. E.g. JCL doesn't catch initialization and finalization exceptions. It also doesn't (automatically) catch thread exceptions. Also it doesn't contain a thread safe exception box. And there are some more details (some more important, some less important) that are missing. But it does catch "normal" exceptions. So it definately is a useful solution. It's just not as good as madExcept...   :-)

This is all "as far as I know". Am I wrong anywhere?

P.S: One thing just comes to my mind. The JCL's TD32 debug info parsing is incomplete. It misses some BCB function/procedure names. You can tell the responsible programmer to contact me. I'm willing to share this information.
TavirgilAuthor Commented:
Is there a place, book to learn about all the TD32 debug infromation, thread safe exceptions and advanced debuging in general for Delphi, or is this a process where you learn by experience over the years ?

I know for example that I need to dig up my old assembly book to see in which CPU registers does the stack information reside and how to compute function addresses and stuff like that. But when you want to specifically know about advanced debuging in Delphi where would one go ?

There were a few articles about exception handling Delphi somewhere. Don't have the links here right now, though. Also there's an article about structured exception handling in Windows by Matt Pietrek. There's a document from Borland about TD32 debug information. But the easiest way to look into all this would probably be to check out the JCL source code.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.