bradpitt88 发表于 2008-06-02 13:22

版主,本人用一个简单的程序试过,在64位的机子上用as、ld生成的程序运行没有问题,但换成了gcc来生成程序运行就会出现“instruction fault“的提示。下面是程序代码(请留意一下注释的部分):
.section .data
msg:
       .asciz"Hello, ubuntu! \n"
.section .text
.globl main
main:
                movq $msg, %rdi
                pushq %rdi
                call printf          #如果把这句话注释起来,在64位机子上用gcc生成的程序运行
                           #起来一切正常,但是加上这句话后在64位机子上用gcc生成的
                           #程序运行起来就会出错,问题出在call printf这个语句上
                movq $0, %rdi
                pushq %rdi
                call exit

[ 本帖最后由 bradpitt88 于 2008-6-2 13:40 编辑 ]

albcamus 发表于 2008-06-02 14:39

回复 #11 bradpitt88 的帖子

不好意思,这个问题我也不懂,尝试在call printf之前加一句:

xorb %al, %al

或者,

movl $0, %eax

就没有段错误了。 根据ABI文档,我猜测可能是SSE寄存器(xmm)导致的。 调用函数时, %al里存放着使用的SSE寄存器的个数, 不知道为什么gcc编译的程序,在此时rax寄存器没有清零,导致出错。

bradpitt88 发表于 2008-06-03 09:13

向“albcamus(百無一用書生) ”表示感谢,我试着在“call printf” 之前加上"movl $0, %eax" 以后,果然没有出现“段错误”了。以后在这方面的问题还请多多指点。再次表示感谢!

bradpitt88 发表于 2008-06-03 09:32

的确,在64位的机子上,gcc可能是存在某些bug的。我今天在单位的一台64位的ubuntu8.04上用gcc编译《Unix高级环境编程》里的一个程序,运行时就会莫名其妙的出现“段错误”的提示。 在家里的32位的ubuntu8.04上一点问题都没有。看来gcc在64位系统上还需改进。
程序如下:
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
        printf("ENOENT: %s.\n", strerror(ENOENT));
        errno=EACCES;
        perror(argv);
        return 0;
}

[ 本帖最后由 bradpitt88 于 2008-6-3 09:34 编辑 ]

bradpitt88 发表于 2008-06-03 09:51

今天,我又试着在64位的机子上把上述代码作了修改, 并用g++来编译,运行起来居然没有问题,看来问题出在printf上。我曾经试着在printf语句之前把eax清零,然后用gcc编译,不过还是不行。
代码如下:
#include <iostream>
#include <errno.h>
using namespace std;
int main(int argc, char *argv[])
{       
        cout<<"Error: "<<strerror(ENOENT)<<endl;
        //printf("ENOENT: %s.\n", strerror(ENOENT));
        errno=EACCES;
        perror(argv);
        return 0;
}

albcamus 发表于 2008-06-03 10:23

回复 #14 bradpitt88 的帖子

加上#include <string.h>就好了。

PS,x86-64我不行,这里的mik版主才是高手,欢迎多讨论:)

bradpitt88 发表于 2008-06-03 11:24

你们都是高手,多谢指点!:lol: :mrgreen:
在下的qq号是30879973,不知mik版主和“书生”兄能否留下你们的qq号以便日后学习交流之用?

[ 本帖最后由 bradpitt88 于 2008-6-3 11:33 编辑 ]

mik 发表于 2008-06-05 00:38

原帖由 albcamus 于 2008-6-2 14:39 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
不好意思,这个问题我也不懂,尝试在call printf之前加一句:

xorb %al, %al

或者,

movl $0, %eax

就没有段错误了。 根据ABI文档,我猜测可能是SSE寄存器(xmm)导致的。 调用函数时, %al里存放着 ...


al 果然一针见血,我贴出 printf 的实现代码来分析一下,一起来参详参详。在 /lib64/libc.so.6 里的

