跳过正文

Linux 内核的 llvm 编译和 clangd 支持

Gnu Kernel Clangd
目录

介绍使用 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 内核相关的包:

  1. linux-image-6.5.0-42-generic
  2. linux-headers-6.5.0-42,linux-headers-6.5.0-42-generic: 内核头文件 /usr/src/linux-headers-6.5.0-44
  3. linux-libc-dev: 内核导出到用户空间的头文件(也可以通过 make headers_install 来从内核头文件源码来安装)
  4. linux-modules-6.5.0-42-generic:内核 module /lib/modules/6.5.0-44-generic/
  5. 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>.

内核头文件不能包含内核之外的其它头文件:

  1. 基本的内核头文件:位于内核根的 include/ 目录下, 如 #include <linux/i2c.h> 对应的头文件是 include/linux/i2c.h
  2. 平台架构相关的头文件:位于 arch/<arch>/include/asmarch/<arch>/include/uapi/asm 下, 包含这些头文件时, 以 asm/ 或 uapi/asm 为前缀, 如 #include <asm/ioctl.h>
    • asm/: 内核接口部分, 只被内核内部使用;
    • uapi/asm/: 内核暴露给用户接口部分, 可以被用户程序,如 glibc 使用;
  3. include/asm-generic/include/uapi/asm-generic/ 目录:
    • 为所有平台公用的头文件, 使用方式: #include <asm-generic/irq.h>
    • 原则上不直接包含 asm-generic 头文件, 而是通过上面 arch 相关的头文件来包含它们;
  4. arch/<arch>/include/generated/asm/arch/<arch>/include/generated/uapi/asm/:
    • 根据 include/asm-generic/ 目录下的头文件生成的头文件
  5. 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

  1. 内核内部模块: 使用内核根目录的 include/arch/*/include/ 下的头文件.
  2. 内核外置模块:
    • 传统方式: 使用 /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
  1. 用户空间程序, 如 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 项目(项目根目录下)进行配置:

  1. .clang-format : 全局或项目级别的配置,指定 clangd 格式化选项的;
  2. compile_commands.json : 全局或项目级别配置文件,指定该项目下各目录文件的编译方式;
  3. compile_flags.txt :全局或项目级别配置,用于为项目指定缺省的编译选项。如果存在则 clangd 会忽略 compile_commands.json 文件。

创建全局 ~/.clang-format 文件,也可以在各 project root 目录创建项目配置文件,主要配置的是:

  1. Tab 和 Indent 缩进;
  2. 不对头文件进行排序,防止编译报错;
# 打印格式化配置参数
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 自动使用项目根目录下的这两个文件来对源码进行编译、补全和导航:

  1. compile_commands.json 文件 :为单独的文件单独指定编译参数;
  2. 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 文件的方式:

  1. Cmake 项目:
  • cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1
  • 或者在 CMakeLists.txt 中添加 set(CMAKE_EXPORT_COMPILE_COMMANDS ON),然后 build project 时会产生 compile_commands.json 文件。
  1. 其它项目,用 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 的项目:

  1. 一些发行版,如 Android, ChromeOS, OpenMandriva 和 Chimera
  2. Google 和 Meta 的项目
  3. 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"
  ...

相关文章

gdb
Gnu Tool Gdb
gdb 个人速查手册。
gas X86_64 汇编 - 个人参考手册
·
Gnu Asm Gcc Manual
GCC 交叉编译工具链
·
Gnu Compiler Glibc Gcc
交叉编译是指编译器能生成和它执行环境不同的 CPU 架构的二进制,例如在 arm64 机器上编译出在 x86_64 机器上运行的二进制。 本文分别以常用的 ubuntu aarch64 和 fedora 40 x86_64 编译环境为例,介绍这两个问题的解决方案。
GCC 编译器 - 个人参考手册
Gnu Gcc Manual
gcc 编译器个人参考手册。