linux — программа c ++ вылетает при соединении с двумя сторонними общими библиотеками

У меня есть две сторонние разделяемые библиотеки для платформы Linux (без источника, без документа). Библиотеки работают нормально, когда они связаны с программой отдельно (g ++ xx.cpp lib1.so или g ++ xx.cpp lib2.so).

Однако, когда любая программа на С ++ связана с этими двумя общими библиотеками одновременно, программа неизбежно завершается с ошибкой «двойного освобождения» (g ++ xx.cpp lib1.so lib2.so).

Даже если программа на С ++ является пустой Привет, мировая программа и не имеет ничего общего с этими библиотеками, она все равно вылетает.

#include <iostream>
using namespace std;
int main(){
cout<<"haha, I crash again. Catch me if you can"<<endl;
return 0;
}

Makefile:

g++ helloword.cpp lib1.so lib2.so

Я кое-что понял, что эти библиотеки lib1.so lib2.so могут иметь общую глобальную переменную, и они дважды уничтожают некоторую переменную. Я пробовал gdb и valgrind, но не могу извлечь полезную информацию из backtrace.

Есть ли способ, которым я мог бы изолировать эти две общие библиотеки и заставить их работать в песочница манера?

EDITED (добавление дампов ядра и трассировки GDB):

Я только что связал вышеупомянутую игрушечную пустую программу helloword с двумя библиотеками (платформа: centos 7.0 64bit с gcc4.8.2):

g++ helloworld.cpp  lib1.so lib2.so -o check

Valgrind:

==29953== Invalid free() / delete / delete[] / realloc()
==29953==    at 0x4C29991: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==29953==    by 0x613E589: __cxa_finalize (in /usr/lib64/libc-2.17.so)
==29953==    by 0x549B725: ??? (in /home/fanbin/InventoryManagment/lib1.so)
==29953==    by 0x5551720: ??? (in /home/fanbin/InventoryManagment/lib1.so)
==29953==    by 0x613E218: __run_exit_handlers (in /usr/lib64/libc-2.17.so)
==29953==    by 0x613E264: exit (in /usr/lib64/libc-2.17.so)
==29953==    by 0x6126AFB: (below main) (in /usr/lib64/libc-2.17.so)
==29953==  Address 0x6afb780 is 0 bytes inside a block of size 624 free'd
==29953==    at 0x4C29991: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==29953==    by 0x613E589: __cxa_finalize (in /usr/lib64/libc-2.17.so)
==29953==    by 0x4F07AC5: ??? (in /home/fanbin/InventoryManagment/lib2.so)
==29953==    by 0x5039900: ??? (in /home/fanbin/InventoryManagment/lib2.so)
==29953==    by 0x613E218: __run_exit_handlers (in /usr/lib64/libc-2.17.so)
==29953==    by 0x613E264: exit (in /usr/lib64/libc-2.17.so)
==29953==    by 0x6126AFB: (below main) (in /usr/lib64/libc-2.17.so)

GDB обратное сообщение:

(gdb) bt
#0  0x00007ffff677d989 in raise () from /lib64/libc.so.6
#1  0x00007ffff677f098 in abort () from /lib64/libc.so.6
#2  0x00007ffff67be197 in __libc_message () from /lib64/libc.so.6
#3  0x00007ffff67c556d in _int_free () from /lib64/libc.so.6
#4  0x00007ffff7414aa2 in __tcf_0 () from ./lib1.so
#5  0x00007ffff678158a in __cxa_finalize () from /lib64/libc.so.6
#6  0x00007ffff739f726 in __do_global_dtors_aux () from ./lib1.so
#7  0x0000000000600dc8 in __init_array_start ()
#8  0x00007fffffffe2c0 in ?? ()
#9  0x00007ffff7455721 in _fini () from ./lib1.so
#10 0x00007fffffffe2c0 in ?? ()
#11 0x00007ffff7debb98 in _dl_fini () from /lib64/ld-linux-x86-64.so.2
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

