Подтвердить что ты не робот

Заставить компоновщик сбой с ошибкой с множественным определением, даже если включить --все архив

Этот пример состоит из нескольких файлов:

// baz.cxx
int wat = 0;
int counter = ++wat;
// foo.cxx (empty)
// bar.cxx (empty)
// main.cxx
#include <iostream>
extern int wat;
int main() {
    std::cout << wat << '\n';
}
// makefile
run : main.cxx foo.so bar.so
    g++ -std=c++11 $^ -o [email protected]

baz.a : baz.cxx
    g++ -std=c++11 -c $^ -o baz.o -fPIC
    ar rcs [email protected] baz.o

%.so : %.cxx baz.a
    g++ -std=c++11 $< -Wl,--whole-archive baz.a -Wl,--no-whole-archive -o [email protected] -shared -fPIC

As-is, если вы просто запустите make && LD_LIBRARY_PATH=. ./run, все с компиляцией, сборкой, ссылкой, запуском и выпуском 2. Это связано с тем, что foo.so и bar.so предоставляют wat, а инициализация для counter выполняется дважды.

Есть ли способ каким-то образом заставить run не связываться с ошибкой множественного определения в этом случае, сохраняя при этом, что оба foo.so и bar.so имеют определение для wat?

4b9b3361

Ответ 1

Вы можете использовать сценарии компоновщика libfoo.so и libbar.so со статической частью, которая создает конфликт символов (и устанавливать фактические DSO в некотором неочевидном месте, так что -lfoo и -lbar не выбирают их вверх).

Что-то вроде этого:

  • libfoo.so

    INPUT(conflict.o)
    INPUT(./foo.so)
    
  • libbar.so

    INPUT(conflict.o)
    INPUT(./bar.so)
    
  • conflict.cxx

    int conflict;
    

Это приводит к:

g++ -std=c++11 main.cxx -o run -L. -lfoo -lbar
conflict.o:(.bss+0x0): multiple definition of `conflict'
conflict.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status

Это приведет к обнаружению конфликтов только в том же запуске редактора ссылок. Вы все равно можете связать два разных DSO с -lfoo и -lbar и связать эти два в приложении, без ошибок в редакторе ссылок.

Аналогичная проблема решается аннотациями ABI, но для этого нужны конкретные изменения ld.

Если проверка времени выполнения является приемлемой, у вас может быть список слабых символов, определяемых каждым DSO, который должен конфликтовать, и реализовать конструктор ELF, который прерывает процесс, если определено более одного из них. Я не знаю ни о чем, что достигает чего-то столь же надежного в статическом времени ссылки с помощью инструментальной цепочки GNU. Возможно, это стоит ошибка RFE против binutils.

Ответ 2

Я думаю, что единственный правильный способ решить проблему - переместить содержимое решения bar.a в некоторую разделяемую библиотеку и, таким образом, решить проблему наследования алмазов... но я думаю, что это выходит за рамки этой проблемы.

Единственное, что я могу думать для вас, это создать "настраиваемый" валидатор, выполненный непосредственно перед связыванием окончательного исполняемого файла. Stg вот так:

nm *.so | \ # get all symbols
    grep " [B|T] " | \ # only exported ones
    egrep -v "__bss_start|_end|_fini|_init" | \ # filter out some commons, probably to be extended
    awk '{print $3}' | \ # get only symbol name
    sort | uniq -c | \ # sort and count
    egrep -v "^[ ]+1 " # get only those that have multiple definitions

Это печатает все сильные (экспортированные) символы из библиотек, которые определены более одного раза. Вы можете легко обернуть его в script, который возвращает код состояния ошибки, если вывод не пуст со значимым сообщением.

Экспериментальная версия вашего исправленного make файла будет выглядеть так:

run: main.cxx foo.so bar.so
    ! nm foo.so bar.so | grep " [B|T] " | egrep -v "__bss_start|_end|_fini|_init" | awk '{print $3}' | sort | uniq -c | egrep -v "^[ ]+1 "
    g++ -std=c++11 $^ -o [email protected]

(обратите внимание ! на обратный код завершения финальной grep, который ищет любой вывод uniq -c, который не начинается с голого 1)

Я знаю, что это действительно хакерское, уродливое и непереносимое решение, но я подумал, что это может иметь значение в таких случаях, как ваши.