If you google around about how to build an osx kext you will find very few results, a few email messages saying not to bother or that it it impossible.
So what’s all the voodoo about. A few defines, some linker options, some compiler options. Also a special info.c file and Info.plist.
Note: if you want to sign your kernel module you will need to apply to apple for a special kernel module code signing certificate.
Setup basic cmake project. We need 3.8 or newer to use BUNDLE_EXTENSION for the .kext bundle type.
1 2 3 |
cmake_minimum_required (VERSION 3.8) project (example) |
Setup some variables for later. These are both optional if you use the default system sdk or you don’t want to code sign.
1 2 |
set(CODE_SIGN_ID "Developer ID Application: Your Name. (XYZZYYZYZY)") set(CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/) |
Add a debug preprocessor def. This is optional.
1 2 3 |
if(CMAKE_BUILD_TYPE MATCHES Debug) add_definitions(-DDEBUG) endif() |
Add the special preprocessor defines to access internal kernel structures.
1 2 3 4 5 6 7 |
add_definitions( -DKERNEL -DKERNEL_PRIVATE -DDRIVER_PRIVATE -DAPPLE -DNeXT ) |
Add include directories for osx kernel headers and private headers.
1 2 3 4 |
include_directories( ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/Kernel.framework/PrivateHeaders ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/Kernel.framework/Headers ) |
Add an executable bundle target.
1 2 3 4 5 6 7 |
add_executable( ${PROJECT_NAME} MACOSX_BUNDLE example.c example_info.c Info.plist ) |
Set the target bundle extension to “kext” and the plist file.
1 |
set_target_properties(${PROJECT_NAME} PROPERTIES BUNDLE_EXTENSION kext MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/Info.plist) |
Set a million compile options. These were taken from a minimal xcode project.
Note: The -mmacosx-version-min=10.10 can be removed or changed to support older versions of OSX.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
add_compile_options(${PROJECT_NAME} -x c -arch x86_64 -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -nostdinc -std=gnu99 -fmodules -gmodules -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module -fno-builtin -Wno-trigraphs -msoft-float -O0 -fno-common -mkernel -Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation -Wunreachable-code -Werror=deprecated-objc-isa-usage -Werror=objc-root-class -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -fasm-blocks -fstrict-aliasing -Wdeprecated-declarations -mmacosx-version-min=10.10 -Wno-sign-conversion -Winfinite-recursion -iquote ) |
Add the libraries required for the kernel module and linker options.
Note: The -mmacosx-version-min=10.10 can be removed or changed to support older versions of OSX.
1 2 3 4 5 6 7 8 9 10 11 |
target_link_libraries(${PROJECT_NAME} "-lkmodc++" "-lkmod" "-lcc_kext" "-arch x86_64" "-mmacosx-version-min=10.10" "-nostdlib" "-Xlinker -object_path_lto" "-Xlinker -export_dynamic" "-Xlinker -kext" ) |
Add a custom target to sign the kernel module. This can be skipped if code signing isn’t needed.
1 2 3 4 5 6 |
add_custom_command (TARGET ${PROJECT_NAME} POST_BUILD COMMENT "Code Signing Kext With: ${CODE_SIGN_ID}" VERBATIM COMMAND /usr/bin/codesign -s "${CODE_SIGN_ID}" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.kext" ) |
The example.c file:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <sys/kernel_types.h> #include <sys/systm.h> kern_return_t example_start(kmod_info_t * ki, void *d) { printf("Loaded example\n"); return KERN_SUCCESS; } kern_return_t example_stop(kmod_info_t *ki, void *d) { printf("example unloading.\n"); return KERN_SUCCESS; } |
The example_info.c File. This is usually generated by xcode during the build process.
It just sets the main entry points to example_start and example_stop
1 2 3 4 5 6 7 8 9 10 11 |
#include <mach/mach_types.h> extern kern_return_t _start(kmod_info_t *ki, void *data); extern kern_return_t _stop(kmod_info_t *ki, void *data); __private_extern__ kern_return_t example_start(kmod_info_t *ki, void *data); __private_extern__ kern_return_t example_stop(kmod_info_t *ki, void *data); __attribute__((visibility("default"))) KMOD_EXPLICIT_DECL(com.example, "1.0.0d1", _start, _stop) __private_extern__ kmod_start_func_t *_realmain = example_start; __private_extern__ kmod_stop_func_t *_antimain = example_stop; __private_extern__ int _kext_apple_cc = __APPLE_CC__ ; |
The complete cmake file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
cmake_minimum_required (VERSION 3.8) project (example) set(CODE_SIGN_ID "Developer ID Application: Your Name. (XYZZYYZYZY)") set(CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/) if(CMAKE_BUILD_TYPE MATCHES Debug) add_definitions(-DDEBUG) endif() add_definitions( -DKERNEL -DKERNEL_PRIVATE -DDRIVER_PRIVATE -DAPPLE -DNeXT ) include_directories( ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/Kernel.framework/PrivateHeaders ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/Kernel.framework/Headers ) add_executable( ${PROJECT_NAME} MACOSX_BUNDLE example.c example_info.c Info.plist ) set_target_properties(${PROJECT_NAME} PROPERTIES BUNDLE_EXTENSION kext MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/Info.plist) add_compile_options(${PROJECT_NAME} -x c -arch x86_64 -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -nostdinc -std=gnu99 -fmodules -gmodules -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module -fno-builtin -Wno-trigraphs -msoft-float -O0 -fno-common -mkernel -Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation -Wunreachable-code -Werror=deprecated-objc-isa-usage -Werror=objc-root-class -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -fasm-blocks -fstrict-aliasing -Wdeprecated-declarations -mmacosx-version-min=10.11 -Wno-sign-conversion -Winfinite-recursion -iquote ) # delete this to not code sign target_link_libraries(${PROJECT_NAME} "-lkmodc++" "-lkmod" "-lcc_kext" "-arch x86_64" "-mmacosx-version-min=10.11" "-nostdlib" "-Xlinker -object_path_lto" "-Xlinker -export_dynamic" "-Xlinker -kext" ) |
The magic Info.plist file. All references to “example” will need to match the binary and com.example items.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>BuildMachineOSBuild</key> <string>15G31</string> <key>CFBundleDevelopmentRegion</key> <string>en</string> <key>CFBundleExecutable</key> <string>example</string> <key>CFBundleIdentifier</key> <string>com.example</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>example</string> <key>CFBundlePackageType</key> <string>KEXT</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleSupportedPlatforms</key> <array> <string>MacOSX</string> </array> <key>CFBundleVersion</key> <string>1</string> <key>DTCompiler</key> <string>com.apple.compilers.llvm.clang.1_0</string> <key>DTPlatformBuild</key> <string>8A218a</string> <key>DTPlatformVersion</key> <string>GM</string> <key>DTSDKBuild</key> <string>16A300</string> <key>DTSDKName</key> <string>macosx10.12</string> <key>DTXcode</key> <string>0800</string> <key>DTXcodeBuild</key> <string>8A218a</string> <key>NSHumanReadableCopyright</key> <string>Copyright © 2017 Someone. All rights reserved.</string> <key>OSBundleLibraries</key> <dict> <key>com.apple.kpi.bsd</key> <string>8.0.0</string> <key>com.apple.kpi.libkern</key> <string>8.0.0</string> <key>com.apple.kpi.mach</key> <string>8.0.0</string> </dict> </dict> </plist> |
GitHub Repository with the example.