From: Frank K. <fbk...@zy...> - 2008-11-10 06:03:33
|
Tyler Littlefield wrote: > awesome, thanks again for the help. > Last thing, I think, then I'll go away. Sorry for the delay. Couldn't bear to see you leave. :) > I've seen code that linkx with the c lib. I'd like to write my own > functions and run those, as they'd probably be faster, but until I learn > how, I'll chill with the c lib. > What is the best way to do this--I seen one post in google, and it was > thurally unrelated to what I was going for--after about 3-5 searches and > scanning through like 5-6 pages per search of results, I gave it up as a > lost cause. Highly inexpert in C as I am, maybe I can come up with some examples that will be better than nothing. There's some info in the Friendly Manual: http://www.nasm.us/doc/nasmdoc9.html#section-9.1 Chilling with the C lib - a.k.a. "just call printf" - is easy: ; nasm -f elf hwc.asm ; gcc hwc.o -o hwc ; ./hwc ; echo $? to see "answer" global main extern printf section .data format_string db 'Hello, World!', 10, 0 section .text main: push format_string call printf add esp, 4 * 1 ; "main" returns int (right???) ; zero is the usual "no error" exitcode, but ; return something "identifiable", just for fun mov eax, 42 ret Maybe a slightly less simple example would be better, showing multiple parameters... and a float - printing floats is the only time "just call printf" is worthwhile, IMO, and it isn't too intuitive... ; nasm -f elf hwfloat.asm ; gcc hwfloat.o -o hwfloat ; ./hwfloat ; echo $? to see "answer" global main extern printf section .data hello_string db 'Hello, World!', 0 the_int dd 42 the_float dd 42.0 ; single precision format_string db 'The string is "%s"', 10 db 'The float is: %f', 10 db 'The int is: %d', 10, 0 section .text main: ; printf("the string is %s\nthe float is: %f\n the int is: %d", ; hello_string, the_float, the_int); ; ; push rightmost parameter first... push dword [the_int] ; printf quietly promotes floats to doubles(!) ; so we'll want 8 bytes on the stack sub esp, 8 ; now put our float there, as double fld dword [the_float] fstp qword [esp] ; and the leftmost parameter... push hello_string ; and the format string last push format_string call printf add esp, 4 * 3 + 8 ; "main" returns int (right???) ; zero is the usual "no error" exitcode, but ; return something "identifiable", just for fun mov eax, 42 ret We can do "better" than this... defined as "less C"... by eliminating the "startup code" and putting "_start" back in our code. We don't need gcc at all - it only calls ld, since there's nothing to "compile". Maybe someday it will be "fixed", but last I checked, ld was expecting to use /lib/ld-linux.so.1 as a dynamic linker. This doesn't exist on my system, and doing "myprog" results in a "file not found" error, just as if "myprog" wasn't there!!! Highly mystifying! The "trick" is to tell ld to use /lib/ld-linux.so.2 with the "--dynamic-linker" or "-I" switch... ; nasm -f elf hwc3.asm ; ld hwc3.o -o hwc3 -lc -I/lib/ld-linux.so.2 ; ./hwc3 ; echo $? to see "answer" global _start extern printf section .data format_string db 'Hello, World!', 10, 0 section .text _start: push format_string call printf add esp, 4 * 1 ; zero is the usual "no error" exitcode, but ; return something "identifiable", just for fun mov ebx, 42 mov eax, 1 ; __NR_exit int 80h Note that, since _start isn't called, we can't exit with "ret"! I imagine using "exit()" would also work(?). Seems a little "shaky" to me, to be calling C library functions without running the C startup code, but it "seems to work". The only "gotcha" I've found is I can't find "stdin"/"stdout" - the "FILE *", not the integer STDIN/STDOUT - without gcc's help (haven't tried that hard). This can be important, since "printf" doesn't actually print anything until we flush the buffer! A linefeed will do it, or "fflush(stdin)"... These "high level" functions - fopen(), etc. - and including printf/scanf - work with buffered input/output, and are much faster (in some cases) than the low(er)-level open(), read() etc. (if we *don't* do our own buffering, C can be faster!) There generally isn't much advantage in calling the C library from an assembly program - might as well do the whole thing in C. More useful, generally, is to write routines in assembly - faster existing routines (can be done, but "good luck") as you mention, or routines that don't yet exist in a C library ("good luck" again, I suspect) - to be called from C code. The "Unix way" to find out about your CPU is to read /proc/cpuinfo (they make me say that). But getting info from cpuid (the instruction) might make an example of an asm function to be called from C - could be called from asm (or other), too, of course... ; nasm -f elf cpuvendor.asm ; nasm -f win32 --prefix _ cpuvendor.asm ??? ; tell ld about us (via the .o file) global cpu_vendor cpu_vendor: ; C expects to have these regs preserved push edi push ebx ; our parameter, the buffer, is "up the stack", ; 4 for ebx, 4 for edi, 4 for return address = 12 mov edi, [esp + 12] xor eax, eax cpuid ; with eax = 0, cpuid returns the vendor string ; in ebx, edx, ecx (why???), and the maximum level ; in eax (our return value). With eax greater than ; this maximum level (for this implementation of cpuid) ; cpuid returns unexpected results - 16 cores in this ; clunker? I don't think so! ; stuff the vendor string into our destination buffer mov [edi], ebx mov [edi + 4], edx mov [edi + 8], ecx ; don't forget to zero-terminate it! mov byte [edi + 12], 0 ; restore caller's regs pop ebx pop edi ; we're done! ret Assemble that first, then compile with "gcc -o getvendor getvendor.c cpuvendor.o" (probably want a -O2 or 3 switch in there - gcc produces some horrible code without any optimization - won't matter here) #include <stdio.h> int cpu_vendor(char *dest); int main(){ char buf[13]; int cpuid_max_level; cpuid_max_level = cpu_vendor(buf); printf("vendor = %s\nmaxlevel = %d\n", buf, cpuid_max_level); return (0); }; Could go on to doing cpuid with eax 1, 2,... up to maxlevel, and print that info (which I'd have to look up), but... this has taken long enough. It's a start... Best, Frank |