-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
arch: aarch64: Compile with -fPIC #31984
arch: aarch64: Compile with -fPIC #31984
Conversation
Under some circumstances (for example when having a SRAM base address very high in memory) the compilation fails with: relocation truncated to fit: R_AARCH64_ADR_PREL_PG_HI21 against symbol One reasonable fix is to compile the code using '-fPIC' and at the same time reintroduce the discarded PLT/GOT sections. Signed-off-by: Carlo Caione <[email protected]>
I think this isn't the right approach.
This isn't a generic problem so applying a generic fix is wrong.
Having the linker script create a symbol that is later casted to be used
as a literal value is the problem. The compiler didn't expect such usage
and therefore didn't emit the necessary code for the linker relocation
to work.
In essence, the way z_mapped_size is used is more or less an abuse of
the linker script. For small values that usually works, but here it
doesn't on ARM64.
Now -fPIC might work around this one issue, but it also introduces some
overhead in the compiled code that will affect the entire binary.
I think the better tradeoff is really to let the compiler generate the
difference between z_mapped_start and z_mapped_end since they are
treated as pointers with proper relocs, and remove z_mapped_size from
the linker script.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a better explanation of the root cause? What's the specific relocation data structure involved? Surely binutils can handle a 64 bit address natively. Is this a relative address for a jump or something? This smells more like a workaround than a fix to me.
On Thu, 4 Feb 2021, Andy Ross wrote:
Is there a better explanation of the root cause?
The problematic line is this one:
```
#define Z_KERNEL_VIRT_SIZE ((size_t)(&z_mapped_size))
```
Where `z_mapped_size` is defined in the linker script as follows:
```
z_mapped_size = z_mapped_end - z_mapped_start;
```
On ARM64 symbol values are relocated and loaded relative to the PC as
those are normally meant to be memory addresses.
Now if you have e.g. `CONFIG_SRAM_BASE_ADDRESS=0x2000000000` then
`z_mapped_size` might still have a reasonable value, say 0x59334. But
when interpreted as an address, that's very very far from the PC whose
value is in the neighborhood of 0x2000000000 and that overflows the 4GB
relocation range.
|
OK, got it.[1] Then maybe what we want is an arch API like ARCH_KERNEL_VIRT_SIZE (and I guess a CONFIG_HAVE_ARCH_KERNEL_VIRT_SIZE or some other way to detect non-default behavior), then on this platform you could have:
Basically you pay for this with one runtime subtraction, but avoid the need to compile the entire application position-independent. [1] Well, not quite: why is this entering code as a runtime relocation and not being inserted into the binary at link time as an immediate storing the absolute address? Linkers on most architectures with this kind of limitation are generally pretty smart about that. |
On Fri, 5 Feb 2021, Andy Ross wrote:
You know what? This is another case where trying to outsmart the
compiler is wrong.
The `z_mapped_end - z_mapped_start` should be unconditional and applied
in all cases. This is used within loops that start from `z_mapped_start`
already, so having `z_mapped_end` or `z_mapped_size` as the loop exit
condition doesn't make much difference.
But, in fact it does! Having `z_mapped_end` as the exit criteria is even
better because you may dispense with the size count and rely only on the
walk pointer (the compiler is smart enough to simplify things even if
the loop still uses `Z_KERNEL_VIRT_SIZE`).
And just to prove that point, here's a binary size difference before and
after such a change:
```
text data bss dec hex filename
1216 8 294936 296160 484e0 mmu.c.obj.arm64.before
1212 8 294936 296156 484dc mmu.c.obj.arm64.after
1110 8 9244 10362 287a mmu.c.obj.x86-64.before
1106 8 9244 10358 2876 mmu.c.obj.x86-64.after
```
So the apparent "cost saving" from using a precomputed `z_mapped_size`
is not providing the expected advantage, neither on ARM64 nor on X86-64.
|
Solved by #32053 |
As usual a bit of rationale.
This is a general fix to a very specific problem. The problem is described in #31952 and it is caused by the symbol:
zephyr/kernel/include/mmu.h
Line 41 in 4d4a636
when compiling on very high SRAM addresses, for example
CONFIG_SRAM_BASE_ADDRESS=0x2000000000
The obvious local fix in this case is just to use:
but doing so we are just hiding the real issue so here I propose a more general fix.