Setup EFI Development environment on Mac OSX Sierra (10.12.X)

2017-07-10 05:55:39 +0200 - Written by Mikal Villa

Oh no! a lot of text. Well, luckly half of the post is troubleshooting. EFI development setup is easy :)

Okay, before starting this guide you should have some tools installed already.

  • Mac OS X. For this guide I run version 10.12.5 (16F73)
  • Xcode 8. For this guide I run version 8.3.2 (8E2002)
  • Homebrew. A package manager for Mac OS X. https://brew.sh

First of all visit https://opensource.apple.com/release/developer-tools-821.html

You need to download the cctools package. This package contains various tools to deal with Mach-O files, which is the default binary file format used by the XNU kernel. It is an equivalent to the GNU binutils package on the GNU OS. (directlink)

While you’re still at it, download the ld64 package as well. This package contains the dynamic linker ld, as well as other tools and libraries related to it. It replaces the old ld-classic from the cctools package that was not 64 bit-capable. (directlink)

You will also need some llvm headers, because of that you need to download the llvm source at http://releases.llvm.org/download.html#4.0 (directlink)

Just to not confuse with paths, set export EFIWORKSPACE=~/EfiWorkspace and make sure it exists with mkdir $EFIWORKSPACE

Buidling ld64

First of all, I recommend that you patch the project file to avoid errors it’s a great chance you get if not. The patch is pasted below.

--- ld64.xcodeproj/project.pbxproj2	2017-05-28 18:14:26.000000000 +0200
+++ ld64.xcodeproj/project.pbxproj	2017-05-28 18:29:44.000000000 +0200
@@ -1463,19 +1463,26 @@
 		F933D92409291AC90083EAC8 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_TREAT_WARNINGS_AS_ERRORS = NO;
+				HEADER_SEARCH_PATHS = "$(SRCROOT)/../cctools-895/include/";
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx.internal;
+				USER_HEADER_SEARCH_PATHS = "";
 			};
 			name = Debug;
 		};
 		F933D92509291AC90083EAC8 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_TREAT_WARNINGS_AS_ERRORS = NO;
+				HEADER_SEARCH_PATHS = "$(SRCROOT)/../cctools-895/include/";
+				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx.internal;
+				USER_HEADER_SEARCH_PATHS = "";
 			};
 			name = Release;
 		};
@@ -1500,9 +1507,12 @@
 		F9849FF810B5DE8E009E9878 /* Release-assert */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_TREAT_WARNINGS_AS_ERRORS = NO;
+				HEADER_SEARCH_PATHS = "$(SRCROOT)/../cctools-895/include/";
 				SDKROOT = macosx.internal;
+				USER_HEADER_SEARCH_PATHS = "";
 			};
 			name = "Release-assert";
 		};
ln -sf /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk $EFIWORKSPACE/ld64-274.2/macosx.internal

# Depending on if you want a debug or release build, choose one of the following:

# Debug
xcodebuild ARCHS="i386 x86_64" ONLY_ACTIVE_ARCH=NO -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -scheme libprunetrie -configuration Debug -destination 'platform=OS X,arch=x86_64' build

# Release
xcodebuild ARCHS="i386 x86_64" ONLY_ACTIVE_ARCH=NO -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -scheme libprunetrie -configuration Release -destination 'platform=OS X,arch=x86_64' build

# Mine ended up in /Users/mikalv/Library/Developer/Xcode/DerivedData/ld64-ffssbvtdnxqygzfcarplcybwxblo/Build/Products/Debug/libprunetrie.a

# Look at the last output from the command to find yours

# Copy it to a path that cctools makefile will look for libraries in
cp /Users/mikalv/Library/Developer/Xcode/DerivedData/ld64-ffssbvtdnxqygzfcarplcybwxblo/Build/Products/Debug/libprunetrie.a /usr/local/lib/libprunetrie.a

Running cmake on llvm

Next up, we need to run cmake on llvm, but we don’t need to compile it.

mkdir $EFIWORKSPACE/build-llvm
cd $EFIWORKSPACE/build-llvm
cmake $EFIWORKSPACE/../llvm-4.0.0.src
cp include/llvm/Support/DataTypes.h $EFIWORKSPACE/../cctools-895/include/llvm/Support/
cd $EFIWORKSPACE

Buidling cctools

cp $EFIWORKSPACE/cctools-895/include/llvm-c/Disassembler.h $EFIWORKSPACE/
rm -fr $EFIWORKSPACE/cctools-895/include/llvm-c
cp -r $EFIWORKSPACE/llvm-4.0.0.src/include/llvm-c cctools-895/include/llvm-c
cd $EFIWORKSPACE/cctools-895
make
cd efitools
make

