Category Archives: kernel

Building an Apple OSX Kernel Module With CMake – C/C++

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.

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.

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.

if(CMAKE_BUILD_TYPE MATCHES Debug)
        add_definitions(-DDEBUG)
endif()

Add the special preprocessor defines to access internal kernel structures.

add_definitions(
        -DKERNEL
        -DKERNEL_PRIVATE
        -DDRIVER_PRIVATE
        -DAPPLE
        -DNeXT
)

Add include directories for osx kernel headers and private headers.

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.

add_executable(
        ${PROJECT_NAME}
        MACOSX_BUNDLE
        example.c
        example_info.c
        Info.plist
)

Set the target bundle extension to “kext” and the plist file.

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.

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.

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.

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:

#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

#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.

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.

<?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.