Calling atoi() from within NASM

From Visual Basic to GNU C, this is the place to talk programming.

Moderators: SecretSquirrel, just brew it!

Calling atoi() from within NASM

Postposted on Sun Mar 31, 2013 8:20 am

Hey folks, I've been getting into some assembly programming recently. (I wish I had one of those old computers where you have to do everything yourself and computing power was limited, and you could write directly to the VRAM, but I don't) I'm on FreeBSD, and I'm writing in 64bit as well because I switched from Linux, and 64bit has a standardized calling convention, whereas in 32bit ASM I've seen people put arguments to syscalls on the stack or in registers.

I came up with this, in which I have to call atoi() from C:
Code: Select all
;RDI, RSI, RDX, RCX, R8, R9, XMM0–7
%define SYS_EXIT 1
%define SYS_READ 3
%define SYS_WRITE 4
%define STDIN 0
%define STDOUT 1
%define STDERR 2

section .bss
inputnum resb 1
sum resb 2

section .data
prompt db 'Input some ****: '

section .text
extern _atoi

global _start
_exit:
   mov rax, SYS_EXIT ; void sys_exit() from FreeBSD
   mov rdi, 0
   syscall

_start:
   mov rax, SYS_WRITE
   mov rdi, STDOUT
   mov rsi, prompt
   mov rdx, 20 ; how many bytes
   syscall
   mov rax, 3 ; SYS_READ
   mov rdi, 0 ; STDIN
   mov rsi, inputnum
   mov rdx, 1 ; how many bytes
   syscall

   push rsi
   call _atoi ; after this the answer should be in rax
   mov r10, rax

   mov rax, SYS_WRITE
   mov rdi, STDOUT
   mov rsi, r10
   mov rdx, 1
   syscall
   call _exit


NASM doesn't have a problem with this, but ld does. And I'm not sure what I have to do. Obviously I have to tell ld to "include" libc somehow, but how?
This is what I use for assembling and linking:

nasm -f elf64 helloworld.asm
ld -o hello helloworld.o
Mothership: Thuban 1055T@3.7GHz, 12GB DDR3, M5A99X EVO, GTX470+Icy Vision Rev.2@840/3800, Vertex 2E 60GB
Supply ship: Sargas@2.8GHz, 12GB DDR3, M4A88TD-V EVO/USB3
Corsair: Macbook Air Ivy Bridge
Crayon Shin Chan
Minister of Gerbil Affairs
 
Posts: 2246
Joined: Fri Sep 06, 2002 11:14 am
Location: Malaysia

Re: Calling atoi() from within NASM

Postposted on Sun Mar 31, 2013 8:49 am

Try:
Code: Select all
ld -o hello helloworld.o -lc
(Note the -lc!)

Hope that helps.
zamb
Gerbil
 
Posts: 14
Joined: Wed Nov 26, 2008 9:38 am

Re: Calling atoi() from within NASM

Postposted on Sun Mar 31, 2013 8:50 am

I'm not an assembly expert but it looks like your call to ld needs to be given all of the files that include the symbols you reference in your assembly (e.g. _atoi). I checked and atoi is under the stdlib.h header, which I believe is just part of the standard libc library. Try adding a path to your libc.a file in the call to ld so that you are passing all of the files that include the symbols you care about to ld. I'm not 100% sure where FreeBSD keeps it (and the filename might be different), but on my Linux system the call would look like:
Code: Select all
nasm -f elf64 helloworld.asm
ld -o hello helloworld.o /lib/libc.a


Note: I'm pretty sure you want to link to the static version of libc (libc.a) for the most basic operation, but has the side effect of generating a pretty gigantic binary from your very small assembled code. It might be possible to dynamically link to the .so version of libc, which would mean a smaller binary. I'll leave that as an exercise for the linking experts. Good Luck!

EDIT: I' just saw zamb's post, and I think the -lc is just an easier way of doing the same job I described above... go with the easier solution :-)
4770K @ 4.7 GHz; 32GB DDR3-2133; GTX-770; 512GB 840 Pro (2x); Fractal Define XL-R2; NZXT Kraken-X60
--Many thanks to the TR Forum for advice in getting it built.
chuckula
Gerbil Elite
Gold subscriber
 
 
Posts: 568
Joined: Wed Jan 23, 2008 9:18 pm
Location: Probably where I don't belong.

Re: Calling atoi() from within NASM

Postposted on Sun Mar 31, 2013 8:56 am

I tried -lc:

Code: Select all
helloworld.o: In function `_start':
helloworld.asm:(.text+0x44): undefined reference to `_atoi'
/usr/lib/libc.so: undefined reference to `__progname'
/usr/lib/libc.so: undefined reference to `environ'


So I figured I'd use "atoi" instead of "_atoi":
Code: Select all
/usr/lib/libc.so: undefined reference to `__progname'
/usr/lib/libc.so: undefined reference to `environ'


What gives?
Mothership: Thuban 1055T@3.7GHz, 12GB DDR3, M5A99X EVO, GTX470+Icy Vision Rev.2@840/3800, Vertex 2E 60GB
Supply ship: Sargas@2.8GHz, 12GB DDR3, M4A88TD-V EVO/USB3
Corsair: Macbook Air Ivy Bridge
Crayon Shin Chan
Minister of Gerbil Affairs
 
Posts: 2246
Joined: Fri Sep 06, 2002 11:14 am
Location: Malaysia

Re: Calling atoi() from within NASM

Postposted on Sun Mar 31, 2013 9:07 am

Crayon Shin Chan wrote:I tried -lc:

Code: Select all
helloworld.o: In function `_start':
helloworld.asm:(.text+0x44): undefined reference to `_atoi'
/usr/lib/libc.so: undefined reference to `__progname'
/usr/lib/libc.so: undefined reference to `environ'


