ChrisR.
ChrisR.

Reputation: 111

gdb not accepting input redirection

I have an issue with gdb that I cannot quite figure out in Debian Linux (Jessie/Testing). When attempting to debug an assembly program, I cannot get gdb to accept input redirection. Here is the code I am attempting to debug:

#The program reverses the input given to it.  For example "123456789" will 
#become "987654321"

.global _start

readme:
    pushw $0          #allocate 2 bytes onto the stack
    movl $3,%eax      #system call for read
    movl $0,%ebx      #stdin
    movl %esp,%ecx    #read to stack pointer
    movl $1,%edx      #number of bytes to read
    int  $0x80        #execute instruction

    cmpl $0,%eax      #check number of bytes read
    jz  returnme      #jump to label 'returnme' if zero bytes are read

writeme:
    call readme       #recursive call to continue to next character

    movl $4,%eax      #system call for write
    movl $1,%ebx      #stdout
    movl %esp,%ecx    #write what is in the stack pointer
    movl $1,%edx      #write one byte
    int  $0x80        #execute instruction

returnme:
    popw %ax          #clean up
    ret               #return to line after previous call

_start:
    call readme       #call subroutine readme

endit:
    movl $1,%eax      #These lines are for exiting the program
    movl $0,%ebx
    int  $0x80

I compile it using these commands:

as -gstabs -o foo.o foo.s
ld -o foo foo.o

Then I run gdb like this:

gdb foo
(gdb) r <test.in 1>test1.out

When I run this on my laptop running Debian Jessie with gdb 7.6.2 installed, it segfaults. However, when I run this on a Debian Linux server (running sid, same gdb version), the code does what it is supposed to. I have already turned this in, but I am curious as to why it segfaults on my laptop. Any ideas?

Upvotes: 0

Views: 122

Answers (3)

ChrisR.
ChrisR.

Reputation: 111

I would not normally answer my own question, but I figured out why it wasn't working. When using a 64-bit operating system, as and ld are supposed to know when you are attempting to compile and link a 32-bit file. For some reason, this had worked just fine in the past. However, with this program, it did not (I would assume I was just lucky in the past). Anyway, a correction to the above commands:

as --32 -gstabs foo.s -o foo.o 
ld -m elf_i386 foo.o -o -foo

was required for the program to work correctly. This is specifically when I was attempting to compile and link assembly code under a 64-bit OS into a 32-bit executable.

Upvotes: 1

Edward
Edward

Reputation: 7090

First, having no comments in the code and no explanation as to what the program is supposed to do is not a good way to ask a question. Because you're relatively new here, though, I'll help you out by posting a commented version of your code:

.global _start

readme:
    pushw $0
    movl $3,%eax        ; sys_read
    movl $0,%ebx        ; file = stdin
    movl %esp,%ecx      ; pointer to userbuff
    movl $1,%edx        ; count = 1
    int  $0x80          ; do it

    cmpl $0,%eax        ; check return value
    jz  returnme        ; if it's zero, return

writeme:
    call readme         ; dangerous recursion!
write2:
    movl $4,%eax        ; sys_write
    movl $1,%ebx        ; file = stdout
    movl %esp,%ecx      ; pointer to userbuff = stack?!
    movl $1,%edx        ; 1 byte
    int  $0x80          ; do it

returnme:
    popw %ax            ; clean up stack
    ret                 ; return 

_start:
    call readme

endit:
    movl $1,%eax        ; sys_exit
    movl $0,%ebx        ; error code = 0 (OK)
    int  $0x80          ; do it

So what the code appears to be attempting to do is copy from input to output. The problem is in the line that I've commented as "dangerous recursion." When the code first starts at start, it pushes the return address for the subroutine which is the 32-bit address of endit. Then at the top of readme you push a 16-bit value of 0. Rendered as 16-bit values, the stack looks like this:

+-----------+
|      0    |
+-----------+
| endit-hi  |
+-----------+
| endit-lo  |
+-----------+

If the sys_read call returns something other than zero (which it does every time it successfully reads a character), we then call readme recursively. The stack now looks like this:

+-----------+
| write2-hi |
+-----------+
| write2-lo |
+-----------+
|      0    |
+-----------+
| endit-hi  |
+-----------+
| endit-lo  |
+-----------+

Then we push a 0 word again and get this:

+-----------+
|      0    |
+-----------+
| write2-hi |
+-----------+
| write2-lo |
+-----------+
|      0    |
+-----------+
| endit-hi  |
+-----------+
| endit-lo  |
+-----------+

Basically, each time a character is read from stdin it will replace the latest 0 on the stack, but will very quickly burn up lots of stack space - three 16-bit words per input character. This is very likely to cause a stack crash if a large file is read.

Upvotes: 0

Jester
Jester

Reputation: 58762

Your use of pushw/popw misaligns the stack pointer, it's a bad idea to do that. My guess is that some component on your laptop (maybe the kernel itself at the time of a system call) doesn't like that and causes the fault.

By the way, does the code run fine on your laptop outside of gdb?

Upvotes: 0

Related Questions