position independant kernel

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
User avatar
os64dev
Member
Member
Posts: 553
Joined: Sat Jan 27, 2007 3:21 pm
Location: Best, Netherlands

position independant kernel

Post by os64dev »

Hi All,

I'm currently trying to get a clearer understanding of gcc compiler options and found the -pic (Position Independant Code) option. As my os i solely 64-bit longmode this might be a good option for my kernel to be compiled with. Is somebody of you also using this option ? and can somebody explains the cons and pros?

thanx in advance
Author of COBOS
Tyler
Member
Member
Posts: 514
Joined: Tue Nov 07, 2006 7:37 am
Location: York, England

Post by Tyler »

I was under the impression all GCC code was already position independent (how else could it link anywhere into the final executable)

PIC, simply means you can run the code from anywhere in memory with the same results... Pros. you can stick the kernel where u like, cons, it is slightly slower because of the use of the base register solely for making hte code PIC. As i said though, i don't see how code could not always be PIC, or it wouldn't link...
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Post by Candy »

Tyler wrote:I was under the impression all GCC code was already position independent (how else could it link anywhere into the final executable)
No, it's sort of like "position fixable" - it outputs a list of locations you have to add its own location to after placing it. Position independant code doesn't need fixing up.
Tyler
Member
Member
Posts: 514
Joined: Tue Nov 07, 2006 7:37 am
Location: York, England

Post by Tyler »

Ahh... ive only ever looked into fixups used to call the functions from a library despite location, i never realised all files needed fixups in order place them anywhere.
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

Normally one compiles into objects which are relocatable, and then the linker "relocates" those at certain address, and gets rid of the relocation information. If we are compiling shared libraries, it however leaves this information into the library, so that it can be relocated at load time instead.

The downside is that if we load the same shared library in several processes, but at different addresses, we need two copies of the library in memory, because different relocations need to be done for each. Position independent code solves this by writing such code that we don't need to relocate it, there for allowing the same copy to be shared, saving memory.

The solution is to collect all the addresses that need relocation into a single table called GOT (global offset table) and write code that always gets the addresses from that table. That way you only need to relocate the table, and and rest of the pages can be shared. This ofcourse adds some overhead, and the GOT address is kept in a register (ebx or ebp, can't remember) so you also lose one of those. As it is, there are some issues with x86, having to do with the lack of EIP relative addressing.

That basicly means that you can't have PIC kernel, because you need some support code for PIC to work. You could theoretically make it work, but you'd need your bootloader to do the relevant relocations for you, and it's most definitely not worth the trouble.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
os64dev
Member
Member
Posts: 553
Joined: Sat Jan 27, 2007 3:21 pm
Location: Best, Netherlands

Post by os64dev »

mystran wrote:The solution is to collect all the addresses that need relocation into a single table called GOT (global offset table) and write code that always gets the addresses from that table. That way you only need to relocate the table, and and rest of the pages can be shared. This ofcourse adds some overhead, and the GOT address is kept in a register (ebx or ebp, can't remember) so you also lose one of those. As it is, there are some issues with x86, having to do with the lack of EIP relative addressing.

That basicly means that you can't have PIC kernel, because you need some support code for PIC to work. You could theoretically make it work, but you'd need your bootloader to do the relevant relocations for you, and it's most definitely not worth the trouble.
And that is why i have a 64-bit AMD64/EM64T environment. It does support RIP relative addressing, thus the whole GOT/relocation scheme is not used. As a test i displaced all the code/data below 1MiB with an offset of 1MiB and made a jump with the folowing inline assembly.

Code: Select all

asm volatile("movq $lp, %rax; addq $0x100000, %rax; jmp *%rax; lp:");
Everything was working fine ;-) and running at an offset of 1MiB from the original code. The problem was the interrupt service routines addresses in the IDT because they are stored with the compiled addresses but that can be easily fixed.
Author of COBOS
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

os64dev wrote: And that is why i have a 64-bit AMD64/EM64T environment. It does support RIP relative addressing, thus the whole GOT/relocation scheme is not used. As a test i displaced all the code/data below 1MiB with an offset of 1MiB and made a jump with the folowing inline assembly.
Well you still need something like a GOT for a shared library to handle it's imported symbols, but IP relative addessing saves you a register..
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
Tyler
Member
Member
Posts: 514
Joined: Tue Nov 07, 2006 7:37 am
Location: York, England

Post by Tyler »

Does anyone know how Linux, Windows/ReactOS allow different kernel loading positions... do they use relocations or PIC?
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

Tyler wrote:Does anyone know how Linux, Windows/ReactOS allow different kernel loading positions... do they use relocations or PIC?
AFAIK they just allow you to link the kernel to different addresses.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Post by ~ »

And the rest of the binaries?

Isn't ELF designed to be dynamically linked to any address by using relocation?

Yes, maybe that doesn't apply to the kernel itself, but whenever one uses an ELF and knows how to relocate its binary image, it can be done, but maybe that would require a complex boot loader (more than a bootstrap), one that contains a decent "dynamic program loader".

But at that point, I guess the relocated image would become "static" for that system run.
Tyler
Member
Member
Posts: 514
Joined: Tue Nov 07, 2006 7:37 am
Location: York, England

Post by Tyler »

mystran wrote:
Tyler wrote:Does anyone know how Linux, Windows/ReactOS allow different kernel loading positions... do they use relocations or PIC?
AFAIK they just allow you to link the kernel to different addresses.
So Windows comes with all versions of the kernel at 2Gb and 3Gb?
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

Tyler wrote:
mystran wrote:
Tyler wrote:Does anyone know how Linux, Windows/ReactOS allow different kernel loading positions... do they use relocations or PIC?
AFAIK they just allow you to link the kernel to different addresses.
So Windows comes with all versions of the kernel at 2Gb and 3Gb?
No idea how Windows works. Maybe they relocate in loader, who knows. They could even do the link at load time if they wanted.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,
Tyler wrote:So Windows comes with all versions of the kernel at 2Gb and 3Gb?
I've no idea how Windows does it, but I'd assume it'd be easier to statically link the kernel and always load it at a high address (like 0xF8000000). If the start of kernel space can be relocated, then the space between the start of kernel space and the kernel's code can still be shifted.

For e.g:
  • 0x00000000 to SOMEWHERE User-space
    SOMEWHERE to 0xF0000000 Space for dynamicaly allocated kernel data
    0xF8000000 to 0xFFFFFFFF Space for kernel code and statically allocated kernel data
With this sort of thing it'd be relatively easy to allow SOMEWHERE to be determined during boot, especially as most OS developers end up with something like "kmalloc()" managing a kernel heap.

For a 64-bit system the address space is larger and the CPU practically forces you to have a hole in the middle of it. In this case it makes a lot of sense to have 131072 GB of user space and 131072 GB of kernel space, and there isn't much point allowing SOMEWHERE to be changed.

For 32-bit systems there's only one reason I can think of for considering using position independant code for the kernel - you might want to run kernel code before you setup paging. To be honest I've never liked this idea much (seems too much like rushing to write a kernel and skipping all the boot code and preperation). For 64-bit systems this isn't really an option - you can't run 64-bit code without paging.


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.
Post Reply