Personal computing discussed

Moderators: renee, SecretSquirrel, just brew it!

 
Crayon Shin Chan
Minister of Gerbil Affairs
Topic Author
Posts: 2313
Joined: Fri Sep 06, 2002 11:14 am
Location: Malaysia
Contact:

Calling atoi() from within NASM

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:
;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: FX-8350, 12GB DDR3, M5A99X EVO, MSI GTX 1070 Sea Hawk, Crucial MX500 500GB
Supply ship: [email protected], 12GB DDR3, M4A88TD-V EVO/USB3
Corsair: Thinkpad X230
 
zamb
Gerbil
Posts: 15
Joined: Wed Nov 26, 2008 9:38 am

Re: Calling atoi() from within NASM

Sun Mar 31, 2013 8:49 am

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

Hope that helps.
 
chuckula
Minister of Gerbil Affairs
Posts: 2109
Joined: Wed Jan 23, 2008 9:18 pm
Location: Probably where I don't belong.

Re: Calling atoi() from within NASM

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:
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; Officially RX-560... that's right AMD you shills!; 512GB 840 Pro (2x); Fractal Define XL-R2; NZXT Kraken-X60
--Many thanks to the TR Forum for advice in getting it built.
 
Crayon Shin Chan
Minister of Gerbil Affairs
Topic Author
Posts: 2313
Joined: Fri Sep 06, 2002 11:14 am
Location: Malaysia
Contact:

Re: Calling atoi() from within NASM

Sun Mar 31, 2013 8:56 am

I tried -lc:

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":
/usr/lib/libc.so: undefined reference to `__progname'
/usr/lib/libc.so: undefined reference to `environ'


What gives?
Mothership: FX-8350, 12GB DDR3, M5A99X EVO, MSI GTX 1070 Sea Hawk, Crucial MX500 500GB
Supply ship: [email protected], 12GB DDR3, M4A88TD-V EVO/USB3
Corsair: Thinkpad X230
 
chuckula
Minister of Gerbil Affairs
Posts: 2109
Joined: Wed Jan 23, 2008 9:18 pm
Location: Probably where I don't belong.

Re: Calling atoi() from within NASM

Sun Mar 31, 2013 9:07 am

Crayon Shin Chan wrote:
I tried -lc:

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":
/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; Officially RX-560... that's right AMD you shills!; 512GB 840 Pro (2x); Fractal Define XL-R2; NZXT Kraken-X60
--Many thanks to the TR Forum for advice in getting it built.
 
notfred
Maximum Gerbil
Posts: 4610
Joined: Tue Aug 10, 2004 10:10 am
Location: Ottawa, Canada

Re: Calling atoi() from within NASM

Sun Mar 31, 2013 10:45 am

 
Crayon Shin Chan
Minister of Gerbil Affairs
Topic Author
Posts: 2313
Joined: Fri Sep 06, 2002 11:14 am
Location: Malaysia
Contact:

Re: Calling atoi() from within NASM

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"?


 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)


;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: FX-8350, 12GB DDR3, M5A99X EVO, MSI GTX 1070 Sea Hawk, Crucial MX500 500GB
Supply ship: [email protected], 12GB DDR3, M4A88TD-V EVO/USB3
Corsair: Thinkpad X230
 
just brew it!
Administrator
Posts: 54500
Joined: Tue Aug 20, 2002 10:51 pm
Location: Somewhere, having a beer

Re: Calling atoi() from within NASM

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.
Nostalgia isn't what it used to be.

Who is online

Users browsing this forum: No registered users and 1 guest
GZIP: On