Обновить

Спасибо за помощь @RaduChivu, я нашел очень похожий сценарий: ошибка сегментации в __tcf_0 при выходе из программы , Похоже, что между двумя библиотеками существует конфликт глобальных переменных. Учитывая то, что у меня нет исходных файлов для этих двух внешних общих библиотек, за исключением использования двух отдельных процессов, есть ли какой-либо другой способ разрешить этот конфликт?

7

Решение

Одним из возможных решений было бы никогда не звонить exit, Чтобы прекратить вашу программу, просто позвоните _exit, Если есть что-то конкретное, что вам нужно сделать, это обычно делается exitПросто сделай это сам перед звонком _exit,

2

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

Я решил эту проблему после дневного поиска и оставил здесь записку на случай, если кто-нибудь еще столкнется с этим в будущем.

объяснение

Это доказывает, что @RaduChivn и мое предположение верны: две общие библиотеки могут иметь общую глобальную переменную. Даже когда пустая программа связана с обеими общими библиотеками одновременно, при выходе из нее глобальная переменная будет пытаться освободиться дважды, что приведет к двойному свободному повреждению.

Подсказка приходит от этого сообщения в gdb backtrace:

#4  0x00007ffff7414aa2 in __tcf_0 () from ./lib1.so

Как описано в этой теме:

Что такое функция __tcf_0? (Видно при использовании gprof и g ++),

tcf_0 — это функция, сгенерированная g ++ для разрушения статического объекта при запуске exit (). Это сообщение указывает на то, что двойное освобождение происходит, когда одна общая библиотека пытается выйти после другой.

Поскольку эти две библиотеки предназначены для совместной работы, коррупция является неприемлемым бедствием для инженеров. Как может такая некачественная, но все же очевидная ошибка выжить в пяти версиях? Вероятно, это связано с тем, что большинство пользователей библиотеки работают на платформе Windows (пакет которой работает нормально). Тем не менее, это предположение дает еще один намек на происхождение ошибки: разделяемая библиотека хорошо работает на Windows, в то время как происходит сбой в Linux; тогда это должно быть некоторое поведение, зависящее от ОС, вызывающее ошибку. Этот поток предоставляет некоторое понимание:

Глобальная переменная имеет несколько копий в Windows и одну в Linux при компиляции в exec и совместно используемой библиотеке..

Короче говоря, «внешние глобалы» из общих библиотек получают одну копию в Linux, но несколько копий в Windows.

Решение

(1) Естественно, у нас есть обходной путь, заключающийся в создании двух процессов, каждый из которых связан с одной библиотекой отдельно.

(2) @DavidSchwartz предоставляет другой обходной путь использования _exit (0) в конце программы, вместо обычного «return 0» или «exit (0)», это работает. В соответствии с

В чем разница между использованием _exit () & exit () в обычном Linux-форке?

, необходимо вручную сбросить файлы и проверить задания atexit; Что касается памяти, так как программа закрывается, ОС все равно восстанавливает всю память процесса, не о чем беспокоиться.

(3) Другой способ — использовать dlopen (xx.so, RTLD_LOCAL), сначала ослепляя все символы, а затем вручную отбрасывая нужные вам функциональные символы.

(@JonathanWakely отмечает, что RTLD_LOCAL имеет побочные эффекты, см. Комментарий).

В этом самом случае библиотечный кодер даже не использовал «extern C» в своих общих библиотеках, что делало искажение имени в файлах so совершенно нечитаемым; Если кому-то еще это понравится, может помочь следующая ветка:

Получение неопределенной ошибки символа при динамической загрузке разделяемой библиотеки

Если ваши общие библиотеки не очень хорошо поддерживаются, как и в моем случае, решения все еще возможны. Я вручную перебрал все необходимые функции и использовал nm, чтобы найти каждый соответствующий символ в файлах .so, связал их один за другим, и это сработало.

1

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