Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

NASM and GDB: Examine Data

58 views
Skip to first unread message

Paul Nickerson

unread,
Jul 7, 2009, 12:39:40 AM7/7/09
to
I'm starting to program in assembly, using NASM in Ubuntu Linux, for
x86. I would like to be able to examine the running of the program in
a debugger now and then, and I've heard GDB may be good for this. It
seems to be all right, except one problem that I can't find a solution
for anywhere on the internet.

In GDB, you can use the print command to examine one variable [db
(define byte), etc.] or register you're using, or you can use the x
command to examine some amount of contiguous data, interpreted as some
data type you can specify. I'd like to examine some data in my .data
section, but GDB doesn't seem to like this.

Below is my example code, and what I do to link, compile, run, and
debug. It's a bit long; I hope it's acceptable to post all of this on
Usenet like this, it's my first post.

------debugLookTest.asm------

section .data ;the standard data section

datasec:
msg db 'Message: X',0xa ;a message string
len equ $ - msg ;length of the string

section .bss ;the standard bss section

bsssec:
buff resb 1 ;reserve one byte of space

section .text ;the standard text section

global main ;must be declared for linker (gcc)
main: ;tell linker entry point

textsec: ;bunch of moving around to test things out
mov AL,'A' ;move the one byte character A into the byte register AL
mov [buff],AL ;move the contents of AL into the location of my one
byte big reserved buffer above in my bss section
mov AH,[buff] ;move the contents of the buff into the one byte
register AH
mov [msg+9],AH ;move the contents of AH to an offsetted location in
msg in my data section, placing an A over the X

mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel

mov eax,1 ;exit with status 0
mov ebx,0
int 80h

------Terminal------

$ nasm -g -f elf -l debugLookTest.lst debugLookTest.asm
$ gcc -o debugLookTest.out debugLookTest.o
$ ./debugLookTest.out
Message: A
$ gdb debugLookTest.out
(gdb) print/c msg
$1 = 77 'M'
(gdb) x/c msg
0x7373654d: Cannot access memory at address 0x7373654d

------------

I get the same error if I run through the program in GDB, or try to do
it during running, after I write to that location. Would anyone know a
good way to examine the .data section during a program run?

Frank Kotler

unread,
Jul 7, 2009, 6:20:50 AM7/7/09
to
Paul Nickerson wrote:
> I'm starting to program in assembly, using NASM in Ubuntu Linux, for
> x86.

Good!

> I would like to be able to examine the running of the program in
> a debugger now and then, and I've heard GDB may be good for this.

I've heard good things about gdb. My actual experience is that it's an
obstinate bitch! :)

> It
> seems to be all right, except one problem that I can't find a solution
> for anywhere on the internet.
>
> In GDB, you can use the print command to examine one variable [db
> (define byte), etc.] or register you're using, or you can use the x
> command to examine some amount of contiguous data, interpreted as some
> data type you can specify. I'd like to examine some data in my .data
> section, but GDB doesn't seem to like this.

No, it doesn't... I stuck a string in your .text section, and gdb *did*
display that, as expected. (actually, I "expect" gdb to give me a
pantload of error messages, but...)

> Below is my example code, and what I do to link, compile, run, and
> debug. It's a bit long; I hope it's acceptable to post all of this on
> Usenet like this,

On-topic? Actual assembly code? Hell yes!!!

> it's my first post.

Welcome! Hope it won't be your last!

> ------debugLookTest.asm------
>
> section .data ;the standard data section
>
> datasec:
> msg db 'Message: X',0xa ;a message string
> len equ $ - msg ;length of the string
>
> section .bss ;the standard bss section
>
> bsssec:
> buff resb 1 ;reserve one byte of space
>
> section .text ;the standard text section
>
> global main ;must be declared for linker (gcc)
> main: ;tell linker entry point

