Связывание 32-битной x86 программы на 64-битном x86 Debian

Вопрос или проблема

У меня есть пример приложения и файл make.

  • Содержимое файла main.c
    #include <stdio.h>
    
    /*int mainentry_entry(int argc, char *argv[]) {*/
    int main(int argc, char *argv[]) {
        int i;
        printf("argc = %d\n", argc);
        for (i = 0; i < argc; i++) {
            printf("argv[%d] = %s\n", i, argv[i]);
        }
        return 0;
    }
    
  • Содержимое файла makefile
    Cl = i686-linux-gnu-ld
    CC = x86_i586-gcc
    CFLAGS = -Wall -Wextra -g
    CFLAGS += -I/usr/i686-linux-gnu/include
    TARGET = test_program
    OBJS = main.o
    
    all: $(TARGET)
    
    $(TARGET): $(OBJS)
            $(Cl) -L/lib/i386-linux-gnu/ -lc -o $(TARGET) $(OBJS)
    
    main.o: main.c
            $(CC) $(CFLAGS) -c main.c
    
    clean:
            rm -f $(TARGET) $(OBJS)
    

Я пытаюсь скомпилировать с помощью инструментария PikeOS (x86_i586-gcc) и связать с 32-битным инструментарем Debian (i686-linux-gnu-ld), но вывод, который я получаю, — это test_program, который ссылается на /usr/lib/libc.so.1, который отсутствует в каталоге /usr/lib/.

Может кто-то объяснить мне, почему не используется libc.so.6, который присутствует в /lib/i386-linux-gnu/libc.so.6?

~$ ldd test_program
        linux-gate.so.1 (0xea48c000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xea200000)
        /usr/lib/libc.so.1 => /lib/ld-linux.so.2 (0xea48e000)

ld использует libc.so в /lib/i386-linux-gnu, поэтому вы получаете

        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xea200000)

в выводе ldd.

Запись /usr/lib/libc.so.1 это динамический компоновщик; на это указывает разрешение, указанное ldd (указывая на настоящий 32-битный x86 динамический компоновщик), и более явно — вывод file:

$ file main
main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), динамически связанный, интерпретатор /usr/lib/libc.so.1, с отладочной информацией, не стрижен

Это происходит потому, что по умолчанию используемый динамический компоновщик в ld — это /usr/lib/libc.so.1, и вы не указали ничего другого в командной строке ld.

Настоящая проблема заключается в том, что ваша команда ld не является правильным способом связать C-программу с GCC; вместо ld вы должны использовать сам gcc:

Cl = i686-linux-gnu-gcc

Это обеспечит правильную передачу аргументов в ld, включая подходящий динамический компоновщик и необходимые C-стартовые файлы. Вы можете увидеть это в деталях, запустив gcc в подробном режиме:

$ i686-linux-gnu-gcc -v -L/lib/i386-linux-gnu/ -lc -o main main.o
[…]
 /usr/lib/gcc-cross/i686-linux-gnu/10/collect2 -plugin /usr/lib/gcc-cross/i686-linux-gnu/10/liblto_plugin.so -plugin-opt=/usr/lib/gcc-cross/i686-linux-gnu/10/lto-wrapper -plugin-opt=-fresolution=/tmp/user/1000/cciJ4Cer.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_i386 --hash-style=gnu --as-needed -dynamic-linker /lib/ld-linux.so.2 -pie -o main /usr/lib/gcc-cross/i686-linux-gnu/10/../../../../i686-linux-gnu/lib/../lib/Scrt1.o /usr/lib/gcc-cross/i686-linux-gnu/10/../../../../i686-linux-gnu/lib/../lib/crti.o /usr/lib/gcc-cross/i686-linux-gnu/10/crtbeginS.o -L/lib/i386-linux-gnu/ -L/usr/lib/gcc-cross/i686-linux-gnu/10 -L/usr/lib/gcc-cross/i686-linux-gnu/10/../../../../i686-linux-gnu/lib/../lib -L/lib/i386-linux-gnu -L/lib/../lib -L/usr/lib/i386-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc-cross/i686-linux-gnu/10/../../../../i686-linux-gnu/lib -lc main.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc-cross/i686-linux-gnu/10/crtendS.o /usr/lib/gcc-cross/i686-linux-gnu/10/../../../../i686-linux-gnu/lib/../lib/crtn.o
