介绍使用 llvm clang 编译器编译 Linux 内核的过程,内核头文件原理和生成 clangd 语言服务器所需的编译命令 JSON 文件的过程。
内核版本 #
内核支持使用 gcc 和 llvm 进行编译:https://docs.kernel.org/kbuild/llvm.html
内核使用的 C 语言标准:
- 5.10 内核使用的 C 标准:-std=gnu89;
- 最新内核使用的 C 标准:-std=gnu11;
ia64 和 x86_64 是不同的架构:
- ia64 是 intel 64 bit Itanium architecture; (intel 专属);
- x86_64 是 x86 兼容和扩展的架构;
内核
arch/x86
目录是 融合 x86 和 x86_64 架构的。
内核相关的包 #
Ubuntu 内核相关的包:
- linux-image-6.5.0-42-generic
- linux-headers-6.5.0-42,linux-headers-6.5.0-42-generic: 内核头文件 /usr/src/linux-headers-6.5.0-44
- linux-libc-dev: 内核导出到用户空间的头文件(也可以通过
make headers_install
来从内核头文件源码来安装) - linux-modules-6.5.0-42-generic:内核 module /lib/modules/6.5.0-44-generic/
- linux-tools-6.5.0-42,linux-tools-6.5.0-42-generic,linux-tools-common,linux-toools-generic
root@lima-dev2:/home/alizj.linux# apt list --installed linux*
Listing... Done
linux-base/mantic,now 4.5ubuntu9 all [installed,automatic]
linux-headers-6.5.0-42-generic/now 6.5.0-42.42 arm64 [installed,local]
linux-headers-6.5.0-42/now 6.5.0-42.42 all [installed,local]
linux-headers-6.5.0-44-generic/mantic-updates,mantic-security,now 6.5.0-44.44 arm64 [installed,automatic]
linux-headers-6.5.0-44/mantic-updates,mantic-security,now 6.5.0-44.44 all [installed,automatic]
linux-headers-generic/mantic-updates,mantic-security,now 6.5.0.44.44 arm64 [installed,automatic]
linux-headers-virtual/mantic-updates,mantic-security,now 6.5.0.44.44 arm64 [installed,automatic]
linux-image-6.5.0-42-generic/now 6.5.0-42.42 arm64 [installed,local]
linux-image-6.5.0-44-generic/mantic-updates,mantic-security,now 6.5.0-44.44 arm64 [installed,automatic]
linux-image-virtual/mantic-updates,mantic-security,now 6.5.0.44.44 arm64 [installed,automatic]
linux-libc-dev/mantic-updates,mantic-security,now 6.5.0-44.44 arm64 [installed,automatic]
linux-modules-6.5.0-42-generic/now 6.5.0-42.42 arm64 [installed,local]
linux-modules-6.5.0-44-generic/mantic-updates,mantic-security,now 6.5.0-44.44 arm64 [installed,automatic]
linux-tools-6.5.0-42-generic/now 6.5.0-42.42 arm64 [installed,local]
linux-tools-6.5.0-42/now 6.5.0-42.42 arm64 [installed,local]
linux-tools-6.5.0-44-generic/mantic-updates,mantic-security,now 6.5.0-44.44 arm64 [installed,automatic]
linux-tools-6.5.0-44/mantic-updates,mantic-security,now 6.5.0-44.44 arm64 [installed,automatic]
linux-tools-common/mantic-updates,mantic-security,now 6.5.0-44.44 all [installed]
linux-tools-generic/mantic-updates,mantic-security,now 6.5.0.44.44 arm64 [installed]
linux-virtual/mantic-updates,mantic-security,now 6.5.0.44.44 arm64 [installed]
root@lima-dev2:/home/alizj.linux# uname -r
6.5.0-44-generic
root@lima-dev2:/home/alizj.linux# ls -l /lib/modules/6.5.0-44-generic/
total 1836
lrwxrwxrwx 1 root root 39 Jun 7 20:59 build -> /usr/src/linux-headers-6.5.0-44-generic
drwxr-xr-x 2 root root 4096 Jun 7 20:59 initrd
drwxr-xr-x 11 root root 4096 Jul 14 06:06 kernel
-rw-r--r-- 1 root root 172794 Jul 14 06:06 modules.alias
-rw-r--r-- 1 root root 184116 Jul 14 06:06 modules.alias.bin
-rw-r--r-- 1 root root 20700 Jun 7 20:59 modules.builtin
-rw-r--r-- 1 root root 12314 Jul 14 06:06 modules.builtin.alias.bin
-rw-r--r-- 1 root root 22957 Jul 14 06:06 modules.builtin.bin
-rw-r--r-- 1 root root 121509 Jun 7 20:59 modules.builtin.modinfo
-rw-r--r-- 1 root root 105090 Jul 14 06:06 modules.dep
-rw-r--r-- 1 root root 145774 Jul 14 06:06 modules.dep.bin
-rw-r--r-- 1 root root 271 Jul 14 06:06 modules.devname
-rw-r--r-- 1 root root 309409 Jun 7 20:59 modules.order
-rw-r--r-- 1 root root 940 Jul 14 06:06 modules.softdep
-rw-r--r-- 1 root root 343241 Jul 14 06:06 modules.symbols
-rw-r--r-- 1 root root 399893 Jul 14 06:06 modules.symbols.bin
drwxr-xr-x 2 root root 4096 Jul 14 06:06 vdso
root@lima-dev2:/home/alizj.linux# ls -l /usr/src/linux-headers-6.5.0-44-generic/
total 2076
lrwxrwxrwx 1 root root 39 Jun 7 20:59 Documentation -> ../linux-headers-6.5.0-44/Documentation
lrwxrwxrwx 1 root root 32 Jun 7 20:59 Kbuild -> ../linux-headers-6.5.0-44/Kbuild
lrwxrwxrwx 1 root root 33 Jun 7 20:59 Kconfig -> ../linux-headers-6.5.0-44/Kconfig
-rw-r--r-- 1 root root 71929 Jun 7 20:59 Makefile
-rw-r--r-- 1 root root 2021552 Jun 7 20:59 Module.symvers
drwxr-xr-x 3 root root 4096 Jul 14 06:06 arch
lrwxrwxrwx 1 root root 31 Jun 7 20:59 block -> ../linux-headers-6.5.0-44/block
lrwxrwxrwx 1 root root 31 Jun 7 20:59 certs -> ../linux-headers-6.5.0-44/certs
lrwxrwxrwx 1 root root 32 Jun 7 20:59 crypto -> ../linux-headers-6.5.0-44/crypto
lrwxrwxrwx 1 root root 33 Jun 7 20:59 drivers -> ../linux-headers-6.5.0-44/drivers
lrwxrwxrwx 1 root root 28 Jun 7 20:59 fs -> ../linux-headers-6.5.0-44/fs
drwxr-xr-x 4 root root 4096 Jul 14 06:06 include
lrwxrwxrwx 1 root root 30 Jun 7 20:59 init -> ../linux-headers-6.5.0-44/init
lrwxrwxrwx 1 root root 34 Jun 7 20:59 io_uring -> ../linux-headers-6.5.0-44/io_uring
lrwxrwxrwx 1 root root 29 Jun 7 20:59 ipc -> ../linux-headers-6.5.0-44/ipc
drwxr-xr-x 2 root root 4096 Jul 14 06:06 kernel
lrwxrwxrwx 1 root root 29 Jun 7 20:59 lib -> ../linux-headers-6.5.0-44/lib
lrwxrwxrwx 1 root root 28 Jun 7 20:59 mm -> ../linux-headers-6.5.0-44/mm
lrwxrwxrwx 1 root root 29 Jun 7 20:59 net -> ../linux-headers-6.5.0-44/net
lrwxrwxrwx 1 root root 39 Jun 7 20:59 rust -> ../linux-lib-rust-6.5.0-44-generic/rust
lrwxrwxrwx 1 root root 33 Jun 7 20:59 samples -> ../linux-headers-6.5.0-44/samples
drwxr-xr-x 8 root root 12288 Jul 14 06:06 scripts
lrwxrwxrwx 1 root root 34 Jun 7 20:59 security -> ../linux-headers-6.5.0-44/security
lrwxrwxrwx 1 root root 31 Jun 7 20:59 sound -> ../linux-headers-6.5.0-44/sound
drwxr-xr-x 3 root root 4096 Jul 14 06:06 tools
lrwxrwxrwx 1 root root 32 Jun 7 20:59 ubuntu -> ../linux-headers-6.5.0-44/ubuntu
lrwxrwxrwx 1 root root 29 Jun 7 20:59 usr -> ../linux-headers-6.5.0-44/usr
lrwxrwxrwx 1 root root 30 Jun 7 20:59 virt -> ../linux-headers-6.5.0-44/virt
root@lima-dev2:/home/alizj.linux# ls -l /usr/src/linux-headers-6.5.0-44
total 184
drwxr-xr-x 13 root root 4096 Jul 14 06:05 Documentation
-rw-r--r-- 1 root root 2592 Jun 7 20:59 Kbuild
-rw-r--r-- 1 root root 580 Jun 7 20:59 Kconfig
-rw-r--r-- 1 root root 71923 Jun 7 20:59 Makefile
drwxr-xr-x 24 root root 4096 Jul 14 06:05 arch
drwxr-xr-x 3 root root 4096 Jul 14 06:05 block
drwxr-xr-x 2 root root 4096 Jul 14 06:05 certs
drwxr-xr-x 4 root root 4096 Jul 14 06:05 crypto
drwxr-xr-x 141 root root 4096 Jul 14 06:05 drivers
drwxr-xr-x 81 root root 4096 Jul 14 06:05 fs
drwxr-xr-x 31 root root 4096 Jul 14 06:05 include
drwxr-xr-x 2 root root 4096 Jul 14 06:06 init
drwxr-xr-x 2 root root 4096 Jul 14 06:06 io_uring
drwxr-xr-x 2 root root 4096 Jul 14 06:06 ipc
drwxr-xr-x 21 root root 4096 Jul 14 06:06 kernel
drwxr-xr-x 21 root root 4096 Jul 14 06:06 lib
drwxr-xr-x 6 root root 4096 Jul 14 06:06 mm
drwxr-xr-x 73 root root 4096 Jul 14 06:06 net
drwxr-xr-x 2 root root 4096 Jul 14 06:06 rust
drwxr-xr-x 41 root root 4096 Jul 14 06:06 samples
drwxr-xr-x 17 root root 12288 Jul 14 06:06 scripts
drwxr-xr-x 14 root root 4096 Jul 14 06:06 security
drwxr-xr-x 27 root root 4096 Jul 14 06:06 sound
drwxr-xr-x 42 root root 4096 Jul 14 06:06 tools
drwxr-xr-x 5 root root 4096 Jul 14 06:06 ubuntu
drwxr-xr-x 3 root root 4096 Jul 14 06:06 usr
drwxr-xr-x 4 root root 4096 Jul 14 06:06 virt
root@lima-dev2:/home/alizj.linux# apt list --installed linux*libc*
linux-libc-dev/mantic-updates,mantic-security,now 6.5.0-44.44 arm64 [installed,automatic]
root@lima-dev2:/home/alizj.linux# dpkg -L linux-libc-dev |cut -d / -f 1-4|sort|uniq|sort
/.
/usr
/usr/include
/usr/include/aarch64-linux-gnu
/usr/include/asm-generic
/usr/include/drm
/usr/include/linux
/usr/include/misc
/usr/include/mtd
/usr/include/rdma
/usr/include/scsi
/usr/include/sound
/usr/include/video
/usr/include/xen
/usr/share
/usr/share/doc
内核头文件 #
内核不能调用用户空间头文件和库文件(如 C 库等), 主要原因是速度和大小限制。但内核也实现了大量 libc 函数, 位于内核根目录的 lib 目录下, 例如 lib/string.c
定义了字符串操作函数, 调用时需要 include 对应的头文件 <linux/string.h>
.
内核头文件不能包含内核之外的其它头文件:
- 基本的内核头文件:位于内核根的
include/
目录下, 如#include <linux/i2c.h>
对应的头文件是include/linux/i2c.h
; - 平台架构相关的头文件:位于
arch/<arch>/include/asm
和arch/<arch>/include/uapi/asm
下, 包含这些头文件时, 以 asm/ 或 uapi/asm 为前缀, 如#include <asm/ioctl.h>
asm/
: 内核接口部分, 只被内核内部使用;uapi/asm/
: 内核暴露给用户接口部分, 可以被用户程序,如 glibc 使用;
include/asm-generic/
和include/uapi/asm-generic/
目录:- 为所有平台公用的头文件, 使用方式:
#include <asm-generic/irq.h>
; - 原则上不直接包含 asm-generic 头文件, 而是通过上面
arch
相关的头文件来包含它们;
- 为所有平台公用的头文件, 使用方式:
arch/<arch>/include/generated/asm/
或arch/<arch>/include/generated/uapi/asm/
:- 根据
include/asm-generic/
目录下的头文件生成的头文件
- 根据
usr/include/
为内核暴露给用户空间的头文件, 如编译 glibc 时使用:
执行 make headers_install
命令来生成导出到用户空间的头文件。
示例: arch/ia64/include/asm/types.h
包含了架构无关的 include/uapi/asm-generic/int-ll64.h
和架构相关的 arch/ia64/include/uapi/asm/types.h
;
// /Users/zhangjun/codes/kernel/linux-5.10.y/include/uapi/linux/bpf.h
#include <linux/types.h>
#include <linux/bpf_common.h>
// /Users/zhangjun/codes/kernel/linux-5.10.y/include/linux/types.h
#include <uapi/linux/types.h>
// /Users/zhangjun/codes/kernel/linux-5.10.y/include/uapi/linux/types.h
#include <asm/types.h>
// /Users/zhangjun/codes/kernel/linux-5.10.y/arch/ia64/include/asm/types.h
#include <asm-generic/int-ll64.h> // include/asm-generic/int-ll64.h
#include <uapi/asm/types.h> // arch/ia64/include/uapi/asm/types.h
内核头文件使用方式: https://kernelnewbies.org/KernelHeaders
- 内核内部模块: 使用内核根目录的
include/
或arch/*/include/
下的头文件. - 内核外置模块:
- 传统方式: 使用
/usr/src/linux
目录下的头文件; - 新的方式(建议): 使用
/lib/modules/${kver}/build
目录下的 Makefile 来构建外置模块
- 传统方式: 使用
内核 module 项目 Makefile 举例:https://sysprog21.github.io/lkmpg/#introduction:
obj-m += hello-1.o
PWD := $(CURDIR)
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
- 用户空间程序, 如 glibc: 使用安装到
/usr/include/
目录下的asm/, asm-generic/, linux/
等内核头文件:
由各发行版提供的内核头文件包来安装, 如 glibc-devel, glibc-kernel-headers 或 linux-libc-dev 等;
内核导出到用户空间头文件可以使用 make headers_install
来安装到 /usr/include/
目录中, 供用户空间程序,如 glibc 来调用,或者构建 glibc 时使用: https://www.linuxfromscratch.org/lfs/view/12.0/chapter05/linux-headers.html
# 清理旧文件和依赖关系
make mrproper
make INSTALL_HDR_PATH=dest headers_install
find dest/include \( -name .install -o -name ..install.cmd \) -delete
cp -rv dest/include/* /usr/include
# 示例: http://smilejay.cn/2013/03/update-linux-headers/
[root@jay-linux kvm.git]# make headers_install
CHK include/generated/uapi/linux/version.h
WRAP arch/x86/include/generated/asm/clkdev.h
SYSHDR arch/x86/syscalls/../include/generated/uapi/asm/unistd_32.h
SYSHDR arch/x86/syscalls/../include/generated/uapi/asm/unistd_64.h
SYSHDR arch/x86/syscalls/../include/generated/uapi/asm/unistd_x32.h
SYSTBL arch/x86/syscalls/../include/generated/asm/syscalls_32.h
SYSHDR arch/x86/syscalls/../include/generated/asm/unistd_32_ia32.h
SYSHDR arch/x86/syscalls/../include/generated/asm/unistd_64_x32.h
SYSTBL arch/x86/syscalls/../include/generated/asm/syscalls_64.h
HOSTCC arch/x86/tools/relocs
HOSTCC scripts/unifdef
INSTALL include/asm-generic (35 files)
INSTALL include/drm (15 files)
INSTALL include/linux/byteorder (2 files)
INSTALL include/linux/caif (2 files)
INSTALL include/linux/can (5 files)
INSTALL include/linux/dvb (8 files)
INSTALL include/linux/hdlc (1 file)
INSTALL include/linux/hsi (1 file)
INSTALL include/linux/isdn (1 file)
...
INSTALL include/asm (64 files)
[root@jay-linux include]# pwd
/usr/include
[root@jay-linux include]# mv asm asm_orig
[root@jay-linux include]# mv asm-generic asm-generic_orig
[root@jay-linux include]# mv linux linux_orig
[root@jay-linux kvm.git]# pwd
/root/kvm_demo/kvm.git
[root@jay-linux kvm.git]# cp -r usr/include/asm /usr/include/
[root@jay-linux kvm.git]# cp -r usr/include/asm-generic/ /usr/include/
[root@jay-linux kvm.git]# cp -r usr/include/linux /usr/include/
安装的内核头文件和目录:
- /usr/include/asm/*.h: Linux API ASM 头文件(架构相关)
- /usr/include/asm-generic/*.h: Linux API ASM 通用头文件
- /usr/include/drm/*.h: Linux API DRM 头文件
- /usr/include/linux/*.h: Linux API Linux 头文件
- /usr/include/misc/*.h: The Linux API Miscellaneous Headers
- /usr/include/mtd/*.h: Linux API MTD 头文件
- /usr/include/rdma/*.h: Linux API RDMA 头文件
- /usr/include/scsi/*.h: Linux API SCSI 头文件
- /usr/include/sound/*.h: Linux API 音频头文件
- /usr/include/video/*.h: Linux API 视频头文件
- /usr/include/xen/*.h: Linux API Xen 头文件
编译 glibc 时依赖上面安装的内核头文件:
- –enable-kernel=4.14 : 告诉 glibc 库支持 4.14 及以后的内核;
- –with-headers=$LFS/usr/include : 告诉 glibc 库安装的内核头文件目录,这样 glibc 就能知晓内核支持的特性并进行对应优化;
# https://www.linuxfromscratch.org/lfs/view/12.0/chapter05/glibc.html
../configure \
--prefix=/usr \
--host=$LFS_TGT \
--build=$(../scripts/config.guess) \
--enable-kernel=4.14 \
--with-headers=$LFS/usr/include \
libc_cv_slibdir=/usr/lib
安装 llvm 编译工具链 #
brew install llvm lld clangd clang-format
export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"
export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
其中 clangd 是基于 clang 编译的 language server,它使用 clang-format 二进制来格式化代码。
C/C++ 源码格式化 #
clangd 使用 clang-format 来格式化代码文件,可以 by 全局或 by 项目(项目根目录下)进行配置:
- .clang-format : 全局或项目级别的配置,指定 clangd 格式化选项的;
- compile_commands.json : 全局或项目级别配置文件,指定该项目下各目录文件的编译方式;
- compile_flags.txt :全局或项目级别配置,用于为项目指定缺省的编译选项。如果存在则 clangd 会忽略 compile_commands.json 文件。
创建全局 ~/.clang-format 文件,也可以在各 project root 目录创建项目配置文件,主要配置的是:
- Tab 和 Indent 缩进;
- 不对头文件进行排序,防止编译报错;
# 打印格式化配置参数
clang-format --dump-config
# ~/.clang-format
# clang-format configuration file. Intended for clang-format >= 11.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
# linux 内核开发风格:
# https://raw.githubusercontent.com/torvalds/linux/master/.clang-format
---
# 基本配置
DisableFormat: false
Language: Cpp # 可以是 'Cpp', 'Java', 'JavaScript', 'Proto', 'TableGen' 等等
BasedOnStyle: WebKit # 基于 WebKit 风格,因为它最接近 Linux 内核的风格
# 缩进
IndentWidth: 8 # 缩进宽度
TabWidth: 8 # 制表符宽度
UseTab: ForIndentation # 使用制表符进行缩进,空格用于对齐
# 换行
AllowShortIfStatementsOnASingleLine: false # 禁止 if 语句在单行内
AllowShortLoopsOnASingleLine: false # 禁止循环语句在单行内
AlwaysBreakBeforeMultilineStrings: true # 多行字符串之前总是换行
BreakBeforeBraces: Linux # 使用 Linux 风格的大括号位置
ColumnLimit: 100 # 每行的字符限制
# 空格
SpaceBeforeParens: ControlStatements # 在控制语句的括号前加空格(if, for, while, 等)
SpaceAfterCStyleCast: true # C 风格的强制类型转换后加空格
# 注释
CommentPragmas: '^ IWYU pragma:' # 支持 IWYU pragmas 注释
IndentExternBlock: AfterExternBlock # 在 extern "C" 块后进行缩进
# 包含文件
SortIncludes: false # 禁用头文件排序,防止编译出错。
IncludeBlocks: Preserve # 保持头文件分组
# 函数定义
AlignTrailingComments: true # 尾部注释对齐
AlignConsecutiveDeclarations: true # 连续的声明对齐
# 其他
NamespaceIndentation: All # 所有命名空间内的代码都缩进
ReflowComments: true # 重新格式化注释
# 特定于 C 语言的设置
Standard: Cpp11 # 使用 C++11 标准
C/C++ 源码编译命令 #
对于复杂项目,如 linux kernel,clangd 需要知道编译各源文件的命令行参数,然后才能正确补全和提示。
clangd 自动使用项目根目录下的这两个文件来对源码进行编译、补全和导航:
- compile_commands.json 文件 :为单独的文件单独指定编译参数;
- compile_flags.txt 文件:为项目中所有文件指定编译参数;
compile_flags.txt #
在项目根目录下创建 compile_flags.txt 文件,文件中的每行为一个 build flags,然后 clangd 用如下形式来使用它们:
clang $FLAGS some_file.cc # $FLAGS 变量内容来源于 compile_flags.txt 文件
compile_flags.txt 示例(如果 flag 的 args 中间有空格,需要分两行);
- 相对路径是相对于 project root 目录。
- -c 是必须的,用来对每个源文件单独编译,否则表示生成可执行程序。
-target
bpf
-D__TARGET_ARCH_x86
-Wall
-Wextra
-I.
-I../headers
-idirafter
/Users/zhangjun/codes/ubuntu-5.15.0-75-headers
-g
-O2
-c
compile_commands.json #
为项目自动生成 compile_commands.json 文件的方式:
- Cmake 项目:
- cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1
- 或者在 CMakeLists.txt 中添加 set(CMAKE_EXPORT_COMPILE_COMMANDS ON),然后 build project 时会产生 compile_commands.json 文件。
- 其它项目,用 bear: apt-get install bear; make clean; bear – make
为 linux 内核生成 compile_commands.json #
切换到 OrbStack 创建的 Ubuntu arm64 虚拟机(MacOS 不能直接编译 linux 源码),配置和编译内核,生成 clangd 所需的 compile_commands.json 文件。
然后再使用 zed 正式版本 ssh remote 打开虚机中的该项目,zed 会自动下载 remote server 和 clangd 等二进制,然后就可以自动补全和跳转了。
设置内核构建环境 #
使用 clang 来编译内核:
sudo apt-get install bc make cmake git build-essential libncurses-dev bison flex libssl-dev libelf-dev
sudo apt-get install clang llvm lld clang-format
# 后续执行的工具 gen_compile_commands.py 依赖 python3
sudo apt install python3 python3-pip
# pip3 install virtualenv && virtualenv myenv && source myenv/bin/activate
清理目录树 #
rm -rf Documentation/kbuild/
git reset --hard HEAD
make clean
修复 4.19.91 内核与 Ubuntu 24.04 的 clang/llvm 18 兼容性问题 #
# 编译报错
/usr/bin/ld: scripts/dtc/dtc-parser.tab.o:(.bss+0x8): multiple definition of `yylloc'; scripts/dtc/dtc-lexer.lex.o:(.bss+0x38): first defined here
collect2: error: ld returned 1 exit status
make[2]: *** [scripts/Makefile.host:99: scripts/dtc/dtc] Error 1
make[1]: *** [scripts/Makefile.build:544: scripts/dtc] Error 2
make: *** [Makefile:1067: scripts] Error 2
# 修复
root@ubuntu:/Users/alizj/go/src/github.com/torvalds/linux-4.19.91# cp scripts/dtc/dtc-lexer.lex.c{,.bak}
root@ubuntu:/Users/alizj/go/src/github.com/torvalds/linux-4.19.91# vim scripts/dtc/dtc-lexer.lex.c
root@ubuntu:/Users/alizj/go/src/github.com/torvalds/linux-4.19.91# diff scripts/dtc/dtc-lexer.lex.c{,.bak}
629c629
< extern YYLTYPE yylloc;
---
> YYLTYPE yylloc;
配置内核 #
使用 clang 的项目:
- 一些发行版,如 Android, ChromeOS, OpenMandriva 和 Chimera
- Google 和 Meta 的项目
- Rust 语言
使用 clang/llvm 编译内核:使用 LLVM=1 变量来指定使用 LLVM 编译工具链:
make LLVM=1 defconfig
# LLVM=1 等效为:
# make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \
# OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump READELF=llvm-readelf \
# HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld
编译内核 #
make LLVM=1 -j8
## Ubuntu 24.04 安装的是 clang/llvm 18 版本, 编译命令如下:
make CC=clang-18 LLVM=1 LLVM_IAS=1 KCFLAGS="-fno-builtin-stpcpy" -j8
# 交叉编译
# make LLVM=1 ARCH=arm64
生成 compile_commands.json #
编译完内核后,执行 gen_compile_commands.py
脚本来生成 clangd 使用的 compile_commands.json 文件:
./scripts/clang-tools/gen_compile_commands.py
## 4.19.91 内核没有该脚本,需要从 5.10 内核拷贝后执行
root@ubuntu:/Users/alizj/go/src/github.com/torvalds/linux-4.19.91# cp ../linux-5.10.y/scripts/clang-tools/gen_compile_commands.py .
root@ubuntu:/Users/alizj/go/src/github.com/torvalds/linux-4.19.91# ./gen_compile_commands.py
生成的 compile_commands.json 文件中,每个内核源文件都对应一条编译命令,需要确保命令和参数路径与 VM 中一致,这样后续 Zed remote ssh 打开该项目后,启动的 clangd 才能使用该命令来编译文件:
zj@a:~/go/src/github.com/torvalds/linux-5.10.y$ head compile_commands.json
[
{
"command": "clang -Wp,-MMD,init/.do_mounts.o.d -nostdinc -isystem /usr/lib/llvm-16/lib/clang/16/include -I./arch/arm64/include -I./arch/arm64/include/generated -I./include -I./arch/arm64/include/uapi -I./arch/arm64/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -mlittle-endian -DKASAN_SHADOW_SCALE_SHIFT=3 -Qunused-arguments -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -fno-integrated-as -Werror=unknown-warning-option -mgeneral-regs-only -DCONFIG_CC_HAS_K_CONSTRAINT=1 -Wno-psabi -fno-asynchronous-unwind-tables -fno-unwind-tables -mbranch-protection=pac-ret+leaf+bti -Wa,-march=armv8.4-a -DARM64_ASM_ARCH='\"armv8.4-a\"' -DKASAN_SHADOW_SCALE_SHIFT=3 -fno-delete-null-pointer-checks -Wno-frame-address -Wno-address-of-packed-member -O2 -Wframe-larger-than=2048 -fstack-protector-strong -Wno-format-invalid-specifier -Wno-gnu -mno-global-merge -Wno-unused-but-set-variable -Wno-unused-const-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -ftrivial-auto-var-init=zero -g -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-array-bounds -fno-strict-overflow -fno-stack-check -Werror=date-time -Werror=incompatible-pointer-types -Wno-initializer-overrides -Wno-format -Wno-sign-compare -Wno-format-zero-length -Wno-pointer-to-enum-cast -Wno-tautological-constant-out-of-range-compare -Wno-unaligned-access -Wno-cast-function-type-strict -Wno-enum-compare-conditional -Wno-enum-enum-conversion -mstack-protector-guard=sysreg -mstack-protector-guard-reg=sp_el0 -mstack-protector-guard-offset=1144 -fno-function-sections -fno-data-sections -DKBUILD_MODFILE='\"init/mounts\"' -DKBUILD_BASENAME='\"do_mounts\"' -DKBUILD_MODNAME='\"mounts\"' -c -o init/do_mounts.o init/do_mounts.c",
"directory": "/Users/alizj/go/src/github.com/torvalds/linux-5.10.y",
"file": "/Users/alizj/go/src/github.com/torvalds/linux-5.10.y/init/do_mounts.c"
},
{
"command": "clang -Wp,-MMD,init/.version.o.d -nostdinc -isystem /usr/lib/llvm-16/lib/clang/16/include -I./arch/arm64/include -I./arch/arm64/include/generated -I./include -I./arch/arm64/include/uapi -I./arch/arm64/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -mlittle-endian -DKASAN_SHADOW_SCALE_SHIFT=3 -Qunused-arguments -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -fno-integrated-as -Werror=unknown-warning-option -mgeneral-regs-only -DCONFIG_CC_HAS_K_CONSTRAINT=1 -Wno-psabi -fno-asynchronous-unwind-tables -fno-unwind-tables -mbranch-protection=pac-ret+leaf+bti -Wa,-march=armv8.4-a -DARM64_ASM_ARCH='\"armv8.4-a\"' -DKASAN_SHADOW_SCALE_SHIFT=3 -fno-delete-null-pointer-checks -Wno-frame-address -Wno-address-of-packed-member -O2 -Wframe-larger-than=2048 -fstack-protector-strong -Wno-format-invalid-specifier -Wno-gnu -mno-global-merge -Wno-unused-but-set-variable -Wno-unused-const-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -ftrivial-auto-var-init=zero -g -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-array-bounds -fno-strict-overflow -fno-stack-check -Werror=date-time -Werror=incompatible-pointer-types -Wno-initializer-overrides -Wno-format -Wno-sign-compare -Wno-format-zero-length -Wno-pointer-to-enum-cast -Wno-tautological-constant-out-of-range-compare -Wno-unaligned-access -Wno-cast-function-type-strict -Wno-enum-compare-conditional -Wno-enum-enum-conversion -mstack-protector-guard=sysreg -mstack-protector-guard-reg=sp_el0 -mstack-protector-guard-offset=1144 -fno-function-sections -fno-data-sections -DKBUILD_MODFILE='\"init/version\"' -DKBUILD_BASENAME='\"version\"' -DKBUILD_MODNAME='\"version\"' -c -o init/version.o init/version.c",
"directory": "/Users/alizj/go/src/github.com/torvalds/linux-5.10.y",
"file": "/Users/alizj/go/src/github.com/torvalds/linux-5.10.y/init/version.c"
...