0000000000400ed0 <_IO_printf>:
400ed0:      48 81 ec d8 00 00 00         sub    $0xd8,%rsp
400ed7:      48 89 54 24 30               mov    %rdx,0x30(%rsp)
400edc:      0f b6 d0                     movzbl %al,%edx
400edf:      48 89 74 24 28               mov    %rsi,0x28(%rsp)
400ee4:      48 8d 04 95 00 00 00         lea    0x0(,%rdx,4),%rax
400eeb:      00
400eec:      ba 30 0f 40 00               mov    $0x400f30,%edx
400ef1:      48 89 4c 24 38               mov    %rcx,0x38(%rsp)
400ef6:      4c 89 44 24 40               mov    %r8,0x40(%rsp)
400efb:      4c 89 4c 24 48               mov    %r9,0x48(%rsp)
400f00:      48 89 fe                     mov    %rdi,%rsi
400f03:      48 29 c2                     sub    %rax,%rdx
400f06:      48 8d 84 24 cf 00 00         lea    0xcf(%rsp),%rax
400f0d:      00
400f0e:      ff e2                        jmpq   *%rdx
400f10:      0f 29 78 f1                  movaps %xmm7,-0xf(%rax)
400f14:      0f 29 70 e1                  movaps %xmm6,-0x1f(%rax)
400f18:      0f 29 68 d1                  movaps %xmm5,-0x2f(%rax)
400f1c:      0f 29 60 c1                  movaps %xmm4,-0x3f(%rax)
400f20:      0f 29 58 b1                  movaps %xmm3,-0x4f(%rax)
400f24:      0f 29 50 a1                  movaps %xmm2,-0x5f(%rax)
400f28:      0f 29 48 91                  movaps %xmm1,-0x6f(%rax)
400f2c:      0f 29 40 81                  movaps %xmm0,-0x7f(%rax)
400f30:      48 8d 84 24 e0 00 00         lea    0xe0(%rsp),%rax
400f37:      00
400f38:      48 8b 3d 49 72 28 00         mov    0x287249(%rip),%rdi      # 688188 <_IO_stdout>
400f3f:      48 89 e2                     mov    %rsp,%rdx
400f42:      c7 04 24 08 00 00 00         movl   $0x8,(%rsp)
400f49:      c7 44 24 04 30 00 00         movl   $0x30,0x4(%rsp)
400f50:      00
400f51:      48 89 44 24 08               mov    %rax,0x8(%rsp)
400f56:      48 8d 44 24 20               lea    0x20(%rsp),%rax
400f5b:      48 89 44 24 10               mov    %rax,0x10(%rsp)
400f60:      e8 4b d3 00 00               callq40e2b0 <_IO_vfprintf>
400f65:      48 81 c4 d8 00 00 00         add    $0xd8,%rsp


1、eax 确实是存放多少个需要解压的 xmm 寄存器的个数。 我没仔细看 ABI 文档,不知这里用意是什么,单从这里很难看出


2、
400edc:      0f b6 d0                     movzbl %al,%edx         
... ...
400ee4:      48 8d 04 95 00 00 00         lea    0x0(,%rdx,4),%rax
... ...
400eec:      ba 30 0f 40 00               mov    $0x400f30,%edx
... ...

400f03:      48 29 c2                     sub    %rax,%rdx
... ...
400f0e:      ff e2                        jmpq   *%rdx
... ...

上面这几条指令, eax 作用是一个 index 定位以地址 0x400f30 为基地址进行索引,所需执行多少条 movaps 指令。
由于每条 movaps xmm, XX(%rax) 指令的长度为4,
所以,lea (, %rdx, 4), %rax 才可以准确以 4 为 scalar 定位。
最后,跳转到 rdx 指针里执行。


3、这里引发另一个问题:
   若手工写汇编程序,不是很熟 ABI 的人,不太可能会想到在 call printf 前面加上 movl $0, eax 指令
   导致程序出现 segmentionfault 。   
   另一个极可能出现的错误是:illegal instruction (无效指令) 的错误,因为 eax 的不确定性,导致 rdx 计算结果出现越指令边界

4、这里执行解压xmm 寄存器到 stack 中的用意是什么?

mik 发表于 2008-06-05 00:47

原帖由 bradpitt88 于 2008-6-3 09:51 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
今天,我又试着在64位的机子上把上述代码作了修改, 并用g++来编译,运行起来居然没有问题,看来问题出在printf上。我曾经试着在printf语句之前把eax清零,然后用gcc编译,不过还是不行。
代码如下:
#includ ...

C 与 C++ 下行为不一致,或者平台不一致所引起的吧。

在我上面的分析中,eax 的不确定性,也有可能不会出现故障,只要 eax 在 0 ~ 7 的值范围内的话


PS: 这种 BUG 真难找,谁会想到这一点

bradpitt88 发表于 2008-06-12 16:45

如果要使用printf函数的话,64位的机子上需要加上#include <string.h>这句话,而32位的机子上则不用加入上述的头文件。我试过了。

[ 本帖最后由 bradpitt88 于 2008-6-12 16:46 编辑 ]
页: 1 [2]
查看完整版本: x86-64的calling convention,和ia32有什么大区别? (汇编程序问题)