1. 程式人生 > >初探iptables自動載入模組原理

初探iptables自動載入模組原理

iptables使用dlopen載入動態庫,每個庫中都定義了void _init(void)函式,在使用dlopen載入庫的時候系統會呼叫_init函式,

在_init函式中呼叫xtables_register_match對模組進行註冊。iptables這種動態載入模組的方式很適合做定製開發,所以我就自己摸索了下。

我自己寫了一個測試的例子:
gcc -O2 -Wall  -fPIC -c hello.c
gcc -shared -o hello.so hello.o
hello.o: In function `_init':
hello.c:(.text+0x0): multiple definition of `_init'
/usr/lib/gcc/i386-redhat-linux/4.1.1/../../../crti.o:(.init+0x0): first defined here

編譯的時候出現了重複定義的錯誤。這是為什麼?

_init是共享庫用來初始化全域性變數和物件的,在庫檔案中已經定義過了。有什麼辦法可以使用_init來初始化我們自己的函式呢?
通過檢視iptables的模組程式碼,發現所有的檔案都包含#include <xtables.h>標頭檔案,會不會是xtables.h中動了什麼手腳?
在hello.c中包含xtables.h標頭檔案,果然編譯通過。

#include <stdio.h>
#include <xtables.h>

int main()
{
        return 0;
}

void _init(void)
{
        printf("init hello\n");

        return ;
}

gcc -O2 -Wall -o hello -I/home/mogo/2.6.17-x86-old/user/iptables/include -fPIC hello.c

[
[email protected]
lion]# ./hello 
init hello

xtables.h標頭檔案中到底做了什麼呢?
我在程式碼中看到一個巨集定義:#   define _init __attribute__((constructor)) _INIT,會不會是這個?
於是在hello.c中去掉標頭檔案xtables.h,將該巨集定義換進去,果然沒問題。

那這個巨集定義是什麼意思呢?
用__attribute__((constructor))來定義的函式,表示函式是建構函式,在main執行之前被呼叫;相應的用__attribute__ ((destructor))解構函式,在main退出時執行。
void _init(void)就相當於是__attribute__((constructor)) _INIT(void),其實不管函式名定義成什麼都會被執行到。

自動初始化的問題解決了,現在就來看看dlopen是怎麼用的。
庫模組原始碼:
  1. #include <stdio.h>
  2. #include <match.h>
  3. #define _init __attribute__((constructor)) _INIT
  4. static int hello()
  5. {
  6.         printf("hello called\n");
  7.         return 0;
  8. }
  9. static struct test_match test = {
  10.         .name  = "hello",
  11.         .match = hello
  12. };
  13. void _init(void)
  14. {
  15.         printf("init hello\n");
  16.         register_match(&test);
  17.         return ;
  18. }
複製程式碼 執行後生成libhello.so檔案

主程式動態載入so檔案並呼叫模組的函式
  1. #include <stdio.h>
  2. #include <match.h>
  3. #include <dlfcn.h>
  4. int main()
  5. {
  6.         struct test_match *ptr;
  7.         if (dlopen("./libhello.so", RTLD_NOW) == NULL) {
  8.                 printf("load lib error.\n");
  9.                 return 0;
  10.         }
  11.         ptr = find_match("hello");
  12.         if (ptr) {
  13.                 printf("find match:%s\n", ptr->name);
  14.                 ptr->match();
  15.         }
  16.         return 0;
  17. }
複製程式碼 編譯成可執行檔案執行:
  1. [[email protected] netcos]# ./main 
  2. init hello
  3. find match:hello
  4. hello called
複製程式碼