The MASM Forum Archive 2004 to 2012

Miscellaneous Forums => 16 bit DOS Programming => Topic started by: allynm on May 06, 2011, 01:54:51 AM

Title: How to use .map file to locate line that crashes
Post by: allynm on May 06, 2011, 01:54:51 AM
Hello everyone,

In 32 bit code there is a very precise method for identifying a source code line that causes a crash.  The method as you know requires the address at which the program crashes, the base of the PE image, and the size of the PE header.  The method assumes line number info in the .map file.

I was hoping someone might be able to give me a quick tutorial on how to do the analogous procedure for 16 bit code.  If I generate a .map file using link16 with /linenumbers switched on, how would I go about finding the offending source line given that I know the seg:offset where the program crashes.

Thanks,
Mark Allyn
Title: Re: How to use .map file to locate line that crashes
Post by: dedndave on May 06, 2011, 03:56:37 AM
the map file won't help you, there   :P
there are a number of ways to go about it
for one, error checking in the code

you can use a beep, now and then to see if the program is making it to that point
        mov     dl,7
        mov     ah,2
        int     21h


also, nice to have a little routine that spits out hex values

of course, there are a number of debuggers for 16-bit
but, i find that i rarely need one to locate a problem
Title: Re: How to use .map file to locate line that crashes
Post by: FORTRANS on May 06, 2011, 12:05:31 PM
Hi,

   As Dave says, in real mode there is not much to report where
a problem occurs.  If the crash generates an interrupt (like a
divide by zero) a debugger or TSR can catch it  But most
"crashes" are silent and don't trigger any interrupt/exception.
Think of an infinite loop that is entered by accident.  Many of my
errors tend to be from the stack being imbalanced, and a Windows
DOS prompt or OS/2 VDM (preferred) can catch a fair number
of those.

   Dave's suggestion of printing a beep is good, though I prefer
something like "got to #1", "got to #2", "got past Problem #3",
and the like.  It is easier to print a single character, but I tend
to lose count if they are all the same.

   As you use Codeview, you could check and see if there are
ways to help it to check for errors or report a line number when
an error is detected.

   I have used scripts with DEBUG for some ugly debugging
sessions.  Time consuming and something of a last resort
kind of thing.

Regards,

Steve N.
Title: Re: How to use .map file to locate line that crashes
Post by: dedndave on May 06, 2011, 12:10:09 PM
interesting, Steve
i used to use the scripts for disassembling   :P
as you figure out more about a program, the script evolves and does a better job of describing what is code and what is data
i disassembled many programs with debug - lol
Title: Re: How to use .map file to locate line that crashes
Post by: FORTRANS on May 06, 2011, 12:28:21 PM
Hi Dave,

   I ended up purchasing Sourcer for disassembling programs.
Does a fair job I guess.  Until things get complicated, or large,
or strange, or...  The authors of "Undocumented DOS" mention
using DEBUG scripts to disassemble MS-DOS itself.  A fun book.

Cheers,

Steve
Title: Re: How to use .map file to locate line that crashes
Post by: dedndave on May 06, 2011, 12:42:09 PM
yes - it was really handy when i was trying to figure out IO.SYS / MSDOS.SYS
that was the worst part of DOS to take apart - the rest was fairly easy
Title: Re: How to use .map file to locate line that crashes
Post by: allynm on May 06, 2011, 05:51:29 PM
Hi Dedndave and Steve,

OK, I'm persuaded.  I guess this is an instance where 16 bit and 32 bit sys's are not "parallel".  But, just what the heck is the value of /linenumbers in the link16 options?  What are linenumbers good for?  I can see by looking at em that you can associate a linenumber with a location in CS, but so what?

Regards,
Mark
Title: Re: How to use .map file to locate line that crashes
Post by: FORTRANS on May 06, 2011, 07:13:49 PM
Quote from: allynm on May 06, 2011, 05:51:29 PM
But, just what the heck is the value of /linenumbers in the link16 options?

Hi,

   Just checked and the reference says:  "... to list the starting
address of each program source line.  ...  Line numbering is
available only in high-level language compilers. If an object file
has no line number information, LINK ignores the /LINENUMBERS
option."  So not applicable to assembler.  So you should check
your compiler's documentation.  If supported, it should give an
example.  Borland may require you to use their linker though.

Regards,

Steve N.
Title: Re: How to use .map file to locate line that crashes
Post by: dedndave on May 06, 2011, 09:06:21 PM
i think that applies to the old (very old) ms/ibm basic compiler
if your program crashed it would report:
crash on line 10050
instead of:
crash at address 10FE
Title: Re: How to use .map file to locate line that crashes
Post by: redskull on May 07, 2011, 12:23:00 AM
I'm not sure what you're asking, or what problem you are having.  When I write this:


.model small
.386
.stack
.data
MsgPrint db "It Rubs the Lotion on it's skin", "$"
.code
start:
    mov ax, seg MsgPrint
    mov ds, ax
lab1: mov ah, 09
    lea dx, MsgPrint
    int 21h
    mov ax, 4c00h
    int 21h
end Start


