问题描述
:标题更清晰。
我试图封装glibc的 __ assert_fail
和 __ assert_perror_fail
函数与我自己的使用 syslog
记录消息。
我已经验证,如果我失败一个断言我的函数被调用。问题出在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>
externCvoid
__wrap ___ assert_perror_fail(int __errnum,const char * __ file,
unsigned int __line,const char * __ function)
{
fprintf (stderr,TESTING123 ::%s:%u%s:意外错误:%s.\\\
,
__file,__line,__function,strerror(__ errnum));
abort();
}
externCvoid
__wrap ___ assert_fail(const char * __ assertion,const char * __ file,
unsigned int __line,const char * __ function)
{
fprintf(stderr,TESTING123 ::%s:%u%s:Assertion'%s'failed.\\\
,
__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\\\
,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
pre>
$ 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):在函数'zmq :: resolve_ip_interface(sockaddr_storage *,unsigned int *,char const * )':
(.text + 0x49b):warning:在静态链接的应用程序中使用'getaddrinfo'需要在运行时从glibc版本的链接
运行它们时,我得到以下结果:
$ 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'失败。
中止
那么我做错了什么?根据
man ld
这不是我看到的。
解决方案您的心理模型如何
- wrap
真的很简单:当你使用
链接一个特定的ELF可执行文件或共享库时 - wrap foo
, all 的链接器是:
- 如果看到
foo
,它会替换为__ wrap_foo
, - 的引用, code> __ real_foo ,它替换为对
foo
的引用。
我重复一遍,那就是全部。特别是,因为你没有用 - wrap
, libzmq重新链接
继续调用 libzmq.so
因此 __ assert_fail
(即不会重命名任何发生在 libzmq.so
。
为了插入 libc
函数,请忘记 - wrap
。
而是只需在您的主机中定义新的 __ assert_fail
可执行文件。当你这样做,无论调用是来自主可执行文件,还是来自 libzmq.so
(或任何地方),你的定义都会被调用。
如果你不想从libc调用 __ assert_fail
的版本,就完成了。如果你这样做,你必须动态查找(通过 dlsym
)。
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
which is not what I am seeing.
Your mental model of how --wrap
linker 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:
- if it sees a reference to
foo
, it replaces it with a reference to__wrap_foo
, - if it sees a reference to
__real_foo
, it replaces with a reference tofoo
.
I repeat, that is all it does. In particular, since you have not relinked libzmq.so
with --wrap
, libzmq.so
continues to call __assert_fail
(i.e. no renaming whatsoever is happening inside libzmq.so
).
In order to interpose libc
function, forget the --wrap
.
Instead, simply define a new __assert_fail
in your main executable. When you do that, your definition will get called regardless of whether the call comes from the main executable, or from libzmq.so
(or from anywhere else).
If you don't want to call the version of __assert_fail
from libc, you are done. If you do, you'll have to look it up dynamically (via dlsym
).
这篇关于仅当链接为-static时,链接库才调用包装函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!