summaryrefslogtreecommitdiff
path: root/doc/www.rodsbooks.com_efi-programming_hello.txt
diff options
context:
space:
mode:
Diffstat (limited to 'doc/www.rodsbooks.com_efi-programming_hello.txt')
-rw-r--r--doc/www.rodsbooks.com_efi-programming_hello.txt307
1 files changed, 307 insertions, 0 deletions
diff --git a/doc/www.rodsbooks.com_efi-programming_hello.txt b/doc/www.rodsbooks.com_efi-programming_hello.txt
new file mode 100644
index 0000000..22609c4
--- /dev/null
+++ b/doc/www.rodsbooks.com_efi-programming_hello.txt
@@ -0,0 +1,307 @@
+ Programming for EFI:
+ Creating a "Hello, World" Program
+
+ by Roderick W. Smith, [1]rodsmith@rodsbooks.com
+
+ Originally written: 5/3/2013
+
+ I'm a technical writer and consultant specializing in Linux
+ technologies. This Web page is provided free of charge and with no
+ annoying outside ads; however, I did take time to prepare it, and Web
+ hosting does cost money. If you find this Web page useful, please
+ consider making a small donation to help keep this site up and running.
+ Thanks!
+
+ Donate $1.00 Donate $2.50 Donate $5.00 Donate $10.00 Donate another
+ value
+ PayPal - The safer, easier way to pay online! Donate with PayPal
+ PayPal - The safer, easier way to pay online! Donate with PayPal
+ PayPal - The safer, easier way to pay online! Donate with PayPal
+ PayPal - The safer, easier way to pay online! Donate with PayPal
+ PayPal - The safer, easier way to pay online! Donate with PayPal
+ __________________________________________________________________
+
+ Note: This page is a sub-page of my Programming for EFI document. If a
+ Web search has brought you here, you may want to start at the
+ [2]introductory page.
+
+ The traditional first program for a new compiler or environment is
+ "Hello, World." I therefore present such a program for EFI, including
+ the program itself, a Makefile for the program, and instructions on how
+ to compile and run it.
+
+Creating the Program File
+
+ A "Hello, World" program for EFI demonstrates some of the unique
+ features of EFI programming. To begin, consider the program itself:
+#include <efi.h>
+#include <efilib.h>
+
+EFI_STATUS
+EFIAPI
+efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
+ InitializeLib(ImageHandle, SystemTable);
+ Print(L"Hello, world!\n");
+
+ return EFI_SUCCESS;
+}
+
+ If you want to try compiling the program itself, cut-and-paste the
+ preceding lines into a file called main.c; the Makefile presented
+ shortly assumes this name. Many of this program's features are similar
+ to those of a similar program for Linux, Windows, or other OSes and
+ environments; however, there are some significant differences, too:
+ * The program begins with two #include directives, which load EFI
+ header files from the GNU-EFI package. You should not normally
+ include regular C header files, such as stdlib.h, because most of
+ these header files define data types and functions that are used by
+ the C library. This library is not available in EFI, though, and
+ because of differences in compilation options, you can't easily
+ compile it into an EFI program. Instead, you must rely exclusively
+ on your development package's library. Also note that if you use
+ TianoCore's EDK II rather than GNU-EFI, you'll need to include
+ different (and usually more) header files than shown here.
+ * Instead of using an entry point function called main(), EFI
+ programs written with GNU-EFI use an entry point called efi_main().
+ This function takes two arguments, as shown in the sample program,
+ which point to the program's image and to the EFI system table,
+ respectively. (The EFI system table is the key to accessing most
+ EFI features, as described on the [3]Using EFI Services page.) If
+ you use the TianoCore EDK II toolkit, you can give the program's
+ entry point another name, but you must specify the entry point when
+ linking the program.
+ * The efi_main() function returns a value of type EFI_STATUS.
+ Possible return values are summarized in the [4]Phoenix EFI
+ documentation, among other places. The sample program always
+ returns EFI_SUCCESS, but a more complex program might return
+ another value.
+ * The definition of EFIAPI varies between platforms. It tells the
+ compiler to use EFI's calling conventions for the specified
+ function. Unfortunately for Linux programmers, EFI uses Microsoft's
+ application binary interface (ABI), rather than the System V (SysV)
+ ABI used by Linux, and therefore used by GCC by default. Functions
+ called from outside your own program, such as efi_main() and
+ functions you pass to the EFI (as drivers are likely to do, and as
+ you might do if you want to patch the EFI's service tables) must be
+ so identified. If you use the TianoCore toolkit, it uses the
+ Microsoft ABI internally, but GNU-EFI uses the SysV ABI internally.
+ * The call to InitializeLib() sets up some critical global variables
+ that are created by GNU-EFI and used to access the firmware's
+ features. Most importantly, the ST variable identifies the system
+ table, the BS variable refers to boot services, and RT refers to
+ runtime services. If you use the TianoCore EDK II, this call
+ doesn't exist, and the variables in question are generally referred
+ to as gST, gBS, and gRT, respectively.
+ * The EFI Print() call takes the place of the printf() function with
+ which you're probably familiar. You can use the two in a very
+ similar fashion, but this example program illustrates one
+ exception: Strings in EFI use a 16-bit encoding, so you must
+ precede string constants with the character L to denote a 16-bit
+ representation. When using string variables, they must normally be
+ defined as a CHAR16* type in order to display properly with Print()
+ or to be passed to most EFI functions. More advanced programs can
+ use common conversion specifiers with Print(), such as %s for
+ strings and %d for decimal numbers.
+
+ More complex programs will of course expose additional differences
+ between a C program written for most OSes and one written for EFI. Most
+ of these differences relate to library differences between EFI and
+ other environments.
+
+Creating the Makefile
+
+ If you were building a "Hello, World" program for Linux in a Linux
+ environment, you could compile it without a Makefile. Building the
+ program in Linux for EFI, though, is essentially a cross-compilation
+ operation. As such, it necessitates using unusual compilation and
+ linker options, as well as a post-linking operation to convert the
+ program into a form that the EFI will accept. Although you could type
+ all the relevant commands by hand, a Makefile helps a lot. Such a file
+ to build the preceding program file looks like this:
+ARCH = $(shell uname -m | sed s,i[3456789]86,ia32,)
+
+OBJS = main.o
+TARGET = hello.efi
+
+EFIINC = /usr/include/efi
+EFIINCS = -I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
+LIB = /usr/lib64
+EFILIB = /usr/lib64/gnuefi
+EFI_CRT_OBJS = $(EFILIB)/crt0-efi-$(ARCH).o
+EFI_LDS = $(EFILIB)/elf_$(ARCH)_efi.lds
+
+CFLAGS = $(EFIINCS) -fno-stack-protector -fpic \
+ -fshort-wchar -mno-red-zone -Wall
+ifeq ($(ARCH),x86_64)
+ CFLAGS += -DEFI_FUNCTION_WRAPPER
+endif
+
+LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared \
+ -Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS)
+
+all: $(TARGET)
+
+hello.so: $(OBJS)
+ ld $(LDFLAGS) $(OBJS) -o $@ -lefi -lgnuefi
+
+%.efi: %.so
+ objcopy -j .text -j .sdata -j .data -j .dynamic \
+ -j .dynsym -j .rel -j .rela -j .reloc \
+ --target=efi-app-$(ARCH) $^ $@
+
+ If you cut-and-paste this code into a text editor, be sure to convert
+ stretches of eight characters to tabs! Also, be aware that you may need
+ to adjust the EFIINC, LIB, and EFILIB variables to point to the
+ relevant portions of your GNU-EFI installation directory. EFIINC
+ should, of course, point to your GNU-EFI include files; LIB should
+ point to the directory that holds the libefi.a and libgnuefi.a files;
+ and EFILIB should point to the directory that holds the
+ crt0-efi-x86_64.o and elf_x86_64_efi.lds files (or equivalents for
+ another architecture). The Makefile shown here works on a Fedora 18
+ installation. On Ubuntu 13.04, both LIB and EFILIB must be set to
+ /usr/lib; and on Gentoo, they must both be set to /usr/lib64.
+
+ The CFLAGS line sets a number of options that are important for getting
+ a working EFI binary. Although some of these could be omitted or
+ changed for the simple "Hello, World" demonstration, they can be
+ important for larger programs:
+ * -fno-stack-protector--Stack protection isn't supported by EFI, so
+ there's no point in building a binary with this feature active.
+ * -fpic--EFI requires that code be position-independent, hence the
+ use of this option.
+ * -fshort-wchar--GCC defines the wchar_t type to be 32 bits by
+ default, but EFI requires it to be 16 bits for 16-bit strings to
+ work correctly.
+ * -fmno-red-zone--On x86-64 systems, the red zone is an area that
+ follows the stack pointer that can be used for temporary variables.
+ The EFI may modify this area, though, so it's not safe to use, and
+ you must compile EFI binaries with this option.
+ * -Wall--When developing EFI applications, you might want to pay
+ extra attention to compiler warnings, and this switch (which causes
+ warnings to be treated as errors) can help.
+ * -DEFI_FUNCTION_WRAPPER--This option is required on the x86-64
+ platform, but is not defined on the 32-bit x86 platform. It relates
+ to the calling conventions for EFI functions, described on the
+ [5]Using EFI Services page.
+
+ Linker flags are defined in LDFLAGS, of course. They have the following
+ effects:
+ * -nostdlib--An EFI application should not be linked against standard
+ libraries, and this argument accomplishes this goal.
+ * -nocombreloc--This argument causes the linker to not combine
+ relocation sections.
+ * -T $(EFI_LDS)--To create an EFI binary, a non-standard linker
+ script must be used, and this option tells ld where to find it.
+ * -shared--Even with GNU-EFI's new linker script, ld can't create the
+ final executable. Instead, it creates a shared library, which is
+ subsequently turned into the final binary.
+ * -Bsymbolic--This option causes references to global symbols to be
+ bound to the definitions within the shared library.
+
+ When ld finishes its work, the result is a file called hello.so, which
+ is technically a shared library. To create an EFI executable, the
+ Makefile calls the objcopy program, which copies the code needed from
+ the library to create an EFI application.
+
+ If you use TianoCore EDK II, many of these options will be different.
+ One extremely important difference is the inclusion of the
+ -DEFIAPI=__attribute__((ms_abi)) GCC flag, which causes the binary to
+ be built using Microsoft's ABI. The linking process is also different;
+ the TianoCore EDK II includes its own program, called GenFw, that
+ builds the final binary instead of objcopy. TianoCore is designed
+ around a build process that doesn't use make, so you must either use
+ TianoCore's own build process or design a Makefile to mimic it. I don't
+ describe either approach here, so you should consult TianoCore's
+ documentation for the first option. If you want to use TianoCore with
+ make, check [6]rEFInd, which uses this approach, as a model.
+
+Compiling and Running the Program
+
+ Compiling the program is simple: Type make. The result should be
+ generation of intermediate files and the final hello.efi program file
+ of about 46KiB on a 64-bit system. If you encounter errors, you'll have
+ to fix them yourself. Be sure that your Makefile uses tabs where
+ necessary, and check the locations of the header and library files, as
+ already described. Note that the build process described here results
+ in a program file for the architecture you're using. If you build on a
+ 64-bit system, the binary won't work on a 32-bit computer, and
+ vice-versa.
+
+ The best way to run the program is likely to be from an EFI shell. You
+ can download a binary from the TianoCore site, for both [7]64-bit
+ (x86-64) and [8]32-bit (x86) platforms. Rename these files as
+ shellx64.efi or shellia32.efi, respectively, and place them in the root
+ directory of your EFI System Partition (ESP). Some ESPs and boot
+ managers, such as [9]rEFInd and [10]gummiboot, recognize this name and
+ location as special, and will enable you to launch a shell when one
+ exists there. Other boot managers, such as GRUB, may require explicit
+ configuration to launch an EFI shell. Treat the shell like an OS's boot
+ loader. For instance, in GRUB 2 you might create an entry in
+ /etc/grub.d/40_custom like the following:
+menuentry "EFI shell" {
+ insmod part_gpt
+ insmod chain
+ set root='(hd0,gpt1)'
+ chainloader /shellx64.efi
+}
+
+ Note: If your computer boots with [11]Secure Boot active, you should
+ disable Secure Boot for your initial tests, since launching your own
+ applications on such a computer complicates matters.
+
+ Details will vary depending on your installation, though. Once you've
+ made these changes, use update-grub or grub-mkconfig to re-create your
+ grub.cfg file with the new entry to launch the EFI shell.
+
+ When you launch your EFI shell, you should type fs0: to change to the
+ first filesystem, which is normally the ESP. You can then type
+ hello.efi to launch the program. You should see its output, as in:
+Shell> fs0:
+
+fs0:\> hello.efi
+Hello, world!
+
+fs0:\> exit
+
+ If the program hangs or otherwise misbehaves, you may need to review
+ the code and build process. Unfortunately, debugging EFI applications
+ can be tedious, because the usual debugging tools don't work with them.
+ I find that using a virtual machine can help. VirtualBox, for instance,
+ supports EFI, so it's possible to install Linux under EFI on VirtualBox
+ and use it for testing EFI applications compiled in the host
+ environment. This procedure at least obviates the need to re-start your
+ editor or IDE after every test of your program. Ubuntu and Linux Mint
+ work well in this role because they both boot very quickly, which can
+ speed things up if you need to make a small but quick change to be
+ tested immediately.
+ __________________________________________________________________
+
+ [12]Go on to "Using EFI Services"
+
+ [13]Return to the "Programming for EFI" main page
+ __________________________________________________________________
+
+ copyright © 2013 by Roderick W. Smith
+
+ If you have problems with or comments about this Web page, please
+ e-mail me at [14]rodsmith@rodsbooks.com. Thanks.
+
+ [15]Return to my main Web page.
+
+References
+
+ 1. mailto:rodsmith@rodsbooks.com
+ 2. http://www.rodsbooks.com/efi-programming/index.html
+ 3. http://www.rodsbooks.com/efi-programming/efi_services.html
+ 4. http://wiki.phoenix.com/wiki/index.php/EFI_STATUS
+ 5. http://www.rodsbooks.com/efi-programming/efi_services.html
+ 6. http://www.rodsbooks.com/refind/
+ 7. https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/EdkShellBinPkg/FullShell/X64/Shell_Full.efi
+ 8. https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/EdkShellBinPkg/FullShell/Ia32/Shell_Full.efi
+ 9. http://www.rodsbooks.com/refind/
+ 10. http://freedesktop.org/wiki/Software/gummiboot
+ 11. http://www.rodsbooks.com/efi-bootloaders/secureboot.html
+ 12. http://www.rodsbooks.com/efi-programming/efi_services.html
+ 13. http://www.rodsbooks.com/efi-programming/index.html
+ 14. mailto:rodsmith@rodsbooks.com
+ 15. http://www.rodsbooks.com/