如何修复构建 Linux 内核时出现的 link 错误?

How do I fix link errors that occur while building the Linux kernel?

我正在尝试在 Ubuntu 16.04 LTS 中编译内核版本 4.4。我添加或修改了一些代码,将块I/O层中写块的块号存储到自定义缓冲区中。

我修改了代码 [linux-kernel-location]/block/blk-core.c (original):

...
#include <custom/custom-buffer.h>
...
blk_qt_t submit_bio(int rw, struct bio *bio)
{
    ...
    if(rw & WRITE)
    {
        unsigned long cntUnit = bio->bi_bdev->bd_super->s_blocks / bdev_logical_block_size(bio->bi_bdev);
        unsigned long blk_no = bio->bi_iter.bi_sector / cntUnit;

        count_vm_events(PGPGOUT, count);

        custom_buf_write_blk_no(blk_no);
    }
    ...
}
...

[linux-内核位置]/block/Makefile (original):

...
obj-y += custom/

[linux-内核位置]/block/Kconfig (original):

...
source "block/custom/Kconfig"

并创建了目录 [linux-kernel-location]/include/custom、[linux-kernel-location]/block/custom。然后,我创建了文件 [linux-kernel-location]/include/custom/custom-buffer.h:

#ifndef _CUSTOM_BUFFER_H_
#define _CUSTOM_BUFFER_H_

extern int custom_buf_write_blk_no(unsigned long blk_no);

#endif

[linux-内核位置]/block/custom/Makefile:

obj-y += custom-buffer.o

[linux-内核位置]/block/custom/Kconfig:

config CUSTOM_BUFFER
    tristate
    depends on BLOCK
    default y

[linux-kernel-location]/block/custom/custom-buffer.c(包括 custom_buf_write_blk_no(unsigned long blk_no)EXPORT_SYMBOL 宏的定义)。

我在linux内核位置的shell中输入了make命令,得到如下结果:

...
LD    init/built-in.o
block/built-in.o: In function `submit_bio':
[linux-kernel-location]/block/blk-core.c:2117: undefined reference to `custom_buf_write_blk_no'
block/built-in.o:(___ksymtab+custom_buf_write_blk_no+0x0): undefined reference to `custom_buf_write_blk_no'
Makefile:927: recipe for target 'vmlinux' failed
make: *** [vmlinux] Error 1

我想我需要修复 Makefile,我该如何修复它?

编辑:我也知道这是因为链接器在链接过程中找不到符号。但是我不知道如何在Kbuild系统适用的Makefile中修复它。

自行解决: custom-buffer.c写错了。我正确地修复了它,所以它编译得很好。

打错字太难找了...

是的,很难找到错别字。但是编译器试图向我们展示打字错误的地方。

我已尝试重现您的问题以写一个可能有助于访问此问题的其他人的答案。

首先,如果您在文件名中输入错误会怎样?让我们说 custom/custom-buffe.c

内核构建系统会报错:

CHK     include/generated/compile.h
make[2]: *** No rule to make target 'block/custom/custom-buffer.o', needed by 'block/custom/built-in.o'.  Stop.
scripts/Makefile.build:403: recipe for target 'block/custom' failed
make[1]: *** [block/custom] Error 2
Makefile:943: recipe for target 'block' failed
make: *** [block] Error 2

因此,构建规则显然正在寻找要创建的文件 custom-buffer.o,但它丢失了。默认情况下,它应该有 custom-buffer.c。 来自 documentation:

The most simple kbuild makefile contains one line:

Example: obj-y += foo.o

This tells kbuild that there is one object in that directory, named foo.o. foo.o will be built from foo.c or foo.S.

这不是你的情况。所以我在函数定义中打错了字(buff vs buf):

int custom_buff_write_blk_no(unsigned long blk_no) {
  printk(KERN_INFO "WIP NUMBER %lu\n", blk_no);
  return 0;
}
EXPORT_SYMBOL(custom_buff_write_blk_no);

现在,在构建之后我从编译器那里得到了信息:

LD      init/built-in.o
block/built-in.o: In function `submit_bio':
(.text+0x8bb0): undefined reference to `custom_buf_write_blk_no'
Makefile:929: recipe for target 'vmlinux' failed
make: *** [vmlinux] Error 1

我们可以通过发出 make V=1:

来追踪这个 LD 步骤到底在做什么
+ ld -m elf_x86_64 --build-id -o .tmp_vmlinux1 -T ./arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_64.o arch/x86/kernel/head64.o arch/x86/kernel/head.o init/built-in.o --start-gr
oup usr/built-in.o arch/x86/built-in.o kernel/built-in.o certs/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o lib/lib.
a arch/x86/lib/lib.a lib/built-in.o arch/x86/lib/built-in.o drivers/built-in.o sound/built-in.o firmware/built-in.o arch/x86/pci/built-in.o arch/x86/power/built-in.o arch/x86/video
/built-in.o arch/x86/ras/built-in.o net/built-in.o virt/built-in.o --end-group
block/built-in.o: In function `submit_bio':
(.text+0x8bb0): undefined reference to `custom_buf_write_blk_no'

首先,我们认为一切都是正确的,我们的custom_buf_write_blk_no符号应该在文件block/custom/build-in.o中。在这里我们看不到这个文件。但是,Kbuild 系统将所有对象递归合并为一个构建-in.o:

Kbuild compiles all the $(obj-y) files. It then calls "$(LD) -r" to merge these files into one built-in.o file. built-in.o is later linked into vmlinux by the parent Makefile.

所以它应该在 block/build-in.o。为了确保我们可以通过调查 make V=1 输出来追踪它:

...
ld -m elf_x86_64   -r -o block/custom/built-in.o block/custom/custom-buffer.o
...
ld -m elf_x86_64   -r -o block/built-in.o block/bio.o block/elevator.o block/blk-core.o block/blk-tag.o block/blk-sysfs.o block/blk-flush.o block/blk-settings.o block/blk-ioc.o 
block/blk-map.o block/blk-exec.o block/blk-merge.o block/blk-softirq.o block/blk-timeout.o block/blk-iopoll.o block/blk-lib.o block/blk-mq.o block/blk-mq-tag.o block/blk-mq-sysfs.o
block/blk-mq-cpu.o block/blk-mq-cpumap.o block/ioctl.o block/genhd.o block/scsi_ioctl.o block/partition-generic.o block/ioprio.o block/partitions/built-in.o block/bounce.o block/b
sg.o block/noop-iosched.o block/deadline-iosched.o block/cfq-iosched.o block/compat_ioctl.o block/custom/built-in.o 

确实有我们的custom-buffer.o。那么为什么我们有未定义的引用呢?那么,我们可以使用 objdump -t block/built-in.o | grep custom:

来调查 block/build-in.o 中的符号
0000000000000000 l    d  ___ksymtab+custom_buff_write_blk_no    0000000000000000 ___ksymtab+custom_buff_write_blk_no
0000000000000000 l    df *ABS*  0000000000000000 custom-buffer.c
000000000000132e l     O __ksymtab_strings      0000000000000019 __kstrtab_custom_buff_write_blk_no
0000000000000000         *UND*  0000000000000000 custom_buf_write_blk_no
0000000000000000 g     O ___ksymtab+custom_buff_write_blk_no    0000000000000010 __ksymtab_custom_buff_write_blk_no
0000000000025ed0 g     F .text  0000000000000017 custom_buff_write_blk_no

现在我们看到符号 custom_buff_write_blk_no 并且编译器抱怨他正在寻找 custom_buf_write_blk_no.