.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 编辑 ]
回复 #11 bradpitt88 的帖子
不好意思,这个问题我也不懂,尝试在call printf之前加一句:xorb %al, %al
或者,
movl $0, %eax
就没有段错误了。 根据ABI文档,我猜测可能是SSE寄存器(xmm)导致的。 调用函数时, %al里存放着使用的SSE寄存器的个数, 不知道为什么gcc编译的程序,在此时rax寄存器没有清零,导致出错。 向“albcamus(百無一用書生) ”表示感谢,我试着在“call printf” 之前加上"movl $0, %eax" 以后,果然没有出现“段错误”了。以后在这方面的问题还请多多指点。再次表示感谢! 的确,在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 编辑 ] 今天,我又试着在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;
}
回复 #14 bradpitt88 的帖子
加上#include <string.h>就好了。PS,x86-64我不行,这里的mik版主才是高手,欢迎多讨论:) 你们都是高手,多谢指点!:lol: :mrgreen:
在下的qq号是30879973,不知mik版主和“书生”兄能否留下你们的qq号以便日后学习交流之用?
[ 本帖最后由 bradpitt88 于 2008-6-3 11:33 编辑 ] 原帖由 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 中的用意是什么? 原帖由 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 真难找,谁会想到这一点 如果要使用printf函数的话,64位的机子上需要加上#include <string.h>这句话,而32位的机子上则不用加入上述的头文件。我试过了。
[ 本帖最后由 bradpitt88 于 2008-6-12 16:46 编辑 ]
页:
1
[2]