Linking order of functions in VirtualBox + EFI32

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Linking order of functions in VirtualBox + EFI32

Post by kzinti »

I am working on a EFI loader. When I compile it in 64 bits mode (bootx64.efi), it works fine on qemu, virtualbox and my real computer. When I compile it in 32 bits mode (bootia32.efi), it only works if efi_main() is the first function in my (only) .cpp source file. If I put any function before it, it stops working. Here is the code:

Code: Select all

static EFI_SYSTEM_TABLE* efi;


extern "C" EFIAPI EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE* systemTable)
{
    efi = systemTable;

    printf("Kiznix EFI Application (efi_main) - %d bits.", sizeof(void*)*8);

    // Wait for a key press
    UINTN index;
    EFI_EVENT event = systemTable->ConIn->WaitForKey;
    systemTable->BootServices->WaitForEvent(1, &event, &index);

    return EFI_SUCCESS;
}



// TODO: If I move this function above efi_main(), it crashes on Virtual Box (32 bits).
// It works fine in 64 bits. No idea.
extern "C" int putchar(int i)
{
    CHAR16 c = (CHAR16)i;
    CHAR16 s[2] = { c, 0 };

    SIMPLE_TEXT_OUTPUT_INTERFACE* out = efi->ConOut;
    out->OutputString(out, s);

    return c;
}
I am using the mingw toolchain (x86_64-w64-mingw32-gcc) to compile, link as a shared library with "-nostdlib -e efi_main" and use "objcopy --target=efi-app-ia32" to properly set the PE header on the resulting executable.

Again, this works fine in 64 bits on 3 platforms and the order of the functions doesn't matter. I only have the issue in 32 bits in virtualbox. I do not have any other system/emulator that will even attempt to boot bootia32.efi.

Google returns nothing.

Any idea?
Last edited by kzinti on Thu Oct 22, 2015 11:34 pm, edited 2 times in total.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Linking order of functions in VirtualBox + EFI32

Post by kzinti »

Forgot to add: I have printf() compiled in a static library and it basically calls putchar() for output.

I also verified that when it does work, efi_main() is right at the start of the .text section. I guess I can just put efi_main() in its own section and make sure it's first... Still this is weird isn't it?
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Linking order of functions in VirtualBox + EFI32

Post by kzinti »

Oh I found this warning:
/usr/bin/i686-w64-mingw32-ld: warning: cannot find entry symbol efi_main; defaulting to 0000000062941000

That's the problem. Why is it not finding efi_main in 32 bits (but does in 64 bits)? Fun times...
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Linking order of functions in VirtualBox + EFI32

Post by kzinti »

OK here it is:

In 64 bits, efi_main's symbol stays "efi_main" in the final image.
In 32 bits, efi_main gets "decorated" to "_efi_main".

Fun times.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Linking order of functions in VirtualBox + EFI32

Post by kzinti »

Just found confirmation that this is expected behaviour (https://en.wikipedia.org/wiki/Name_mangling)
In the stdcall and fastcall mangling schemes, the function is encoded as _name@X and @name@X respectively, where X is the number of bytes, in decimal, of the argument(s) in the parameter list (including those passed in registers, for fastcall). In the case of cdecl, the function name is merely prefixed by an underscore.

Note that the 64-bit convention on Windows (Microsoft C) has no leading underscore. This difference may in some rare cases lead to unresolved externals when porting such code to 64 bits. For example, Fortran code can use 'alias' to link against a C method by name as follows:
Loving this monologue =)
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Linking order of functions in VirtualBox + EFI32

Post by jnc100 »

Glad you found the answer - yes, name mangling (on 32-bit) is one of the issues with using the MinGW tools for UEFI, however most UEFI systems are x86_64 compatible anyway, so if you use that instead it shouldn't be a problem. The other issue you may come across is that it defines some macros (like WINDOWS, WIN32 etc) which cause some third party libraries to think they are being built under windows, and therefore expect the presence of the full win32 sdk. You may have to edit some configure scripts/makefiles if trying to build things like zlib, libpng etc for your bootloader.

As an aside, the build commands in UEFI Bare Bones provide a way of setting the appropriate PE file headers without needing the separate objcopy step (which presumably needs a cross-compiled binutils?).

Regards,
John.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Linking order of functions in VirtualBox + EFI32

Post by kzinti »

I didn't need any cross-build, as the objcopy on my system already supported PE formats. But yes, I noticed the "-Wl,--subsystem,10" and now use that instead of invoking objcopy.

So far I haven't run into any issues with Windows definitions, but thanks for the heads up.

One issue I am struggling with right now is weak functions. I am defining weak functions in my libc implementation so that the consumer of libc (multiboot loader, efi loader, kernel, etc) can overwrite them. As an example, I declare puts() as a weak function. For some reason, MingW will not find that symbol at link time and complain about missing function "puts". This works fine with normal GCC.

I suspect it has to do with me putting libc objects into a library, I will play with the -whole-archive tonight and see where that gets me. But if you have any experience with this (weak symbols on MingW), please speak up =)
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Linking order of functions in VirtualBox + EFI32

Post by kzinti »

Looks like the issue is that weak symbols don't interact well with builtins. So if I define my own functions weak, everything works fine. If I declare puts() weak and use -fbuiltins, it won't resolve. If I do not use -fbuiltins, it works.

So, on MingW, make sure you don't enable builtins if you intend on declaring (some of) them as weak symbols. But with normal GCC, go wild.
Post Reply