Статическая переменная компиляции C ++ и общие объекты - PullRequest
3 голосов
/ 17 октября 2011

Описание:

а. Класс X содержит статический член приватных данных ptr и статический член публичной функции getptr () / setptr ().
В X.cpp для ptr установлено значение NULL.

б. libXYZ.so (общий объект) содержит объект класса X (то есть libXYZ.so содержит X.o).

с. libVWX.so (общий объект) содержит объект класса X (то есть libVWX.so содержит X.o).

д. Исполняемый файл a.exe содержит X.cpp как часть модулей перевода и, наконец, связан с libXYZ.so, libVWX.so

PS:
1. Ни в одном из классов нет пространств имен пользователей.
2. Библиотеки и исполняемый файл также содержат много других классов.
3. не было сделано dlopen (). Все библиотеки связаны во время компиляции с использованием флагов -L и -l.

Постановка задачи:

  1. При компиляции и связывании a.exe с другими библиотеками (например, libXYZ.so и libVWX.so) я ожидал ошибку компоновщика (конфликт / возникновение одного и того же символа несколько раз), но не получил ее.

  2. Когда программа была выполнена - поведение было странным в SUSE 10 Linux и HP-UX 11 IA64.
    В Linux, когда поток выполнения передавался по всем объектам в разных библиотеках, эффект был зарегистрирован только в одной копии X.
    В HPUX, когда поток выполнения распространялся на все объекты в разных библиотеках, эффект регистрировался в 3 разных копиях X (по 2 в каждой библиотеке и по 1 для исполняемого файла)

PS: я имею в виду, что во время работы программы поток прошел через несколько объектов, принадлежащих a.exe, libXYZ.so и libVWX.so), которые взаимодействовали со статическим указателем, принадлежащим X.

Вопрос:

  • Ошибка ожидаемого компоновщика неверна? Поскольку два компилятора проходили компиляцию без вывода сообщений, может быть, в случае сценария такого типа, который мне не хватает, существует стандартное правило. Если так, пожалуйста, дайте мне знать то же самое.
  • Как компилятор (gcc в Linux и aCC в HPUX) определяет, сколько копий X сохранить в конечном исполняемом файле, и ссылается на них в таких сценариях.
  • Существует ли какой-либо флаг, поддерживаемый gcc и aCC, который будет предупреждать / останавливать компиляцию для пользователей в подобных сценариях?

Заранее спасибо за помощь.

Ответы [ 3 ]

1 голос
/ 17 октября 2011

Я не слишком уверен, что полностью понял сценарий. Тем не мение, поведение по умолчанию при загрузке динамических объектов в Linux (и других Unices) - сделать все символы в библиотеке доступными и использовать только первый встреченный. Таким образом, если вы оба libXYZ.so и libVWX.so содержать символ X::ourData, это не ошибка; если вы загрузите их в в этом порядке libVWX.so будет использовать X::ourData из libXYZ.so, вместо своего. По логике это очень похоже на определение шаблона в заголовке: компилятор выбирает один, более или менее случайно, и если любое из определений не совпадает со всеми остальными, это неопределенное поведение. Такое поведение может быть отменяется путем передачи флага RTLD_LOCAL на dlopen.

По поводу ваших вопросов:

  • Компоновщик просто реализует поведение по умолчанию dlopen (то, что вы получаете, когда система неявно загружает библиотеку). Таким образом, нет ошибки (но логический эквивалент неопределенного поведения, если любое из определений не совпадает).

  • Компилятор не решает. Решение принимается при загрузке .so в зависимости от того, указали ли вы RTLD_GLOBAL или RTLD_LOCAL при вызове dlopen. Когда среда выполнения вызывает неявно dlopen, чтобы разрешить зависимость, она будет использовать RTLD_GLOBAL, если это происходит при загрузке основного исполняемого файла, и то, что когда-либо использовалось для загрузки библиотеки, когда зависимость приходит из библиотеки. (Это, конечно, означает, что RTLD_GLOBAL будет распространяться до тех пор, пока вы не вызовете dlopen явно.)

0 голосов
/ 17 октября 2011

Функция "public static", поэтому я предполагаю, что это OOP-значение "static" (не требует экземпляра), а не C-значение static (file-static; локально для модуля компиляции).Поэтому функции являются внешними.

Теперь в Linux у вас есть явное право переопределять символы библиотеки, как с использованием другой библиотеки, так и в исполняемом файле.Все внешние символы в библиотеках разрешаются с использованием глобальной таблицы смещений, даже той, которую фактически определяет библиотека.И хотя функции, определенные в исполняемом файле, обычно не разрешаются таким образом, но компоновщик замечает, что символы попадут в таблицу символов из библиотек, и помещает туда ссылку на определенную исполняемым файлом.Таким образом, библиотеки увидят символ, определенный в исполняемом файле, если вы его сгенерировали.

Это явная функция, разработанная для того, чтобы вы могли выполнять такие вещи, как замена функций выделения памяти или операции переноса файловой системы.HP-UX, вероятно, не имеет этой функции, поэтому каждая библиотека в конечном итоге вызывает свою собственную реализацию, в то время как любой другой объект, у которого был бы символ undefined, увидит один из них.

0 голосов
/ 17 октября 2011

Существует разница между символами "extern" (по умолчанию в c ++) и "extern совместно используемой библиотеки".По умолчанию символы являются только «внешними», что означает область действия одного «блока ссылок», например исполняемого файла или библиотеки.Таким образом, ожидаемое поведение будет таким: ошибка компилятора отсутствует, и каждый модуль работает со своей собственной копией.Это, конечно, приводит к проблемам в случае встроенной компиляции и т. Д. И т. Д. ... Чтобы объявить символ "разделяемая библиотека extern", вы должны использовать файл ".def" или объявление компилятора.например, в Visual C ++ это будет "_ declspec (dllexport)" и " _declspec (dllimport)".В настоящее время я не знаю объявлений для gcc, но уверен, что кто-то знает: -)

...