Thiết lập 1: biên dịch glibc của riêng bạn mà không cần GCC chuyên dụng và sử dụng nó
Vì dường như không thể thực hiện được chỉ với các bản hack lập phiên bản biểu tượng, hãy tiến thêm một bước nữa và tự biên dịch glibc.
Thiết lập này có thể hoạt động và nhanh chóng vì nó không biên dịch lại toàn bộ chuỗi công cụ GCC, chỉ là glibc.
Nhưng nó không phải là đáng tin cậy như nó sử dụng các đối tượng máy chủ C runtime như crt1.o
, crti.o
và crtn.o
được cung cấp bởi glibc. Điều này được đề cập tại: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Những đối tượng đó thực hiện thiết lập sớm mà glibc dựa vào, vì vậy tôi sẽ không ngạc nhiên nếu mọi thứ gặp sự cố tuyệt vời và những cách vô cùng tinh tế.
Để có thiết lập đáng tin cậy hơn, hãy xem Thiết lập 2 bên dưới.
Xây dựng glibc và cài đặt cục bộ:
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`
Thiết lập 1: xác minh bản dựng
test_glibc.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\n", gnu_get_libc_version());
/* Exercise thrd_create from -pthread,
* which is not present in glibc 2.27 in Ubuntu 18.04.
* /programming/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\n", acnt);
printf("The non-atomic counter is %u\n", cnt);
}
Biên dịch và chạy với test_glibc.sh
:
#!/usr/bin/env bash
set -eux
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" \
-std=c11 \
-o test_glibc.out \
-v \
test_glibc.c \
-pthread \
;
ldd ./test_glibc.out
./test_glibc.out
Chương trình đưa ra kết quả mong đợi:
gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674
Lệnh được điều chỉnh từ https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location nhưng --sysroot
không thành công với:
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
vì vậy tôi đã gỡ bỏ nó.
ldd
đầu ra xác nhận rằng ldd
và các thư viện mà chúng tôi vừa xây dựng đang thực sự được sử dụng như mong đợi:
+ 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)
Đầu gcc
ra gỡ lỗi biên dịch cho thấy rằng các đối tượng thời gian chạy máy chủ của tôi đã được sử dụng, điều này không tốt như đã đề cập trước đó, nhưng tôi không biết cách giải quyết nó, ví dụ: nó chứa:
COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
Thiết lập 1: sửa đổi glibc
Bây giờ hãy sửa đổi glibc với:
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)");
Sau đó biên dịch lại và cài đặt lại glibc, biên dịch lại và chạy lại chương trình của chúng tôi:
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
và chúng tôi thấy hacked
được in một vài lần như mong đợi.
Điều này khẳng định thêm rằng chúng tôi thực sự sử dụng glibc mà chúng tôi đã biên dịch chứ không phải máy chủ.
Đã thử nghiệm trên Ubuntu 18.04.
Thiết lập 2: Thiết lập nguyên sơ crosstool-NG
Đây là một thay thế cho thiết lập 1, và nó là thiết lập đúng nhất mà tôi đã đạt được cho đến nay: mọi thứ đều đúng như xa như tôi có thể quan sát, bao gồm thời gian chạy C đối tượng như crt1.o
, crti.o
, và crtn.o
.
Trong thiết lập này, chúng tôi sẽ biên dịch một chuỗi công cụ GCC chuyên dụng đầy đủ sử dụng glibc mà chúng tôi muốn.
Nhược điểm duy nhất của phương pháp này là quá trình xây dựng sẽ lâu hơn. Nhưng tôi sẽ không mạo hiểm thiết lập sản xuất với bất kỳ thứ gì ít hơn.
crosstool-NG là một bộ tập lệnh tải xuống và biên dịch mọi thứ từ nguồn cho chúng tôi, bao gồm GCC, glibc và binutils.
Vâng, hệ thống xây dựng GCC quá tệ nên chúng tôi cần một dự án riêng cho việc đó.
Thiết lập này không hoàn hảo chỉ vì crosstool-NG không hỗ trợ xây dựng tệp thực thi mà không có -Wl
cờ bổ sung , điều này cảm thấy kỳ lạ vì chúng tôi đã tự xây dựng GCC. Nhưng mọi thứ dường như hoạt động, vì vậy đây chỉ là một sự bất tiện.
Nhận crosstool-NG và định cấu hình nó:
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
Tùy chọn bắt buộc duy nhất mà tôi có thể thấy, là làm cho nó khớp với phiên bản hạt nhân máy chủ của bạn để sử dụng các tiêu đề hạt nhân chính xác. Tìm phiên bản hạt nhân máy chủ của bạn bằng:
uname -a
cho tôi thấy:
4.15.0-34-generic
vì vậy trong menuconfig
tôi làm:
vì vậy tôi chọn:
4.14.71
là phiên bản đầu tiên bằng hoặc cũ hơn. Nó phải cũ hơn vì hạt nhân tương thích ngược.
Bây giờ bạn có thể xây dựng với:
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
và bây giờ đợi khoảng ba mươi phút đến hai giờ để biên dịch.
Thiết lập 2: cấu hình tùy chọn
Cái .config
mà chúng tôi đã tạo ./ct-ng x86_64-unknown-linux-gnu
có:
CT_GLIBC_V_2_27=y
Để thay đổi điều đó, menuconfig
hãy làm:
C-library
Version of glibc
lưu .config
và tiếp tục với bản dựng.
Hoặc, nếu bạn muốn sử dụng nguồn glibc của riêng mình, ví dụ: sử dụng glibc từ git mới nhất, hãy tiến hành như sau :
Paths and misc options
Try features marked as EXPERIMENTAL
: đặt thành true
C-library
Source of glibc
Custom location
: nói có
Custom location
Custom source location
: trỏ tới thư mục chứa nguồn glibc của bạn
trong đó glibc được sao chép thành:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Thiết lập 2: thử nghiệm
Khi bạn đã xây dựng chuỗi công cụ mà bạn muốn, hãy thử nghiệm nó với:
#!/usr/bin/env bash
set -eux
install_dir="${CT_PREFIX}/x86_64-unknown-linux-gnu"
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" \
-v \
-o test_glibc.out \
test_glibc.c \
-pthread \
;
ldd test_glibc.out
./test_glibc.out
Mọi thứ dường như hoạt động như trong Thiết lập 1, ngoại trừ việc bây giờ các đối tượng thời gian chạy chính xác đã được sử dụng:
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
Thiết lập 2: nỗ lực biên dịch lại glibc hiệu quả không thành công
Điều này dường như không khả thi với crosstool-NG, như được giải thích bên dưới.
Nếu bạn chỉ xây dựng lại;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
thì các thay đổi của bạn đối với vị trí nguồn glibc tùy chỉnh được tính đến, nhưng nó xây dựng mọi thứ từ đầu, khiến nó không thể sử dụng được để phát triển lặp đi lặp lại.
Nếu chúng ta làm:
./ct-ng list-steps
nó cung cấp một cái nhìn tổng quan tốt đẹp về các bước xây dựng:
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.
do đó, chúng tôi thấy rằng có các bước glibc đan xen với một số bước GCC, đáng chú ý nhất là libc_start_files
trước cc_core_pass_2
đó, đây có thể là bước đắt nhất cùng với cc_core_pass_1
.
Để xây dựng chỉ một bước, trước tiên bạn phải đặt .config
tùy chọn "Lưu các bước trung gian" trong tùy chọn cho bản dựng nội dung:
và sau đó bạn có thể thử:
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
nhưng rất tiếc, +
yêu cầu như đã đề cập tại: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536
Tuy nhiên, lưu ý rằng việc khởi động lại ở bước trung gian sẽ đặt lại thư mục cài đặt về trạng thái mà nó có trong bước đó. Tức là, bạn sẽ có một libc được xây dựng lại - nhưng không có trình biên dịch cuối cùng nào được xây dựng với libc này (và do đó, không có thư viện trình biên dịch nào như libstdc ++).
và về cơ bản vẫn làm cho việc xây dựng lại quá chậm để có thể phát triển được và tôi không biết cách khắc phục điều này mà không cần vá crosstool-NG.
Hơn nữa, bắt đầu từ libc
bước dường như không sao chép lại nguồn từ đó Custom source location
, khiến cho phương pháp này không thể sử dụng được.
Phần thưởng: stdlibc ++
Một phần thưởng nếu bạn cũng quan tâm đến thư viện chuẩn C ++: Làm thế nào để chỉnh sửa và xây dựng lại nguồn thư viện chuẩn GCC libstdc ++ C ++?