赞
踩
XNU(全称XNU's not Unix)是macOS及iOS操作系统的内核,由苹果公司开发。它是一个混合内核,分为两层:Mach层和BSD层。Mach层是系统底层,提供任务调度、进程间通信和内存管理等最基本的功能,还有IOKit框架和libkern,方便开发者将驱动插入Mach层。BSD层建立在Mach层之上,提供Unix系统调用、网络、文件系统和安全等高级功能。XNU内核的结构如下图所示:
(图片来源:Mac OS X And iOS Internals,引自macOS 进化史 | zhangferry的技术博客)
从Apple Open Source下载xnu-8019.61.5(我已上传到CSDN,也可以在CSDN上免费下载:https://download.csdn.net/download/2301_76160635/88683218),解压后的目录如下图所示:
要编译XNU内核,可参考这篇文章:XNU内核编译简要教程 | 老牛的技术田地。文章中提到的库可以从Apple Open Source下载,也可以从CSDN下载。我已上传到CSDN,将下载地址汇集到下表:
下载完成后,我们参考网上的教程,编译出XNU内核。我们现在对XNU内核还不熟悉,不知道这些目录是干什么的。这个时候,我们就要打开README.md,去查看详细信息。当我们刚拿到一个项目,感到茫然的时候,可以先打开README文件,初步地了解它。
README.md的内容如下:
- What is XNU?
- ===========
-
- XNU kernel is part of the Darwin operating system for use in macOS and iOS operating systems. XNU is an acronym for X is Not Unix.
- XNU is a hybrid kernel combining the Mach kernel developed at Carnegie Mellon University with components from FreeBSD and a C++ API for writing drivers called IOKit.
- XNU runs on x86_64 for both single processor and multi-processor configurations.
-
- XNU Source Tree
- ===============
-
- * `config` - configurations for exported apis for supported architecture and platform
- * `SETUP` - Basic set of tools used for configuring the kernel, versioning and kextsymbol management.
- * `EXTERNAL_HEADERS` - Headers sourced from other projects to avoid dependency cycles when building. These headers should be regularly synced when source is updated.
- * `libkern` - C++ IOKit library code for handling of drivers and kexts.
- * `libsa` - kernel bootstrap code for startup
- * `libsyscall` - syscall library interface for userspace programs
- * `libkdd` - source for user library for parsing kernel data like kernel chunked data.
- * `makedefs` - top level rules and defines for kernel build.
- * `osfmk` - Mach kernel based subsystems
- * `pexpert` - Platform specific code like interrupt handling, atomics etc.
- * `security` - Mandatory Access Check policy interfaces and related implementation.
- * `bsd` - BSD subsystems code
- * `tools` - A set of utilities for testing, debugging and profiling kernel.
-
- How to build XNU
- ================
-
- Building `DEVELOPMENT` kernel
- -----------------------------
-
- The xnu make system can build kernel based on `KERNEL_CONFIGS` & `ARCH_CONFIGS` variables as arguments.
- Here is the syntax:
-
- make SDKROOT=<sdkroot> ARCH_CONFIGS=<arch> KERNEL_CONFIGS=<variant>
-
- Where:
-
- * \<sdkroot>: path to macOS SDK on disk. (defaults to `/`)
- * \<variant>: can be `debug`, `development`, `release`, `profile` and configures compilation flags and asserts throughout kernel code.
- * \<arch> : can be valid arch to build for. (E.g. `X86_64`)
-
- To build a kernel for the same architecture as running OS, just type
-
- $ make
- $ make SDKROOT=macosx.internal
-
- Additionally, there is support for configuring architectures through `ARCH_CONFIGS` and kernel configurations with `KERNEL_CONFIGS`.
-
- $ make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=DEVELOPMENT
- $ make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS="RELEASE DEVELOPMENT DEBUG"
-
-
- Note:
- * By default, architecture is set to the build machine architecture, and the default kernel
- config is set to build for DEVELOPMENT.
-
-
- This will also create a bootable image, kernel.[config], and a kernel binary
- with symbols, kernel.[config].unstripped.
-
- To intall the kernel into a DSTROOT, use the `install_kernels` target:
-
- $ make install_kernels DSTROOT=/tmp/xnu-dst
-
- Hint:
- For a more satisfying kernel debugging experience, with access to all
- local variables and arguments, but without all the extra check of the
- DEBUG kernel, add something like:
- CFLAGS_DEVELOPMENTARM64="-O0 -g -DKERNEL_STACK_MULTIPLIER=2"
- CXXFLAGS_DEVELOPMENTARM64="-O0 -g -DKERNEL_STACK_MULTIPLIER=2"
- to your make command.
- Replace DEVELOPMENT and ARM64 with the appropriate build and platform.
-
-
- * To build with RELEASE kernel configuration
-
- make KERNEL_CONFIGS=RELEASE SDKROOT=/path/to/SDK
-
-
- Building FAT kernel binary
- --------------------------
-
- Define architectures in your environment or when running a make command.
-
- $ make ARCH_CONFIGS="X86_64" exporthdrs all
-
- Other makefile options
- ----------------------
-
- * $ make MAKEJOBS=-j8 # this will use 8 processes during the build. The default is 2x the number of active CPUS.
- * $ make -j8 # the standard command-line option is also accepted
- * $ make -w # trace recursive make invocations. Useful in combination with VERBOSE=YES
- * $ make BUILD_LTO=0 # build without LLVM Link Time Optimization
- * $ make BOUND_CHECKS=1 # enable -fbound-attributes for this build
- * $ make REMOTEBUILD=user@remotehost # perform build on remote host
- * $ make BUILD_JSON_COMPILATION_DATABASE=1 # Build Clang JSON Compilation Database
-
- The XNU build system can optionally output color-formatted build output. To enable this, you can either
- set the `XNU_LOGCOLORS` environment variable to `y`, or you can pass `LOGCOLORS=y` to the make command.
-
-
- Debug information formats
- =========================
-
- By default, a DWARF debug information repository is created during the install phase; this is a "bundle" named kernel.development.\<variant>.dSYM
- To select the older STABS debug information format (where debug information is embedded in the kernel.development.unstripped image), set the BUILD_STABS environment variable.
-
- $ export BUILD_STABS=1
- $ make
-
-
- Building KernelCaches
- =====================
-
- To test the xnu kernel, you need to build a kernelcache that links the kexts and
- kernel together into a single bootable image.
- To build a kernelcache you can use the following mechanisms:
-
- * Using automatic kernelcache generation with `kextd`.
- The kextd daemon keeps watching for changing in `/System/Library/Extensions` directory.
- So you can setup new kernel as
-
- $ cp BUILD/obj/DEVELOPMENT/X86_64/kernel.development /System/Library/Kernels/
- $ touch /System/Library/Extensions
- $ ps -e | grep kextd
-
- * Manually invoking `kextcache` to build new kernelcache.
-
- $ kextcache -q -z -a x86_64 -l -n -c /var/tmp/kernelcache.test -K /var/tmp/kernel.test /System/Library/Extensions
-
-
-
- Running KernelCache on Target machine
- =====================================
-
- The development kernel and iBoot supports configuring boot arguments so that we can safely boot into test kernel and, if things go wrong, safely fall back to previously used kernelcache.
- Following are the steps to get such a setup:
-
- 1. Create kernel cache using the kextcache command as `/kernelcache.test`
- 2. Copy exiting boot configurations to alternate file
-
- $ cp /Library/Preferences/SystemConfiguration/com.apple.Boot.plist /next_boot.plist
-
- 3. Update the kernelcache and boot-args for your setup
-
- $ plutil -insert "Kernel Cache" -string "kernelcache.test" /next_boot.plist
- $ plutil -replace "Kernel Flags" -string "debug=0x144 -v kernelsuffix=test " /next_boot.plist
-
- 4. Copy the new config to `/Library/Preferences/SystemConfiguration/`
-
- $ cp /next_boot.plist /Library/Preferences/SystemConfiguration/boot.plist
-
- 5. Bless the volume with new configs.
-
- $ sudo -n bless --mount / --setBoot --nextonly --options "config=boot"
-
- The `--nextonly` flag specifies that use the `boot.plist` configs only for one boot.
- So if the kernel panic's you can easily power reboot and recover back to original kernel.
- Creating tags and cscope
- ========================
- Set up your build environment and from the top directory, run:
- $ make tags # this will build ctags and etags on a case-sensitive volume, only ctags on case-insensitive
- $ make TAGS # this will build etags
- $ make cscope # this will build cscope database
- How to install a new header file from XNU
- =========================================
- To install IOKit headers, see additional comments in [iokit/IOKit/Makefile]().
- XNU installs header files at the following locations -
- a. $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
- b. $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders
- c. $(DSTROOT)/usr/include/
- d. $(DSTROOT)/usr/local/include/
- e. $(DSTROOT)/System/DriverKit/usr/include/
- f. $(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders
- `Kernel.framework` is used by kernel extensions.\
- The `System.framework`, `/usr/include` and `/usr/local/include` are used by user level applications. \
- `/System/DriverKit/usr/include` is used by userspace drivers. \
- The header files in framework's `PrivateHeaders` are only available for ** Apple Internal Development **.
-
- The directory containing the header file should have a Makefile that
- creates the list of files that should be installed at different locations.
- If you are adding the first header file in a directory, you will need to
- create Makefile similar to `xnu/bsd/sys/Makefile`.
-
- Add your header file to the correct file list depending on where you want
- to install it. The default locations where the header files are installed
- from each file list are -
-
- a. `DATAFILES` : To make header file available in user level -
- `$(DSTROOT)/usr/include`
- `$(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders`
-
- b. `DRIVERKIT_DATAFILES` : To make header file available to DriverKit userspace drivers -
- `$(DSTROOT)/System/DriverKit/usr/include`
-
- c. `PRIVATE_DATAFILES` : To make header file available to Apple internal in
- user level -
- `$(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders`
-
- d. `EMBEDDED_PRIVATE_DATAFILES` : To make header file available in user
- level for macOS as `EXTRA_DATAFILES`, but Apple internal in user level
- for embedded OSes as `EXTRA_PRIVATE_DATAFILES` -
- `$(DSTROOT)/usr/include` (`EXTRA_DATAFILES`)
- `$(DSTROOT)/usr/local/include` (`EXTRA_PRIVATE_DATAFILES`)
-
- d. `KERNELFILES` : To make header file available in kernel level -
- `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers`
- `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders`
-
- e. `PRIVATE_KERNELFILES` : To make header file available to Apple internal
- for kernel extensions -
- `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders`
-
- f. `MODULEMAPFILES` : To make module map file available in user level -
- `$(DSTROOT)/usr/include`
-
- g. `PRIVATE_MODULEMAPFILES` : To make module map file available to Apple
- internal in user level -
- `$(DSTROOT)/usr/local/include`
-
- The Makefile combines the file lists mentioned above into different
- install lists which are used by build system to install the header files. There
- are two types of install lists: machine-dependent and machine-independent.
- These lists are indicated by the presence of `MD` and `MI` in the build
- setting, respectively. If your header is architecture-specific, then you should
- use a machine-dependent install list (e.g. `INSTALL_MD_LIST`). If your header
- should be installed for all architectures, then you should use a
- machine-independent install list (e.g. `INSTALL_MI_LIST`).
-
- If the install list that you are interested does not exist, create it
- by adding the appropriate file lists. The default install lists, its
- member file lists and their default location are described below -
-
- a. `INSTALL_MI_LIST`, `INSTALL_MODULEMAP_MI_LIST` : Installs header and module map
- files to a location that is available to everyone in user level.
- Locations -
- $(DSTROOT)/usr/include
- Definition -
- INSTALL_MI_LIST = ${DATAFILES}
- INSTALL_MODULEMAP_MI_LIST = ${MODULEMAPFILES}
-
- b. `INSTALL_DRIVERKIT_MI_LIST` : Installs header file to a location that is
- available to DriverKit userspace drivers.
- Locations -
- $(DSTROOT)/System/DriverKit/usr/include
- Definition -
- INSTALL_DRIVERKIT_MI_LIST = ${DRIVERKIT_DATAFILES}
-
- c. `INSTALL_MI_LCL_LIST`, `INSTALL_MODULEMAP_MI_LCL_LIST` : Installs header and
- module map files to a location that is available for Apple internal in user level.
- Locations -
- $(DSTROOT)/usr/local/include
- Definition -
- INSTALL_MI_LCL_LIST =
- INSTALL_MODULEMAP_MI_LCL_LIST = ${PRIVATE_MODULEMAPFILES}
-
- d. `INSTALL_SF_MI_LCL_LIST` : Installs header file to a location that is available
- for Apple internal in user level.
- Locations -
- $(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders
- Definition -
- INSTALL_SF_MI_LCL_LIST = ${DATAFILES} ${PRIVATE_DATAFILES}
-
- e. `INSTALL_KF_MI_LIST` : Installs header file to location that is available
- to everyone for kernel extensions.
- Locations -
- $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
- Definition -
- INSTALL_KF_MI_LIST = ${KERNELFILES}
-
- f. `INSTALL_KF_MI_LCL_LIST` : Installs header file to location that is
- available for Apple internal for kernel extensions.
- Locations -
- $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders
- Definition -
- INSTALL_KF_MI_LCL_LIST = ${KERNELFILES} ${PRIVATE_KERNELFILES}
-
- g. `EXPORT_MI_LIST` : Exports header file to all of xnu (bsd/, osfmk/, etc.)
- for compilation only. Does not install anything into the SDK.
- Definition -
- EXPORT_MI_LIST = ${KERNELFILES} ${PRIVATE_KERNELFILES}
-
- If you want to install the header file in a sub-directory of the paths
- described in (1), specify the directory name using two variables
- `INSTALL_MI_DIR` and `EXPORT_MI_DIR` as follows -
-
- INSTALL_MI_DIR = dirname
- EXPORT_MI_DIR = dirname
-
- If you want to install the module map file in a sub-directory, specify the
- directory name using the variable `INSTALL_MODULEMAP_MI_DIR` as follows -
-
- INSTALL_MODULEMAP_MI_DIR = dirname
-
- A single header file can exist at different locations using the steps
- mentioned above. However it might not be desirable to make all the code
- in the header file available at all the locations. For example, you
- want to export a function only to kernel level but not user level.
-
- You can use C language's pre-processor directive (#ifdef, #endif, #ifndef)
- to control the text generated before a header file is installed. The kernel
- only includes the code if the conditional macro is TRUE and strips out
- code for FALSE conditions from the header file.
- Some pre-defined macros and their descriptions are -
- a. `PRIVATE` : If defined, enclosed definitions are considered System
- Private Interfaces. These are visible within xnu and
- exposed in user/kernel headers installed within the AppleInternal
- "PrivateHeaders" sections of the System and Kernel frameworks.
- b. `KERNEL_PRIVATE` : If defined, enclosed code is available to all of xnu
- kernel and Apple internal kernel extensions and omitted from user
- headers.
- c. `BSD_KERNEL_PRIVATE` : If defined, enclosed code is visible exclusively
- within the xnu/bsd module.
- d. `MACH_KERNEL_PRIVATE`: If defined, enclosed code is visible exclusively
- within the xnu/osfmk module.
- e. `XNU_KERNEL_PRIVATE`: If defined, enclosed code is visible exclusively
- within xnu.
- f. `KERNEL` : If defined, enclosed code is available within xnu and kernel
- extensions and is not visible in user level header files. Only the
- header files installed in following paths will have the code -
- $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
- $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders
- g. `DRIVERKIT`: If defined, enclosed code is visible exclusively in the
- DriverKit SDK headers used by userspace drivers.
- Module map file name convention
- ===============================
- In the simple case, a subdirectory of `usr/include` or `usr/local/include`
- can be represented by a standalone module. Where this is the case, set
- `INSTALL_MODULEMAP_MI_DIR` to `INSTALL_MI_DIR` and install a `module.modulemap`
- file there. `module.modulemap` is used even for private modules in
- `usr/local/include`; `module.private.modulemap` is not used. Caveat: in order
- to stay in the simple case, the module name needs to be exactly the same as
- the directory name. If that's not possible, then the following method will
- need to be applied.
-
- `xnu` contributes to the modules defined in CoreOSModuleMaps by installing
- module map files that are sourced from `usr/include/module.modulemap` and
- `usr/local/include/module.modulemap`. The naming convention for the `xnu`
- module map files are as follows.
-
- a. Ideally the module map file covers an entire directory. A module map
- file covering `usr/include/a/b/c` would be named `a_b_c.modulemap`.
- `usr/local/include/a/b/c` would be `a_b_c_private.modulemap`.
- b. Some headers are special and require their own module. In that case,
- the module map file would be named after the module it defines.
- A module map file defining the module `One.Two.Three` would be named
- `one_two_three.modulemap`.
-
- Conditional compilation
- =======================
-
- `xnu` offers the following mechanisms for conditionally compiling code:
-
- a. *CPU Characteristics* If the code you are guarding has specific
- characterstics that will vary only based on the CPU architecture being
- targeted, use this option. Prefer checking for features of the
- architecture (e.g. `__LP64__`, `__LITTLE_ENDIAN__`, etc.).
- b. *New Features* If the code you are guarding, when taken together,
- implements a feature, you should define a new feature in `config/MASTER`
- and use the resulting `CONFIG` preprocessor token (e.g. for a feature
- named `config_virtual_memory`, check for `#if CONFIG_VIRTUAL_MEMORY`).
- This practice ensures that existing features may be brought to other
- platforms by simply changing a feature switch.
- c. *Existing Features* You can use existing features if your code is
- strongly tied to them (e.g. use `SECURE_KERNEL` if your code implements
- new functionality that is exclusively relevant to the trusted kernel and
- updates the definition/understanding of what being a trusted kernel means).
-
- It is recommended that you avoid compiling based on the target platform. `xnu`
- does not define the platform macros from `TargetConditionals.h`
- (`TARGET_OS_OSX`, `TARGET_OS_IOS`, etc.).
-
-
- Debugging xnu
- =============
-
- By default, the kernel reboots in the event of a panic.
- This behavior can be overriden by the `debug` boot-arg -- `debug=0x14e` will cause a panic to wait for a debugger to attach.
- To boot a kernel so it can be debugged by an attached machine, override the `kdp_match_name` boot-arg with the appropriate `ifconfig` interface.
- Ethernet, Thunderbolt, and serial debugging are supported, depending on the hardware.
-
- Use LLDB to debug the kernel:
-
- ; xcrun -sdk macosx lldb <path-to-unstripped-kernel>
- (lldb) gdb-remote [<host-ip>:]<port>
-
- The debug info for the kernel (dSYM) comes with a set of macros to support kernel debugging.
- To load these macros automatically when attaching to the kernel, add the following to `~/.lldbinit`:
-
- settings set target.load-script-from-symbol-file true
-
- `tools/lldbmacros` contains the source for these commands.
- See the README in that directory for their usage, or use the built-in LLDB help with:
-
- (lldb) help showcurrentstacks

通过阅读README.md,我们可以知道每个目录的作用。osfmk目录中是Mach层代码,它是系统最核心的部分,源码分析应该从这里开始。我们进入osfmk目录,其内容如下图所示:
到这一级目录,我们发现没有README了,就只好猜一下这些子目录是干什么的。从名称看,kern和mach里应该有核心代码,machine里应该有操作硬件的代码,vm里应该有虚拟内存(内存管理)的代码,arm,arm64和x86_64里应该是机器相关的代码。
目前,我们只关注x86_64架构下的XNU内核,暂时忽略arm架构。于是,我们进入x86_64子目录,内容如下:
我们发现这里面有一堆.s的文件,这是汇编语言源代码。操作系统内核中,汇编语言用的地方不多,也就是启动代码和对性能要求高的代码用汇编写。我们发现有一个文件叫start.s,这就应该是启动代码了,之后将带大家分析。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。