[C] rhel vs CentOs
После копирования исполняемого файла с одной системы в другую, столкнулся с периодически возникающими ошибками работы с памятью (приложение вылетает, ругаясь на free() или malloc()).
Увы, из-за недостатка знаний и опыта, не могу определиться с причиной возникновения проблем:
1. разница в разрядности систем
2. неправильное использование free и malloc/calloc
Прошу помочь разобраться в причине и тем самым наметить пути к решению проблемы.
Прошу помочь разобраться в причине и тем самым наметить пути к решению проблемы.
причина - перенос бинарника с 32-бит на 64-бита.
пересоберите приложение на новой системе
ldd <исполняемый_файл_вашего_приложения>
Это, конечно, может ничего не дать. Но в моей практике работают приложения в режиме совместимости с 32-бит и ничего. Может причина в другом? И что значит "ругаясь на free() и malloc()"? Как ругаясь? Где ругаясь? Что за приложение (хотя бы из какой области)? Пишите больше информации.
да работают, конечно. только зачем так извращаться? или у аффтара исходников нету? :)
пересоберите приложение на новой системе
поставил себе на виртуальную машину CentOS 5.4 x86-64
собрал на нем исполняемый файл
закинул на целевой сервер CentOS
поставил права 755
при запуске получаю ошибку:
мой CentOS (виртуальная машина):
Linux localhost.localdomain 2.6.18-164.el5 #1 SMP Thu Sep 3 03:28:30 EDT 2009 x86_64 x86_64 x86_64 GNU/Linux
целевой CentOS:
Linux remotehost 2.6.29.5-grsec-hostnoc-4.2.0-i386-libata #1 SMP Wed Jul 8 17:07:07 EDT 2009 i686 i686 i386 GNU/Linux
ldd <исполняемый_файл_вашего_приложения>
Это, конечно, может ничего не дать. Но в моей практике работают приложения в режиме совместимости с 32-бит и ничего. Может причина в другом? И что значит "ругаясь на free() и malloc()"? Как ругаясь? Где ругаясь? Что за приложение (хотя бы из какой области)? Пишите больше информации.
Целевой CentOS:
[root@remotehost sbin]# ldd mysoft
linux-gate.so.1 => (0xb7fa2000)
libdl.so.2 => /lib/libdl.so.2 (0x496a6000)
libcrypto.so.6 => /lib/libcrypto.so.6 (0x49b61000)
libssl.so.6 => /lib/libssl.so.6 (0x4a22b000)
librt.so.1 => /lib/librt.so.1 (0x496ee000)
libpthread.so.0 => /lib/libpthread.so.0 (0x496ac000)
libc.so.6 => /lib/libc.so.6 (0x4955f000)
/lib/ld-linux.so.2 (0x4953c000)
libz.so.1 => /usr/lib/libz.so.1 (0x4975b000)
libgssapi_krb5.so.2 => /usr/lib/libgssapi_krb5.so.2 (0x49e3e000)
libkrb5.so.3 => /usr/lib/libkrb5.so.3 (0x49e6e000)
libcom_err.so.2 => /lib/libcom_err.so.2 (0x49d91000)
libk5crypto.so.3 => /usr/lib/libk5crypto.so.3 (0x49f0d000)
libresolv.so.2 => /lib/libresolv.so.2 (0x49dad000)
libkrb5support.so.0 => /usr/lib/libkrb5support.so.0 (0x4a10e000)
libkeyutils.so.1 => /lib/libkeyutils.so.1 (0x49d96000)
libselinux.so.1 => /lib/libselinux.so.1 (0x49741000)
libsepol.so.1 => /lib/libsepol.so.1 (0x496f9000)
ошибка, с которой вылетает 32-битное приложение на 64-битной системе (просьба скажите если нужен листинг memory map):
*** glibc detected *** mysoft: free(): invalid pointer: 0x4969fff4 ***
======= Backtrace: =========
/lib/libc.so.6[0x495c8595]
/lib/libc.so.6(cfree+0x59)[0x495c89d9]
mysoft[0x804e1eb]
/lib/libpthread.so.0[0x496b15ab]
/lib/libc.so.6(clone+0x5e)[0x49630cfe]
======= Memory map: ========
...
Операционная система......: CentOS 5 - 64-BIT
однако:
[root@remoteserver bin]# file ls
ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped
правильно ли я понимаю, что разрядность целевой CentOS 32 бит?
Если я правильно понимаю, вы тут попутали немного. Тут же информация о файле:
ELF - исполняемый файл
32-bit - собран под 32-х битную систему
Но я профан в Юникс, могу ошибаться
ELF - исполняемый файл
32-bit - собран под 32-х битную систему
Но я профан в Юникс, могу ошибаться
дело в том, что информация о системных файлах в совокупности с информацией о ядре (где написано i386) и невозможностью запустить 64-битный исполняемый файл (ldd mysoft - not a binary file) заставляют задуматься о корректности указанных хостером данных.
Linux remotehost 2.6.29.5-grsec-hostnoc-4.2.0-i386-libata #1 SMP Wed Jul 8 17:07:07 EDT 2009 i686 i686 i386 GNU/Linux
и с целевой
то тут вы правы, имеет место жестокий обман
собрал на нем исполняемый файл
закинул на целевой сервер CentOS
поставил права 755
при запуске получаю ошибку:
Все говорит о том, что у хостера 32-битная система (вывод ldd тоже).
Ищите разницу в сборках библиотеки libc.
А вообще squirl в начале был прав :) Если есть исходник (а он судя по всему есть), то проще пересобрать приложение на стороне хостера.
Есть еще такой вариант. Хостер может предоставлять ssh в chroot-режиме (см. man sshd_config, ChrootDirectory). То есть сессия будет работать в некотором специально созданном окружении. С точки зрения безопасности - это правильный подход. Чтобы пользовавтель не делал, он не положит систему. Другое дело, что хостер предлагает в этом окружении. Может действительно система CentOS-64бита, а доступное Вам окружение - это 32-битная урезанная среда. Покапайтесь в файловой системе хостера. Обратите внимание на содержимое /etc /dev /proc /usr/lib/gcc.
Ищите разницу в сборках библиотеки libc.
А вообще squirl в начале был прав :) Если есть исходник (а он судя по всему есть), то проще пересобрать приложение на стороне хостера.
Есть еще такой вариант. Хостер может предоставлять ssh в chroot-режиме (см. man sshd_config, ChrootDirectory). То есть сессия будет работать в некотором специально созданном окружении. С точки зрения безопасности - это правильный подход. Чтобы пользовавтель не делал, он не положит систему. Другое дело, что хостер предлагает в этом окружении. Может действительно система CentOS-64бита, а доступное Вам окружение - это 32-битная урезанная среда. Покапайтесь в файловой системе хостера. Обратите внимание на содержимое /etc /dev /proc /usr/lib/gcc.
I.
мое:
glibc-2.5-42.el5_4.2.i686
libc 2.5
сервер:
glibc-2.5-42.i686
libc 2.5
версии стало быть одинаковые, однако в локальной системе никаких оповещений об ошибках нету (как включить?), даже при запуске с MALLOC_CHECK_=3, когда приложение обязано сообщить об ошибке работы с памятью и завершиться.
II.
насчет режимов - не проверял, но мне кажется что смысла устанавливать на выделенный сервер такие ограничения - нет, но на всякий случай хостеру тикет напишу
III.
попробовал собрать сорцы на целевой системе, все та же ошибка из поста №6.
IV.
приложение имеет один поток, который каждую секунду делает рутинную работу. в нормальном состоянии (в моей системе) приложение постоянно оповещает об очередном старте внутри потока (бесконечный цикл). на сервере же выводится лишь 6-7 сообщений, после чего - тишина. почему так происходит?
Попробуйте следующее:
1. Увеличить verbosity у приложения, поставить дополнительные проверки на большинство системных вызовов, особенно, где malloc, free, а также там, где идет работа с потоками, их синхронизация и работа с общими данными.
ТАКЖЕ СТОИТ! воспользоваться стандартной отладочной методикой. Переопределить вызовы malloc и free на, например, my_malloc и my_free. В этих процедурах записываем в лог-файл все, что передается в качестве параметров (указатель, размер). Потом в лог-файле ищите вызов free для двух одниковых указателей, веротяно кто-то у вас вызывает free на память, которая уже удалена. Частая проблема в многопоточных приложениях.
2. Привлечь gdb :) По своему интерфейсу он неказист (непривычно с командной строкой работать), но когда привыкаешь, то оказывается весьма полезной программой.
Буквально на прошлой неделе занимался тем же самым. Собираем приложение из исходников на glibc2.6.1+gcc4.1.2 все работает, собираем на glibc2.3.3+gcc3.3.3 приложение валится. Перерыли 200 килобайт исходников, ошибка оказалась банальна. Кто-то случайно вместо semname="MySemaphore" написал semname=="MySemaphore". Новый glibc прожевал вызов sem_open с неверно проинициализированным именем, а старый отказывался. Данная строчка была закопана, ну очень глубоко. Убили 3 дня. Кто-то потом проставлялся :)
Проблема зарылась в потоке, который выполнял рутинную работу. Для отправки данных юзал сокеты в совокупности с сетами (fd_set) для таймаутов (в частности для connect). В моем коде было 2 ошибки:
tv.tv_sec и tv.tv_usec я устанавливал до запуска бесконечного цикла while(1), в локальных условиях ошибка не возникала, а на удаленном сервере после первой итерации значения tv.tv_sec и tv.tv_usec приравнивались к нулю и случайному числу соответственно.
Все было бы неплохо, если бы не было второй ошибки, которая заключалась в обработке результата select, где в случаях неудач (а таких вариантов там 4-5) я поставил continue в расчете на то, чтобы приступать к следующей итерации цикла while(1), однако я совсем забыл, что обработка select тоже сидит внутри цикла do .. while() и отсюда поток в бесконечно-быстром режиме (так как sleep там не предусмотрен) бегал по замкнутому кругу.
Это объясняет почему в консоль больше не выводились сообщения + работоспособность программы была подорвана.
Правда для меня до сих пор остается загадкой, почему приложение ругалось на работу с памятью и gdb bt указывал на совсем другие участки кода.