UEFI printing issue: Print statements showing as non-printable characters in QEMU and serial output
I’m developing my first EFI application with a simple wait for input loop, but I’m encountering an issue where all my print statements display as non-printable characters instead of the expected text. This problem occurs in both QEMU and serial output.
The function I’m using is:
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"..")
The Print()
function exhibits the same behavior.
I’ve confirmed that I’m compiling with the -fshort-wchar
flag. My linking commands are:
ld -nostdlib -znocombreloc -T ${EFI_LIB_DIR}/elf_x86_64_efi.lds -shared -Bsymbolic -L ${EFI_LIB_DIR} ${EFI_LIB_DIR}/crt0-efi-x86_64.o $<TARGET_OBJECTS:efi_obj> -o efi.so -lefi -lgnuefi
objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .reloc --target=efi-app-x86_64 efi.so bootx64.efi
What could be causing this printing issue in my UEFI application, and how can I resolve it?
Brief Answer
The issue with non-printable characters in your UEFI print statements is likely related to string encoding or console initialization problems. Even with -fshort-wchar
flag set, the problem may stem from incorrect string formatting, improper console setup, or mismatched character encodings between your code and the UEFI environment.
Contents
- Common Causes of UEFI Printing Issues
- String Encoding and Format Solutions
- Console Initialization and Configuration
- QEMU and Serial Output Specific Fixes
- Alternative Printing Methods
- Complete Working Example
Common Causes of UEFI Printing Issues
The display of non-printable characters instead of expected text in UEFI applications typically stems from one of these primary issues:
- Character encoding mismatch: UEFI expects UTF-16 encoded strings, but your code might be generating UTF-8 or other encodings
- Improper string initialization: Missing null terminators or incorrect wide character string definitions
- Console not properly initialized: Attempting to print before the console output protocol is ready
- Linking issues: Missing or incorrectly ordered EFI libraries in your linking command
Important Note: Even though you’re using
-fshort-wchar
, which is correct for UEFI development, this doesn’t automatically solve all string-related problems. The flag only ensures thatwchar_t
is 2 bytes, but string handling still requires proper attention.
String Encoding and Format Solutions
Correct String Format
When working with UEFI, all string literals must be properly defined as wide character strings:
// Correct way to define wide character strings in UEFI
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"Hello, UEFI World!");
// Instead of:
// uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, "Hello"); // Wrong
String Termination
Ensure all strings are properly null-terminated:
// Good practice: explicitly null-terminate your strings
CHAR16 greeting[] = L"Hello, UEFI!\0";
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, greeting);
Character Selection
Start with simple ASCII characters before moving to Unicode:
// Begin with simple ASCII characters
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"System Ready");
// Avoid complex Unicode characters initially
Console Initialization and Configuration
Proper Console Setup
Before attempting to print, ensure your console is properly initialized:
// Initialize console output
InitializeLib(SystemTable, ImageHandle);
// Reset console to clean state
uefi_call_wrapper(SystemTable->ConOut->Reset, 2, SystemTable->ConOut, TRUE);
Console Mode Configuration
Set the console mode to ensure proper text display:
// Check and set console mode
UINT64 Mode;
uefi_call_wrapper(SystemTable->ConOut->QueryMode, 4, SystemTable->ConOut,
SystemTable->ConOut->Mode->Mode, &Mode, NULL);
// Set to standard text mode if needed
uefi_call_wrapper(SystemTable->ConOut->SetMode, 2, SystemTable->ConOut, 1);
QEMU and Serial Output Specific Fixes
QEMU Configuration
When running your UEFI application in QEMU, ensure you’re using the correct parameters to display output:
# Command to run QEMU with UEFI output visible
qemu-system-x86_64 -bios OVMF.fd -serial stdio -monitor none -display none
# Or for graphical output
qemu-system-x86_64 -bios OVMF.fd -serial stdio
Serial Output Configuration
If using serial output specifically, configure your terminal properly:
# For Linux/macOS
stty -F /dev/ttyS0 115200 cs8 -cstopb -parenb
# For Windows, use a terminal program like PuTTY or Tera Term
# Configure for 115200 baud, 8 data bits, no parity, 1 stop bit
Alternative Printing Methods
Using the EFI_PRINT Macro
Many UEFI development environments provide a PRINT macro that handles string encoding automatically:
// If available in your development environment
PRINT(L"Hello, UEFI World!\n");
Direct Simple Text Output Protocol Usage
Try using the protocol directly instead of through the wrapper:
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut = SystemTable->ConOut;
ConOut->OutputString(ConOut, L"Direct output test\n");
Custom Print Function
Create a simple wrapper function that handles string formatting:
VOID Print(IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut, IN CHAR16 *String) {
uefi_call_wrapper(ConOut->OutputString, 2, ConOut, String);
}
// Usage
Print(SystemTable->ConOut, L"Custom print function works!\n");
Complete Working Example
Here’s a minimal UEFI application with proper printing setup:
#include <efi.h>
#include <efilib.h>
EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
// Initialize the library
InitializeLib(ImageHandle, SystemTable);
// Reset console
uefi_call_wrapper(SystemTable->ConOut->Reset, 2, SystemTable->ConOut, TRUE);
// Test with simple ASCII string
Print(L"UEFI Application Starting...\n");
// Test with more complex output
CHAR16 message[] = L"System initialized successfully!\0";
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, message);
// Simple wait loop
while(1) {
gBS->Stall(1000000); // 1 second delay
}
return EFI_SUCCESS;
}
Compilation and Linking Commands
Ensure your compilation and linking process includes these flags:
# Compilation
gcc -fshort-wchar -fno-stack-protector -fno-stack-check -mno-red-zone -m64 -c your_code.c -o your_code.o
# Linking
ld -nostdlib -znocombreloc -T ${EFI_LIB_DIR}/elf_x86_64_efi.lds -shared -Bsymbolic -L ${EFI_LIB_DIR} \
${EFI_LIB_DIR}/crt0-efi-x86_64.o your_code.o -o your_code.so -lefi -lgnuefi
# Object copy
objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-x86_64 your_code.so bootx64.efi
Conclusion
To resolve your UEFI printing issue, focus on these key points:
- Verify all string literals are properly defined with the
L
prefix for wide characters - Ensure your console is properly initialized before attempting to print
- Start with simple ASCII characters to validate basic functionality
- Check your QEMU configuration to ensure output is properly displayed
- Consider using alternative printing methods if the standard approach continues to fail
The issue is most likely related to string encoding or console initialization rather than a fundamental problem with your approach. With these adjustments, your print statements should display correctly in both QEMU and serial output.