COLLECT_GCC_OPTIONS='-v' '-L/lib/i386-linux-gnu/' '-o' 'main' '-mtune=generic' '-march=i686'

Как вы можете видеть, связывание main требует гораздо больше, чем ваша команда ld учитывает. (Строго говоря, используя i686-linux-gnu-gcc, вы также не должны указывать -L/lib/i386-linux-gnu/ -lc.)

Кстати, если вы собираете для 32-битного x86 на Debian, я не уверен, что есть причина использовать инструментарий PikeOS — вы должны использовать кросс-компилятор GCC для всего.

Ответ или решение

Компиляция 32-битной программы x86 на 64-битной Debian

Введение

При работе с компиляцией 32-битной программы на 64-битной системе Debian могут возникать различные проблемы, в том числе ошибки с разрешением динамических библиотек. В данной статье мы подробно разберем, как связать 32-битную программу с использованием Makefile и инструментов разработки, таких как gcc и ld.

Проблема

В вашем случае, программа, содержащая файл main.c, компилируется с использованием двух различных инструментов, что приводит к тому, что скомпилированный бинарный файл test_program имеет ссылки на библиотеку /usr/lib/libc.so.1, которая отсутствует в данной системе. Это вызывает путаницу, так как необходимо использовать /lib/i386-linux-gnu/libc.so.6, которая присутствует в системе.

Причины возникновения проблемы

Основной причиной является использование ld для компоновки, вместо использования компилятора gcc. Когда вы начинаете скомпилировать программу с помощью ld, он не получает необходимые параметры для правильного связывания с библиотеками и динамическими компоновщиками. В частности, для 32-битной программы необходим правильный динамический компоновщик, который отвечает за загрузку необходимых библиотек во время выполнения.

Вывод команды ldd test_program показывает, что:

libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xea200000)

Хотя этот вывод показывает корректное назначение для библиотеки, упоминание о /usr/lib/libc.so.1 указывает на то, что действительно была использована неправильная библиотека для загрузки.

Решение проблемы

Чтобы избежать данной проблемы, рекомендуется использовать gcc для компоновки программ. gcc автоматически добавляет необходимые параметры, включая правильный динамический компоновщик. Измените ваш Makefile следующим образом:

CC = i686-linux-gnu-gcc
CFLAGS = -Wall -Wextra -g
TARGET = test_program
OBJS = main.o

all: $(TARGET)

$(TARGET): $(OBJS)
    $(CC) -o $(TARGET) $(OBJS)

main.o: main.c
    $(CC) $(CFLAGS) -c main.c

clean:
    rm -f $(TARGET) $(OBJS)

Поскольку gcc сам обрабатывает связывание с библиотеками, нет необходимости вручную указывать пути к библиотекам, в вашем случае -L/lib/i386-linux-gnu/ и -lc могут быть опущены.

Подробности

  1. Использование gcc: gcc обеспечивает более надежный способ компиляции и связывания, поскольку он управляет всем необходимым, включая компоновку с правильными библиотеками и динамическими компоновщиками.

  2. Параметры компиляции: Важно помнить, что использование CFLAGS позволяет установить опции компиляции, такие как режим отладки и предупреждения.

  3. Зависимости от динамических библиотек: С помощью команды ldd вы можете проверить зависимости созданного исполняемого файла. Это полезно для диагностики проблем с библиотеками.

Заключение

Соблюдая представленные рекомендации и используя gcc вместо ld, вы сможете без труда компилировать и связывать свои 32-битные программы на 64-битных системах Debian. Это не только упростит процесс, но и снизит вероятность ошибок, связанных с неправильными зависимостями библиотек. Убедитесь, что все необходимые пакеты и инструменты установлены для корректной работы, особенно при работе с кросс-компиляторами.

Оцените материал
Добавить комментарий

Капча загружается...