从源代码编译软件
本页面介绍了如何在 Alpine Linux 上从源代码编译软件。在 Alpine Linux 中安装软件的推荐方式是创建 APKBUILD 文件并使用 apk 安装。
以下示例步骤基于 reddit 用户 Beneficial_Bug_4892 在 reddit 上为 ImHex(一个面向逆向工程师和程序员的十六进制编辑器)编写的安装过程。尽管 imhex 软件包在 Alpine Linux 中可用,但它对作者不起作用,因此有了这篇文章。
在 Alpine Linux 上编译 ImHex
在 Linux 上,ImHex 通过常规 GCC(或可选的 Clang)构建。
- 使用 git clone https://github.com/WerWolv/ImHex --recurse-submodules 克隆仓库
- 根据 Arch 脚本 dist/get_deps_archlinux.sh 手动安装所有依赖项。
- 使用以下命令构建 ImHex 本身
cd ImHex mkdir -p build cd build CC=gcc-12 CXX=g++-12 \ cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="/usr" \ .. ninja install All paths follow the XDG Base Directories standard, and can thus be modified with the environment variables XDG_CONFIG_HOME, XDG_CONFIG_DIRS, XDG_DATA_HOME and XDG_DATA_DIRS.
由于 lseek 导致的错误
以下命令最终失败。
# ninja install
查看编译器错误后,我发现由于某种原因,lseek(2) 在其中一个源文件的开头被 #define 为 lseek64。我猜在其他 Linux 发行版上,它应该是 64 位版本的函数,但 Alpine 的 MUSL lseek(2) 已经是 64 位的,没有任何前缀。我按如下方式删除它并继续构建。
--- a/plugins/builtin/source/content/providers/disk_provider.cpp +++ b/plugins/builtin/source/content/providers/disk_provider.cpp @@ -44,7 +44,8 @@ #endif #if defined(OS_LINUX) && !defined(OS_FREEBSD) - #define lseek lseek64 + /* I use Alpine btw */ +// #define lseek lseek64 #elif defined(OS_FREEBSD) #include <sys/disk.h> #define DEFAULT_SECTOR_SIZE 512
下一个问题很容易解决:缺少 magic.h 头文件。稍微搜索一下表明它似乎是库的一部分,file(1) 使用该库来确定给定文件的类型。添加了 file-dev 并继续使用以下命令。
apk add file-dev
自定义编译器标志
这一个真的很奇怪。这是一个链接问题(如果您是一位经验丰富的 C/C++ 开发人员,您可能应该知道链接是您可能遇到的最令人诅咒和烦恼的领域)。我收到了很多对 glfw* 函数族未定义的引用,这些引用显然都来自 glfw 库。起初,这没有任何意义:我已经安装了 glfw 和 glfw-dev 软件包。但在一段时间后,我注意到 ninja 提供的编译行中没有提及 glfw 的内容(?)。
我不知道如何向 ninja 添加某种自定义 LDFLAGS,所以我决定做一个肮脏的变通方法(我认为是我一生中最肮脏的变通方法):从 shell 包装器调用编译器,该包装器将参数转换为原始编译器,并另外附加所有必要的 glfw 标志以进行链接...
# rm /usr/bin/clang++ # cat <<EOF >/usr/bin/clang++ > #!/bin/sh > /usr/lib/llvm17/bin/clang++ $@ -L/usr/lib `pkg-config --libs glfw3` > EOF # chmod 755 /usr/bin/clang++
之后,它已成功构建并安装了 ImHex!
使用 gdb 进行故障排除
不幸的是,它在首次启动时仍然崩溃。所以我决定通过 gdb 运行它,看看哪里出错了以及在哪里发生了段错误
$ gdb ./imhex ... (gdb) r ... Thread 1 "Main" received signal SIGSEGV, Segmentation fault. get_meta (p=p@entry=0x7ffff63387b0 "e") at src/malloc/mallocng/meta.h:141 warning: 141 src/malloc/mallocng/meta.h: No such file or directory (gdb)
这似乎是堆损坏问题,或者堆本身就有问题。当我检查堆栈回溯时,事情变得更加清楚了
(gdb) bt #0 get_meta (p=p@entry=0x7ffff63387b0 "e") at src/malloc/mallocng/meta.h:141 #1 0x00007ffff7f80db3 in __libc_free (p=0x7ffff63387b0) at src/malloc/mallocng/free.c:105 #2 0x00007ffff7f80422 in free (p=<optimized out>) at src/malloc/free.c:5 #3 0x00007ffff7fa4f6c in regfree (preg=<optimized out>) at src/regex/regcomp.c:2926 #4 0x00007ffff7314d99 in ?? () from /usr/lib/libglfw.so.3 #5 0x00007ffff730ec39 in ?? () from /usr/lib/libglfw.so.3 #6 0x00005555555bafdc in hex::init::WindowSplash::~WindowSplash() () #7 0x00005555555debc9 in hex::init::runImHex() () #8 0x0000555555576482 in main () (gdb)
它在 free(3) 实现的深处崩溃的事实并不表明 LibC 本身存在错误,而是表明 NULL 指针或其他无效位置被传递给它,因此它没有预料到这一点,并且表现得好像它收到了 malloc(3) 分配的有效页面。
如果那是更重要的东西,也许值得修复。但因为它只是内存释放,我们可以直接摆脱它。我的意思是,当然这是一个内存泄漏,但它并不关键:此代码仅在启动期间运行,而不是在循环中或某种中断内部运行。Linux 将在进程终止后释放为此进程分配的所有页面,所以谁在乎我们在这里泄漏几个字节。
--- a/main/gui/source/init/splash_window.cpp +++ b/main/gui/source/init/splash_window.cpp @@ -65,8 +65,8 @@ namespace hex::init { } WindowSplash::~WindowSplash() { - this->exitImGui(); - this->exitGLFW(); + /* Who needs it, right?... */ + ; }
重新编译后,终于能够启动 ImHex 编辑器。