gdb不接受输入重定向

时间:2014-04-21 00:05:25

标签: linux assembly x86 gdb debian

我遇到了gdb的问题,我在Debian Linux(Jessie / Testing)中无法弄清楚。在尝试调试汇编程序时,我无法让gdb接受输入重定向。这是我试图调试的代码:

#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

我使用以下命令编译它:

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

然后我像这样运行gdb:

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

当我在安装了gdb 7.6.2的Debian Jessie笔记本电脑上运行时,会出现段错误。但是,当我在Debian Linux服务器上运行它(运行sid,相同的gdb版本)时,代码会执行它应该执行的操作。我已经把它打开了,但我很好奇为什么它会在我的笔记本电脑上出现段错误。有什么想法吗?

3 个答案:

答案 0 :(得分:1)

我通常不会回答我自己的问题,但我弄清楚为什么它不起作用。当使用64位操作系统时,as和ld应该知道您何时尝试编译和链接32位文件。出于某种原因,这在过去运作得很好。然而,通过这个程序,它没有(我想我过去只是幸运)。无论如何,修改上述命令:

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

是程序正常运行所必需的。特别是当我尝试将64位操作系统下的汇编代码编译并链接到32位可执行文件时。

答案 1 :(得分:0)

您使用pushw / popw错误对齐堆栈指针,这样做是个坏主意。我的猜测是你的笔记本电脑上的某些组件(可能是系统调用时的内核本身)并不是那样并导致错误。

顺便说一句,代码在gdb以外的笔记本电脑上运行良好吗?

答案 2 :(得分:0)

首先,代码中没有注释,也没有关于程序应该做什么的解释,这不是提问的好方法。但是,因为您在这里相对较新,我会通过发布您的代码的评论版来帮助您:

.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

所以代码似乎试图做的是从输入复制到输出。问题出在我所评论的“危险递归”中。&#34;当代码首先从start开始时,它会推送子程序的返回地址,该子程序是endit的32位地址。然后在readme的顶部推送一个16位值为0.渲染为16位值,堆栈如下所示:

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

如果sys_read调用返回的值不是零(每次成功读取一个字符时都会返回),我们会递归调用readme。堆栈现在看起来像这样:

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

然后我们再推一个0字,得到这个:

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

基本上,每次从stdin读取一个字符时,它将替换堆栈中的最新0,但会很快烧掉大量的堆栈空间 - 每个输入字符有三个16位字。如果读取大文件,这很可能导致堆栈崩溃。