Вопрос по c – Как собрать программу на C, используя пользовательскую версию glibc и статические ссылки?

17

Я собрал glibc 2.14 и установил его в каталог~/GLIBC/glibc_install, Теперь я хочу создавать и запускать программы, используя эту библиотеку C вместо стандартной библиотеки C моей системы.

To be sure that I was using my custom glibc, I added a call to puts into glibc/stdio-common/printf.c:__printf to print a message.

Then I rebuilt and reinstalled glibc.

Then I wrote a "Hello, World" program and tried to compile and link it as follows:

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

Что я делаю неправильно?

GCC версия должна быть не связана. Символы, которые GCC не может найти, должны быть определены вlibc.a, так что если они пропали, может быть,libc.a был неправильно скомпилирован ..? R..
GCC в значительной степени зависит от внутренних функций Glibc. Вам придется пересобрать более раннюю версию GCC, чтобы не ожидать этих внутренних функций. Это не будет кусок пирога ... user529758
ерсии @GCC не имеют ничего общего с этой проблемой. Пожалуйста, смотрите мое решение, опубликованное ниже (второй ответ). Amittai Aviram
Я не понимаю, зачем мне нужна более ранняя версия GCC. Glibc, который я создал, является вторым по последнему выпуску, 2.14. (У меня были проблемы со сборкой 2.15 в моей системе Ubuntu из-за известных проблем, которые я не мог решить с помощью рекомендуемых обходных путей.) Amittai Aviram

Ваш Ответ

3   ответа
17

[email protected]), у меня есть решение. Оказывается, эта задача немного сложна, потому что вы должны указать компоновщику пропуститьвс он обычно включает автоматически (и без вывода сообщений), а затем включает обратно все, что ему нужно, включая набор начальных и конечных файлов. Некоторые из начальных и конечных файлов приходят из libc, а некоторые из gcc, поэтому правило make немного сложнее. Ниже приведен общий примерный make-файл для иллюстрации подхода. Я предполагаю, что вы создаете программу под названием Прог из исходного файла с именем Prog.c и что вы установили свой пользовательский glibc в каталог / Дома / my_acct / glibc_install.

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 [email protected] $(STARTFILES) $^ $(LIBGROUP) $(ENDFILES) 

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

clean:
        rm -f *.o *.~ $(TARGET)
в следующий раз хороший способ искать такие вещи - бегатьnm над твоим/lib /usr/lib каталоги, чтобы найти символы, которые вам не хватает. по сутиlibcrt содержит только вызов main. Alex
Можете ли вы дать ссылку на ветку списка рассылки GCC? Я бы тоже добавил-nostdinc -I чтобы контролировать заголовки. Ciro Santilli 新疆改造中心 六四事件 法轮功
6

Командная строка у тебя просто фальшивая. Пытаться

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

или похожие. Вы пропустили-lc, а также по ошибке поместили ваши библиотеки перед входными файлами.

И вы искали библиотеку под названиемlibibgcc скорее, чемlibgcc ...

Re -lgcc: см. Правку - это была опечатка; моя командная строка имела «-lgcc». Я попробовал именно ту командную строку, которую вы предложили выше, и получил те же самые ошибки, что и в моем исходном сообщении, так что, боюсь, это не работает. Amittai Aviram
Кстати, у меня не было своих библиотек до входных файлов. У меня была спецификация пути поиска в библиотеке, который AFAIK может указывать в любом месте командной строки (поскольку это флаг, а не ввод). В любом случае, смотрите решение ниже. Amittai Aviram
Почему-lgcc -lc передается дважды? Ciro Santilli 新疆改造中心 六四事件 法轮功
Это не работает для меня в Ubuntu 14.04 GCC 4.8 сundefined reference to __gcc_personality_v0 а также_Unwind_Resume, оба из которых определены наlibgcc_s.so но не вlibgcc.a (здесь нетlibgcc.so). Если я удалю-static по какой-то причине работает даже без добавления-lgcc_s. Ciro Santilli 新疆改造中心 六四事件 法轮功
@ CiroSantilli 新疆 改造 中心 六四 事件 法轮功, не могли бы вы выписать всю команду? Я добавить-lgcc_en, но я все еще получаю ошибку. gfan
2
Установка 1: скомпилируйте свой собственный glibc без выделенного GCC и используйте его

Несколько библиотек glibc на одном хосте

Эта установка может работать и работает быстро, так как не перекомпилирует всю цепочку инструментов GCC, только glibc.

Но это ненадежно, так как использует объекты среды выполнения C, такие какcrt1.o, crti.o, а такжеcrtn.o предоставлено glibc. Это упоминается в:https: //sourceware.org/glibc/wiki/Testing/Builds действие = напомним & ДОХОД = 21 # Compile_against_glibc_in_an_installed_location Эти объекты выполняют раннюю настройку, на которую опирается glibc, поэтому я не удивлюсь, если что-то пойдет не так чудесно и удивительно.

