Reputation:
I am writing a simple program to display a name supplied by the user. The result is that I should be able to enter the command and get the expected result.
Command
./hello John
Result
Hello, John.
Yet when the program gets around to displaying the name, it doesn't. I believe it has something to do with calculating the length of the argument. May you guys please take a look at my code and tell me what you think?
; 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
Upvotes: 3
Views: 2773
Reputation: 22979
Ok, since you never used a debugger, I'll show you how. First, compile with nasm -f elf -g hello.asm
. The -g
switch helps the debugger, this way you can set breakpoints etc. Now start it typing gdb ./hello -q
and type break 34
. This tells gdb
to stop at line 34. Run the program (type run emi
(emi is my name :P)). You should see something like this:
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)
Ok, let's see what ecx
is, typing display (char *) $ecx
:
(gdb) display (char *) $ecx
1: (char *) $ecx = 0xbffff63e "/home/blackbear/hello"
You can use step
to continue by one instruction:
(gdb) step
35 mov edx, $ ; Beginning of line
1: (char *) $ecx = 0xbffff654 "emi"
Ok, here we are. ecx
points to my name, so the problem isn't here. Now we don't need to watch ecx
anymore, so using undisplay
gdb
won't show it anymore. But we need to check 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
Mmh, guess you didn't expect this, right? :) The problem seems to be here: mov edx, $
. I don't get that $
(never used NASM), could you please explain?
EDIT
Ok got it. You misunderstood what the tutorial said. The $
represents the current location of it, in fact:
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
now edx
contains the address of the instruction mov edx, $
. which is 5 bytes long (opcode (1 byte) + address (4 bytes)), that's why eip - edx = 5
.
In order to get the length of the argument your only way is to use something like strlen()
, but I can't help you here, NASM isn't my assembler. :)
Upvotes: 5