OSDev.org https://forum.osdev.org/ |
|
Calling Global Constructors in UEFI + Clang https://forum.osdev.org/viewtopic.php?f=13&t=57103 |
Page 1 of 1 |
Author: | Geometrian [ Wed Jan 31, 2024 5:31 pm ] |
Post subject: | Calling Global Constructors in UEFI + Clang |
Hi, Consider the following simple example, "main.cpp": Code: #include <Uefi.h> For simplicity, it can be directly compiled with:#include <Library/UefiLib.h> struct GlobalType { int var; GlobalType () { var=1; } }; GlobalType foo, bar, baz; EFI_STATUS EFIAPI efi_main( IN EFI_HANDLE /*image_handle*/, IN EFI_SYSTEM_TABLE* sys_table ) { sys_table->ConOut->OutputString( sys_table->ConOut, (CHAR16*)L"Var is: \"" ); CHAR16 str[3+1] = { (CHAR16)(foo.var+'0'), (CHAR16)(bar.var+'0'), (CHAR16)(baz.var+'0'), '\0' }; sys_table->ConOut->OutputString( sys_table->ConOut, str ); sys_table->ConOut->OutputString( sys_table->ConOut, (CHAR16*)L"\"" ); while (1) asm("hlt"); return 0; } Quote: #Path to EDK II (https://github.com/tianocore/edk2) root "edk2/" directory, for UEFI headers When you boot with the resulting "bootx64.efi", you'll get a screen that cheerfully saysUEFI_ROOT=/path/to/edk2/ clang -I${UEFI_ROOT}MdePkg/Include/ -I${UEFI_ROOT}MdePkg/Include/X64 -ffreestanding -fshort-wchar -target x86_64-pc-win32-coff -c main.cpp lld-link -subsystem:efi_application -dll -entry:efi_main -nodefaultlib -filealign:16 -out:bootx64.efi main.o Quote: Var is: "000" I.e., the global constructors for `foo`, `bar`, and `baz`did not run. That's not particularly surprising, since we didn't call them. I want to know how.The wiki has a page Calling Global Constructors which is pretty out-of-date, and GCC-centric anyway. I also found a blog post and more practically a previous thread where it was explained that one should walk through symbols `__init_array_start` to `__init_array_end`, but all of this seems to be for "-target i386-elf", which is not usable for a UEFI application. At any rate, I tried various things in these directions without really knowing what I was doing, and either failed to get it to link or it didn't work. How is it supposed to work? |
Author: | Octocontrabass [ Wed Jan 31, 2024 10:12 pm ] |
Post subject: | Re: Calling Global Constructors in UEFI + Clang |
Geometrian wrote: The wiki has a page Calling Global Constructors which is pretty out-of-date, and GCC-centric anyway. GCC and Clang both call global constructors the same way, it's just that a lot of targets have switched from using .init/.fini/.ctors/.dtors to using .init_array/.fini_array, and different parts of the page were written at different times. Geometrian wrote: I also found a blog post and more practically a previous thread where it was explained that one should walk through symbols `__init_array_start` to `__init_array_end`, but all of this seems to be for "-target i386-elf", which is not usable for a UEFI application. It could be usable for a UEFI application if you choose a target that uses .init_array for constructors. (GNU-EFI does this, but GNU-EFI is an ugly hack and I wouldn't recommend using it as an example.) Geometrian wrote: How is it supposed to work? Good question! It depends on which runtime Clang thinks you're using. For Microsoft's runtime, it's basically the same as .init_array, except you might need to define symbols to find the start and end of the list For MinGW, it works more like the backwards .ctors, and I think the linker defines all the symbols you need. You can use llvm-objdump to examine main.o and see which runtime Clang expects. |
Author: | nullplan [ Wed Jan 31, 2024 10:39 pm ] |
Post subject: | Re: Calling Global Constructors in UEFI + Clang |
I'm also no longer sure if the code I had provided back then isn't technically undefined behavior, since I am doing pointer arithmetic between two different arrays. Although I am comparing with !=, so that may work out. But it still might be better to do something like Code: typedef void initfunc_t(void); This way, nfunc is calculated using integer arithmetic, and the loop is definitely defined behavior, and I still avoid the casting orgy the musl initialization code has.extern initfunc_t *__init_array_start[], *__init_array_end[]; static void handle_init_array(void) { size_t nfunc = ((uintptr_t)__init_array_end - (uintptr_t)__init_array_start)/sizeof (initfunc_t *); for (initfunc_t **p = __init_array_start; p < __init_array_start + nfunc; p++) (*p)(); } But yeah, this code is not i386 specific, but it might be ELF specific. Which might work out for you if ELF is an intermediate target in building your PE UEFI application. |
Page 1 of 1 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |