从源代码编译软件

来自 Alpine Linux

本页面介绍了如何在 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!

注意: 恢复您的 clang++ 符号链接以避免以后出现困难。

使用 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 编辑器。

参见