Linking a c function and a asm assembly file - c

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.

Related

Using custom main loader with GCC

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

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.

Calling NASM function in C

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

Comma, colon, decorator or end of line expected after operand

I am programming in c and i compiled a c code to assembly code but when i re-compile the code with the NASM assembler , it is giving me a error
Expected comma , colon , decorator or end of line expected after operand . This occurs in
line number 6 , line number 7 and 8 . Please help me with this .
push ebp
mov ebp, esp
and esp, -16
sub esp, 16
call ___main ;
mov DWORD PTR [esp+12], 753664
mov eax, DWORD PTR [esp+12]
mov BYTE PTR [eax], 65
leave
ret
Thanks,
Syntactically, using NASM, there is no PTR keyword. Removing those allows the code to compile up to the undefined ___main. For example:
push ebp
mov ebp, esp
and esp, -16
sub esp, 16
call ___main: ; semi-colon starts comment (should be colon)
mov DWORD [esp+12], 753664
mov eax, DWORD [esp+12]
mov BYTE [eax], 65
leave
ret
Then compiling with:
$ nasm -felf -o asm_recompile.o asm_recompile.asm
The only error returned is:
asm_recompile.asm:5: error: symbol `___main' undefined
Generally, NASM assembly programs require:
section .text
global _start
_start:
Note: Just because you compile to assembly with gcc, do not expect to be able to simply compile the code back to a working elf executable using NASM. gcc by default generates AT&T syntax that is incompatible with NASM. Even telling gcc to output assembly using the -masm=intel option to produce intel format assembly will not compile as-is in NASM. gcc uses as as the assembler. You will have varying luck using as as well, due to the myriad of compiler scripts and options gcc uses by default. The best examination of the process you can get with gcc is to compile your c program to executable using the -v, --verbose option. That will show all of the compiler commands gcc uses to generate the assembly associated with the c code.
It seems that the errors occured due to white spaces after commas. Try to change for example statement
mov DWORD PTR [esp+12], 753664
to
mov DWORD PTR [esp+12],753664

nasm , 64 ,linux, segmentation fault core dumped

this is foo.asm
extern choose;
[section .data]
num1st dq 3
num2nd dq 4
[section .text]
global main
global myprint
main:
push qword [num2nd]
push qword [num1st]
call choose
add esp,8
mov ebx,0
mov eax,1
int 0x80
; pop qword [num1st]
; pop qword [num2nd]
myprint:
mov edx,[esp+8]
mov ecx,[esp+4]
mov ebx,1
mov eax,4
int 0x80
; pop qword [num1st]
; pop qword [num2nd]
ret
it is a C-asm-program
this is bar.c
void myprint(char * msg ,int len);
int choose(int a,int b)
{
if (a>=b){
myprint("the 1st one\n",13);}
else {
myprint("the 2nd one\n",13);}
return 0;
}
nasm -f elf64 foo.asm
gcc -c bar.c
gcc -s -o foobar bar.o foo.o
./foobar ,it says segmentation fault core dumped
I use gdb to debug ,but it says missing debuginfo-install, I am also trying to install it.
maybe the problem has sth to do with the 86_64 arch...
Segmentation fault when pushing on stack (NASM)
after watched this link,I add some 'pop' into it but it doesn't work
Arguments are not passed on the stack in 64-bit mode, unless you have more than 6 of them. The first two arguments will be in RDI and RSI.
There's also a difference in how you should use system calls in 64-bit mode. The syscall number and arguments should be placed in the following registers (source):
syscall nr rax
arg 1 rdi
arg 2 rsi
arg 3 rdx
arg 4 r10
arg 5 r9
arg 6 r8
And the sys_write syscall number in 64-bit mode is 1, not 4. Also, instead of int 0x80 you should use syscall. Performing syscalls with int 0x80 might work in 64-bit mode depending on how your kernel has been configured, but you still need to consider how function arguments are passed.

Resources