So I figured I'd use "atoi" instead of "_atoi":
Code: Select all
/usr/lib/libc.so: undefined reference to `__progname'
/usr/lib/libc.so: undefined reference to `environ'


What gives?


More symbols that need resolved since you are now linking into the rather large libc library. This thread has some information about the issue: http://compgroups.net/comp.lang.asm.x86 ... asm/125234
Note: you may need to use GCC to do the linking instead of simply using ld.
4770K @ 4.7 GHz; 32GB DDR3-2133; GTX-770; 512GB 840 Pro (2x); Fractal Define XL-R2; NZXT Kraken-X60
--Many thanks to the TR Forum for advice in getting it built.
chuckula
Gerbil Elite
Gold subscriber
 
 
Posts: 568
Joined: Wed Jan 23, 2008 9:18 pm
Location: Probably where I don't belong.

Re: Calling atoi() from within NASM

Postposted on Sun Mar 31, 2013 10:45 am

notfred
Grand Gerbil Poohbah
 
Posts: 3748
Joined: Tue Aug 10, 2004 10:10 am
Location: Ottawa, Canada

Re: Calling atoi() from within NASM

Postposted on Mon Apr 01, 2013 2:13 am

Cool, I didn't know that gcc could be used just to link. It output (with -v) a very long ld command and increased the binary's size from 1KB to ~4KB. I had to change _start to main though.

However, according to ktrace, I can't output the result which I copied to r10 from rax (which atoi placed the answer in) because "bad address"?


Code: Select all
 95392 hello    CALL  write(0x1,0x6008c8,0x11)
 95392 hello    GIO   fd 1 wrote 17 bytes
       "Input some ****: "
 95392 hello    RET   write 17/0x11
 95392 hello    CALL  read(0,0x6008ec,0x2)
 95392 hello    GIO   fd 0 read 2 bytes
       "3
       "
 95392 hello    RET   read 2
 95392 hello    CALL  write(0x1,0x3,0x64)
 95392 hello    RET   write -1 errno 14 Bad address
 95392 hello    CALL  write(0x1,0,0x1)
 95392 hello    RET   write -1 errno 14 Bad address
 95392 hello    CALL  exit(0)


Code: Select all
;RDI, RSI, RDX, RCX, R8, R9, XMM0–7
%define SYS_EXIT 1
%define SYS_READ 3
%define SYS_WRITE 4
%define STDIN 0
%define STDOUT 1
%define STDERR 2

section .bss
inputnum resb 2
sum resb 2

section .data
prompt db 'Input some ****: '
promptlen equ $-prompt
lolcopter db 'lol-copter'
section .text
extern atoi

global main

sys_write:
   mov rax, SYS_WRITE
   mov rdi, STDOUT
   mov rsi, lolcopter
   mov rdx, 10 ; how many bytes
   ret

_exit:
   mov rax, SYS_EXIT ; void sys_exit() from FreeBSD
   mov rdi, 0
   syscall
main:
   mov rax, SYS_WRITE
   mov rdi, STDOUT
   mov rsi, prompt
   mov rdx, promptlen ; how many bytes
   syscall

   mov rax, 3 ; SYS_READ
   mov rdi, 0 ; STDIN
   mov rsi, inputnum
   mov rdx, 2 ; how many bytes
   syscall

   mov rdi, inputnum
   call atoi ; after this the answer should be in rax
   mov r10, rax

   mov rax, SYS_WRITE
   mov rdi, STDOUT
   mov rsi, r10
   mov rdx, 100
   syscall

   mov rax, SYS_WRITE ; I WANT A NEW LINE
   mov rdi, STDOUT
   mov rsi, 0
   mov rdx, 1
   syscall

   call _exit
Mothership: Thuban 1055T@3.7GHz, 12GB DDR3, M5A99X EVO, GTX470+Icy Vision Rev.2@840/3800, Vertex 2E 60GB
Supply ship: Sargas@2.8GHz, 12GB DDR3, M4A88TD-V EVO/USB3
Corsair: Macbook Air Ivy Bridge
Crayon Shin Chan
Minister of Gerbil Affairs
 
Posts: 2246
Joined: Fri Sep 06, 2002 11:14 am
Location: Malaysia

Re: Calling atoi() from within NASM

Postposted on Mon Apr 01, 2013 2:46 am

Caveat: It is possible I'm mistaken on some of the details here, as I've not done 64-bit x86 assembly programming. I did a ton of 8, 16, and 32-bit assembly back in the day though!

1. sys_write() can't handle integer values directly. You need to put the data in a buffer, and pass the buffer's address.

2. I think you may also be confused about the difference between the string and binary representation of an integer. It looks like you are reading only 2 bytes from stdin; this is going to allow you to input only single-digit numbers, since one byte needs to be reserved for the null terminator.

2a. Unless the user explicitly inputs that null byte somehow, the terminator will be missing and atoi() will run off the end of the buffer (and likely return garbage as a result). You probably need to scan the input for a newline, and replace it with a null byte?

3. Unless you intend to write the result to stdout as raw binary data, you will need to convert it back to a string again before writing.

4. Why are you setting the length to 100 in the sys_write() call? If your intent is to write the data in binary, length should be 4 (the size of an int). If your intent is to write it as a string, then you need to set the length to however many digits the result has after you convert it back to a string.

5. Kudos for attempting assembly language; IMO more people should do that. Even if you generally code in a high level language, understanding what goes on under the hood can help you to write more efficient code.
(this space intentionally left blank)
just brew it!
Administrator
Gold subscriber
 
 
Posts: 37842
Joined: Tue Aug 20, 2002 10:51 pm
Location: Somewhere, having a beer


Return to Developer's Den

Who is online

Users browsing this forum: No registered users and 1 guest