Edit: Made title a bit clearer.
I am trying to wrap glibc’s __assert_fail and __assert_perror_fail functions with my own that log the messages using syslog.
I have verified that if I fail an assert my functions get called. The problem lies in libzmq’s assertions. libzmq’s assertions only invoke my wrapper functions when I build with -static.
NOTES
-
I patched libzmq to call
__assert_*instead offprintf(stderr, ...), and I have verified that it correctly calls__assert_*. -
I also patched libzmq to randomly have assertion failures from within the zmq_assert macros so that I can easily reproduce. If the patch is wanted, I will put it up.
Here is some test code
#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;
}
Which I build 4 ways with/without DO_ASSERT, dynamic/static
$ 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
And I get the following when running them
$ 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
So what am I doing wrong? According to man ld
If you link other code with this file using –wrap malloc, then all calls to “malloc” will call the function “__wrap_malloc” instead.
which is not what I am seeing.
Your mental model of how
--wraplinker option works is likely all wrong.It’s quite simple really: when you are linking a particular ELF executable or shared library with
--wrap foo, all the linker does is:foo, it replaces it with a reference to__wrap_foo,__real_foo, it replaces with a reference tofoo.I repeat, that is all it does. In particular, since you have not relinked
libzmq.sowith--wrap,libzmq.socontinues to call__assert_fail(i.e. no renaming whatsoever is happening insidelibzmq.so).In order to interpose
libcfunction, forget the--wrap.Instead, simply define a new
__assert_failin your main executable. When you do that, your definition will get called regardless of whether the call comes from the main executable, or fromlibzmq.so(or from anywhere else).If you don’t want to call the version of
__assert_failfrom libc, you are done. If you do, you’ll have to look it up dynamically (viadlsym).