[NSWI004] _kernel_end

Petr Tuma petr.tuma at d3s.mff.cuni.cz
Fri Oct 23 08:52:07 CEST 2020


Hello,

> gcc version 10.2.1. I use the lab machine.
> So I'm still confused, probably because I'm not that familiar with the notion of linking and exporting/importing symbols.
> Can you please explain what is happening behind the scenes and why I need to take an address of _kernel_end.

OK, now I can reproduce the error (the reason I could not before was GCC getting smarter between versions 9 and 10).

I think you have worked around it in the meantime, here is just to explain in a bit more detail what is happening.

First, why `_kernel_end` is not just a normal variable. The idea, obviously, is to have some way of telling where the kernel image ends in memory (so that we can put the dynamic data after that). This is not something that a compiler can do, because it is only the linker who knows how all the bits and pieces of the kernel image are assembled together - which is why it is also the linker that defines the `_kernel_end` symbol for us. This is not automatic - for the details of how it is done, you can look into the `kernel.lds` file - this is a linker script that the linker uses to put the image together, and, among other things, it also tells the linker to define `_kernel_end` as the address at the very end of the `.kernel` section (`.` is a shorthand for current address).

Now, having the `_kernel_end` symbol defined by the linker is not enough, we also want to use it in the C language. For that, we need it to be something that the C language understands - such as a function name or a variable name. Here is where the `main.h` file comes in - it says that `_kernel_end` is an external variable of type `uint8_t [0]` (and, unlike other external variables, which will have some source file where they are actually defined, this one happens to be external everywhere, because it actually comes from the linker, not from another source module as would be common).

As far as the linker is concerned, the type of `_kernel_end` is not important - it cares only about the symbol address, we can specify just about any type we want in the header and the linker will be fine. But we want to pick a type that has a reasonable semantics in C - so we were aiming for "this is really an array of bytes but we have no idea how long, we only know that it starts here". And we wanted to avoid `extern uint8_t _kernel_end []`, because many people not used to working in C equate array names with pointers and would think `_kernel_end` is a pointer (see http://c-faq.com/aryptr/aryptr2.html for more on this). So we used `extern uint8_t _kernel_end [0]`, but in your case GCC got too smart and was able to track the size, hence the error you encountered.

We could experiment with the definition some more, but chances are it would lead to errors in code of other teams, so this is something we try not to do too often. Maybe just making `_kernel_end` an `uint8_t` would be safest, even if not entirely semantically descriptive.

Petr


More information about the NSWI004 mailing list