Using custom main loader with GCC - c

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.

Related

Linking a c function and a asm assembly file

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.

Loading elf-i386 from my boot loader

I am doing operating system project, until now I have my bootloader running. I can load binary file using bios interuppt, but I am unable to load and call C function from ELF file format:
Here is my C program that I want to finally execute:
//build :: cc -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -c -o kmain.o kmain.c
void kmain(){
int a = 5;
for(;;);
}
Here is assembly code to call kmain()
; build :: nasm -f elf loader.asm
[BITS 32]
[GLOBAL start]
[EXTERN kmain]
section .text
start:
mov eax, 0
call kmain
This is my linker script
ENTRY(start)
and this how I am linking everything together
ld -m elf_i386 -T link.ld -o kernel loader.o kmain.o
Now to call start from my bootloader, I am using e_entry offset field from elf header( 24 byte away from starting address) :
xor edx, edx
mov edx, 24
add edx, IMAGE_PMODE_BASE
add ebx, dword[edx]
add ebx, IMAGE_PMODE_BASE
call ebx
where IMAGE_PMODE_BASE is address of elf file loaded in memory.
My question is "Is This the correct way of loading and calling a function in C in ELF file format?".
Thank you for reading, please help.

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

Assembly function call from c

I cannot combine my kernel_entry.asm and main.c. My main.c calls an asm function Sum. Both nasm and gcc compiles respective files. However, the linker gives an error.
Kernel_entry.asm:
[bits 32]
[extern _start]
[global _Sum]
....
_Sum:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ecx, [ebp+12]
add eax, ecx
pop ebp
ret
main.c:
....
extern int Sum();
void start() {
....
int x = Sum(4, 5);
....
}
To compile source files, I use following commands:
nasm kernel_entry.asm -f win32 -o kernel_entry.o
gcc -ffreestanding -c main.c -o main.o
....
ld -T NUL -o kernel.tmp -Ttext 0x1000 kernel_entry.o main.o mem.o port_in_out.o screen.o idt.o
Linker gives following error:main.o:main.c:(.text+0xa82): undifened reference to 'Sum'. I tried everything but couldn't find any solution. When I remove asm function call from main.c, it works.
The TL;DR version of the answer is that mixing nasm's -f win32 generates an object file that is not compatible with the GNU toolchain on Windows - you need to use -f elf if you want to link using ld. That is described in NASM's documentation here under sections 7.5 and 7.9.
The hint for me was that by running nm kernel_entry.o generated:
00000000 a .absolut
00000000 t .text
00000001 a #feat.00
U _start
U _Sum
Which basically shows Sum as an undefined symbol. After compiling as ELF, I got:
U _start
00000000 T _Sum
indicating Sum as a recognised symbol in the text section.

Resources