редактировать: Сделал название немного понятнее.
Я пытаюсь обернуть глиба __assert_fail
а также __assert_perror_fail
функции с моей собственной, которые регистрируют сообщения, используя syslog
,
Я проверил, что если я не смогу выполнить assert, то будут вызваны мои функции. Проблема заключается в утверждениях libzmq. Утверждения libzmq вызывают мои функции-оболочки только при сборке с -static
,
ЗАМЕТКИ
Я пропатчил libzmq, чтобы позвонить __assert_*
вместо fprintf(stderr, ...)
и я проверил, что он правильно звонит __assert_*
,
Я также исправил libzmq для случайного сбоя проверки утверждений из макросов zmq_assert, чтобы я мог легко воспроизвести. Если патч нужен, я его выложу.
Вот тестовый код
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <zmq.h>
extern "C" void
__wrap___assert_perror_fail(int __errnum, const char *__file,
unsigned int __line, const char *__function)
{
fprintf(stderr, "TESTING123:: %s:%u %s: Unexpected error: %s.\n",
__file, __line, __function, strerror(__errnum));
abort();
}
extern "C" void
__wrap___assert_fail(const char *__assertion, const char *__file,
unsigned int __line, const char *__function)
{
fprintf(stderr, "TESTING123:: %s:%u %s: Assertion '%s' failed.\n",
__file, __line, __function, __assertion);
abort();
}
int main()
{
#ifdef DO_ASSERT
assert(1 == 0);
#endif
void *ctx = zmq_init(0);
void *req = zmq_socket(ctx, ZMQ_REQ);
void *rep = zmq_socket(ctx, ZMQ_REQ);
zmq_bind(rep, "inproc://inproc-1");
zmq_connect(req, "inproc://inproc-1");
unsigned long long c = 0;
while (1) {
zmq_msg_t msg;
zmq_msg_init_size(&msg, 1024);
zmq_send(req, &msg, 0);
zmq_msg_close(&msg);
zmq_msg_init(&msg);
zmq_recv(rep, &msg, 0);
zmq_send(rep, &msg, 0);
zmq_msg_close(&msg);
zmq_msg_init(&msg);
zmq_recv(req, &msg, 0);
zmq_msg_close(&msg);
++c;
if (c % 1000000 == 0) {
fprintf(stderr, "processed %llu messages\n", c);
}
}
return 0;
}
Который я строю 4 способа с / без DO_ASSERT, динамический / статический
$ g++ -DDO_ASSERT -o t-me-dyn t.cc -Wl,-wrap,__assert_fail -Wl,-wrap,__asser_perror_fail -lzmq -lpthread -luuid -lrt
$ g++ -static -DDO_ASSERT -o t-me-sta t.cc -Wl,-wrap,__assert_fail -Wl,-wrap,__asser_perror_fail -lzmq -lpthread -luuid -lrt
$ g++ -o t-zmq-dyn t.cc -Wl,-wrap,__assert_fail -Wl,-wrap,__asser_perror_fail -lzmq -lpthread -luuid -lrt
$ g++ -static -o t-zmq-sta t.cc -Wl,-wrap,__assert_fail -Wl,-wrap,__asser_perror_fail -lzmq -lpthread -luuid -lrt
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.1/../../../../lib/libzmq.a(libzmq_la-ip.o): In function 'zmq::resolve_ip_interface(sockaddr_storage*, unsigned int*, char const*)':
(.text+0x49b): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
И я получаю следующее при запуске их
$ for bin in t-{me,zmq}-{dyn,sta}; do echo ==== $bin ====; ./$bin; done
==== t-me-dyn ====
TESTING123:: t.cc:29 int main(): Assertion '1 == 0' failed.
Aborted
==== t-me-sta ====
TESTING123:: t.cc:29 int main(): Assertion '1 == 0' failed.
Aborted
==== t-zmq-dyn ====
t-zmq-dyn: lb.cpp:142: int zmq::lb_t::send(zmq_msg_t*, int): Assertion 'rc == 0' failed.
Aborted
==== t-zmq-sta ====
TESTING123:: lb.cpp:142 int zmq::lb_t::send(zmq_msg_t*, int): Assertion 'rc == 0' failed.
Aborted
Так что я делаю не так? В соответствии с man ld
Если вы связываете другой код с этим файлом, используя —wrap malloc, тогда все вызовы «malloc» будут вызывать функцию «__wrap_malloc».
это не то, что я вижу.
Ваша ментальная модель того, как --wrap
опция компоновщика работает, скорее всего, все неправильно.
Это действительно очень просто: когда вы связываете конкретный исполняемый файл ELF или разделяемую библиотеку с --wrap foo
, все компоновщик делает это:
foo
, он заменяет его ссылкой на __wrap_foo
,__real_foo
заменяется ссылкой на foo
,Повторяю, это все оно делает. В частности, так как вы не переподключены libzmq.so
с --wrap
, libzmq.so
продолжает звонить __assert_fail
(т.е. не переименовывать то, что происходит внутри libzmq.so
).
Для того, чтобы вставить libc
функция, забудьте --wrap
,
Вместо этого просто определять новый __assert_fail
в вашем основном исполняемом файле. Когда вы это сделаете, ваше определение будет вызываться независимо от того, поступает ли вызов от основного исполняемого файла или от libzmq.so
(или откуда угодно).
Если вы не хотите называть версию __assert_fail
из libc, все готово. Если вы это сделаете, вам придется искать его динамически (через dlsym
).
Других решений пока нет …