4

编辑:使标题更清晰。

我正在尝试用我自己的来包装 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>

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

那么我做错了什么?根据man ld

如果您使用 --wrap malloc 将其他代码与此文件链接,则所有对“malloc”的调用将改为调用函数“__wrap_malloc”。

这不是我所看到的。

4

1 回答 1

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(或来自其他任何地方),你的定义都会被调用。

如果你不想调用__assert_failfrom libc 的版本,你就完成了。如果这样做,您将不得不动态地查找它(通过dlsym)。

于 2012-10-07T04:04:12.513 回答