编辑:使标题更清晰。
我正在尝试用我自己的函数包装glibc的__assert_fail
和__assert_perror_fail
函数,使用syslog
记录消息。
我已经验证过,如果我失败了断言,我的函数就会被调用。问题在于libzmq的断言。当我使用-static
构建时,libzmq的断言仅调用我的包装器函数。
备注
我修补了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;
}
我使用/不使用DO_ASSERT构建4种方式,动态/静态
$ 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
那么我做错了什么?根据{{1}}
如果使用--wrap malloc将其他代码与此文件链接,则对“malloc”的所有调用都将调用函数“__wrap_malloc”。
这不是我所看到的。
答案 0 :(得分:2)
--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
(或来自其他任何地方),您的定义都会被调用。
如果您不想从libc调用__assert_fail
的版本,那么您就完成了。如果您这样做,则必须动态查找(通过dlsym
)。