I'm trying to learn x86 assembler, and I want to call a NASM function in C. When i run my program I get this error:
Segmentation fault (Core dumped)
I've tried dozens of variations of my simple test function but it stops every time at the same position.
Here are my asm and c files:
div.asm:
global _test
_test:
push ebp
mov ebp, esp
push ebx
mov eax, [ebp+8]
mov ebx, [ebp+12]
div ebx
pop ebp
ret
main.c:
#include <stdio.h>
extern unsigned int test (unsigned int, unsigned int);
int main(void)
{
printf("%d\n", div(85,5));
return 0;
}
I compile & link the files with:
nasm -f elf -o div.o div.asm
gcc -m32 -c -o main.o main.c
gcc -m32 -o run div.o main.o
I use a 64 Bit Linux in a Virtual Machine.
What is my mistake here, and how can I fix it?
You forget to pop ebx (or at least make the stack in order):
push ebp
mov ebp, esp
push ebx ; you push it here
mov eax, [ebp+8]
mov ebx, [ebp+12]
xor edx,edx ; ..and you must zero edx
div ebx
pop ebx ; forgot to pop it here
pop ebp
ret
It is unclear if you ever got your problem solved. In addition to the other issues, you would need to make your function call in your main.c match the call in div.asm. For example if you have created an assembly function _test, you need to declare it as extern and actually use the function in main. e.g.:
#include <stdio.h>
extern unsigned int _test (unsigned int, unsigned int);
int main(void)
{
printf("%d\n", _test (85,5)); /* you are calling div here, not _test */
return 0;
}
(your function name is not the name for your assembly object file div.o -- and as pointed out in the comments, div is an unsigned division declared in stdlib.h along with ldiv, and lldiv)
Your global declaration in your assembly function file must match the name you declared in main. e.g.:
global _test
_test:
push ebp
mov ebp, esp
mov eax, [ebp+8]
xor edx, edx
div dword [ebp+12]
mov esp, ebp
pop ebp
ret
Now, you can compile, link and run your test file:
$ nasm -f elf -o div.o div.asm
$ gcc -m32 -c -o main.o main.c
$ gcc -m32 -o run div.o main.o
$./run
17
or for the compilation/link, simply:
$ nasm -f elf -o div.o div.asm
$ gcc -m32 -o run main.c div.o
Related
I am trying to learn the x86 assembly language, and would like to set up a little environment to do it efficiently. For that I have written some simple code (factorial, fibonnaci,etc...), added a main.c for testing and a Makefile to compile.
But I was never able to debug my code with gdb, no matter what flags I have tried...
Here is the code of facto.asm
global fact
section .text
fact:
push rbx
mov rcx, rdi
xor rax, rax
xor rbx, rbx
inc rax
inc rbx
loop:
mov rdx, rbx
mul rdx
inc rbx
dec rcx
jnz loop
pop rbx
ret
The Makefile:
EXE := facto
BIN := $(EXE).o
ASM := $(EXE).asm
SRC := main
all: $(EXE)
$(BIN): $(ASM)
nasm -f elf64 -gdwarf $(ASM) -o $(BIN)
$(EXE): $(SRC).c $(BIN)
gcc -no-pie -g $(SRC).c $(BIN) -o $(EXE)
.PHONY: clean
clean:
$(RM) *.o
$(RM) $(EXE)
And finally the main.c in which I am doing the tests:
#include <stdio.h>
extern int fact();
int main(void)
{
size_t res = fact(5);
printf("%zu\n",res);
}
When running GDB and trying to "skip" into the assembler code, it just does not get in there and goes directly to the printf, even though I have set the -g flag for gcc and -gdwarf for nasm. Furthermore I am manually setting a break on that line with b.
What is it that I am missing? I would be glad for any clues!
If this can help by any means,I am currently working on an arch linux machine.
Thanks in advance!
I have a postfix program that does a while loop and prints 10 numbers, and it needs an extern print function which i defined, but for some reason after i create the object file if i do ld -m elf_i386 -s -o p11 p11.o print.o it gives me some strange errors.
what i want to do is link those files and create an executable that prints values from 1 to 10, but for some reason its giving me those errors when clearly what i want is use that c function to print those values
ps: if i use gcc -m32 -o p11 p11.o print.o i get the following error:
/usr/bin/ld: p11.o: warning: relocation in read-only section `.text'
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib32/Scrt1.o: in function `_start':
(.text+0x22): undefined reference to `main'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
commands:
$ nasm -f elf32 p11.asm -o p11.o
$ gcc -m32 -o print.o -c print.c
$ ld -m elf_i386 -s -o p11 p11.o print.o
Error:
ld: warning: cannot find entry symbol _start; defaulting to 0000000008049000
ld: print.o: in function `print':
print.c:(.text+0x21): undefined reference to `printf'
I dont get why because printf is defined in c and clearly coded somewhere
postfix program:
EXTERN print ; extern print(int)
DATA
ALIGN
GLOBAL ix, OBJ; static ix
LABEL ix
SINT 0; static ix=0
TEXT
ALIGN
INT 0
DUP32
ADDR ix
STINT
TRASH 4
ALIGN
LABEL whilecond
ADDR ix
LDINT
INT 10
LT
JZ endwhile
ADDR ix
LDINT
INT 1
ADD
DUP32
ADDR ix
STINT
TRASH 4
ADDR ix
LDINT
CALL print
TRASH 4
JMP whilecond
ALIGN
LABEL endwhile
assembly program:
extern print
segment .data
align 4
global ix:object
ix:
dd 0
segment .text
align 4
push dword 0
push dword [esp]
push dword $ix
pop ecx
pop eax
mov [ecx], eax
add esp, 4
align 4
whilecond:
push dword $ix
pop eax
push dword [eax]
push dword 10
pop eax
xor ecx, ecx
cmp [esp], eax
setl cl
mov [esp], ecx
pop eax
cmp eax, byte 0
je near endwhile
push dword $ix
pop eax
push dword [eax]
push dword 1
pop eax
add dword [esp], eax
push dword [esp]
push dword $ix
pop ecx
pop eax
mov [ecx], eax
add esp, 4
push dword $ix
pop eax
push dword [eax]
call print
add esp, 4
jmp dword whilecond
align 4
endwhile:
c program:
#include <stdio.h>
void print(int num)
{
printf("%d\n",num);
}
I dont get why because printf is defined in c and clearly coded somewhere
Because you are not linking that somewhere (namely, you are not linking with libc).
On UNIX systems, you should never use ld to link anything (with the exceptions of the kernel and boot loader).
Instead you should always use appropriate compiler driver (gcc here). Gcc will automatically add -lc to the link line.
P.S. you should also define the main program -- it's unclear how you expect your code to be invoked, since you defined nighter main, nor _start symbol.
if i use gcc -m32 -o p11 p11.o print.o i get the following error:
Your gcc is configured to build PIE binaries by default, but your assembly is not written to be compatible with PIE.
Add the -fno-pie to your compile lines and -no-pie flag to your link line.
I wrote the following loader:
GLOBAL _start
EXTERN main
section .text
_start:
xor ebp, ebp ; ebp = 0
pop esi ; esi = argc
mov ecx, esp ; ecx = argv
and esp, 0xFFFF ; align esp
push ecx ; load argv
push esi ; load argc
call main ; call main
push eax ; exit with main's ret value
mov ebx,0
int 80h
And a short main function, now I'm trying to run compile and link these files using gcc, but using the commands
nasm -f elf32 loader.asm
gcc -c -m32 main.c
gcc -m32 main.o loader.o -o main.out
Results in a multiple definition of _start error. I imagine this is because gcc is trying to link his own _start. How can I prevent this from happening?
You haven't told GCC to not link to the standard startup code, so GCC links to it.
To tell GCC to not link in _start, pass in the -nostartfiles flag to GCC when linking.
Note that the standard libraries (stdlib, stdio, etc) will still be linked in, unless you also use the -nodefaultlibs flag. The -nostdlib flag combines the two.
How to specify Win32 as output when invoking GCC using MinGW on Windows.
Below I've posted my source code. My objective is to interface assembly with C code and produce an executable.
I start assembling the add.asm to Win32 using the following NASM command:
nasm -f win32 add.asm
Then it should be possible to invoke GCC using both C and object files?
gcc -o add add.obj call_asm.c
However, this results in an a linkage error:
C:\Users\nze\AppData\Local\Temp\cckUvRyC.o:call_asm.c:(.text+0x1e): undefined reference to `add'
collect2.exe: error: ld returned 1 exit status
If I instead compile to ELF using
nasm -f elf add.asm
the command (this time using the ELF file add.o)
gcc -o add add.o call_asm.c
works perfectly.
How can I tell GCC that my object files are in Win32 format, so that it should compile call_asm.c to Win32 before linking? (I guess this is the core of the problem, please comment whether I'm correct).
call_add.c:
#include <stdio.h>
extern int add(int a, int b);
int main()
{
printf("%d", add(7, 6));
}
add.asm:
BITS 32
global _add
_add:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ebx, [ebp+12]
add eax, ebx
mov esp, ebp
pop ebp
ret
The problem isn't what you assume it is. GCC is generating "win32" format (more commonly know as PECOFF) object files. The problem is that your assembly code doesn't define a section, and this results in NASM not defining the symbol _add in the generated object file.
If you add a SECTION directive your code links and runs without error:
BITS 32
SECTION .text
global _add
_add:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ebx, [ebp+12]
add eax, ebx
mov esp, ebp
pop ebp
ret
Telling NASM to generate and ELF object file changes its behaviour, for whatever reason, and causes it to define the _add symbol in the ELF object file.
Just add this before the label:
.globl _add
To get that symbol to export in a .DLL you should add this at the end of the file:
.section .drectve
.ascii " -export:\"add\""
Note that the leading underscore is left out.
From what I understand, when a parameter is passed in a function call in C, the callee can find the first parameter at [ebp+8].
Returning a value through eax works for me, reading the right parameter value from the stack doesn't.
Right now I'm just trying to write an assembly function, that can be called from C and returns the same value, that it is being passed.
When I run the following program, it prints number: 1 to the console, no matter what value is passed into myFunc. What am I doing wrong?
assembly.s
section .text
global _myFunc
_myFunc:
mov eax, [ebp+8]
ret
main.c
#include <stdio.h>
extern unsigned int myFunc(unsigned int somedata);
int main() {
unsigned int i = myFunc(6);
printf("number: %i\n",i);
return 0;
}
I'm using a Mac, nasm to assemble the code and gcc for C compilation.
Makefile
macho32:
nasm -f macho32 assembly.s
gcc -m32 -o macho32 assembly.o main.c
You need to setup to access the the parameters first by saving esp. This is explained in:
http://www.nasm.us/doc/nasmdoc9.html
in section "9.1.2 Function Definitions and Function Calls"
The following works for me
assembly.s
section .text
global myFunc:function
myFunc:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov esp, ebp
pop ebp
ret
main.c
#include <stdio.h>
extern unsigned int myFunc(unsigned int somedata);
int main() {
unsigned int i = myFunc(6);
printf("number: %i\n",i);
return 0;
}
Assembly & Compilation
ericu#eric-phenom-linux:~$ nasm -f elf32 assembly.s
ericu#eric-phenom-linux:~$ gcc -m32 assembly.o main.c
ericu#eric-phenom-linux:~$ ./a.out
number: 6
I am on a linux machine so I use elf32. The use of macho32 is correct on your Mac.
you refer to argument on stack, by reading from [EBP+offset] - has EBP been set up to actually point at stack? If no, you may have to do that first, conventionally done by:
push ebp
mov ebp,esp
Only then points EBP to its stacked previous contents, below stacked return address, and below passed arguments.
Your function should look like this,
_myFunc:
push ebp ; setup ebp as frame pointer
mov ebp, esp
mov eax, [ebp + 8]
leave ; mov esp,ebp / pop ebp
ret
The convention is to use ebp to access the parameters, to do so you need to save ebp on the stack and make it to point to the new top of the stack. At function exit you should restore ebp and esp, as the leave instruction does.
In nasm there is a macro package, c32.mak, that can help in supporting the C calling convention, these macros are arg, proc and endproc.
Using these macros your code should look like,
proc _myfunc
%$i arg
mov eax, [ebp + %$i]
endproc