Undefined reference to

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Modestas
Posts: 8
Joined: Sat Dec 20, 2014 5:17 am

Undefined reference to

Post by Modestas »

Hello. I'm beginner to operating system development, and got problem calling function that is defined in assembly, from kernel that is programmed in C. When I try to link all compiled C sources to one binary file I get this error:
tables.o: In function `init_gdt':
tables.c:(.text+0x10b): undefined reference to `gdt_flush'
But it's actually defined.

Here is the line in tables.c:

Code: Select all

extern void gdt_flush(unsigned int);
And in assembly I have this code in the bootloader sector:

Code: Select all

[GLOBAL gdt_flush]
gdt_flush:
	mov eax, [esp+4]
	lgdt [eax]
	
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	jmp 0x08:.flush
.flush:
	ret
This is how I link them:

Code: Select all

ld -o kernel_l.bin -Ttext 0x7e00 kernel_l.o console.o tables.o -m elf_i386 --oformat binary
I had same problem when I've been linking those 3 files separately. So I should somehow link bootloader together with compiled C sources?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Undefined reference to

Post by Combuster »

ld
Posting Checklist

Apart from using a dodgy linker and a dodgy reference, which file that you pass contains the implementation of gdt_flush?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Modestas
Posts: 8
Joined: Sat Dec 20, 2014 5:17 am

Re: Undefined reference to

Post by Modestas »

Combuster wrote:
ld
Posting Checklist

Apart from using a dodgy linker and a dodgy reference, which file that you pass contains the implementation of gdt_flush?
It is declared in tables.c and defined in boot.asm
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Undefined reference to

Post by Combuster »

which file that you pass (...)
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Modestas
Posts: 8
Joined: Sat Dec 20, 2014 5:17 am

Re: Undefined reference to

Post by Modestas »

Combuster wrote:
which file that you pass (...)
None. It's in boot.bin and I think I know my problem. I do not link boot.bin (which has implementation of gdt_flush) together with tables.o and this is the problem. But how I could link them if linker gives me this error when I add boot.bin to ld line?
boot.bin: file not recognized: File format not recognized
And if I compile my boot.asm to elf format instead of bin (so I could link it) using this:
nasm boot.asm -f elf -o boot.elf
Again I'm getting error:
boot.asm:1: error: unrecognised directive [ORG]
So the solution is to rewrite boot.asm, so it would compile to elf format? Or there is any other solutions?
User avatar
Bender
Member
Member
Posts: 449
Joined: Wed Aug 21, 2013 3:53 am
Libera.chat IRC: bender|
Location: Asia, Singapore

Re: Undefined reference to

Post by Bender »

'boot.bin' sounds like a binary file to me, you need an object file to be compiled as ELF32/64 (or other but I'd just assume ELF) to be passed to the linker. Also, you would have to use a linker script instead of an ORG, to define your start address.
And the most important part,
Please use a cross compiler, as Combuster previously pointed out, even if you'd somehow get it to compile, chances are you are going to have a lot of trouble afterwards.
Follow the instructions here: http://wiki.osdev.org/GCC_Cross-Compiler
It takes a bit of work and time to build but it's worth it.

Along with that I see you're probably using code from JamesM tutorials (judging from the names of symbols), be sure to check out (if you're): http://wiki.osdev.org/James_Molloy%27s_ ... Known_Bugs
"In a time of universal deceit - telling the truth is a revolutionary act." -- George Orwell
(R3X Runtime VM)(CHIP8 Interpreter OS)
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Undefined reference to

Post by Combuster »

-Ttext 0x7e00
Definitely not JamesM's, although it's most likely a mix and match of everything in which case it's especially not going to work considering the OP has no clue as to how a linker actually works.
So the solution is to rewrite boot.asm, so it would compile to elf format? Or there is any other solutions?
The bootsector runs in 16-bit mode (and should not be using anything like a 32-bit stack. Homework for the OP: explain why this is wrong). C code (in your apparent case) runs in 32-bit mode, and you can not share code between the two modes.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Modestas
Posts: 8
Joined: Sat Dec 20, 2014 5:17 am

Re: Undefined reference to

Post by Modestas »

Bender wrote:Also, you would have to use a linker script instead of an ORG, to define your start address.
How the hell I can do this? I've tried few hours to do it, tried to google some info, but still nothing.

I have this assembly code:

Code: Select all

[org 0x7c00]
[bits 16]

Start:
	mov al, 'A'
	mov ah, 0eh
	int 10h
	
	jmp $
	
