[GSoC] Ways to make the x86_64 port work with UEFI

Amaan Cheval amaan.cheval at gmail.com
Tue May 29 15:58:50 UTC 2018


Noted, thanks a ton for the details! Unrelated to the topic at hand,
but out of interest, is this the only reading material for further
details? http://ceur-ws.org/Vol-1697/EWiLi16_12.pdf

In brief: My tests for keeping a libfake.a (compiled without -fpic)
and a loader.so with a user-appXYZ.c have been successful, but I'm not
sure if my assumptions hold for all cases. See the details below for
more.

Next actions:
- Read more of Linkers and Loaders since it seems to be the only
detailed resource I've found at this point
- Experiment with actually using the existing librtems*.a I've got and
making them boot as a PE UEFI application image

--------------------------------------------------------------------------------

In more detail:

The problem:
That UEFI needs a relocatable PE file, i.e. one that can function
regardless of the physical address it's loaded at (no virtual
addresses that early).
To build an ELF of that kind, the resources I've seen all build their
source with -fpic, and then use objcopy to convert the ELF into a
relocatable PE, with an embedded runtime self-relocator (akin to
load-time relocation, if I'm understanding correctly).

What Joel suggested seems to be the simplest option - see if not using
-fpic for _all_ of RTEMS' build system is fine. I think it might be
from some testing, but I'm not sure if this is conclusive since I need
to understand the specifics of the entire development process better.

So here's my understanding of the situation at the moment:

- librtems*.a is made up of object files, compiled without -fpic, and
that should be fine because I believe object files will use RIP
relative addressing code by default on x64 where it can, and leave the
rest for link-time relocations to handle. IF this is true, this works
perfectly for us because all memory accesses and jumps/calls are
relative.

- We can have a loader.c which acts as the core with the efi_main
function - compile it with -fpic into loader.so, and then link
loader.so, librtems*.a, and user-appXYZ.c together to form a
relocatable ELF, then convert it into a PE using objcopy. Note that
from what I can tell, the ELF generated from this still has type EXEC,
not DYN, according to readelf.

The concerns I have are about my assumptions; if GCC generates any
code that uses absolute addressing and that is resolved as a link-time
relocation, that could be problematic because the final relocatable PE
may not match up with the resolved absolute address.

My tests with a fake static archive library, and creating a PE have
been successful, but I'm unsure of how to trigger the relocation
behavior by the UEFI firmware (i.e. loading the UEFI image at an
address other than it's preferred one). One idea is to have a UEFI
application image that loads this test UEFI application image through
the "LoadImage" function UEFI provides as a service and then to use
QEMU's monitor / gdb inspection capabilities to see if the address the
image is loaded at genuinely changes.

If any of you have any resources, that'd be highly appreciated. Some
resources I'm using so far are:
- https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/
- https://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries/
- https://eli.thegreenplace.net/2012/01/03/understanding-the-x64-code-models

Sorry about the length of the email!

On Fri, May 25, 2018 at 10:51 PM, Joel Sherrill <joel at rtems.org> wrote:
>
>
> On Fri, May 25, 2018, 12:11 PM Amaan Cheval <amaan.cheval at gmail.com> wrote:
>>
>> Hey! Could you link me to some code that you used for the Deos setup
>> you mentioned?
>> My understanding is that the -shared option can link static archives
>> to create a "shared" library in the sense that it doesn't include the
>> usual crt0 runtime environment and whatnot, but the code within is
>> still position-dependent. Given that the PE image that EFI needs is
>> one that needs to be truly relocatable, this may not work - BUT, I've
>> only just noticed the ./gnuefi/reloc_x86_64.c file which seems to
>> handle some kinds of runtime relocations encoded within the converted
>> PE file, so maybe this will work after all. I'll continue to
>> investigate and let you know how it goes!
>
>
> Deos isn't a good example except that you can compile with -fPIC and put
> that code into a static library. Deos is a closed source Level A (man rated
> flight) ARINC 653 RTOS. It's boot process reads configuration information
> about each partition and associates .so's with each address space per the
> configuration. It can't change after that.
>
> The RTEMS exe is mostly linked as normal except to use some arguments to say
> some symbols are from a shared library.
>
> The base address of the exe is that of the provided virtual address space
> with .data and .bss in their respective spaces.
>
> And our entry point is in C so there is no asm before that. Great
> simplification.
>
>>
>> Regarding how TLS differs with PIC - could you elaborate? Is it
>> something we'll need to solve for if we go with the -fPIC option, or
>> is it something we need to keep in mind as a limitation, but isn't
>> really a blocker?
>
>
> I don't think PIC changes the TLS mechanism on arm or PowerPC but on i386,
> when not PIC the TLS base is in %gs and it's a subroutine call when PIC.
> Just as well for Deos since they assume an application won't change the
> segment register values.
>
> Other than this one TLS difference, it is a normal exe to me. They just
> magically provide their .so's before we run.
>>
>>
>> On Fri, May 25, 2018 at 10:13 PM, Joel Sherrill <joel at rtems.org> wrote:
>> >
>> >
>> > On Fri, May 25, 2018, 11:15 AM Amaan Cheval <amaan.cheval at gmail.com>
>> > wrote:
>> >>
>> >> Hey!
>> >>
>> >> Skippable details about how FreeBSD handles the UEFI boot process!
>> >>
>> >>
>> >> --------------------------------------------------------------------------------
>> >>
>> >> Having looked into it a bit more, my understanding of how FreeBSD
>> >> handles this process is:
>> >> - They build a two-stage bootloader for EFI, called boot1.efi and
>> >> loader.efi[1]
>> >> - loader.efi is an interactive prompt which may autoboot, or a "boot
>> >> kernelImg" command can be used to load the actual kernel
>> >> - The kernel is loaded as an ELF through helper functions. The
>> >> command_boot[2] function drives this:
>> >>   - In brief, through calls go through:
>> >>     command_boot -> mod_loadkld -> file_load ->
>> >> file_formats[i]->l_load (actually the loadfile function in
>> >> load_elf.c[3])
>> >>   - The loadfile function parses the program and section headers of
>> >> the ELF file (through more function detours that are not really
>> >> important)
>> >>   - Once the ELF has been loaded at the correct entry_addr that it
>> >> expects to be loaded at in memory, the l_exec[4] function is called,
>> >> which is actually elf64_exec in elf64_freebsd.c[5], at which hopefully
>> >> through trampolining magic, the control flow will transfer to the
>> >> kernel or ELF module
>> >>
>> >>
>> >>
>> >> --------------------------------------------------------------------------------
>> >>
>> >> What this means for RTEMS if we go with gnu-efi is essentially 2
>> >> options, given that the objcopy method of converting from ELF->PE
>> >> requires the ELF to be a position-independent shared library:
>> >>
>> >> - Using -fPIC to compile all of RTEMS, including the RTEMS user's
>> >> application code. This way we'd have librtemsbso.so, librtemscpu.so,
>> >> etc. which would then be linked into user_app.c through -fPIC and
>> >> -shared flags still, creating one singular hello.so, which can then
>> >> finally be converted into hello.efi and put on a FAT filesystem and
>> >> booted. This seems doable, but I'm fairly concerned about it further
>> >> complicating our build system and likely being quite singular in its
>> >> focus on EFI.
>> >
>> >
>> > I'm using PIC on the Deos BSP. RTEMS is still a .a and exes are linked
>> > with
>> > our static libraries and Deos .so.
>> >
>> > Unless the loader forces something, you can use PIC with no build system
>> > changes.
>> >
>> > note that thread local storage is different on i386 with and without
>> > PIC.
>> >>
>> >>
>> >> - The FreeBSD way of a (loader.efi) and a hello.exe (ELF64) put on
>> >> possibly the same partition on the FAT filesystem required for UEFI
>> >> application images anyway. The loader.efi can find the hello.exe file
>> >> through perhaps a config file it can read or by having a magic-name
>> >> like rtems.exe or something. This effectively means we need an ELF
>> >> dynamic linker / loader (akin to ld.so) within RTEMS' source. I think
>> >> using FreeBSD's code for this should be fine. One added benefit of
>> >> this method is that librtems* and user applications remain as ELF64s,
>> >> which in the future could also be used with Multiboot with a slightly
>> >> modified "loader" (i.e. one which generates the apt Multiboot magic
>> >> header, and boots the PC from 32-bit protected mode to 64-bit long
>> >> mode).
>> >>
>> >> I prefer the latter approach personally. If both of these seem too
>> >> complicated, we can of course go back to considering generating the PE
>> >> header format in ASM the way Linux distros use EFISTUB and the code
>> >> Chris shared (as I mentioned in my original blog post) for wimboot.
>> >> Those approaches may be significantly simpler in a sense, but may
>> >> limit how we use UEFI Services - I'm not sure about the details of
>> >> this yet - I can investigate if y'all aren't fond of the option I laid
>> >> down above.
>> >>
>> >> Let me know!
>> >>
>> >> [1]
>> >>
>> >> https://www.freebsd.org/cgi/man.cgi?query=loader&apropos=0&sektion=8&manpath=FreeBSD+11.1-RELEASE+and+Ports&arch=default&format=html
>> >> [2]
>> >>
>> >> https://github.com/freebsd/freebsd/blob/433bd38e3a0349f9f89f9d54594172c75b002b74/stand/common/boot.c#L53
>> >> [3]
>> >>
>> >> https://github.com/freebsd/freebsd/blob/d8596f6f687a64b994b065f3058155405dfc39db/stand/common/load_elf.c#L150
>> >> [4]
>> >>
>> >> https://github.com/freebsd/freebsd/blob/433bd38e3a0349f9f89f9d54594172c75b002b74/stand/common/boot.c#L107
>> >> [5]
>> >>
>> >> https://github.com/freebsd/freebsd/blob/d8596f6f687a64b994b065f3058155405dfc39db/stand/efi/loader/arch/amd64/elf64_freebsd.c#L93
>> >>
>> >> On Sun, May 20, 2018 at 10:52 PM, Joel Sherrill <joel at rtems.org> wrote:
>> >> >
>> >> >
>> >> > On Sun, May 20, 2018, 12:10 PM Amaan Cheval <amaan.cheval at gmail.com>
>> >> > wrote:
>> >> >>
>> >> >> On Sat, May 19, 2018 at 6:51 PM, Gedare Bloom <gedare at rtems.org>
>> >> >> wrote:
>> >> >> > On Fri, May 18, 2018 at 5:53 PM, Joel Sherrill <joel at rtems.org>
>> >> >> > wrote:
>> >> >> >>
>> >> >> >>
>> >> >> >> On Fri, May 18, 2018 at 3:24 PM, Amaan Cheval
>> >> >> >> <amaan.cheval at gmail.com>
>> >> >> >> wrote:
>> >> >> >>>
>> >> >> >>> Hi everyone!
>> >> >> >>>
>> >> >> >>> I've written a quick blog post summarizing the options I've
>> >> >> >>> considered
>> >> >> >>> to make the x86_64 port work with UEFI firmware - the primary
>> >> >> >>> winner
>> >> >> >>> seems to be in my eyes to use "gnu-efi" and to add support for
>> >> >> >>> the
>> >> >> >>> target "pei-x86-64" (aliased to "efi-app-x86_64") to
>> >> >> >>> "x86_64-rtems5-objcopy" in binutils. I've submitted a patch for
>> >> >> >>> this
>> >> >> >>> here[1].
>> >> >> >>
>> >> >> >>
>> >> >> >> That patch is quite simple so shouldn't be a problem if this is
>> >> >> >> the
>> >> >> >> direction
>> >> >> >> that gets consensus.
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> The blog post is here:
>> >> >> >>> https://blog.whatthedude.com/post/uefi-app-options/
>> >> >> >>>
>> >> >> >>> I'd appreciate all feedback (and please do let me know if I
>> >> >> >>> haven't
>> >> >> >>> provided enough context)!
>> >> >> >>>
>> >> >> >>> Specifically, some concerns I'd like to discuss are:
>> >> >> >>>
>> >> >> >>> - Does everyone agree with me on choosing gnu-efi + objcopy as
>> >> >> >>> our
>> >> >> >>> method of choice?
>> >> >> >>
>> >> >> >>
>> >> >> >> Does using gnu-efi add code that runs on the target? Can you
>> >> >> >> point
>> >> >> >> us to the files, if so.
>> >> >>
>> >> >> Sure. The files would run on the target, yes. These are the ones
>> >> >> listed here (as linked to in my blog post, perhaps without
>> >> >> sufficient
>> >> >> emphasis):
>> >> >> https://wiki.osdev.org/UEFI#Developing_with_GNU-EFI
>> >> >>
>> >> >> >>
>> >> >> >> Can you tell which approach FreeBSD takes?
>> >> >>
>> >> >> FreeBSD takes the gnu-efi approach I see as the "winner" here (also
>> >> >> a
>> >> >> link in the post):
>> >> >>
>> >> >>
>> >> >>
>> >> >> https://github.com/freebsd/freebsd/blob/996b0b6d81cf31cd8d58af5d8b45f0b4945d960d/stand/efi/loader/Makefile#L98-L1
>> >> >
>> >> >
>> >> > This is (no surprise) appropriately licensed and IMO the winning
>> >> > solution.
>> >> > Knowing it is what FreeBSD does makes it an easy choice.
>> >> >
>> >> > A comment in the readme mentions there is a i386 version of this code
>> >> > so
>> >> > that could be used to let pc386 boot from UEFI.
>> >> >
>> >> >>
>> >> >>
>> >> >> >>
>> >> >> >>>
>> >> >> >>> - How do we integrate gnu-efi into our build process? A part of
>> >> >> >>> the
>> >> >> >>> RSB, making sure the path to the libraries are in an exported
>> >> >> >>> variable? Or perhaps a part of the RTEMS kernel itself if the
>> >> >> >>> licenses
>> >> >> >>> are compatible (I don't see any on the project[2], only
>> >> >> >>> copyright
>> >> >> >>> notices within the source files of the release versions).
>> >> >> >>
>> >> >> >>
>> >> >> >> GNU-efi would be built like qemu or the device tree compiler
>> >> >> >> would
>> >> >> >> be my guess and x86_64-rtems toolset might add that to the
>> >> >> >> standard
>> >> >> >> set of tools. License on host tools being GPL isn't an issue.
>> >> >> >>
>> >> >> >
>> >> >> > It appears to be a standard 2-clause BSD released by Intel as
>> >> >> > specified in the README file of gnu-efi.
>> >> >> >
>> >> >> >>
>> >> >> >>>
>> >> >> >>> - Regardless of how we manage UEFI, do we require Multiboot
>> >> >> >>> support
>> >> >> >>> too? Multiboot drops us in a 32-bit protected mode environment,
>> >> >> >>> whereas 64-bit UEFI firmware will boot us into 64-bit long mode
>> >> >> >>> -
>> >> >> >>> this
>> >> >> >>> would mean the kernel would need to support separate code-paths
>> >> >> >>> for
>> >> >> >>> the 2 if we want to support both methods.
>> >> >> >>
>> >> >> >>
>> >> >> >> That's a good question. For GSoC, I think UEFI is fine and
>> >> >> >> perhaps a
>> >> >> >> ticket
>> >> >> >> under the general "modern PC support" ticket for multiboot
>> >> >> >> support.
>> >> >> >> Unless
>> >> >> >> that eliminates a LOT of PCs.
>> >> >> >>
>> >> >> >> I don't want you to spend all summer getting an image to boot
>> >> >> >> both
>> >> >> >> ways. Personally, I want you to have a working BSP one way. :)
>> >> >> > +1
>> >> >> >
>> >> >>
>> >> >> Noted, thanks!
>> >> >>
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> [1] https://www.sourceware.org/ml/binutils/2018-05/msg00197.html
>> >> >> >>> [2] https://sourceforge.net/projects/gnu-efi/
>> >> >> >>
>> >> >> >>
>> >> >> >> --joel
>> >> >> >>
>> >> >> >> _______________________________________________
>> >> >> >> devel mailing list
>> >> >> >> devel at rtems.org
>> >> >> >> http://lists.rtems.org/mailman/listinfo/devel



More information about the devel mailing list