and build it using this:


ml /omf /c /Zi /Zd 16tst.asm
link16 16tst.obj /LINENUMBER



I get this .map file.



Start  Stop   Length Name                   Class
00000H 00011H 00012H _TEXT                  CODE
00012H 00031H 00020H _DATA                  DATA
00040H 0043FH 00400H STACK                  STACK

Origin   Group
0001:0   DGROUP

Line numbers for 16tst.obj(16tst.asm) segment _TEXT

     8 0000:0000     9 0000:0003    10 0000:0005    11 0000:0007
    12 0000:000b    13 0000:000d    14 0000:0010

Program entry point at 0000:0000



Isn't that what you need to cross reference addresses to line numbers?

-r
Title: Re: How to use .map file to locate line that crashes
Post by: dedndave on May 07, 2011, 12:45:25 AM
well - if you want to make all your symbols/labels global and public

better to learn a little troubleshooting technique - it will be a skill you use a lot - lol
Title: Re: How to use .map file to locate line that crashes
Post by: allynm on May 07, 2011, 01:22:03 AM
Hi Red' and 'Dave and Steve N.

Yes, what Red' shows is what I can get too with /linenumbers.  The original question was:  how to figure out on which line a 16 bit prgm crashes.  If we have the linenumbers (as in Red's output) and if we have the seg:offset of the crash point, how do we get back to the guilty linenumber?  That's the question.  Yes, for sure the linenumber info cross references to the code offsets, but that isn't the problem I was asking about.  Sorry if I was misleading on this point. 

SteveN and 'Dave state that there isn't any straightforward way to work off the .map file to find the culprit.  Both suggest simple and effective techniques for finding where the prgrm fails (like "beep", or "got this far"). 

I had been thinking that 16-bit ought to work like 32-bit.  In 32-bit if you know the crash address  and if you know the preferred load address and you have linenumber info you can precisely pinpoint the offending instruction.  WITHOUT OLLY!  My question was:  how do you do this in 16-bit (if it can be done!) since OLLY ain't no help.

Regards to all,
Mark
Title: Re: How to use .map file to locate line that crashes
Post by: FORTRANS on May 07, 2011, 11:56:55 AM
Hi,

   Interesting results redskull.  However you get the same
information in a more relevant way by creating a listing.  But
good to know I suppose.  Thanks.

   Dave, looked at the Microsoft FORTRAN documentation,
and it mentions /LINENUMBERS in the linker section.  But I
cannot immediately find any further information to flesh that
out.  (??  A to do I guess.)

   Mark, if you have the address of the crash, then the LIST
file (or a MAP file) should give you the code that is failing.  If
you are running the program in a debugger or emulator, you
can display the code directly.  The segment that your running
program shows is probably not relevant, as it depends on the
load point in memory.  The offset is what you look at to see
where the crash occurs in your program.  The offset should
be relative to the code segment start.

   The problem is that (in my experience with my code, etc.)
that you do not have that information in many cases.  So you
have to find the point of failure in another manner.  And that,
of course, depends on how your program decides to crash
and what symptoms are evident.  What Dave and I were
discussing were general techniques to address that problem.

   Your case may show symptoms that make one procedure
more convenient to use than the others.  It depends on how
much information you have.  Or. as they say, YMMV.

HTH,

Steve

Edit:

   Bochs may give you the information you are used to get
from Olly.

SRN
Title: Re: How to use .map file to locate line that crashes
Post by: allynm on May 07, 2011, 12:32:51 PM
Hi all,

One other point of interest in 'Red's code is that he doesn't load DGROUP or _DATA, he loads using seg MsgPrint. 

Mark
Title: Re: How to use .map file to locate line that crashes
Post by: allynm on May 07, 2011, 12:39:08 PM
Hi Steve,

It's hard to find any info on /linenumbers, at least nowadays.  Red's command line uses the /Zd switch for the ml.  I have found that you do not need to set this switch to get the linenumbers.  All you need is to set /linenumbers when you link16.  Perhaps these are "bogus", but they sure look like Red's.

I roughly follow what you are saying the third paragraph.  I would like to see a real example of what you describe.  Problem is---just as you say---getting a program to fail with the failure address produced.  Otherwise you have to "step" through it the way you and 'Dave discuss.

Regards,
Mark
Title: Re: How to use .map file to locate line that crashes
Post by: dedndave on May 07, 2011, 12:39:52 PM
that's symantics, in a way
it is the same as loading _DATA (or whatever you name the segment)
normally, that is the first segment in DGROUP
so, it's probably the same as loading DGROUP
however, if segments are out of order, then things can go haywire - lol
the GROUP mechanism is provided so that you can get the base of the group of segments
(so that you may access more than one segment without switching registers)

the linenumbers thing is documented in the ibm basic compiler (v1.0) manual   :P
what ? - you don't have a copy ?   :eek

(probably in the orignal ibm fortran compiler manual, too)
Title: Re: How to use .map file to locate line that crashes
Post by: allynm on May 07, 2011, 01:50:29 PM
Hi 'Dave,

