I try to write a simple start uefi program for my kernel, but I have a problem linking everything together.
I want to call functions defined in my header files but this results in my uefi application to crash. (After the intended call the following print statements are not executed so I assume an crash.) I use gnu-efi but I am not sure if I am doing something gnu-efi specific or uefi general wrong. Everything compiles without warnings or errors and removing the "problem-function" causes the program to work (I can execute my efi file and the print statements work).
My main.c file:
#include "gnu-efi/inc/efi.h"
#include "gnu-efi/inc/efilib.h"
#include <inttypes.h>
#include "conversion.h"
EFI_STATUS
EFIAPI
efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
InitializeLib(ImageHandle, SystemTable);
CONVSTRING(numstr) = L"00000000000000000000";
ui64str(numstr, 5); //The line causing the problem
Print(L"Hello, world!\n");
Print(numstr);
Print(L"\n");
return EFI_SUCCESS;
}
My conversion.h file:
#include "gnu-efi/inc/efi.h"
#include<inttypes.h>
#define CONVSTRING(name) CHAR16 name[21]
// String has to be the length of 21
void ui64str(CHAR16* string, uint64_t number);
My conversion.c file:
#include<inttypes.h>
#include "gnu-efi/inc/efi.h"
#include "conversion.h"
void ui64str(CHAR16* string, uint64_t number)
{
// "clear" string
string = L"00000000000000000000";
return;
}
(If needed I use a batch-script to build every thing)
gcc main.c \
-c \
-fno-stack-protector \
-fpic \
-fshort-wchar \
-mno-red-zone \
-I gnu-efi/inc \
-I gnu-efi/inc/x86_64 \
-I . \
-DEFI_FUNCTION_WRAPPER \
-o tmp/main.o
ld tmp/main.o \
gnu-efi/x86_64/gnuefi/crt0-efi-x86_64.o \
-nostdlib \
-znocombreloc \
-T gnu-efi/gnuefi/elf_x86_64_efi.lds \
-shared \
-Bsymbolic \
-L gnu-efi/lib \
gnu-efi/x86_64/gnuefi/libgnuefi.a \
gnu-efi/x86_64/lib/libefi.a \
-o tmp/main.so
objcopy -j .text \
-j .sdata \
-j .data \
-j .dynamic \
-j .dynsym \
-j .rel \
-j .rela \
-j .reloc \
--target=efi-app-x86_64 \
tmp/main.so \
output/BOOTX64.EFI
Your build script does not actually build conversion.c
into an object file or link it. While you do textually include the header, a header is enough for a compiler to know declarations and prototypes, but it is not enough for the linker to actually link the definitions into the final binary.
Because of the way that you're linking, you never actually get a link-time Unresolved Reference error - ld
doesn't mind when compiling with -shared
1, and objcopy doesn't mind the unresolved reference either.
You'll need to ensure that you actually build and link conversion.c
. The simplest approach would be to add a second similar gcc
command that builds conversion.o
from the source in conversion.c
, and then add conversion.o
to your ld
command line.
1 My instinct (and possibly other readers' instinct) may be to assume that the EFI executable should be statically linked; this page explains the rationale for doing it this way - a shared, relocatable library is built and then objcopy'ed into a final EFI binary. This does seem a tad brittle however.