[edk2-devel] ArmVirt and Self-Updating Code

Marvin Häuser mhaeuser at posteo.de
Sat Jul 31 19:08:39 UTC 2021


On 23.07.21 16:34, Ard Biesheuvel wrote:
> On Fri, 23 Jul 2021 at 16:27, Marvin Häuser <mhaeuser at posteo.de> wrote:
>>
>>
>> On 23.07.21 16:09, Ard Biesheuvel wrote:
>>> On Fri, 23 Jul 2021 at 12:47, Marvin Häuser <mhaeuser at posteo.de> wrote:
>>>> On 23.07.21 12:13, Ard Biesheuvel wrote:
>>>>> On Fri, 23 Jul 2021 at 11:55, Marvin Häuser <mhaeuser at posteo.de> wrote:
> ...
>>>>>> 2) emit a GOT, which ends up being converted to PE/COFF Relocations (->
>>>>>> self-relocation), for global data that cannot be referenced relatively?
>>>>>> Is there any way to know/force that no symbol in GOT is accessed up
>>>>>> until the end of the self-relocation process?
>> Do you maybe have one final comment regarding that second question,
>> please? :)
> The RELA section is not converted into PE/COFF relocations. This would
> not achieve a lot, given that no prior PE/COFF loader exists to
> process them. There is a snippet of asm code in the startup code that
> processes the R_AARCH64_RELATIVE relocation entries before calling
> into C code.

I searched for said ASM code till my fingers fell asleep and at last 
found this: 
https://github.com/tianocore/edk2/commit/b16fd231f6d8124fa05a0f086840934b8709faf9#diff-3d563cc4775c7720900f4895bf619eed06291044aaa277fcc57eddc7618351a1L12-R148

If I understand the commit message correctly, it is basically "pray the 
C code does not use globals at all", which is fair enough, so maybe I 
should document this in my proposed new library? I trust that this is 
enough of a constraint for both ARM and AArch64, because I do not know 
them at all.

What worries me is that StandaloneMmCore has no such ASM entry point at 
all and instead it's just executing C directly. Also, it is not passed 
the "-fno-jump-tables" flag that is commented to be important in the 
commit linked above.

Best regards,
Marvin

