静态编译libudev

编译静态链接库的目的不再赘述了,之前的文章已经讲了很多次。
这次直接看过程。

1. “理应可以”

源码包目录结构如下图所示:

udev源码目录结构

正常来说,按照以下顺序执行命令,理应可以生成想要的库。

  • 1. aclocal
  • 2. autoconf
  • 3. autoheader
  • 4. automake --add-missing
  • 5. ./configure
  • 6. make
  • 7. make install

2. “必由之路”

俗话说,编译过程绝不可能一蹴而就(手动狗头),解决报错是一件有趣的事(并不是)。

2.1 aclocal 未找到命令

原因:automake未安装
解决方法:sudo apt install automake,有可能还要装libtool

2.2 automake --add-missing时,报“configure: error: cannot guess build type; you must specify one”

原因:无法猜测系统的版本类型
解决方法:很多时候,CMake可以猜出当前的生成类型;如果猜失败了(原因有很多,暂不深究),手动指定当前需要的生成类型即可。例如我要编译arm下的版本,则指定./configure --build=arm-linux
如果实在不知道build type,可以通过lscpu命令,可以获取当前PC的CPU架构(Architecture)。直接将build参数指向具体架构,多数情况也是可行的。例如龙芯mips架构的处理器下编译,可以指定./configure --build=mips64el
龙芯loongarch64架构,暂时无法配置通过,这里先挖个坑,以后有机会补上。
【2023.4.3填坑】
loongnix20版本已经更新了config.guess和config.sub文件,从/usr/share/misc/拷贝到/build-aux中,而后指定--build=loongarch64。也可以使用提供的更新这两个文件的包(在文字末尾处)执行编译。

2.3 automake --add-missing 时,报“error while making link: Operation not supported”

原因:一开始使用虚拟机“共享文件夹”功能,直接使用windows中代码文件(linux中在/mnt/hgfs/下的某个文件夹);这种方式对于linux来说,相当于外挂了一块硬盘,而这块硬盘的分区格式是出乎意料的。
解决方法:将代码文件整体移动到linux的“本地磁盘”上,重新执行automake命令,问题得到解决。
参考:https://www.cnblogs.com/lisuyun/p/4184629.html

2.4 automake --add-missing 时,报“required file 'build-aux/ltmain.sh' not found”

原因:libtoolize未配置
解决方法:
$libtoolize --version
$libtoolize --automake --copy --debug --force
参考:https://blog.csdn.net/caizi001/article/details/38871141

2.5 configure: error: Package requirements (blkid >= 2.20) were not met:

原因:libblkid-dev未安装
解决方法:sudo apt install libblkid-dev

2.6 configure: error: Package requirements (libkmod >= 5) were not met:

原因:libkmod-dev未安装

解决方法:sudo apt install libkmod-dev

2.7 configure: error: Package requirements (glib-2.0 >= 2.22.0 gobject-2.0 >= 2.22.0) were not met:

原因:libglib2.0-dev未安装

解决方法:sudo apt install libglib2.0-dev

2.8 configure: error: Package requirements (gobject-introspection-1.0 >= 0.6.2) were not met:

原因:libgirepository1.0-dev未安装

解决方法:sudo apt install libgirepository1.0-dev

2.9 configure: error: Package requirements (usbutils >= 0.82) were not met

原因:usbutils未安装

解决方法:sudo apt install usbutils

2.10 configure: error: cannot open < gtk-doc.make

原因:gtkdoc-tools包不全

解决方法:sudo apt install gtkdoc-tools

2.11 configure: error: gperf is needed

原因:gperf未安装

解决方法:sudo apt install gperf

