Защита от случайного несовместимости объектов?

TL; DR

Защита от двоичной несовместимости, возникающей из-за опечаток аргументов компилятора в директивах препроцессора общих, возможно, шаблонных заголовков, которые управляют условной компиляцией, в разных единицах компиляции?

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, а затем еще несколько мер / методов / подходов потерпели неудачу.

В полном опустошении мое внимание обратилось на мельчайшие детали в процессе сборки, который был аналогичен:

  • Постройте одну маленькую библиотеку.
  • Постройте одну большую библиотеку, которая использует маленькую.
  • Создайте набор тестов из большой библиотеки.

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

Решение»

В итоге получилась самая простая вещь: одиночная (!) Опечатка.

Оказывается, флаги компиляции отличались одним символом в наборе тестов, и большая библиотека: определение, которое управляло поведением маленькой библиотеки, было написано с ошибкой.
Кусочек важной информации: небольшая библиотека имела немного шаблоны. Они использовались непосредственно в каждом случае, без явного указания заранее. Содержимое одного из шаблонных классов изменялось при переключении флага: некоторые поля данных просто не присутствовали в случае определения флага!
Линкер ничего этого не заметил. (Так как класс был шаблонным, результирующие символы были слабыми.)
Код использовал динамическое приведение, и класс, затронутый этой проблемой, наследовал от искаженного класса -> дела пошли на юг.

Мой вопрос заключается в следующем: как бы вы защитились от такого рода проблем? Существуют ли какие-либо инструменты или решения для решения этой конкретной проблемы?

Будущая проверка

Я подумал о двух вещах и считаю, что на уровне объектных файлов невозможно построить защиту:

  • 1: Сохранение параметров, реализованных в виде символов препроцессора, в некотором четко определенном месте, предпочтительно выделенных отдельным этапом сборки. Предоставьте скрипт проверки, который использует его для проверки всех определений компилятора и определений в пользовательском коде. Интегрируйте эту проверку в процесс сборки. Возможно, используйте расстояние Левенштейна или подобное для проверки орфографических ошибок. Дорого, и сценарий / решение может быть сложным. Возможна проблема с подобными флагами (но зачем их иметь?), Дополнительные файлы должны сопровождать код скомпилированной библиотеки. (Ну, может быть, с DWARF 2 это не соответствует действительности, но давайте предположим, что мы этого не хотим.)
  • 2: Централизовать опции сборки: дешево, опция настройки оставлена ​​открытой (например, makefile.local), но создает монолитные чудовища, сильные связи проекта.

Я хотел бы пойти дальше и погасить несколько вероятных источников пламени, которые, возможно, вспыхивают в некоторых читателях: «не используйте символы препроцессора» здесь не вариант.

  • Условная компиляция имеет свое место в высокопроизводительном коде, и выполнение всего с шаблонами и enable_if-s приведет к ненужному усложнению. Хотя вышеуказанное решение обычно не желательный оно может возникать сформировать процесс разработки.
  • Пожалуйста, предположите, что вы не можете контролировать ситуацию, предположите, что у вас есть устаревший код, и сделайте все возможное, чтобы заставить себя избежать побочных действий.
  • Если это не сработает, обобщите в обнаружении несовместимости ABI, хотя это может слишком расширить область вопроса для SO.

Я знаю о:

1

Решение

Если это имеет значение, не используйте регистр по умолчанию.

#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.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]