> This also gives us the guarantee that no GOT indirections are
> dereferenced, given that our asm code simply does not do that.
>
>> Let's drop "GOT" and make it "any instruction that requires prior
>> relocation to function correctly".
>>
> The thing to keep in mind here is that R_AARCH64_RELATIVE relocations
> never target instructions, but only memory locations that carry
> absolute addresses. This could be locations in .rodata or .data
> (global vars carrying pointer values), or GOT entries.
>
>>>>> It is not really a GOT. Actually, a GOT is undesirable, as it forces
>>>>> global variables to be referenced via an absolute address, even when a
>>>>> relative reference could be used.
>>>> Hmm, the GCC docs say a GOT is used for "all constant addresses" (I took
>>>> it as "absolute"?), it is kind of vague. I understood it this way:
>>>> 1) no-pie emits relocations that can target the .text and .data sections
>>>> for instructions that embed and variables that hold an absolute address
>>>> (I thought this was RELA?)
>>>> 2) pie emits a GOT such that there are no relocations as described in
>>>> 1), because all absolute addresses are indirected by GOT (just GOT
>>>> references are relocated)
>>>>
>>> Correct. And this works really well for shared libraries, where all
>>> text and data sections can be shared between processes, as they will
>>> not be modified by the loader. All locations targeted by relocations
>>> will be nicely lumped together in the GOT.
>>>
>>> However, for bare metal style programs, there is no sharing, and there
>>> is no advantage to lumping anything together. It is much better to use
>>> relative references where possible, and simply apply relocations
>>> wherever needed across the text and data sections,
>>>
>>>> If I understood the process right, but the term (GOT) is wrong, sorry,
>>>> that is what I gathered from the docs. :)
>>>> I have a x86 + PE background, so ARM + ELF is a bit of a learning curve...
>>>>
>>> The GOT is a special data structure used for implicit variable
>>> accesses, i.e., global vars used in the code. Statically initialized
>>> pointer variables are the other category, which are not code, and for
>>> which the same considerations do not apply, given that the right value
>>> simply needs to be stored in the variable before the program starts.
>>>
>>>>> For instance, a statically initialized pointer always carries an
>>>>> absolute address, and so it always needs an entry in the RELA table
>>>>>
>>>>> E.g.,
>>>>>
>>>>> int foo = 10; // external linkage
>>>>> static int *bar = &foo;
>>>>>
>>>>> In this case, there is no way to use relative addressing because the
>>>>> address of foo is taken at build time.
>>>>>
>>>>> However, if bar would be something like
>>>>>
>>>>> static int *bar() { return &foo; }
>>>>>
>>>>> the address is only taken at runtime, and the compiler can use a
>>>>> relative reference instead, and no RELA entry is needed. With a GOT,
>>>>> we force the compiler to allocate a variable that holds the absolute
>>>>> address, which we would prefer to avoid.
>>>> And this is not forced by whatever table -fpie uses, as per my
>>>> understanding above?
>>>>
>>> The selection of 'code model' as it is called is controlled by GCC's
>>> -mcmodel= argument, which defaults to 'small' on AArch64, regardless
>>> of whether you use PIC/PIE or not.
>> Aha, makes sense, thanks!
>>
>> Best regards,
>> Marvin
>>
>>>>>>>> “Now, StandaloneMmPkg has similar (self-)relocation code too:https://github.com/tianocore/edk2/blob/17143c4837393d42c484b42d1789b85b2cff1aaf/StandaloneMmPkg/Library/StandaloneMmCoreEntryPoint/AArch64/StandaloneMmCoreEntryPoint.c#L379-L386
>>>>>>>>
>>>>>>>> Because I cannot find such elsewhere, I assume it must be for the same ARM virtualised environment as above.
>>>>>>> No.
>>>>>>>
>>>>>>>> The binary it applies the Relocations to is documented to be the Standalone MM core, but in fact SecCore is located:
>>>>>>>>
>>>>>>>> https://github.com/tianocore/edk2/blob/17143c4837393d42c484b42d1789b85b2cff1aaf/StandaloneMmPkg/Library/StandaloneMmCoreEntryPoint/AArch64/SetPermissions.c#L131-L158
>>>>>> As per your comments below, I think SecCore should not be located here.
>>>>>> Is the Standalone MM core of *type* SecCore in the FFS (without *being*
>>>>>> SecCore)? This confused me the most.
>>>>>>
>>>>> If the FFS SecCore section type is used here, it does not mean that
>>>>> the image is a SEC image in the strict PI sense.
>>>>>
>>>>> Perhaps we were just too lazy to add a new type to the FFS spec?
>>>> That is what I meant to imply with the middle question (well, not
>>>> necessarily "lazy", for ARM there simply seems to not be any reason to
>>>> distinguish if the environments are fully separate), just wanted to make
>>>> sure I understand what the code does before modifying it.
>>>>
>>>> Thank you again!
>>>>
>>>> Best regards,
>>>> Marvin
>>>>
>>>>>>>> “This yields the following questions to me:
>>>>>>>>
>>>>>>>> 1) What even invokes Standalone MM on ARM? It is documented it is spawned during SEC, but I could not find any actual invocation.
>>>>>>>>
>>>>>>> It is not spawned by the normal world code that runs UEFI. It is a
>>>>>>> secure world component that runs in a completely different execution
>>>>>>> context (TrustZone). The code does run with the MMU enabled from the
>>>>>>> start, but running from an a priori fixed offset was considered to be
>>>>>>> a security hazard, so we added self relocation support.
>>>>>>>
>>>>>>> The alternative would have been to add metadata to the StMmCore
>>>>>>> component that can be interpreted by the secure world component that
>>>>>>> loads it, but this would go beyond any existing specs, and make
>>>>>>> portability more problematic.
>>>>>>>
>>>>>>>> 2) Why does Standalone MM (self-)relocation locate SecCore? Should it not already have been relocated with the code from ArmPlatformPkg? Is Standalone MM embedded into ARM SecCore?
>>>>>>>>
>>>>>>> No and no. Standalone MM has nothing to do with the code that runs as
>>>>>>> part of UEFI itself. ArmPlatformPkg is completely separate from
>>>>>>> StandaloneMmPkg.
>>>>>>>
>>>>>>>> 3) Why is SecCore the only module relocated? Are all others guaranteed to be "properly" loaded?
>>>>>>>>
>>>>>>> SecCore contains a PE/COFF loader, so all subsequent modules are
>>>>>>> loaded normally. This is similar to the ArmVirtQemuKernel
>>>>>>> self-relocating SEC module, which only relocates itself in this
>>>>>>> manner, and relies on standard PE/COFF metadata for loading other
>>>>>>> modules.
>>>>>> Interesting... this definitely is vastly different from the x86 side of
>>>>>> things. I think most things became very clear. Thanks a lot!
>>>>>>
>>>>>>>> 4) Is there maybe some high-level documented about the ARM boot flow? It seems to be significantly different from the x86 routes quite vastly.”
>>>>>>>>
>>>>>>> trustedfirmware.org may have some useful documentation.
>>>>>> I'll check it some time, hopefully this weekend. Thanks!
>>>>>>
>>>>> My pleasure.



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#78466): https://edk2.groups.io/g/devel/message/78466
Mute This Topic: https://groups.io/mt/84380729/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-






More information about the edk2-devel-archive mailing list