Защита от двоичной несовместимости, возникающей из-за опечаток аргументов компилятора в директивах препроцессора общих, возможно, шаблонных заголовков, которые управляют условной компиляцией, в разных единицах компиляции?
Ex.
g++ ... -DYOUR_NORMAl_FLAG ... -o libA.so
/**Another compilation unit, or even project. **/
g++ ... -DYOUR_NORMA1_FLAG ... -o libB.so
/**Another compilation unit, or even project. **/
g++ ... -DYOUR_NORMAI_FLAG ... main.cpp libA.so //The possibilities!
Недавно я столкнулся со странной ошибкой: симптомом был одиночный SIGSEGV, который после перекомпиляции всегда появлялся в одном и том же месте. Это заставило меня поверить, что происходит какое-то повреждение памяти, и фактический базовый указатель вообще не указатель, а какой-то раздел данных.
Я спасаю вас от долгого и напряженного путешествия, на которое уйдет почти два, в остальном, отличных рабочих дня, чтобы отследить проблему. Достаточно сказать, что Valgrind, GDB, nm, readelf, электрическое ограждение, защита от взлома стека GCC, а затем еще несколько мер / методов / подходов потерпели неудачу.
В полном опустошении мое внимание обратилось на мельчайшие детали в процессе сборки, который был аналогичен:
Только в случае, когда большая библиотека использовалась в качестве статической или динамической библиотечной зависимости (т.е. динамический компоновщик загружал ее автоматически, без дублирования)
была ли проблема. В тестовом случае, когда весь код библиотеки был просто включен в тесты, все работало: это был самый важный ключ.
В итоге получилась самая простая вещь: одиночная (!) Опечатка.
Оказывается, флаги компиляции отличались одним символом в наборе тестов, и большая библиотека: определение, которое управляло поведением маленькой библиотеки, было написано с ошибкой.
Кусочек важной информации: небольшая библиотека имела немного шаблоны. Они использовались непосредственно в каждом случае, без явного указания заранее. Содержимое одного из шаблонных классов изменялось при переключении флага: некоторые поля данных просто не присутствовали в случае определения флага!
Линкер ничего этого не заметил. (Так как класс был шаблонным, результирующие символы были слабыми.)
Код использовал динамическое приведение, и класс, затронутый этой проблемой, наследовал от искаженного класса -> дела пошли на юг.
Мой вопрос заключается в следующем: как бы вы защитились от такого рода проблем? Существуют ли какие-либо инструменты или решения для решения этой конкретной проблемы?
Я подумал о двух вещах и считаю, что на уровне объектных файлов невозможно построить защиту:
Я хотел бы пойти дальше и погасить несколько вероятных источников пламени, которые, возможно, вспыхивают в некоторых читателях: «не используйте символы препроцессора» здесь не вариант.
Я знаю о:
Если это имеет значение, не используйте регистр по умолчанию.
#ifdef YOUR_NORMAL_FLAG
// some code
#elsif YOUR_SPECIAL_FLAG
// some other code
#else
// in case of a typo, this is a compilation error
# error "No flag specified"#endif
Это может привести к большому списку опций компилятора, если условная компиляция чрезмерно используется, но есть способы обойти это, например, определение конфигурационных файлов
flag=normal
flag2=special
которые анализируются сценариями сборки и генерируют параметры, и, возможно, могут проверять опечатки или могут быть проанализированы непосредственно из Makefile.
Других решений пока нет …