I'm trying to call C function in assembler. This is my code:
C:
int multiply(int what)
{
return what * 2;
}
ASM:
extern multiply
start:
mov eax, 10
push eax
call multiply
jmp $
;empty
times 510-($-$$) db 0
dw 0xAA55
I'm compiling C code to elf by gcc (MinGW) and ASM code by NASM. I'm compiling it without any problems, but when I'm trying to use this code(for creating .bin file):
gcc -o test.bin work.o test.o
I' getting this error:
Does anybody know how to call C function from ASM code, compile it and link it to working .bin file? Please help.
Try to add '_' to multiply:
extern _multiply
Works for me in this simple example:
global _main
extern _printf
section .data
text db "291 is the best!", 10, 0
strformat db "%s", 0
section .code
_main
push dword text
push dword strformat
call _printf
add esp, 8
ret
Try "global multiply" instead of "extern multiply" in your .asm file. You shouldn't need the underscore for ELF (I don't think), but you can get Nasm to automagically add an underscore to anything "extern" or "global" by adding "--prefix _" to Nasm's command line.
Edit: I take that back, "extern" is correct. You seem not to have a "main". Try adding "--nostartfiles"
(may be only one hyphen) to gcc's command line.
Best,
Frank
Related
;this file -> test1.asm
includelib msvcrt.lib
includelib legacy_stdio_definitions.lib
extern _CRT_INIT: proc ;custom entrypoint needs c-runtime to be initialised by the programmer.
extern printf: proc
extern ExitProcess: proc
.data
msg db "hello world!", 13, 10, 0
.code
fun proc
sub rsp, 32 ;for shadow space
call _CRT_INIT ;called _CRT_INIT because I have my own entry point named "fun"
lea rcx,msg
call printf
add rsp, 32
call ExitProcess
fun endp
end
commandline for assembler: ml64 /nologo /c test1.asm
commandline for linker: link /entry:fun /subsystem:console test1.obj
This standalone assembly program seems to be working fine. But doing some changes(mentioned below) make the program crash.
1st change -> according to this link both libcmt.lib and msvcrt.lib statically links the native CRT startup (ie both can be used to call _CRT_INIT) into my code. The difference is msvcrt.lib is used with dll. As I don't have any dll I considered using libcmt.lib in place of msvcrt.lib but then the program crashes. Considering there is only printf function associated, that concludes printf function breaks the program. But why?
2nd change -> Now I tried calling this fun() function from a simple .C file. For that I made the necessary changes.
;this file -> test1.asm
;not including any libraries. linking the .c file make the libraries link too because that is in my libpath environment variable
;extern _CRT_INIT: proc ;no need of _CRT_INIT because now mainCRTStartup() will be the entrypoint and .c file will take care of initializing the c-runtime
extern printf: proc
;extern ExitProcess: proc ;no need of exitprocess. Instead i'm using ret instruction because I'm calling fun() from .c file.
.data
msg db "hello world!", 13, 10, 0
.code
fun proc c ;even if I replace 'c' with something like "abed" the program works. but if I dont give anything after proc then the program crashes.
sub rsp, 32 ;for shadow space
lea rcx,msg
call printf
add rsp, 32
ret
fun endp
end
//this file -> test2.c
#include <stdio.h>
#include <conio.h>
void fun(); //in C no need of extern keyword. extern is needed in c++
int main()
{
fun();
getch();
}
commandline for assembler: ml64 /nologo /c test1.asm
commandline for compiler: cl /nologo /c test2.c
commandline for linker: link test1.obj test2.obj
In the previous working example (ie. with msvcrt.lib) I don't have to give any suffixes after 'proc' directive. But in this case when I am calling 'fun' as a function from my C file I need to give anything like 'a'/'b'/combination('abcd') after 'proc' directive and only then the program works. If I don't give anything after 'proc' the program crashes. According to official MSDN documentation proc directive also accept language type. Even so wrong/random 'language type name' ie. any word seem to work. But how?
I tried many things like changing the libraries, using different version of printf (like vfprintf) and tried assigning more shadow space also googled much but unable to find any answer.
I'm trying to run some assembly code in Visual Studio 2012 and call it in C just for testing purposes. As I have no experience writing assembly code I have no idea what is going wrong, so I would greatly appreciate some help!
I get the following errors trying to compile the code:
Error 5 error MSB3721: The command "ml.exe /c /nologo /Zi /Fo"Debug\callee.obj" /W3 /errorReport:prompt /Tacallee.asm" exited with code 1. C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\BuildCustomizations\masm.targets 49 5 ProjetoASM
Error 2 error A2206: missing operator in expression C:\Users\Suporte\Desktop\ASM\ProjetoASM\ProjetoASM\callee.asm 18 1 ProjetoASM
Error 3 error A2206: missing operator in expression C:\Users\Suporte\Desktop\ASM\ProjetoASM\ProjetoASM\callee.asm 21 1 ProjetoASM
Error 4 error A2206: missing operator in expression C:\Users\Suporte\Desktop\ASM\ProjetoASM\ProjetoASM\callee.asm 8 1 ProjetoASM
Error 1 error A2022: instruction operands must be the same size C:\Users\Suporte\Desktop\ASM\ProjetoASM\ProjetoASM\callee.asm 15 1 ProjetoASM
And the Assembly code:
PUBLIC hello_from_asm
EXTERN puts:PROC
.model flat
.data
msg db 'Hello, world!',0xa
len equ $ - msg
.code
hello_from_asm PROC
mov edx,len
mov ecx,msg
mov ebx,1
mov eax,4
int 0x80
mov eax,1
int 0x80
hello_from_asm ENDP
END
This was supposed to output a "Hello, world!", so any other ideas that might work are also welcome.
C code for completeness:
#include <stdio.h>
extern void hello_from_asm();
int main(){
printf("Hello from C");
hello_from_asm();
return 0;
}
Those detailed MASM error messages tell it all.
mov ecx,msg
You're required to use MASM style. The above instruction tries to load the first byte of msg in a 32-bit register. That's the "size mismatch" error.
What you need is loading the address of msg in ECX. Use
mov ecx, offset msg
The other errors might be about not recognizing the 0x hexadecimal prefix. Try using the h hexadecimal suffix instead. (0Ah, 80h)
The above is easy enough to change, and your code will assemble fine. However don't run it because the int 80h instruction is a Linux system call that is not going to work on Visual Studio 2012 (Windows).
Example 32 bit Visual Studio | Masm program to print "Hello World". I included the most common directives. "legacy_stdio_definitions.lib" is used for VS2015 and later, since printf and scanf were changed to be inlined with the output of the C compiler. You may not need it for VS2012.
.686p ;enable instructions
.xmm ;enable instructions
.model flat,c ;use C naming convention (stdcall is default)
; include C libraries
includelib msvcrtd
includelib oldnames
includelib legacy_stdio_definitions.lib ;for scanf, printf, ...
.data ;initialized data
pfstr db "Hello world!",0dh,0ah,0
.data? ;uinitialized data
.stack 4096 ;stack (optional, linker will default)
.code ;code
extrn printf:near
public main
main proc
push offset pfstr ; 32-bit mode uses stack args
call printf
add esp,4 ; cdecl is caller-pops
xor eax,eax ; return 0
ret
main endp
end
I tried to write a char[] in stdout using inline NASM (note .intel_syntax and .att_syntax are added so it can be compiled with gcc)
but it doesn't write anything in stdout.
I am using Linux 16 (x86) on Virtual machine
Is it cause of char c[] ? ( I read by this way of compiling we cannot use memory variables but what to do instead?)
#include<stdio.h>
char c[] = "abcd";
int main(){
asm (".intel_syntax noprefix");
// write c into stdout
asm("mov EAX,4"); // 4: write
asm("mov EBX,1"); // 1: stdout
asm("mov ECX,c");
asm("mov EDX,5");
asm("int 0x80");
// exit program
asm("mov EAX,1")
asm("int 0x80")
asm (".att_syntax noprefix");
}
the output is nothing
The GNU assembler (which is what gcc uses) does not use NASM syntax. It rather uses a variant of Microsoft's MASM syntax where no brackets are needed to dereference a variable. Since you don't want to load the value of the c variable but rather its address, you need an offset keyword:
mov ecx, offset c
I strongly recommend you to avoid inline assembly as much as possible for learning assembly. Using inline assembly in gcc requires good knowledge of how exactly this whole thing works and writing random instructions usually leads to wrong code. Even your simple code is already fundamentally broken and would not work if it was any more complicated than that (so the compiler had a chance to try to use the registers you overwrote without telling).
Instead, put your assembly in a separate file and link it in. This sidesteps all issues you have with inline assembly and allows you to use NASM as you wanted. For example, try something like this:
main.c
char c[] = "abcd";
/* the function you define in print_c.asm */
extern void print_c();
int main() {
print_c(); /* call your assembly function */
}
print_c.asm
; pull in c defined in main.c
extern c
section .text
global print_c
print_c:
; write c to stdout
mov eax, 4
mov ebx, 1
mov ecx, c
mov edx, 5
int 0x80
; exit program
mov eax, 1
int 0x80
Then assemble, compile, and link with:
nasm -felf print_c.asm
cc -m32 -o print_c print_c.o main.c
I am trying to implement the C function "exp" in NASM for Linux. The function takes a double value x, and returns a double value r = e^x, where e is Euler's Number. This is my implementation:
extern exp
SECTION .bss
doubleActual: resq 1
doubleX: resq 1
SECTION .text
main:
;some other code here
;calculate actual result
push doubleActual ; place to store result
push doubleX ;give the function what x is.
call exp
add esp, 8
On compile attempt, i get the following:
hw7_3.o: In function `termIsLess':
hw7_3.asm:(.text+0xf9): undefined reference to `exp'
This is referring to when i actually call exp, which is odd, because "extern exp" seems to work just fine. What am i doing incorrectly?
via http://www.linuxtopia.org/online_books/an_introduction_to_gcc/gccintro_17.html ....
I need to do the following with gcc:
gcc -m32 name.o -lm -o name
The "-lm" tag is a shortcut to link the C math library, which is separate from the standard library.
Trying to deal with my assignment of Assembly Language...
There are two files, hello.c and world.asm, the professor ask us to compile the two file using gcc and nasm and link the object code together.
I can do it under 64 bit ubuntu 12.10 well, with native gcc and nasm.
But when I try same thing on 64 bit Win8 via cygwin 1.7 (first I try to use gcc but somehow the -m64 option doesn't work, and since the professor ask us to generate the code in 64-bit, I googled and found a package called mingw-w64 which has a compiler x86_64-w64-mingw32-gcc that I can use -m64 with), I can get the files compiled to mainhello.o and world.o and link them to a main.out file, but somehow when I type " ./main.out" and wait for the "Hello world", nothing happens, no output no error message.
New user thus can't post image, sorry about that, here is the screenshot of what happens in the Cygwin shell:
I'm just a newbie to everything, I know I can do the assignment under ubuntu, but I'm just being curious about what's going on here?
Thank you guys
hello.c
//Purpose: Demonstrate outputting integer data using the format specifiers of C.
//
//Compile this source file: gcc -c -Wall -m64 -o mainhello.o hello.c
//Link this object file with all other object files:
//gcc -m64 -o main.out mainhello.o world.o
//Execute in 64-bit protected mode: ./main.out
//
#include <stdio.h>
#include <stdint.h> //For C99 compatability
extern unsigned long int sayhello();
int main(int argc, char* argv[])
{unsigned long int result = -999;
printf("%s\n\n","The main C program will now call the X86-64 subprogram.");
result = sayhello();
printf("%s\n","The subprogram has returned control to main.");
printf("%s%lu\n","The return code is ",result);
printf("%s\n","Bye");
return result;
}
world.asm
;Purpose: Output the famous Hello World message.
;Assemble: nasm -f elf64 -l world.lis -o world.o world.asm
;===== Begin code area
extern printf ;This function will be linked into the executable by the linker
global sayhello
segment .data ;Place initialized data in this segment
welcome db "Hello World", 10, 0
specifierforstringdata db "%s", 10,
segment .bss
segment .text
sayhello:
;Output the famous message
mov qword rax, 0
mov rdi, specifierforstringdata
mov rsi, welcome
call printf
;Prepare to exit from this function
mov qword rax, 0
ret;
;===== End of function sayhello
;Purpose: Output the famous Hello World message.
;Assemble: nasm -f win64 -o world.o world.asm
;===== Begin code area
extern _printf ;This function will be linked into the executable by the linker
global _sayhello
segment .data ;Place initialized data in this segment
welcome db "Hello World", 0
specifierforstringdata db "%s", 10, 0
segment .text
_sayhello:
;Output the famous message
sub rsp, 40 ; shadow space and stack alignment
mov rcx, specifierforstringdata
mov rdx, welcome
call _printf
add rsp, 40 ; clean up stack
;Prepare to exit from this function
mov qword rax, 0
ret
;===== End of function sayhello
When linked together with the C wrapper in the question and run in plain cmd window it works fine:
Depending on your toolchain you might need to remove the leading underscores from symbols.