times 510 - ($ - $$) db 0
dw 0xAA55
If I do:

Code: Select all

nasm boot.asm -f bin -o boot.bin
qemu boot.bin
It prints 'A' character. But how to do it with linker script?
I've tried this:

Code: Select all

ENTRY(Start)
INPUT(boot.o)
OUTPUT_FORMAT(elf32-i386)
OUTPUT(boot.bin)
SECTIONS
{
	. = 0x7c00;
	.text : { *(.text) }
}
Assembly code:

Code: Select all

section .text
	[global Start]
[bits 16]

Start:
	mov al, 'A'
	mov ah, 0eh
	int 10h
	
	jmp $
	
times 510 - ($ - $$) db 0
dw 0xAA55
But nothing happens, qemu won't find bootable floppy drive.

And with linker script boot.bin is not 512 bytes as it should be, it's greater than 3kb.
seuti
Member
Member
Posts: 74
Joined: Tue Aug 19, 2014 1:20 pm

Re: Undefined reference to

Post by seuti »

Modestas wrote:
Bender wrote:Also, you would have to use a linker script instead of an ORG, to define your start address.
How the hell I can do this? I've tried few hours to do it, tried to google some info, but still nothing.

I have this assembly code:

Code: Select all

[org 0x7c00]
[bits 16]

Start:
	mov al, 'A'
	mov ah, 0eh
	int 10h
	
	jmp $
	
times 510 - ($ - $$) db 0
dw 0xAA55
If I do:

Code: Select all

nasm boot.asm -f bin -o boot.bin
qemu boot.bin
It prints 'A' character. But how to do it with linker script?
I've tried this:

Code: Select all

ENTRY(Start)
INPUT(boot.o)
OUTPUT_FORMAT(elf32-i386)
OUTPUT(boot.bin)
SECTIONS
{
	. = 0x7c00;
	.text : { *(.text) }
}
Assembly code:

Code: Select all

section .text
	[global Start]
[bits 16]

Start:
	mov al, 'A'
	mov ah, 0eh
	int 10h
	
	jmp $
	
times 510 - ($ - $$) db 0
dw 0xAA55
But nothing happens, qemu won't find bootable floppy drive.

And with linker script boot.bin is not 512 bytes as it should be, it's greater than 3kb.
Maybe it is because you are telling the linker you want the elf format and I think the MBR has to be a flat binary.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Undefined reference to

Post by Combuster »

So you're still trying to use the exact same code for both the bootsector and whatever comes after that, even though you have already been told you can't mix code assembled for 16-bit mode with code assembled for 32-bit mode?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Modestas
Posts: 8
Joined: Sat Dec 20, 2014 5:17 am

Re: Undefined reference to

Post by Modestas »

seuti wrote:
Modestas wrote:
Bender wrote:Also, you would have to use a linker script instead of an ORG, to define your start address.
How the hell I can do this? I've tried few hours to do it, tried to google some info, but still nothing.

I have this assembly code:

Code: Select all

[org 0x7c00]
[bits 16]

Start:
	mov al, 'A'
	mov ah, 0eh
	int 10h
	
	jmp $
	
times 510 - ($ - $$) db 0
dw 0xAA55
If I do:

Code: Select all

nasm boot.asm -f bin -o boot.bin
qemu boot.bin
It prints 'A' character. But how to do it with linker script?
I've tried this:

Code: Select all

ENTRY(Start)
INPUT(boot.o)
OUTPUT_FORMAT(elf32-i386)
OUTPUT(boot.bin)
SECTIONS
{
	. = 0x7c00;
	.text : { *(.text) }
}
Assembly code:

Code: Select all

section .text
	[global Start]
[bits 16]

Start:
	mov al, 'A'
	mov ah, 0eh
	int 10h
	
	jmp $
	
times 510 - ($ - $$) db 0
dw 0xAA55
But nothing happens, qemu won't find bootable floppy drive.

And with linker script boot.bin is not 512 bytes as it should be, it's greater than 3kb.
Maybe it is because you are telling the linker you want the elf format and I think the MBR has to be a flat binary.
Oh god, thanks, now it runs good in qemu.

And, Combuster, in the latest post assembly code I'm not mixing 16 and 32 bit code, it's only 16 bits.
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: Undefined reference to

Post by sortie »

May I recommend you take a look at http://wiki.osdev.org/Bare_Bones and consider discarding your current boot method? This approach is verified, community edited and well supported by the osdev community. Feel free to continue doing things your way, but if you're having trouble like this (or the many other trouble I expect you to have in the future, unless you're more skilled than I estimate), I only suggest developing in this manner if you are absolutely certain you can do it and it's the best way for you.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Undefined reference to

