3

У меня 2 библиотеки: test.1 и test.2. Обе библиотеки содержат одну глобальную функцию extern «C»void f(); с различными реализациями (всего лишь cout для теста).Одинаковые символы в разных библиотеках и порядок связи

я сделал следующий тест:

Тест 1 Динамическая компоновка:
Если добавить libtest.1.so и затем libtest.2.so в Makefile исполняемого файла, а затем вызвать f(); в main, libtest.1.so->f() называется.
Если изменить порядок в Makefile, libtest.2.so->f() называется

Тест 2 Статическое связывание:
Абсолютно то же самое происходит со статическими библиотеками

Тест 3 Динамическая загрузка
Как библиотека ручная загрузка, все работает так, как ожидалось.


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

Кроме того, это не нарушает правило с одним определением, так как ситуация отличается.

Это также не зависимость-ад (не то, что это связано с этим вообще), ни сшивание облом ..

Таким образом, чем это? Неопределенное поведение? Неопределенное поведение? Или это действительно зависит от порядка привязки?

И есть ли способ легко обнаружить такие ситуации?


Похожие вопросы:
dlopen vs linking overhead
What is the difference between dynamic linking and dynamic loading
Is there a downside to using -Bsymbolic-functions?
Why does the order in which libraries are linked sometimes cause errors in GCC?
linking two shared libraries with some of the same symbols


EDIT я сделал еще два испытания, которые подтверждают это UB:

Я добавил вторую функцию void g() в test.1 и НЕ в test.2.

Использование динамической компоновки и .so libs, то же самое происходит - f вызывается таким же образом, g также исполняется (как и ожидалось).

Но используя статические ссылки в настоящее время изменяет вещи: если test.1 является перед темtest.2, нет никаких ошибок, обе функции от test.1 называется.
Но когда порядок изменен, возникает ошибка «нескольких определений».

Понятно, что «нет необходимости в диагностике» (см. Ответ @ MarkB), но «странно», что иногда происходит ошибка, иногда - нет.

В любом случае, ответ довольно ясен и объясняет все выше - UB.

+0

/Я считаю, что стандартом, над которым вы должны проверять это поведение, является стандарт ELF, а не стандарт C++. – ninjalj

ответ

1

Это абсолютно нарушает одно правило определения в случаях 1 & 2. В случае 3, поскольку вы явно указываете, какая версия функции для ее выполнения может быть или не быть. Нарушение ODR - это неопределенное поведение, при этом не требуется диагностика.

3,2/3:

Каждая программа должна содержать ровно одно определение каждого не-рядный функции или переменной, которая является ODR используемых в этой программе; не требуется диагностика .

+0

Хм .. Теперь я не уверен, почему и как я решил, что это не нарушит ODR. Но мне все равно беспокоит, почему для этого нет ошибки, поскольку линкер четко видит два определения в этом случае (не то он должен, но все же). Итак, если эти функции не являются глобальными, но некоторые «внутренние», «-символьные» «решают» это? –

5

Библиотека представляет собой набор объектных файлов. Компилятор извлекает объекты из библиотек по мере необходимости, чтобы удовлетворить неразрешенные символы. Важно то, что компоновщик проверяет библиотеки в том порядке, в каком они появляются в командной строке, каждый раз просматривает каждую библиотеку (если только командная строка не упоминает библиотеку более одного раза) и принимает только объекты, которые удовлетворяют некоторой ссылке.

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

Теперь второй комплект тестов. В случае успеха test.1 удовлетворяет как f, так и g ссылкам, поэтому test.2 не имеет значения. В случае сбоя test.2 удовлетворяет заданию f, но g остается неопределенным. Чтобы удовлетворить g, компоновщик должен вытащить какой-то объект из test.1, который также может поставлять f. Очевидно, это множественное определение.

Обратите внимание, что для получения ошибки вы должны иметь f и g в том же объекте. Если test.1 состоит из 2 объектов (один определяет f, а другой определяет g), ошибка исчезает.

+0

Хорошее объяснение, спасибо. +1 –