2.12 make时报错: src/sd-daemon.c:394: undefined reference to `mq_getattr'

原因:使用了未定义的函数名(废话)。
排查过程:在网络上虽未搜索到完全一致的报错,但有相关性极高的一篇参考文章里提到:
在编译时报undefined reference to mq_openundefined reference to mq_close时,除了要包含头文件
#include <mqueue>
#include <fcntl>
外,还需要加上编译选项 -lrt
解决方法:在sd-daemon.c中,#include<mqueue> #include <fcntl>都已包含,不需要冗余添加;编译选项增加一个-lrt,解决。

2.13 make时报错: (standard input): No keywords in input file!

原因:源码中有一处宏定义,实际未定义
排查过程:跟踪这个宏,最终指向内核的一处定义,暂时没有空去深入调查。考虑到实际使用场景,不需要用到keymap,更改configure选项,禁用keymap后重新编译。
解决方法:configure --disable-keymap

2.14 make时报错: mtd_probe.h --> error: unknown type name ‘uint32_t’;unknown type name ‘uint8_t’

原因:类型未定义
解决方法:mtd_probe.h添加头文件:#include <stdint.h>

2.15 make时报错: 找不到 -lgudev-1.0

原因:libgudev未安装
解决方法:sudo apt install libgudev-1.0-dev

【以下为2023.4.3补充】

2.16 configure: error: Package requirements (usbutils >= 0.82) were not met: No package 'usbutils' found

原因:没有找到usbutils.pc文件
排查过程:
首先检查了usbutils这个依赖包,确实已经正确安装。
全盘搜索usbutils.pc文件,未有所得。
查询到一篇参考文章(附后),该文中指出usbutils.pc实际上应是libusb-1.0.pc的异名文件。尝试将libusb-1.0.pc复制到pkgconfig文件夹(loongarch64环境在/usr/lib/loongarch64-linux-gnu/pkgconfig),并重命名为usbutils.pc,得解。
解决方法:
sudo apt install libusb-1.0-0-dev
sudo cp libusb-1.0.pc usbutils.pc

2.17 make: src/udevadm-udev-event.o: in function `.L40':
udev-event.c:(.text+0x728): undefined reference to `major'等一系列报错

原因:有一些宏未定义
排查过程:
查询到一篇参考文章(附后),该文中指出这些宏都定义在系统头文件sys/sysmacros.h中,加之,得解。也可以使用提供的更新这两个文件的包(在文字末尾处)执行编译。
解决方法:
在udev.h和libudev.h中增加#include <sys/sysmacros.h>

3. 总结与说明

3.1 最终的configure 指令:

sudo ./configure --build=arm-linux --prefix /home/jeemy/Source/udev-182/Bin CFLAGS="-fPIC -lrt" --disable-keymap --enable-static=yes --enable-shared=no
--build:显式指定需要生成的架构版本,详见本文2.2
--prefix:生成的路径
-fPIC:详见本文3.2
-lrt:解决2.12所述的问题
--disable-keymap:解决2.13所述的问题
--enable-static=yes:生成静态库(.a)
--enable-shared=no:不生成动态库(.so)
【2023.4.3补充:loongarch64下的configure指令】
sudo ./configure --build=loongarch64 --prefix /home/jeemy/Source/udev-182/Bin CFLAGS="-fPIC -lrt" --disable-keymap --enable-static=yes --enable-shared=no

3.2 关于-fPIC的说明:

之前打算把若干个静态库(.a)编进一个共享库(.so)后外发,在链接阶段报了个错,大意就是某个库没有使用-fPIC编译选项进行编译,生成失败;请使用-fPIC编译选项重新编译某个库。很显然,这个编译选项是生成动态库时候必须要有的;而且如果编译动态库的时候,还要包含若干个静态库,则这些静态库编译时也必须带着这个编译选项。
那么PIC到底是个啥玩意儿?
首先查到的,PIC是Position-Independent Code的缩写,字面意思是“位置无关代码”。人话?本着看不懂就查字典的精神,我去翻了下gcc的文档(https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Link-Options.html#fn-1):
-shared
Produce a shared object which can then be linked with other objects to form an executable. Not all systems support this option. For predictable results, you must also specify the same set of options used for compilation (-fpic, -fPIC, or model suboptions) when you specify this linker option.
这之后还有一句注解:
On some systems, ‘gcc -shared’ needs to build supplementary stub code for constructors to work. On multi-libbed systems, ‘gcc -shared’ must select the correct support libraries to link against. Failing to supply the correct flags may lead to subtle defects. Supplying them in cases where they are not necessary is innocuous.
和我上面理解的差不多,反正就是生成共享库的时候要用到的一个编译选项,而且用于生成这个共享库的各种静态库(如果有)也要使用相同的编译选项。哎,这货居然还有大小写两个版本?接着查。
还是gcc文档,在Code Gen Option这章,有这俩的详细说明(https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Code-Gen-Options.html#Code-Gen-Options):
-fpic
Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000. The x86 has no such limit.)Position-independent code requires special support, and therefore works only on certain machines. For the x86, GCC supports PIC for System V but not for the Sun 386i. Code generated for the IBM RS/6000 is always position-independent.When this flag is set, the macros __pic__ and __PIC__ are defined to 1.-fPIC

If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on the m68k, PowerPC and SPARC.Position-independent code requires special support, and therefore works only on certain machines.
When this flag is set, the macros __pic__ and __PIC__ are defined to 2.

也就是说,加了这两个编译选项后,动态加载程序(dynamic loader)加载库后,会通过全局偏移表(GOT)访问所有常量地址。这篇文章,对这个机制有比较深入的研究。对于编译来说,倒是不要求很深入地理解操作系统行为。只要知道带着-fPIC或者-fpic是生成共享库必须的就够了。

3.3 源码文件

udev源码提供下载如下:原始文件包修复问题2.14的文件包【2023.4.3补充:修复问题2.2、2.17的文件包】

顾毅
2021年12月3日 初稿
2022年1月7日写于厦门
2023年4月3日 修订于厦门