English 中文(简体)
如何使用自定义版本的 libc 和静态连接构建 C 程序?
原标题:How to build a C program using a custom version of glibc and static linking?

我已建造了 Glibc 2. 14 并将其安装在目录 < code_ / GLIBC/ glibc_安裝 。 现在我要用此 C 库来构建和运行程序, 而不是系统默认的 C 库 。

  • 为了确定我在使用我习惯的glibc, 我添加了一个呼叫, 以输入 < code> glibc/stdio- common/ printf. c:_ printf 来打印消息 。

  • 然后我重建并重新恢复了精神失常

  • 然后我写了一个“哈罗,世界”的节目, 试图把它编成并链接如下:

    gcc -nodefaultlibs -static -lgcc -L~/GLIBC/glibc_install/lib -o myprog myprog.c
    

但我收到以下链接错误报告:

/usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crt1.o: In function `_start :
(.text+0x19): undefined reference to `__libc_csu_init 
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crt1.o: In function `_start :
(.text+0x25): undefined reference to `__libc_start_main 
/tmp/ccACTQEp.o: In function `main :
c1.c:(.text+0xa): undefined reference to `puts 
collect2: ld returned 1 exit status

我做错什么了?

最佳回答

遵循 glibc 帮助邮件列表 (< a href=' ” / cdn- cgi/ l/ email- protect" class = "_cf_ email_" 类" <_cfer_ email_" ”, 数据cfemail=" f894919a9bd5909d9488b8888b98b978d8a9b9d89b9d898f9988A9d699dd6978a9f> [email 保护] [email email 保护] 的几条建议, 我有一个解决方案。 这个任务有点棘手, 因为它通常会自动( 默默地), 包含它需要的所有内容, 包括一系列启动和结束文件。 有些启动和结束文件来自libc, 使规则有点复杂。 下面是用于说明方法的一般样本配置。 我将假设您正在从名为 < enger > prog > < prog_ prog_ must_ must > 和 您 在 bal_ glibtal_ glib> 已经安装了您 的源文件 < trust_ dust_ glistral

TARGET = prog
OBJ = $(TARGET).o
SRC = $(TARGET).c
CC = gcc
CFLAGS = -g
LDFLAGS = -nostdlib -nostartfiles -static
GLIBCDIR = /home/my_acct/glibc_install/lib
STARTFILES = $(GLIBCDIR)/crt1.o $(GLIBCDIR)/crti.o `gcc --print-file-name=crtbegin.o`
ENDFILES = `gcc --print-file-name=crtend.o` $(GLIBCDIR)/crtn.o
LIBGROUP = -Wl,--start-group $(GLIBCDIR)/libc.a -lgcc -lgcc_eh -Wl,--end-group

$(TARGET): $(OBJ)
        $(CC) $(LDFLAGS) -o $@ $(STARTFILES) $^ $(LIBGROUP) $(ENDFILES) 

$(OBJ): $(SRC)
        $(CC) $(CFLAGS) -c $^

clean:
        rm -f *.o *.~ $(TARGET)
问题回答

Setup 1: compile your own glibc without dedicated GCC and use it

无需静态工作 https://stackoverflow.com/ questions/8471779/multiple-glibc-libraries-on-a-single-host/5244503#52454603”

这一设置可能有效,而且很快,因为它不会使整个海合会的工具链重新组合,只是粗略的。

但它并不可靠, 因为它使用由 glibc 提供的主机 C 运行时间天体, 如 crit1. o , crti.o crentn.o 。 这在以下网站被提及: < a href="https://sourceware.org/glibc/wiki/testing/Builds? action=21#compile_ontroduce_glibc_in_an_stalled_place ? Action=21# concompile_ agroduction_glibc_in_ an_ adalled_place ? Action= reclibc 所依赖的早期设置? rec=21# conpall_ apple_ amp_ aganed_ an_ replated_ platedationtregistry.

更可靠的设置见下面的设置2。

构建 glibc 并安装本地端 :

export glibc_install="$(pwd)/glibc/build/install"

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
mkdir build
cd build
../configure --prefix "$glibc_install"
make -j `nproc`
make install -j `nproc`

Setup 1: verify the build

测试小球体c

#define _GNU_SOURCE
#include <assert.h>
#include <gnu/libc-version.h>
#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>

atomic_int acnt;
int cnt;

int f(void* thr_data) {
    for(int n = 0; n < 1000; ++n) {
        ++cnt;
        ++acnt;
    }
    return 0;
}

int main(int argc, char **argv) {
    /* Basic library version check. */
    printf("gnu_get_libc_version() = %s
", gnu_get_libc_version());

    /* Exercise thrd_create from -pthread,
     * which is not present in glibc 2.27 in Ubuntu 18.04.
     * https://stackoverflow.com/questions/56810/how-do-i-start-threads-in-plain-c/52453291#52453291 */
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);
    printf("The atomic counter is %u
", acnt);
    printf("The non-atomic counter is %u
", cnt);
}

test_glibc.sh 编译并运行:

#!/usr/bin/env bash
set -eux
rm -rf tmp
mkdir tmp
gcc 
  -L "${glibc_install}/lib" 
  -I "${glibc_install}/include" 
  -Wl,--rpath="${glibc_install}/lib" 
  -Wl,--dynamic-linker="${glibc_install}/lib/ld-linux-x86-64.so.2" 
  -static 
  -std=c11 
  -o tmp/test_glibc.out 
  -v 
  测试小球体c 
  -pthread 
;
sudo chroot tmp /test_glibc.out

方案产出预期:

gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674

尽管我们用一个干净的根,所以“代码”-静态一定有效。

“https://sourceware.org/glibc/wiki/Testreting/Builds?action=recall&rev=21#compile_ontile_again_glibc_in_an_安裝_replated_replace”中改编的命令,rel=“noreferrer'rel@https://sourceware.org/glibc/wiki/Testing/Builds?appering=21#recall&rev=21#compile_again_glibc_in_an_安裝设_place ,但 -sysroot 使其失败:

cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install

所以我把它取下来了

ld 输出确认,我们刚刚建造的 ld 和图书馆实际上正在按预期使用:

+ ldd test_glibc.out
        linux-vdso.so.1 (0x00007ffe4bfd3000)
        libpthread.so.0 => /home/ciro/glibc/build/install/lib/libpthread.so.0 (0x00007fc12ed92000)
        libc.so.6 => /home/ciro/glibc/build/install/lib/libc.so.6 (0x00007fc12e9dc000)
        /home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000)

gcc 汇编调试输出显示,我的主机运行时间天体已被使用,如前所述情况很差,但我不知道如何围绕它工作,例如,它包含:

COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o

Setup 1: modify glibc

现在让我们用 :

diff --git a/nptl/thrd_create.c b/nptl/thrd_create.c
index 113ba0d93e..b00f088abb 100644
--- a/nptl/thrd_create.c
+++ b/nptl/thrd_create.c
@@ -16,11 +16,14 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */

+#include <stdio.h>
+
 #include "thrd_priv.h"

 int
 thrd_create (thrd_t *thr, thrd_start_t func, void *arg)
 {
+  puts("hacked");
   _Static_assert (sizeof (thr) == sizeof (pthread_t),
                   "sizeof (thr) != sizeof (pthread_t)");

然后重新组合和重新安装glibc, 重新组合和重新运行我们的程序:

cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh

我们看到 hacked 打印了预期的几倍。

这进一步证实,我们实际上使用了我们编集的软体,而不是主机的软体。

测试Ubuntu 18.04。

Setup 2: crosstool-NG pristine setup

这是设置 1 的替代方案, 这是我所实现的最正确的设置: 就我所观察到的情况而言,一切都是正确的, 包括C运行时间天体, 如 crit1. o crti.o crentn.o

在此设置中,我们将汇编一个完全专门的海合会工具链,利用我们想要的微调。

这种方法的唯一不利之处是建筑需要更长的时间。 但我不会冒任何风险去生产一个生产装置。

crosstool-NG 是一套下载和汇编我们所有来源的脚本,包括海合会、Glibc和bunutils。

是的,海合会的建设系统非常糟糕,我们需要一个单独的项目。

这个设置并不完美, 因为 < a href="https://github.com/ crosstool-ng/ crosstool-ng/issues/1030" rel="noreferrer" > crosstool-NG 不支持在没有额外 -Wl 旗帜 的情况下构建可执行文件 < a >, 这感觉怪怪的, 因为我们建了海合会本身。 但一切都在起作用, 所以这只是不便。

获得跨工具NG并配置它 :

git clone https://github.com/crosstool-ng/crosstool-ng
cd crosstool-ng
git checkout a6580b8e8b55345a5a342b5bd96e42c83e640ac5
export CT_PREFIX="$(pwd)/.build/install"
export PATH="/usr/lib/ccache:${PATH}"
./bootstrap
./configure --enable-local
make -j `nproc`
./ct-ng x86_64-unknown-linux-gnu
./ct-ng menuconfig

我看到的唯一强制选项是使其与您的主机内核版本匹配, 以便使用正确的内核信头。 找到您的主机内核版本 :

uname -a

显示我:

4.15.0-34-generic

因此,在 menuconfig I do:

  • Operating System
    • Version of linux

所以我选择:

4.14.71

这是第一个相同或更老的版本。 它必须更老, 因为内核是向后兼容的 。

现在你们可以建造:

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

现在等待大约30分钟到2小时的汇编。

Setup 2: optional configurations

我们用 生成的 .config ./ct-ng-ng x86_64 - 未知的linux-gnu 有:

CT_GLIBC_V_2_27=y

要改变这一点,请在 menuconfig 中:

  • C-library
  • Version of glibc

保存 .config ,继续构建。

或者,如果您想要使用您自己的 glibc 源, 例如, 使用最新 git 的 glibc, 继续 < a href="https://github.com/ crosstool-ng/ crosstool- g/ rosstool- g/ issues/ 1024" rel=" norefererr" 类似 :

  • Paths and misc options
    • Try features marked as EXPERIMENTAL: set to true
  • C-library
    • Source of glibc
      • Custom location: say yes
      • Custom location
        • Custom source location: point to a directory containing your glibc source

Glibc被克隆为:

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28

Setup 2: test it out

一旦您建立了您想要的工具链, 请用下列方法测试 :

#!/usr/bin/env bash
set -eux
install_dir="${CT_PREFIX}/x86_64-unknown-linux-gnu"
rm -rf tmp
mkdir tmp
PATH="${PATH}:${install_dir}/bin" 
  x86_64-unknown-linux-gnu-gcc 
  -Wl,--dynamic-linker="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib/ld-linux-x86-64.so.2" 
  -Wl,--rpath="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib" 
  -static 
  -v 
  -o tmp/test_glibc.out 
  测试小球体c 
  -pthread 
;
sudo chroot tmp /test_glibc.out

所有东西似乎都像设置1一样有效,但现在使用的是正确的运行时间天体:

COLLECT_GCC_OPTIONS=/home/ciro/crosstool-ng/.build/install/x86_64-unknown-linux-gnu/bin/../x86_64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crt1.o

Setup 2: failed efficient glibc recompilation attempt

如下文所述,似乎不可能在跨工具NG的情况下做到这一点。

如果你只是重建;

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

然后将您对自定义的 Glibc 源位置的更改考虑在内, 但它从零开始构建一切, 使得它无法用于迭代开发 。

如果我们这样做:

./ct-ng list-steps

它很好地概括了建设步骤:

Available build steps, in order:
  - companion_tools_for_build
  - companion_libs_for_build
  - binutils_for_build
  - companion_tools_for_host
  - companion_libs_for_host
  - binutils_for_host
  - cc_core_pass_1
  - kernel_headers
  - libc_start_files
  - cc_core_pass_2
  - libc
  - cc_for_build
  - cc_for_host
  - libc_post_cc
  - companion_libs_for_target
  - binutils_for_target
  - debug
  - test_suite
  - finish
Use "<step>" as action to execute only that step.
Use "+<step>" as action to execute up to that step.
Use "<step>+" as action to execute from that step onward.

因此,我们看到,有些平滑步骤与海湾合作委员会的若干步骤交织在一起,最显著的是,在>cc_core_pass_2 之前出现了 libc_start_files ,这可能是最昂贵的一步,加上cc_core_pass_1

为了构建仅一个步骤, 您必须先在 < code>. config 选项中设定“ 保存中间步骤 ”, 用于 Intial building :

  • Paths and misc options
    • Debug crosstool-NG
      • Save intermediate steps

然后,你可以尝试:

env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`

但不幸的是,以下提到的 要求:https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuescomment-424877536

但请注意, 在一个中间阶梯重新启动时, 将安装目录重置到该步骤期间的状态 。 I. e., 您将会有一个重建的 libc - 但是没有与这个 libc 一起建立最后的编译器( 因此, 也没有像 libstdc++ 那样的编译库 ) 。

并且基本上仍然使得重建过于缓慢,无法为发展提供可行的条件, 我不认为如何克服这一点而不修补跨门的NG。

此外,从 libc 步骤开始,似乎没有从 Custom源位置 再次复制源代码,从而使该方法无法使用。

Bonus: stdlibc++

如果你对C++标准图书馆感兴趣的话,一个奖金:

您的命令行只是假冒的。 尝试 :

gcc -nodefaultlibs -static -L~/GLIBC/glibc_install/lib -o myprog myprog.c -lgcc -lc -lgcc -lc

或类似。您忽略了 - lc ,并且在输入文件之前错误地存有您的图书馆 。

您正在寻找一个名为 libibgcc 而不是 libgcc 的图书馆...





相关问题
Fastest method for running a binary search on a file in C?

For example, let s say I want to find a particular word or number in a file. The contents are in sorted order (obviously). Since I want to run a binary search on the file, it seems like a real waste ...

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

Tips for debugging a made-for-linux application on windows?

I m trying to find the source of a bug I have found in an open-source application. I have managed to get a build up and running on my Windows machine, but I m having trouble finding the spot in the ...

Trying to split by two delimiters and it doesn t work - C

I wrote below code to readin line by line from stdin ex. city=Boston;city=New York;city=Chicago and then split each line by ; delimiter and print each record. Then in yet another loop I try to ...

Good, free, easy-to-use C graphics libraries? [closed]

I was wondering if there were any good free graphics libraries for C that are easy to use? It s for plotting 2d and 3d graphs and then saving to a file. It s on a Linux system and there s no gnuplot ...

Encoding, decoding an integer to a char array

Please note that this is not homework and i did search before starting this new thread. I got Store an int in a char array? I was looking for an answer but didn t get any satisfactory answer in the ...

热门标签