仅当链接为-static时,链接库才调用包装函数 [英] Wrapped function is only called from linked library when linking as -static

查看:252
本文介绍了仅当链接为-static时,链接库才调用包装函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

:标题更清晰。



我试图封装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 
$ 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版本的链接
pre>

运行它们时,我得到以下结果:

  $ 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 malloc,那么所有调用malloc将调用函数__wrap_malloc。


这不是我看到的。

解决方案

您的心理模型如何 - 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 of fprintf(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 --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 to foo.

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时,链接库才调用包装函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