2012-10-04 68 views
2

編輯:使標題更清晰一些。包裝的功能僅在鏈接爲靜態鏈接庫時調用

我試圖用我自己的函數來包裝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; 
} 

我所要建造4點的方式有/無DO_ASSERT,動態/靜態

$ 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

如果您使用的malloc --wrap該文件鏈接其他代碼,然後將所有來電「的malloc」將調用函數「--wrap malloc中調用」代替。

這不是我所看到的。

回答

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)。

+0

我在想,我的' - wrap'這個想法就像你說的那樣錯了。在試圖定義我自己的'__assert_fail'後,我來找到'--wrap',但那不起作用。我會再次嘗試驗證。我終於可以使用'__attribute __((別名(「my_assert_fail」))' – mmlb

+0

所以我回去測試了這裏發佈的示例代碼,以及我的應用程序,並且它們都在我定義'__assert_fail'和'__assert_perror_fail'時工作。 !我想知道爲什麼我認爲它不起作用......無論如何@Employed感謝您的答案。 – mmlb