我正在编写一个简单的程序来显示用户提供的名称.结果是我应该能够输入命令并获得预期的结果.
命令
./hello John
结果
Hello, John.
然而,当程序开始显示名称时,它却没有.我认为它与计算参数的长度有关.请你们看看我的代码并告诉我你的想法?
; hello.asm
;
; Assemble: nasm -f elf hello.asm
; Link: ld -o hello hello.o
; Run: ./hello <name>
section .data
period: db ".", 10
periodLen: equ $-period
helloMsg: db "Hello, "
helloMsgLen: equ $-helloMsg
usageMsg: db "Usage: hello <name>", 10
usageMsgLen: equ $-usageMsg
section .text
global _start
_start:
pop eax ; Get number of arguments
cmp eax, 2 ; If one argument
jne _help ; Not equal, show help + exit
mov eax, 4 ; System call to write
mov ebx, 1 ; Write to console
mov ecx, helloMsg ; Display "Hello, "
mov edx, helloMsgLen ; Length of hello message
int 80h
mov eax, 4 ; System call to write
mov ebx, 1 ; Write to console
pop ecx ; Get program name
pop ecx ; Get name
mov edx, $ ; Beginning of line
sub edx, ecx ; Get length of name
int 80h
mov eax, 4 ; System call to write
mov ebx, 1 ; Write to console
mov ecx, period ; Display a period
mov edx, periodLen ; Length of period
int 80h
mov eax, 1 ; System call to exit
mov ebx, 0 ; No errors
int 80h
_help:
mov eax, 4 ; System call to write
mov ebx, 1 ; Write to console
mov ecx, usageMsg ; Display usage message
mov edx, usageMsgLen ; Length of usage message
int 80h
mov eax, 1 ; System call to exit
mov ebx, 0 ; No errors
int 80h
解决方法:
好的,既然你从未使用过调试器,我会告诉你如何.首先,使用nasm -f elf -g hello.asm进行编译. -g开关帮助调试器,这样你可以设置断点等.现在开始输入gdb ./hello -q并输入break 34.这告诉gdb在第34行停止.运行程序(输入run emi(emi是我的名字:P)).你应该看到这样的东西:
blackbear@blackbear-laptop:~$gdb ./hello -q
Reading symbols from /home/blackbear/hello...done.
(gdb) break 34
Breakpoint 1 at 0x80480a9: file hello.asm, line 34.
(gdb) run emi
Starting program: /home/blackbear/hello emi
Hello,
Breakpoint 1, _start () at hello.asm:34
34 pop ecx ; Get name
(gdb)
好的,让我们看看ecx是什么,输入display(char *)$ecx:
(gdb) display (char *) $ecx
1: (char *) $ecx = 0xbffff63e "/home/blackbear/hello"
您可以使用步骤继续一条指令:
(gdb) step
35 mov edx, $ ; Beginning of line
1: (char *) $ecx = 0xbffff654 "emi"
好的,我们在这里. ecx指向我的名字,所以问题不在这里.现在我们不再需要观看ecx了,所以使用undisplay gdb将不再显示它.但我们需要检查edx:
(gdb) undisplay
Delete all auto-display expressions? (y or n) y
(gdb) display $edx
2: $edx = 7
(gdb) step
36 sub edx, ecx ; Get length of name
2: $edx = 134512810
(gdb) step
37 int 80h
2: $edx = 1208257110
嗯,猜你没想到这个,对吗? :)问题似乎在这里:mov edx,$.我没有得到那个$(从未使用过NASM),请你解释一下吗?
编辑
好的,我知道了.你误解了教程说的内容. $代表它的当前位置,实际上:
36 sub edx, ecx ; Get length of name
11: $edx = 134512810
(gdb) display (void *) $edx
12: (void *) $edx = (void *) 0x80480aa
(gdb) display (void *) $eip
13: (void *) $eip = (void *) 0x80480af
现在edx包含指令mov edx,$的地址.这是5个字节长(操作码(1个字节)地址(4个字节)),这就是为什么eip – edx = 5.
为了获得参数的长度,唯一的方法是使用像strlen()这样的东西,但是我不能帮助你,NASM不是我的汇编程序.