Despite my advanced age and the fact that I once-upon-a-time programmed 360/65 with a deck of JCL and fortran cards, and despite the fact that these programs routinely bombed and I had to go to a systems programmer who picked through a .map file and never quite explained what he was looking at but invariably found the screwed-up code line, I do not have a copy of the ibm book you mentioned.

Yet another instance of my lack of foresight.

As you surmised....

Yes, indeed, if the user data is actually in the _data segment, then what Red' did works just fine.  As you say, if it ain't there, it won't work---as we have found.  Ferinstance, if it is in DSEG.

Regards,
Mark
Title: Re: How to use .map file to locate line that crashes
Post by: FORTRANS on May 07, 2011, 03:29:56 PM
Quote from: allynm on May 07, 2011, 12:39:08 PM
I roughly follow what you are saying the third paragraph.  I would like to see a real example of what you describe.  Problem is---just as you say---getting a program to fail with the failure address produced.  Otherwise you have to "step" through it the way you and 'Dave discuss.

Hi Mark,

   Okay.  I deliberately crashed a program by sticking an undefined
opcode in the program to cause a trap that would kill the session.
I used:

>  0x0F 0x0B UD2 Intel Undefined opcode 2

which generated a trap caught by the OS/2 VDM as an error
at CS:EIP 19f1:000001a2.  Looking in the listing at address
01A2, I see it is line 88 and the undefined opcode 2 that
caused the trap.  As shown in the following snippet.


      84 019B  BA 0103 R         MOV     DX,OFFSET str1  ; load address str1
      85 019E  B4 09         MOV     AH,9            ; DOS Print String to console
      86 01A0  CD 21         INT     21H             ; DOS syscall
      87
      88 01A2  0F 0B         DB      0FH, 0BH
      89 01A4  33 C0         XOR     AX,AX


   A Windows 2000 Command Prompt also caught it with a
similar message:

16 Bit MS-DOS Subsystem
Command Prompt - examp3
The NTVDM has encountered an illegal instruction
CS:068b IP:01a2 OP:0f 0b 33 ...

   As you can see the CS changed, but IP has the same value.

   Does that help?  BTW I tried a divide by zero error, but
that just displayed a message and stopped the program.

Steve N.
Title: Re: How to use .map file to locate line that crashes
Post by: jj2007 on May 07, 2011, 03:55:01 PM
This works...

Quote.Model small   ; credits to DednDave (http://www.masm32.com/board/index.php?topic=12621.msg97236#msg97236)
.Stack 512

sofar MACRO arg
   pushad
   mov dx, offset ec&arg&
   mov ah, 9
   int 21h
   popad
ENDM

.Data
MsgText   db "Hello 16-bit World", 13, 10, 10
   db "Hit any key to quit ...", 13, 10, "$"

eca   db "A", 13, 10, "$"
ecb   db "B", 13, 10, "$"
ecc   db "C", 13, 10, "$"

.Code

_main proc FAR

; set the DS register to DGROUP (will fail with some Masm versions - use ml 6.15 or higher, or JWasm)
   mov ax, @DATA
   mov ds, ax
   sofar a

; display the message
   mov dx, offset MsgText
   sofar b
   mov ah, 9
   int 21h

; wait for a key
   mov ah, 0
   sofar c
   int 16h

; the DOS equivalent to ExitProcess
   mov ax, 4C00h
   int 21h

_main endp

end _main
Title: Re: How to use .map file to locate line that crashes
Post by: redskull on May 07, 2011, 05:12:10 PM
Quote from: allynm on May 07, 2011, 12:39:08 PM
It's hard to find any info on /linenumbers, at least nowadays.  Red's command line uses the /Zd switch for the ml.  I have found that you do not need to set this switch to get the linenumbers.

I'm not sure anyone knows the real answer to the difference between /Zi and /Zd.  I recall going around a few times to try and see what difference they make, and didn't find any.  However, I always include them both just to be safe.  Just remember it's a two step process: you have to get the assembler to put the info into the OBJ file, and then the linker to put it in the exe/map/pdb/dbg.  Also, FWIT, there is also a /CODEVIEW switch which makes codeview style debugging information (if you can get codeview to work).

But like Steve said, there's any number of places it can crash apart from just "your" code; a bad argument will crash inside the call to the INT, a branch to a bad pointer might crash somewhere inside the .DATA section, and infinite loops won't crash at all.  You have to work backwards from the stack trace to decipher the seminal cause.  But these are things that are common to 16-bit and 32-bit.

-r
Title: Re: How to use .map file to locate line that crashes
Post by: allynm on May 07, 2011, 06:42:41 PM
Hi Steve, JJ, & Redskull,

Steve, thanks.  The example is EXACTLY what I was seeking.  Very clear.  All of this makes one appreciate the virtue of GetLastError!

JJ- Very nice job.  It's a nice template.

RedSkull-  Regarding codeview---yes, I have been using codeview and setting the switch.  As you say, CV is not particularly user-friendly but a whole lot more friendly than Windbg!  The version I use comes with Masm 6.15.  Seems to work OK, just hard to use.

Regards.

Mark