我正在学习一些Linux的汇编程序,我有这个示例程序,它应该调用write syscall并在屏幕上打印“Hello,World!”,但它会产生段错误。我在空闲时间学习,而不是做作业,我不再去上学了!
有人能看到这段代码有什么问题吗?
xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
jmp short string
code:
pop ecx
mov bl,1
mov dl,13
mov al,4
int 0x80
dec bl
mov al,1
int 0x80
string:
call code
db 'Hello, World!'
答案 0 :(得分:2)
适合我。这就是我所做的(注意,我在64位机器上,所以我有一个额外的标志来创建一个32位二进制文件):
TEST.ASM
_start:
xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
jmp short string
code:
pop ecx
mov bl,1
mov dl,13
mov al,4
int 0x80
dec bl
mov al,1
int 0x80
string:
call code
db 'Hello, World!'
命令:
$ nasm -felf test.asm -o test.o
$ gcc -m32 -nostdlib -nostdinc test.o -o test
产生了警告,但没关系。
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.1/../../../../x86_64-pc-linux-gnu/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080
然后我跑了:
$ ./test
确实输出"Hello, World!"
(没有换行符)。没有段错误。
答案 1 :(得分:1)
我对linux程序集并不是很熟悉,但这是猜测:
调用API时,必须使用特定的调用约定。约定指定的内容之一是在API调用中保留的寄存器列表。在您的情况下,错误是使用dec bl
而不是xor ebx, ebx
。因为bl
用作输入参数,所以它不太可能被保留。 mov al, 1
也是如此,写mov eax, 1
我同意@Greg Hewgil,你获取字符串地址的方式很不寻常。使用字符串编写与位置无关的代码的常见方法是:
...
call my_print
db 'hello world!', 0
...
my_print:
pop ecx
xor edx, edx
lp:
cmp byte [ecx + edx], 0
inc edx
jne lp
lea eax, [ecx + edx]
push eax // return address
dec edx
mov eax, 4
int 0x80
ret
答案 2 :(得分:0)
如果编译并在64位内核下运行,此代码可能会崩溃。 64位返回地址不适合32位ecx寄存器,你必须弹出rcx。此外,此代码使用32位API,在64位内核下可能无法使用。您应该使用64位API,如我的博文中所述:x86-64 assembly on Linux。