переменная extern, определенная внутри .so, и исполняемые отведения имеют неопределенное поведение? - PullRequest
0 голосов
/ 24 января 2012

Я работаю в устаревшем коде и столкнулся со странной проблемой. У меня есть исполняемый файл, и он использует библиотеку .so с именем dbaccess.so.

У меня также есть библиотека с именем lib_base, и эта библиотека статически связана с обоими проектами (dbaccess.so и исполняемым файлом).

              ________   
             |lib_base|
             |________|
           /           \
          / statically  \ 
         /   linked      \
 ______ /                 \ _____________
|my_app|                   | dbaccess.so |
|______|  <---dinamically  |_____________|

Проблема в том, что внутри "lib_base" в файле .cpp (misc.cpp) есть переменная, определенная как

char apName[_MAX_FNAME];

И внутри .cpp (clientconn.cpp) в "dbaccess.so" у меня есть:

extern char apName[_MAX_FNAME];

И я заметил странное поведение в коде. Похоже, что переменная "extern" сбивает с толку определение "apName" внутри lib_base my_app и определение внутри dbaccess lib_base.

При отладке функции dbaccess с помощью gdb происходит следующее:

strcpy( apName, "test" );
printf( apName );

в консоли выводится «test», но если я напишу следующую строку в консоли gdb после strcpy:

print apName

Он печатает "apfile.ini".

Кто-нибудь знает, действительно ли эта проблема связана с тем, что lib_base связан с обоими проектами? Есть ли какой-нибудь флаг компиляции или что-то, что можно было бы передать при компиляции dbaccess.so, чтобы избежать этого?

Я использую Linux и GCC в качестве компилятора.

1 Ответ

1 голос
/ 25 января 2012

Если вы определяете переменную в файле .cpp, которая должна быть видна только внутри этого файла, вы должны использовать static.

Если вы хотите разделить значение между двумя lib_base, вам не следует статически связывать его дважды - вы получаете дубликаты как кода, так и данных, что неэффективно и запутанно.

В C ++ есть такая вещь, которая называется правилом единого определения, которая гласит, что вы можете определять одну и ту же вещь столько раз, сколько захотите (так что вы можете включать один и тот же заголовочный файл в несколько файлов cpp), и до тех пор, пока все они определены одинаково , и это будет просто работать. По сути это означает, что компоновщику разрешено выбрасывать дублирующиеся объекты и просто сохранять один случайным образом. Если вы прервете ODR, компилятор не узнает, а компоновщик, вероятно, не узнает, и , тогда , вы получите неопределенное поведение.

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

Если предположить, что вы не хотите, чтобы оба экземпляра lib_base совместно использовали определение aPname, тогда ваше приложение, по-видимому, просто отлично связано (printf доказывает это), но GDB плохо работает с неоднозначными именами символов. Когда GDB ищет имена символов, он не обязательно знает, где искать в первую очередь, поэтому вы не получите то, что ожидали.

Иногда GDB может разобраться в себе, если вы сначала выполните list main (или что-то еще), чтобы установить желаемый контекст. Хотя, по сути, не дублируйте код - отладчику это не понравится.

Если вы должны это сделать, проверьте команды symbol-table и add-symbol-table - вы можете выбрать загрузку только символов из одного или другого файла и отладку нужного вам бита.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...