End of Kernel Linker Script
-
- Member
- Posts: 32
- Joined: Fri Jan 31, 2014 8:21 am
End of Kernel Linker Script
To determine the End-of-Kernel (using end defined in Linker Script), should you use &end or just end? On other posts, I have seen people argue both sides, so I am not exactly sure who is right...
- Combuster
- 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: End of Kernel Linker Script
If you look at it you can have two optionsWith basic C you should already know the difference between these operations: one returns the location where a variable is stored, the other returns the contents of that variable. That means you have to choose according to what end really does in the linker script.
A linker script has however no idea what a variable is, but only what symbols are: names with a value. If you for instance have the typical construct in your linker scriptthen the symbol end has the value that corresponds with that location, i.e. the end as the symbol equals the end of the kernel.
variables in C work by reserving space, and giving that space a name. The corresponding symbol gets the name of that variable, and the linker is asked to fill in the address where it is stored as that symbol's value. Using the variable then results in using that symbol's variable as an address and perform a memory access on it, whereas an reference operator will simply return the actual symbol as that is the address where that data is stored.
Therefore in this particular example, variable a, which implies a memory access, will return whatever data was stored directly after the kernel. variable b will hold the numeric address of that location, i.e. the location of the first bytes after the kernel. Hence answer b is correct for this particular set-up. There are of course methods to be found where you don't use the unary &-operator, but they all depend on how end gets defined.
Code: Select all
extern uint32_t end;
(...)
a = end;
b = &end;
A linker script has however no idea what a variable is, but only what symbols are: names with a value. If you for instance have the typical construct in your linker script
Code: Select all
end = .
variables in C work by reserving space, and giving that space a name. The corresponding symbol gets the name of that variable, and the linker is asked to fill in the address where it is stored as that symbol's value. Using the variable then results in using that symbol's variable as an address and perform a memory access on it, whereas an reference operator will simply return the actual symbol as that is the address where that data is stored.
Therefore in this particular example, variable a, which implies a memory access, will return whatever data was stored directly after the kernel. variable b will hold the numeric address of that location, i.e. the location of the first bytes after the kernel. Hence answer b is correct for this particular set-up. There are of course methods to be found where you don't use the unary &-operator, but they all depend on how end gets defined.
Re: End of Kernel Linker Script
Furthermore, from the above example, B works better on 64-bit system since you don't need to care about address size, and just use pointer directly.
you may even do:
you may even do:
Code: Select all
extern void end;
void* end_of_kernel = &end;
Re: End of Kernel Linker Script
I never quite understood the purpose of using this hack. What is so difficult about parsing your image headers and getting the end of the last segment in memory? Both will work however the latter won't tie the software component to a specific linker feature and thus more compatible to other tool chains.
I suppose it would not matter if you don't ever plan on switching linkers; however it is something to be considered for long term projects.
I suppose it would not matter if you don't ever plan on switching linkers; however it is something to be considered for long term projects.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
Re: End of Kernel Linker Script
The problem is that some loader may not preserve the headers in the visible address space of the loaded kernel, unless you link it in a special way.neon wrote:I never quite understood the purpose of using this hack. What is so difficult about parsing your image headers and getting the end of the last segment in memory? Both will work however the latter won't tie the software component to a specific linker feature and thus more compatible to other tool chains.
I suppose it would not matter if you don't ever plan on switching linkers; however it is something to be considered for long term projects.
I'm not aware any linker, that support the linker script, that do not support emitting a variable.
In the worst case you could write a .c file with a variable named end and ask the linker to put it at the end.
If all that do not work, I would consider such linker crap and switch to other toolchain.
Re: End of Kernel Linker Script
The kernel should be able to access its own section information in some standard form expected by the kernel image; this includes debug information, resources, and special section information specific to that kernel that the loader might not (and should not care to) know about.The problem is that some loader may not preserve the headers in the visible address space of the loaded kernel, unless you link it in a special way.
I don't see any particular case where stripping the headers is a good thing provided that the loader does not provide an alternative means of access (as of this writing, Multiboot does not.)
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: End of Kernel Linker Script
An ELF header parser is a whole bunch of code wasting memory I don't want nor need?neon wrote:The kernel should be able to access its own section information in some standard form expected by the kernel image; this includes debug information, resources, and special section information specific to that kernel that the loader might not (and should not care to) know about.The problem is that some loader may not preserve the headers in the visible address space of the loaded kernel, unless you link it in a special way.
I don't see any particular case where stripping the headers is a good thing provided that the loader does not provide an alternative means of access (as of this writing, Multiboot does not.)
Besides, my linker script is sufficiently complicated for a whole bunch of other reasons - static initialization, exception handling, matching up the vectors segment with the 0xFFFF0000 ARM HIVECS location
Re: End of Kernel Linker Script
Perhaps we just have different ideas in design structure. If the operating system is to support ELF, an associated ELF header parser implementation is a necessity anyways. The only real drawback is that it increases the complexity of kernel initialization.
I suppose it does not really matter anyways as we typically encourage the use of LD scripts for tasks as long as they are not tightly coupled with the source.
I suppose it does not really matter anyways as we typically encourage the use of LD scripts for tasks as long as they are not tightly coupled with the source.
Last edited by neon on Sun Feb 02, 2014 6:06 pm, edited 1 time in total.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
Re: End of Kernel Linker Script
For ELF specification, the loader is not required to retain headers information when loading objects (And there is no defined memory address to put such information).
To have the header accessible from kernel you then require to do non-trivial mechanism asking the linker to attach the headers into the kernel sections - ld support that by tweaking linker script, but I doubt if many other linkers support that too.
And I do not see this any simpler then putting an end on linker script.
To have the header accessible from kernel you then require to do non-trivial mechanism asking the linker to attach the headers into the kernel sections - ld support that by tweaking linker script, but I doubt if many other linkers support that too.
And I do not see this any simpler then putting an end on linker script.
You are right, there are more than one way to do it.neon wrote:The point here is that the script does simplify a lot of things -- but is not a necessity.
Re: End of Kernel Linker Script
Sorry, I updated my last post before you responded. We do typically encourage its use; and I agree using the linker script is far simpler.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: End of Kernel Linker Script
You assume the binary loader is a part of the kernel. True for most monolithic kernels, but those of us developing microkernels are inclined towards it being a part of some library and/or service.neon wrote:Perhaps we just have different ideas in design structure. If the operating system is to support ELF, an associated ELF header parser implementation is a necessity anyways. The only real drawback is that it increases the complexity of kernel initialization.
I suppose it does not really matter anyways as we typically encourage the use of LD scripts for tasks as long as they are not tightly coupled with the source.
Re: End of Kernel Linker Script
Good observation there. For a microkernel architecture it will create yet another chicken and egg scenario.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}