NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: MJaoune on May 25, 2011, 03:33:15 PM
-
How to call C functions from an ASM project?? I searched the whole forum and didn't find anything handy :-\, so please post a good full help that everybody can read this topic.
Thanks
-
Well, the Friendly Manual has a pretty good run-down on it:
http://www.nasm.us/xdoc/2.09.08/html/nasmdoc9.html#section-9.1
"global" and "extern" are the keys to it. A symbol - function or variable - which exists in your file, that you wish to make known to the outside world, is "global". A symbol - function or variable - which is used in your code, but exists someplace else, is "extern". (some assemblers use "public" and "extrn") This applies to languages other than C, BTW - I think I have an example of an asm function that can be called from Fortran!
There's the question of "how do we spell it". Almost everybody besides ELF spells it "_main" and "_printf". ELF spells it "main" and "printf". OpenWatcom C spells it "main_" and "prinf_"! Lord knows what C++ is going to do with it - declare your asm function as 'extern "C" myfunc', I think. The section(s) in the Friendly Manual mentions that you may wish to use a macro to do this conversion. Better yet (IMO) is to write it as "main" and "printf", and use the "--prefix _ " and/or "--postfix _" command line options to put the underscores where your compiler expects. Strictly speaking, I think this is a "linker requirement" - the compiler has to put the underscores the same places we do, but it amounts to the same thing...
Push the parameters (right to left, for C), and "call _foof" ("_foof" being my idea of how C would spell "foo" :) ). That's about it...
A couple of "clues"... "printf" always expects floats to be double precision (8 bytes, 64 bits), and "printf" doesn't print anything until the buffer is flushed.
What part(s) are you having trouble with?
Best,
Frank
-
Push the parameters (right to left, for C), and "call _foof" ("_foof" being my idea of how C would spell "foo" :) ). That's about it...
... for 32-bit/CDECL*
-
Good point! All of the above applies to 32-bit code. The Fine Manual has sections on interfacing with 64-bit C(s) - different for Unix and Windows. Links to the abi(s) and everything. (the careful observer will note that, if you delve "under the hood", C ain't quite as "standardized" and "portable" as it says on the tin) We can cope! :)
Best,
Frank
-
example for 32 bit linux
EXTERN gnu_get_libc_version
EXTERN gnu_get_libc_release
EXTERN printf
;for 32bit linux
;build
;nasm -f elf -g ctest.asm
;gcc ctest.o -o test
; macro for calling C functions from assembly language programs.
; pushes the arguments on the stack, makes the function call and
; then cleans up the stack. follows the cdecl calling convention
%macro ccall 1-*
%define __procedure_call %1
%rep %0-1
%rotate -1
push %1
%endrep
call __procedure_call
%if (%0-1)
add esp, (%0-1)*4
%endif
%endmacro
EXIT_SUCCESS equ 0
NULL equ 0
SECTION .data
fmtstr db "You are currently using glibc version %s %s", 10, 0
version dd NULL ;will contain pointer to a version string
release dd NULL ;will contain pointer to a release string
SECTION .text
GLOBAL main
main:
ccall gnu_get_libc_version
mov [version], eax
ccall gnu_get_libc_release
mov [release], eax
ccall printf, fmtstr, dword[version], dword[release]
mov eax, EXIT_SUCCESS
ret
i've spent the last couple of months creating macros and include files for system programming with 32-bit linux, i'm so far from finished its not even funny :D
-
Good one, Brethren! (How old is "2.3.6 stable"? :) )
Where'd you learn of the existance of these functions? I can't find 'em anywhere in the man pages!
The most extensive include files for Linux (and BSD and BeOS) I've seen are in the "asmutils" package.
Best,
Frank
-
i'm reading a great book called the linux programming interface
http://www.amazon.com/Linux-Programming-Interface-System-Handbook/dp/1593272200
its a massive book and its geared toward C programmers but i'm having a lot of fun discovering stuff i had no idea existed :)
btw i've seen the asmutils package and its a great resource but i want a set of inc files that are functionally reasonably similar to the C header files to be useful.
This is an example of what i mean, stdio.inc (its not yet finished)
%ifndef __STDIO_INC__
%define __STDIO_INC__
; end-of-file character
EOF equ -1
%ifndef __NULL_defined__
%define __NULL_defined__
NULL equ 0 ;also defined in stdlib.inc, string.inc, unistd.inc
%endif
; values from /usr/include/bits/stdio_lim.h
L_tmpnam equ 20 ;how long an array of bytes needs to be for tmpnam
TMP_MAX equ 238328 ;the minimum number of unique filenames generated by tmpnam
FILENAME_MAX equ 4096 ;maximum length of a filename
L_ctermid equ 9 ;how long an array of bytes to be passed to ctermid
L_cuserid equ 9 ;how long an array of bytes to be passed to cuserid
FOPEN_MAX equ 16 ;max number of files that can be open at once
IOV_MAX equ 1024
%defstr P_tmpdir "/tmp" ;default path prefix for tmpnam and tempnam
; possibilities for third argument to fseek
; these value are also defined in unistd.inc
%ifndef __UNISTD_INC__
SEEK_SET equ 0
SEEK_CUR equ 1
SEEK_END equ 2
%endif
; stdio.h defines BUFSIZ as BUFSIZ=_IO_BUFSIZ, in libio.h _IO_BUFSIZ=_G_BUFSIZ
; and finally in _G_config.h _G_BUFSIZ=8192
%ifndef BUFSIZ
%define BUFSIZ 8192
%endif
;---------------------------------------------------
; standard streams
;---------------------------------------------------
; example usage:
; in C: fputs(sentence, stderr);
; in asm ccall fputs, sentence, dword[stderr]
;---------------------------------------------------
EXTERN stdin
EXTERN stdout
EXTERN stderr
;---------------------------------------------------
; C Functions
;---------------------------------------------------
; operations on files
EXTERN remove ;remove file
EXTERN rename ;rename file
EXTERN tmpfile ;open a temporary file
%ifdef __USE_UNSAFE_FUNCS__
EXTERN tempnam ;generate temporary filename
EXTERN tmpnam ;generate temporary filename
EXTERN tmpnam_r ;reentrant version of tmpnam
%endif
; file access
EXTERN fclose ;close file
EXTERN fflush ;flush stream
EXTERN fopen ;open file
EXTERN freopen ;reopen stream with different file or mode
EXTERN setbuf ;set stream buffer
EXTERN setvbuf ;change stream buffering
; formatted input/output
EXTERN fprintf ;write formatted output to stream
EXTERN fscanf ;read formatted data from stream
EXTERN printf ;print formatted data to stdout
EXTERN scanf ;read formatted data from stdin
EXTERN sprintf ;write formatted data to string
EXTERN sscanf ;read formatted data from string
EXTERN vfprintf ;write formatted variable argument list to stream
EXTERN vprintf ;print formatted variable argument list to stdout
EXTERN vsprintf ;print formatted variable argument list to string
; character input/output
EXTERN fgetc ;get character from stream
EXTERN fgets ;get string from stream
EXTERN fputc ;write character to stream
EXTERN fputs ;write string to stream
EXTERN getc ;get character from stream
EXTERN getchar ;get character from stdin
%ifdef __USE_UNSAFE_FUNCS__
EXTERN gets ;get string from stdin
%endif
EXTERN putc ;write character to stream
EXTERN putchar ;write character to stdout
EXTERN puts ;write string to stdout
EXTERN ungetc ;unget character from stream
; direct input/output
EXTERN fread ;read block of data from stream
EXTERN fwrite ;write block of data to stream
; file positioning
EXTERN fgetpos ;get current position in stream
EXTERN fseek ;reposition stream position indicator
EXTERN fsetpos ;set position indicator of stream
EXTERN ftell ;get current position in stream
EXTERN rewind ;set position indicator to the beginning
; error handling
EXTERN clearerr ;clear error indicators
EXTERN feof ;check end-of-file indicator
EXTERN ferror ;check error indicator
EXTERN perror ;print error message
%ifndef __UNISTD_INC__
EXTERN ctermid ;return the name of the controlling terminal
EXTERN cuserid ;return the name of the current user
%endif
%endif
btw those functions, gnu_get_libc_version and the other one are in /usr/include/gnu/libc-version.h
-
Ah, so they are. Don't all those unused EXTERNs make your filesize huge? Some people don't mind that, I guess. I'm a "small freak", myself...
Here's one that may (or may not) be of more use to the 'doze folk. A question was raised, "How do I find out the month in Linux?". I wrote an all-asm example for Linux (I think - can't find it now... I have a ctime() equivalent at 556 bytes) Someone - maybe Keith? - suggested strftime(). I wrote a really perverse "mixture of asm and C" - used system calls to find the time and print the result, but did the "hard parts" in C. :) This is my attempt to make it "portable". No idea if it works or not. Still works in Linux. I should point out, in case it isn't obvious, that there is no point whatsoever in doing this in asm!
; get month in Linux... attempt to make it portable :)
; mixed asm and C :)
; nasm -f elf timec.asm
; gcc -o timec timec.o
;
; nasm -f win32 timec.asm --prefix _
; gcc -o timec.exe timec.o
; should be similar for your compiler?
global main
extern gettimeofday
extern strftime
extern localtime_r
extern puts
;----------------------------------------------
; some structures for "time stuff"
; note that these are mere "typedefs" - no memory allocated
struc tv
tv_sec resd 1
tv_usec resd 1
endstruc
struc tz
tz_minuteswest resd 1
tz_dsttime resd 1 ; obsolete! do not use!
endstruc
struc tm
tm_sec resd 1 ; Seconds. [0-60] (1 leap second)
tm_min resd 1 ; Minutes. [0-59]
tm_hour resd 1 ; Hours. [0-23]
tm_mday resd 1 ; Day. [1-31]
tm_mon resd 1 ; Month. [0-11]
tm_year resd 1 ; Year - 1900.
tm_wday resd 1 ; Day of week. [0-6]
tm_yday resd 1 ; Days in year.[0-365]
tm_isdst resd 1 ; DST. [-1/0/1]
endstruc
%define OUTBUFSIZ 100h
section .bss
; reserve memory for our structures
timeval resb tv_size
timezone resb tz_size
the_time resb tm_size
; etc...
outbuf resb OUTBUFSIZ
section .data
; note that the "back apostrophes" allow us to use "\n" etc.
; a fairly "new" Nasm feature...
;
; as requested, the month.
; the_format db `%B! :)\n`, 0
; or give strftime a bit more of a workout...
the_format db `%A, %B %d, %G\n%l:%M %p - %D\n`, 0
;---------------------------------
section .text
main:
; find out what time it is
push timezone
push timeval
call gettimeofday
add esp, 4 * 2
; make C do the difficult long division
push the_time ; tm structure to fill
push timeval ; tv structure to work from
call localtime_r
add esp, 4 * 2
; and the tedious text lookup...
push the_time ; tm structure to work from
push the_format ; format string in strftime syntax
push OUTBUFSIZ ; max
push outbuf ; buffer to fill
call strftime
add esp, 4 * 4
; we can print it without C's help...
; but we won't :)
push outbuf
call puts
add esp, 4 * 1
ret
;--------------------------
Best,
Frank
-
Don't all those unused EXTERNs make your filesize huge?
to test that everything works and there are no symbols being redefined i use a test file that drags everything in (everything that i've done so far, that is). it looks like this
%define __USE_UNSAFE_FUNCS__
%include "stdio.inc"
%include "stdlib.inc"
%include "errno.inc"
%include "ctype.inc"
%include "unistd.inc"
%include "fcntl.inc"
%include "string.inc"
%include "limits.inc"
%include "setjmp.inc"
%include "pwd.inc"
%include "grp.inc"
%include "misc/syscalls.inc"
%include "misc/macros.inc"
%include "sys/stat.inc"
%include "sys/uio.inc"
%include "sys/reboot.inc"
%include "gnu/libc-version.inc"
SECTION .text
cproc main, argc, argv, envp
;this is just a test file to check the
;files work without errors when everything
;is included
cendproc
that file comes out at about 40kb and once its stripped its down to 11kb, i can live with that :D
btw nice example, works fine here ;) i did assemble and then used strip to get the file size down and even then its still 3.1kb so for the convenience of using a mix of glibc/asm you're always going to pay a price
-
I think what frank means is, you're probably not going to use every one of those EXTERN'd procedures, so including them adds a bit of overhead. In NASMX we would handle this in INVOKE. Instead of putting 'EXTERN baka' in the headers, try creating a more dynamic CCALL macro. Like so:
%macro externdef 1-*
%rep %0
%define __%{1}_is_external
%rotate 1
%endrep
%endmacro
%macro ccall 1-*
%ifndef __%{1}_defined
%define __%{1}_defined
%ifdef __%{1}_is_external
EXTERN %{1}
%endif
%endif
%define __procedure_call %1
%rep %0-1
%rotate -1
push %1
%endrep
call __procedure_call
%if (%0-1)
add esp, (%0-1)*4
%endif
%endmacro
In the above, I've modified your ccall implementation and included a new 'externdef' macro which defines an external symbol for dynamic inclusion when referenced by the ccall macro. This will reduce overall generated code size and only requires you to substitute 'extern baka' for 'externdef baka' in your include files. :)
-
That's pretty much what I had in mind... instead of declaring "everything possible" as extern. But if Brethren wants his .inc files to match the C .h files... well that's what they do. As he says, there's a "price" (for everything!).
Unless I'm mistaken, some assemblers (Masm?) seem to exclude unused "extern"s, but Nasm puts 'em in if you say so. "strip" eliminates much of the "bloat" (but 11k for a file that "doesn't do anything" still seems a bit "beefy" to me). Do Windows users have an equivalent to "strip"?
Nice thing about being "the programmer" is that we have choices about that kind of thing!
Best,
Frank
-
thank you so much:)that is an ingenius solution to the problem. when i said i could live with the extra file size the externs generated it was purely because i couldn't think of a solution myself :D
btw i've many questions for you two ;D and i'll be posting later
-
my first question is how would you two handle C data types?
an example of what i mean is uid_t which is a dword sized data type, they are used for user id's
the problem i have is that storage for dwords in the .bss section use resd then in the data section you use dd and finally in the text section you use mov dword[somevar], 1
ideally i would like to use something like...
SECTION .bss
somevar resb uid_t
SECTION .data
anothervar uid_t 0
SECTION .text
mov uid_t[somevar], 0
is this even possible and if not what method would you use for c data types?
-
brethren,
for examples of what you are trying to do you should download the NASMX package and look at the example demos contained therein.
The work contained in that package provides a ton of features that you are free to learn from and even help us out simply by testing them and submitting ideas, bug reports, etc...
please visit http://www.asmcommunity.net/projects/nasmx/ (http://www.asmcommunity.net/projects/nasmx/) and download and play around a bit ;)
-
thanks for the link rob, i've downloaded it and the few examples and inc files i've looked at seem to include everything i need and much, much more. theres just one small problem i've not been able to find much in the way of documentation except for the source and for me reading other programmers asm code is akin to reverse engineering :D
is there any documentation for the nasmx project?
-
hmmm....documentation....that's probably the ONE key item missing from the package. I've been busy trying to finish the full 64-bit Linux/Windows build ( rather tricky, but almost there! ) and should seriously consider adding some docs to the project. After I get the next RC out ( and I don't hear anything negative ) I'll try and whip up something. In the meantime you can peruse some of my postings both here and on asmcommunity.net (http://asmcommunity.net) where you'll catch a glimmer of ideas. ;D
-
i finally got C types working, thanks for the info Rob. Plus dynamic inclusion is working great, thanks Bryant
heres an example
%include "stdio.inc"
%include "stdlib.inc"
%include "time.inc"
SECTION .bss
var1 reserve(time_t) 1
SECTION .text
cproc main
ccall time, var1
ccall printf, `Number of seconds since the epoch: %d\n`, time_t[var1]
mov eax, EXIT_SUCCESS
cendproc
-
heres another question ;D
can you use a procedure that has the same name as an nasm keyword?
i'm just reading about a linux function called 'times' that returns the amount of CPU time a process has used in clock ticks (would be useful for timing code). obviously times is also an nasm directive :(
is it possible to use this function?
btw for anybody using linux the function prototype is in /usr/include/sys/times.h
-
can you use a procedure that has the same name as an nasm keyword?
For TIMES in particular, no, as it is handled at assemble time, so I don't think any amount of preprocessing or overloading voodoo will help.
This is where you'd want to prepend an underscore and treat it as _TIMES within NASM and during link phase.
However, and in hindsight, there should be some code within NASM to handle this corner case, e.g. "when TIMES is used as a MEMREF, and if EXTERN or LABEL, then don't ASSEMBLE as TIMES" or something like that.
-
"$times" for the external function?
Best,
Frank
-
I knew I'd seen it documented! Hard to find, though. There it is, in section 3.1!
"An identifier may also be prefixed with a $ to indicate that it is intended to be read as an identifier and not a reserved word; thus, if some other module you are linking with defines a symbol called eax, you can refer to $eax in NASM code to distinguish the symbol from the register."
Best,
Frank
-
just tested and then disassembled the code, worked perfectly:)
%include "sys/times.inc"
SECTION .bss
buf resb tms_size
SECTION .text
cproc main
ccall $times, buf
cendproc
0x080483d0 <main+0>: push ebp
0x080483d1 <main+1>: mov ebp,esp
0x080483d3 <main+3>: push 0x80495c4
0x080483d8 <main+8>: call 0x80482fc <times@plt>
0x080483dd <main+13>: add esp,0x4
0x080483e3 <main+19>: mov esp,ebp
0x080483e5 <main+21>: pop ebp
0x080483e6 <main+22>: ret
End of assembler dump.
-
I knew I'd seen it documented! Hard to find, though. There it is, in section 3.1!
Nice find, I'll have to remember that one.