An alternative, since you're not actually using any C, would be to
replace "main" with "_start", and link directly with ld (this doesn't help).

If you use "_start", gdb seems happier if there's a one-byte instruction
first:

nop ; parking place for gdb

Dunno if this would help after "main" when linking with gcc. Doesn't
help either way, in this case.

> textsec: ;bunch of moving around to test things out
> mov AL,'A' ;move the one byte character A into the byte register AL
> mov [buff],AL ;move the contents of AL into the location of my one
> byte big reserved buffer above in my bss section
> mov AH,[buff] ;move the contents of the buff into the one byte
> register AH
> mov [msg+9],AH ;move the contents of AH to an offsetted location in
> msg in my data section, placing an A over the X
>
> mov edx,len ;message length
> mov ecx,msg ;message to write
> mov ebx,1 ;file descriptor (stdout)
> mov eax,4 ;system call number (sys_write)
> int 0x80 ;call kernel
>
> mov eax,1 ;exit with status 0
> mov ebx,0
> int 80h
>
> ------Terminal------
>
> $ nasm -g -f elf -l debugLookTest.lst debugLookTest.asm

Recent versions of Nasm have added the "dwarf" debugging format. Use "-F
dwarf" to enable it. Lower case 'f' for output format, upper case 'F'
for debug format. "-F" implies "-g", so you don't need to use both
(unlike older versions of Nasm). This doesn't help.

> $ gcc -o debugLookTest.out debugLookTest.o

Do we need the "-g" switch to pass debugging information through?
(doesn't help)

> $ ./debugLookTest.out
> Message: A

So far, so good. Maybe we should quit while we're ahead. :)

> $ gdb debugLookTest.out
> (gdb) print/c msg
> $1 = 77 'M'

Okay...

> (gdb) x/c msg
> 0x7373654d: Cannot access memory at address 0x7373654d

Same here - curiously, same "address" with or without the "nop". As
you've probably tried, "x/c buff" doesn't like address 0x0(!). Well, I
wouldn't "expect" it to like those addresses. Damned if I know where
it's coming up with them, though!

> ------------
>
> I get the same error if I run through the program in GDB, or try to do
> it during running, after I write to that location. Would anyone know a
> good way to examine the .data section during a program run?

I'll bet Chuck Crayne would know the answer! Unfortunately, he passed
away in February. He is sorely missed! Besides contributing to Nasm (he
wrote the dwarf debugging and elf64, among other things), he posted a
".gdbinit" file that makes gdb more asm-friendly. I'll see if I can dig
it up and post it (I'm not currently using it). I doubt if it'll help
here... I'll try it.

What I use more frequently than gdb is Patrick Alken's "ald" - the
assembly language debugger:

http://ald.sourceforge.net/

There's also a modification of this, which borrows some code from Nasm
to add assembling capabilities (seems to lack the "examine" command...
"d msg" works... I've gotta look more at this!):

http://modest-proposals.com/Furball.htm

Most often, I "debug" my code by staring at it and scratching my head. :)

I don't know the answer, but I sure admire the question, Paul. Maybe
somebody else can come up with something more helpful.

Best,
Frank

H. Peter Anvin

unread,
Jul 7, 2009, 12:02:15 PM7/7/09
to Frank Kotler
Frank Kotler wrote:
>
>> $ gdb debugLookTest.out
>> (gdb) print/c msg
>> $1 = 77 'M'
>
> Okay...
>
>> (gdb) x/c msg
>> 0x7373654d: Cannot access memory at address 0x7373654d
>
> Same here - curiously, same "address" with or without the "nop". As
> you've probably tried, "x/c buff" doesn't like address 0x0(!). Well, I
> wouldn't "expect" it to like those addresses. Damned if I know where
> it's coming up with them, though!
>

Those are the ASCII letters "Mess", which is somewhat appropriate. It
looks like gdb defaults to assigning a type of "int" instead of
something more appropriate, like a character array, when it doesn't have
detailed info.

Note also that this may depend on if you're using stabs or dwarf as the
debugging format (-g -F stabs or -g -F dwarf). In general, dwarf is better.

Try something like:

x/s &msg

-hpa

Frank Kotler

unread,
Jul 7, 2009, 12:39:52 PM7/7/09
to
H. Peter Anvin wrote:

...


> Try something like:
>
> x/s &msg

Bingo! Actually, since the "string" isn't zero-terminated, gdb runs off
the end into inaccessible memory, but at a "sane" address.
Zero-terminating the string fixes that. It's all in knowin' how! Thanks,
Peter!

Best,
Frank

Paul Nickerson

unread,
Jul 7, 2009, 4:10:17 PM7/7/09
to
On Jul 7, 12:02 pm, "H. Peter Anvin" <h...@zytor.com> wrote:
> Those are the ASCII letters "Mess", which is somewhat appropriate.

Oh, good eye. It looks like GDB was trying to use my data at location
msg as a 32 bit pointer to some other location. My data in msg started
with "Message:", and since Intel is little endian, it was taking the
32 bit "Mess" as "sseM", and trying to dereference that. Gah, pointers
and dereferencing always seem to byte me. Anyway, using $msg should
help with that.

(gdb) x/s $msg
Value can't be converted to integer.

OK, different error now. I'll try that dwarf thing now.

$ nasm -f elf -F dwarf -l debugLookTest.lst debugLookTest.asm

Hmm, still the same error. OK, I'll put in that NOP buffer... nope,
still no good. BTW, using x with no /s, or using it with /c gives the
same error. /s is string, and /c is character.

I did try using LD as the linker early on, but if I remember correctly
I had some trouble getting GDB working with that, and so went with
GCC. I wouldn't mind trying it again. I could also switch over to
using ALD; I'm still early in the learning process, and so I can
switch around debuggers if need be. Or maybe it'd be good to have
multiple tools in the first place.

Oh, and before I forget, I did consider trying to move things around
in my program to see if that inaccessible memory address would change,
like when you put the NOP in the beginning of the .text section Frank,
but I'd read that the different sections are compiled and ordered
separately. I guess I could have inserted something extra at the
beginning of the .data section, but I didn't think of that. Doesn't
really matter now, because I now know it was trying to dereference
ASCII data as a pointer, ugh.

Anyway, before trying the ideas above, I'm going to look up that
"Value can't be converted to integer." error and see if I can't attack
that directly... Hmm, it doesn't seem to matter if I use a real
variable or not:

(gdb) x/s $asdf
Value can't be converted to integer.

Oh hey, you know what it might be. Maybe I should use & instead of $.
Yeah, that might do it. Ugh, kind of embarrassed it took me 20 minutes
to see that. Anyway, it works:

(gdb) x/s &msg
0x804a010 <msg>: "Message: X\n"
(gdb) x/10cb &msg
0x804a010 <msg>: 77 'M' 101 'e' 115 's' 115 's' 97 'a' 103 'g' 101 'e'
58 ':'
0x804a018: 32 ' ' 88 'X'

Thank you Peter and Frank for the solutions. And thanks for the very
warm welcome, Frank. I'll keep the alternative debugger and linker in
mind, and I'll keep using the buffer for GDB and dwarf to avoid
potential annoyances in the future. I'm also going to keep in mind to
null-end my strings from now on, as /s seems nicer than /c for those
things.

In conclusion, I find it weird that GDB examines data properly and as
expected in the .text section, but in the .data section, it tries to
dereference data as a pointer. And so we have to tell GDB that the
variable msg is in fact a pointer to my data by using the &. Oh, and
using & for something inside .text works in addition to not using &
there. Very strange indeed.

Paul Nickerson

unread,
Jul 7, 2009, 9:33:48 PM7/7/09
to
I tried some of your suggestions of what to use instead. I tried using
DWARF, but I lost line numbers that way:

$ nasm -f elf -F dwarf -l printPixels.lst printPixels.asm
$ gcc -g -o printPixels.out printPixels.o
$ gdb printPixels.out
(gdb) break main
Breakpoint 1 at 0x80483a0
(gdb) run
Starting program: printPixels.out
Breakpoint 1, 0x080483a0 in main ()
Current language: auto; currently asm
(gdb) step
Single stepping until exit from function main,
which has no line number information.
double_to_ascii () at printPixels.asm:113
113 mov ecx,ascii

It just steps through everything in the main function starting at line
23 until another function gets called, which starts way down on line
113. I'm not sure what is loosing the line information, but I can see
that the printPixels.lst file is a text file with line numbers in the
text, and the main function does have its line numbers.

And I tried LD too with changing main to _start, but while GDB seems
to set the break point OK, when I hit run, it just runs right through
until the program exits, seemingly skipping right over the break point
that was successfully set.

$ ld -o printPixels.out printPixels.o

0 new messages