Для более надежной настройки см. Настройка 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`
Установка 1: проверить сборку

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.
     * 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\n", acnt);
    printf("The non-atomic counter is %u\n", 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 \
  test_glibc.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

даже если мы запустили его на чистом chroot, так что-static должно быть, сработало.

Команда адаптирована изhttps: //sourceware.org/glibc/wiki/Testing/Builds действие = напомним & ДОХОД = 21 # Compile_against_glibc_in_an_installed_location но--sysroot сделал неудачу с:

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

так что я его убрал.

ldd вывод подтверждает, чтоldd и библиотеки, которые мы только что создали, фактически используются, как и ожидалось:

+ 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)

Thegccезультаты отладки @ compilation показывают, что использовались мои объекты среды выполнения хоста, что плохо, как упоминалось ранее, но я не знаю, как обойти это, например. это содержит

COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
Настройка 1: изменить glibc

Теперь давайте изменим 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 напечатано несколько раз, как и ожидалось.

Это также подтверждает, что мы фактически использовали скомпилированный glibc, а не хост.

Испытано в Ubuntu 18.04.

Установка 2: первоначальная настройка crosstool-NG

Это альтернатива настройке 1, и это самая правильная установка, которую я добился далеко: насколько я могу наблюдать, все правильно, включая объекты времени выполнения C, такие какcrt1.o, crti.o, а такжеcrtn.o.

В этой настройке мы скомпилируем полный выделенный набор инструментов GCC, который использует желаемый glibc.

Единственный недостаток этого метода - сборка займет больше времени. Но я бы не стал рисковать производственной установкой с чем-то меньшим.

Crosstool-NG - это набор скриптов, который загружает и компилирует для нас все из исходного кода, включая GCC, glibc и binutils.

Да, система сборки GCC настолько плоха, что нам нужен отдельный проект для этого.

Эта установка не идеальна, потому что crosstool-NG не поддерживает сборку исполняемых файлов без дополнительных-Wl флаги, что странно с тех пор, как мы создали GCC. Но все, кажется, работает, так что это только неудобство.

Получите crosstool-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 Я делаю

Operating SystemVersion of linux

так что я выбираю:

4.14.71

которая является первой равной или более старой версией. Он должен быть старше, поскольку ядро обратно совместимо.

Теперь вы можете строить с помощью:

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

а теперь подожди около тридцати минут до двух часов для компиляции.

Настройка 2: необязательные конфигурации

The.config что мы сгенерировали с помощью./ct-ng x86_64-unknown-linux-gnu имеет:

CT_GLIBC_V_2_27=y

Чтобы изменить это, вmenuconfig делать

C-libraryVersion of glibc

спасти.config и продолжить сборку.

Или, если вы хотите использовать свой собственный источник glibc, например, чтобы использовать glibc из последней версии git, продолжайтета:

Paths and misc optionsTry features marked as EXPERIMENTAL: установить в истинное значениеC-librarySource of glibcCustom location: скажи дCustom locationCustom source location: указать каталог, содержащий ваш исходный код glibc

где glibc был клонирован как:

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Настройка 2: проверить это

Как только ты собрал нужный набор инструментов, протестируй его:

#!/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 \
  test_glibc.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
Установка 2: неудачная попытка эффективной перекомпиляции glibc

Это не представляется возможным для crosstool-NG, как объясняется ниже.

Если ты просто перестроишь;

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

тогда ваши изменения в пользовательском местоположении источника glibc учитываются, но он строит все с нуля, что делает его непригодным для итеративной разработки.

Если мы это сделаем:

./ct-ng list-steps

it дает хороший обзор шагов сборки:

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.

поэтому мы видим, что есть этапы glibc, переплетенные с несколькими этапами GCC, прежде всегоlibc_start_files предшествуетcc_core_pass_2, что, вероятно, самый дорогой шаг вместе сcc_core_pass_1.

Чтобы построить только один шаг, вы должны сначала установить «Сохранить промежуточные шаги» в.config опция для первоначальной сборки:

Paths and misc optionsDebug crosstool-NGSave intermediate steps

и тогда вы можете попробовать:

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

но, к сожалению,+ требуется, как указано на:https: //github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-42487753

Заметим, однако, что при перезапуске на промежуточном этапе каталог установки возвращается в состояние, в котором он находился на этом этапе. Т.е. у вас будет восстановленный libc - но окончательный компилятор не будет собран с этим libc (и, следовательно, также нет библиотек компиляторов, таких как libstdc ++).

и в основном все еще делает перестройку слишком медленной, чтобы ее можно было реализовать в процессе разработки, и я не вижу, как преодолеть это, не исправляя crosstool-NG.

Кроме того, начиная сlibc step, похоже, больше не копирует источник изCustom source location, что делает этот метод непригодным для использования.

Bonus: stdlibc ++

Бонус, если вы также заинтересованы в стандартной библиотеке C ++: Как редактировать и перестраивать исходный код библиотеки GCC libstdc ++ C ++?

Похожие вопросы