Post by Brendan »

Hi,

Let's go back to the start...

A kernel needs different boot loaders for different cases:
  • for floppy (unpartitioned, with BPB, with old "int 0x13")
  • for BIOS MBR partitioned hard disk (MBR partitioned, without BPB, with "int 0x13 extensions")
  • for BIOS GPT/hybrid hard disk (GPT partitioned, without BPB, with "int 0x13 extensions")
  • for BIOS network boot/PXE (completely different, no disk IO at all)
  • for BIOS "no emulation El Torito" CDs
  • for 32-bit UEFI
  • for 64-bit UEFI
Building a boot loader into the kernel itself is silly. The boot loader and the kernel need to be completely separate; where (hopefully) all the different boot loaders start the same kernel, and where the OS installer is responsible for installing the appropriate boot loader/s onto whatever the boot device is. Note: For some cases it does make sense to have multiple boot loaders on the same device - e.g. a single CD that contains a UEFI system partition with both 32-bit and 64-bit UEFI boot loaders, plus the BIOS "no emulation El Torito" boot loader, where the firmware auto-selects the boot loader it needs.

Now; given that the boot loader and kernel are completely separate binaries, you can not expect the kernel to be able to call anything in the boot loader directly. You either avoid this (e.g. build "gdt_flush" into your kernel so that kernel doesn't need to call a function in the boot loader); or you need to define some sort of interface/API (e.g. maybe boot loader provides a software interrupt that kernel can use, or maybe boot loader passes a table of function pointers to kernel when it starts the kernel, etc). If the boot loader does provide some sort of interface/API the linker isn't involved (either the kernel doesn't need to know the address of the function in the boot loader, or the kernel is told the address of the function in the boot loader at run-time and not at compile/link time).

Finally; I recommend documenting the requirements for the state of the computer when the boot loader passes control to the kernel. This should be a formal specification; and should include things like:
  • what the physical and/or virtual address space will look like when the kernel is started
  • which data (memory map, video buffer details, etc) is passed to the kernel and how
  • what can the kernel assume about the CPU's state - which mode will the CPU be in, what will registers contain, can the kernel assume FPU has been initialised, is there a GDT or IDT, will caches be enabled, will MTRRs be configured, will IRQs be enabled, etc.
  • the state/s of all other hardware - e.g. is A20 guaranteed to be enabled, what state are other CPUs in, are PCI devices guaranteed to be correctly preconfigured (and how) or in an undefined state, what about PIC/IO APIC (and local APIC), etc.
  • if the boot loader has to provide some sort of interface/API, then that has to be included too. What CPU mode is used for the interface/API and which calling conventions. For each function the interface/API provides; what do they do, what are the input and output parameters, what are the limitations.
In general, someone should be able to download your specification and implement a boot loader that works with your kernel/s; without ever talking to you and without ever downloading or seeing any of your source code.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Bender
Member
Member
Posts: 449
Joined: Wed Aug 21, 2013 3:53 am
Libera.chat IRC: bender|
Location: Asia, Singapore

Re: Undefined reference to

Post by Bender »

I don't know what you are actually trying to achieve. I'm sorry but this thread is getting a bit confusing, you first link your program with "elf_i386" flags, looking like you're compiling 32-bit code, and then you show 16-bit code.

I think what you actually intend to accomplish is a 32-bit kernel which needs to be loaded by another bootloader, GRUB (or other), you don't need to have 16-bit code with the kernel, if you really want to do it "that" way, you'd have to switch from 16-bit to 32-bit protected mode.

You must write 32-bit code only, for a 32-bit executable, it's not a good idea to mix 16-bit and 32-bit code. (unless in a bootloader, of course).

(ninja'd by Brendan, his post explains this better)
"In a time of universal deceit - telling the truth is a revolutionary act." -- George Orwell
(R3X Runtime VM)(CHIP8 Interpreter OS)
Modestas
Posts: 8
Joined: Sat Dec 20, 2014 5:17 am

Re: Undefined reference to

Post by Modestas »

Brendan wrote:The boot loader and the kernel need to be completely separate;
I thought everything should be in one file.. But anyway, I made it working. Now I compile bootloader to binary file, then another assembly file which one have function that must be called from C (kernel.c) compiled to elf format and linked together with kernel, now it works good.

Thank you everyone for helping.
Post Reply