# Finally we got the mtoc binary, now copy it to a directory in your PATH

cp mtoc.NEW /usr/local/bin/mtoc
cd $EFIWORKSPACE

Installing remaining dependencies

In this guide I assume you got /usr/local/bin in your path because of Homebrew. If not, this will not work at all.

Now we need to install, or upgrade the tools we need from Homebrew. brew install nasm acpica qemu

You can verify all tools are installed with checking their version.

mtoc
qemu-system-x86_64 --version
nasm –v
iasl –v

EDK II

Finally we’re done with dependencies and can start with the fun part. Since using git tags is too complicated for the EDK II developers, we have to sync via commit hashes. The one I’m using is f4d3ba87bb8f5d82d3b80532ea4c83b7bbca41c0

cd $EFIWORKSPACE
git clone https://github.com/tianocore/edk2.git
cd edk2
make -C BaseTools

# Optionally use the same version as me.
git checkout f4d3ba87bb8f5d82d3b80532ea4c83b7bbca41c0

source edksetup.sh

Now you can start your own projects and play around with EFI. Enjoy :)











Troubleshooting compiler errors

If you encounte

r errors, I collected a list below of some errors that you might have gotten.

The missing llvm/Support/DataTypes.h header file

=========== /Applications/Xcode.app/Contents/Developer/usr/bin/make all for libstuff =============
cc -std=c99 -Os -DLTO_SUPPORT -g -I../../include -Wall -D_MACH_I386_THREAD_STATUS_FPSTATE_LEGACY_FIELD_NAMES_ -D_ARCHITECTURE_I386_FPU_FPSTATE_LEGACY_FIELD_NAMES_    -c \
		-I/Developer/usr/local/include \
		-I/usr/local/include \
		-o ./lto.o ../lto.c
cc -Os -DLTO_SUPPORT -g -I../../include -Wall -D_MACH_I386_THREAD_STATUS_FPSTATE_LEGACY_FIELD_NAMES_ -D_ARCHITECTURE_I386_FPU_FPSTATE_LEGACY_FIELD_NAMES_    -c -o ./llvm.o ../llvm.c
In file included from ../llvm.c:6:
../../include/llvm-c/Disassembler.h:18:10: fatal error: 'llvm/Support/DataTypes.h' file not found
#include "llvm/Support/DataTypes.h"
         ^
1 error generated.
make[2]: *** [llvm.o] Error 1

Solution is: If you’re missing DataTypes.h you probably forgot to run cmake on llvm and copy the header generated. There is an DataTypes.h.cmake in that directory, which gets generated to DataTypes.h after running cmake.

The missing mach-o/prune_trie.h header file

cc -Os -DLTO_SUPPORT -DTRIE_SUPPORT -g -Wall -I. -I./../include -I.  -I/usr/local/include  -c \
		-o ./nmedit.o ./strip.c -DNMEDIT
./strip.c:50:10: fatal error: 'mach-o/prune_trie.h' file not found
#include <mach-o/prune_trie.h>
         ^
1 error generated.
make[1]: *** [nmedit.o] Error 1
make: *** [all] Error 1

Solution is:

cd /usr/include/mach-o
sudo wget https://gist.githubusercontent.com/mikalv/205beaae50d688c1be8816a4ac74cca8/raw/253348d8d558c7f627acef0832149bc32d6791d9/prune_trie.h

The missing libprunetrie.a library

c++   -o ./strip.NEW \
		./strip.private.o -L/usr/local/lib -lprunetrie -stdlib=libc++
ld: library not found for -lprunetrie
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[1]: *** [strip.NEW] Error 1
make: *** [all] Error 1

Solution is: You seem to have forgotten to copy the libprunetrie.a library to a directory the cctools makefile looks for it. On my Mac it worked fine after copying the file to /usr/local/lib/.

The missing mach-o/arm/reloc.h header file

In file included from /Users/mikalv/EfiWorkspace/ld64-274.2/src/other/PruneTrie.cpp:26:
/Users/mikalv/EfiWorkspace/ld64-274.2/src/abstraction/MachOFileAbstraction.hpp:591:10: error: 'mach-o/arm/reloc.h' file not found with <angled>
      include; use "quotes" instead
#include <mach-o/arm/reloc.h>
         ^~~~~~~~~~~~~~~~~~~~
         "mach-o/arm/reloc.h"
1 error generated.

** BUILD FAILED **

Solution is: Seems like it requires a header file found in cctools-895/include/mach-o/arm. The patch for the ld64.xcodeproj/project.pbxproj is pasted below.