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.
Related
Im trying to do a simple excercise in compilation.
I have 1 c file 1 assembly file and a makefile.
when I run the 'make' command I get the following error:
gcc -g -m32 -Wall -o mainAssignment0.o mainAssignment0.c
/tmp/ccXfVxtg.o: In function `main':
/home/caspl202/Desktop/tasks/Assignment0/mainAssignment0.c:12: undefined reference to `do_Str'
collect2: error: ld returned 1 exit status
makefile:10: recipe for target 'mainAssignment0.o' failed
make: * [mainAssignment0.o] Error 1
Meaning that for some reason the c program doesnt recognize the external ASM function.
Whats even weirder is that when I run the same makefile on the same files on a different machine it works like a charm. I would really like someone to shed some light on this thing.
C code:
#include <stdio.h>
#define MAX_LEN 100
extern int do_Str(char*);
int main(int argc, char** argv) {
char str_buf[MAX_LEN];
int counter = 0;
fgets(str_buf, MAX_LEN, stdin);
counter = do_Str (str_buf);
printf("%s%d\n",str_buf,counter);
return 0;
}
ASM code:
section .data
an: dd 0
section .text
global do_Str
do_Str:
push ebp
mov ebp, esp
pushad
mov ecx, dword [ebp+8]
loop:
cmp byte [ecx], 32
jnz noS
inc dword [an]
noS:
cmp byte [ecx], 65
jl noC
cmp byte [ecx], 90
jg noC
add byte [ecx], 32
noC:
inc ecx
cmp byte [ecx], 0
jnz loop
popad
mov eax,[an]
mov esp, ebp
pop ebp
ret
Makefile:
all: exec
libs: asm-lib
asm-lib: asmAssignment0.s
nasm -g -f elf -o asmAssignment0.o asmAssignment0.s
exec: mainAssignment0.c libs
gcc -g -m32 -c -o mainAssignment0.o mainAssignment0.c
gcc -g -m32 -o Assignment0.out mainAssignment0.o asmAssignment0.o
.PHONY: clean
clean:
rm -rf ./*.o Assignment0.out
You don't need to declare the function external.
int do_Str(char*);
Also, a function in C is prefixed with an underscore, so you must name it accordingly in your asm file.
global _do_Str
_do_Str:
The underscore is automatically added by the C compiler, so you don't have to use it in the C module.
The reason for your error you quote here is that your compile line is wrong. You can tell because you're trying to create an object file, but getting errors from the linker, so something is clearly not right:
gcc -g -m32 -Wall -o mainAssignment0.o mainAssignment0.c
...
collect2: error: ld returned 1 exit status
The problem is you forgot to add the -c flag to this compile line, so that the compiler generates an object file.
However, in your makefile the -c is present, so clearly this error you quote is not generated from the makefile you show us.
exec: mainAssignment0.c libs
gcc -g -m32 -c -o mainAssignment0.o mainAssignment0.c
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.
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.
Suppose I write boot loader on C. What happens when I create some global variable? What is it's logical address? How does it correspond to physical address? For example if I created some string (global)
const char* s = "some string";
Am I right that s stored in .data section? What would be the physical address of s and what would be a logical one? Should we do some extra work to make this addresses correspond each other.
My OS is Linux and I compile my code like this:
as --32 boot.S -o boot.o
gcc -c -m32 -g -Os -ffreestanding -Wall -Werror -I. -o mbr.o mbr.c
ld -Tlinker.ld -nostdlib -o mbr boot.o mbr.o
boot.S is just where I initilize some registers and call c code:
.code16
.text
.global _start
_start:
cli
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov $0x7c00, %sp
ljmp $0, $mmain
mmain -- function in C code. My linker script is:
OUTPUT_FORMAT(binary)
OUTPUT_ARCH(i8086)
ENTRY(_start)
SECTIONS
{
. = 0x7C00;
.text : { *(.text) }
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
I wrote a simple piece of Assembly software (nasm) and a simple application in C.
My C code calls a function from the Assembly code, but I don't know how to compile the C code without receiving a 'undefined reference' error from the 'extern int Sum();' line.
C code:
#include <stdio.h>
extern int Sum();
main()
{
int a1, a2, x;
x = Sum(a1, a2);
printf("value of x is: %d\n", x);
}
Assembly code:
global _Sum
_Sum:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ecx, [ebp+12]
add eax, ecx
pop ebp
ret
How would i compile these two files separately and link them together into one unified file afterwards?
You should assemble the .asm file to get a .o (object) file, as you said you've done. Then you should compile (but not link) the .c file to get another .o file, like this:
gcc -o whatever.o -c whatever.c
Then you should link them together, like this:
gcc whatever.o asm.o
Then the linker will be run with all the object code at once, and should be able to resolve the references it needs.
In ABI used with ELF, C names aren't mangled with an initial _.
For someone who comes searching here: Check out the code and book over here http://www.drpaulcarter.com/pcasm/index.php : Downloadthe linux examples over here : The file you would want to refer is first.asm - (Compile it according to architecture: 32 bits example below )
nasm -f elf -d ELF_TYPE asm_io.asm
nasm -f elf32 first.asm
gcc -o first first.o driver.c asm_io.o
There are two possibilities to solve this, compile the C source as C (NOT C++), to compile it as C you must give it the extension .c
The second way, in C++ code (extension .cpp) names are mangled, when you compile the C++ code use a hex editor to look in the object file and search for Sum. In my case the name becomes _Z3Sumii. If you change the name in the assembler source to this, you'll find that it works.
Also change the extern statement in the C++ source to the following because it takes two parameters of type int, for example, extern int Sum(int, int);
global _Z3Sumii
_Z3Sumii:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ecx, [ebp+12]
add eax, ecx
mov esp, ebp
pop ebp
ret
To compile:
gcc -c file.cpp
nasm -f elf32 sum.asm -o sum.o
gcc file.o sum.o -o file
The most native way is to use GCC toolchain to obtain assembler code of your C program, then manually assembly both assembler files and link them.
In your circumstance:
nasm -f elf -o sumasm.o sumasm.asm
gcc -o sum sumasm.o sum.c
you may need elf32