linux庫打樁 《深入理解計算機(jī)系統(tǒng) 第七章》

[toc]

概述

搞一個包裝,把c庫函包起來,同時順帶加上自己的私貨。
抄的如下鏈接,作者總結(jié)的挺好,不過代碼有編譯錯誤等問題。這里修正一下,所有的代碼都能直接跑
https://blog.csdn.net/liangzc1124/article/details/104974649

編譯時打樁

直接自己寫個庫函數(shù)調(diào)用,加上私貨包起來,再用宏把庫函數(shù)里面的實(shí)現(xiàn),給替換成自己的實(shí)現(xiàn)

建立包裝函數(shù)

建立mymalloc.c文件,定義需要的包裝函數(shù)mymalloc和myfree.

#ifdef COMPILETIME
#include <stdio.h>
#include <malloc.h>

void *mymalloc(size_t size)
{
    void *ptr = malloc(size);
    printf("malloc(%d)=%p\n", size, ptr);
    return ptr;
}

void myfree(void *ptr)
{
    free(ptr);
    printf("free(%p)\n", ptr);
}

#endif

建立頭文件 malloc.h

該文件向預(yù)處理器指明用mymalloc.c中的包裝函數(shù)替換庫里的目標(biāo)函數(shù),需要在gcc編譯的時候,讓編譯器優(yōu)先去用這個 .h

#define malloc(size) mymalloc(size)
#define free(ptr) myfree(ptr)

void *mymalloc(size_t size);
void myfree(void *ptr);

建立自己的程序文件

建立文件myprog.c,并在其中正常調(diào)用malloc函數(shù)

#include <stdio.h>
#include "mymalloc.h"

int main(void)
{
    int *p = malloc(32);
    free(p);
    return 0;
}

編譯鏈接

gcc -c -DCOMPILETIME  mymalloc.c
gcc -I. mymalloc.o -o myprog myprog.c

-I.:指示C預(yù)處理器在搜索通常的系統(tǒng)目錄前,先在當(dāng)前目錄中查找malloc.h

鏈接時打樁

wrap是一種非常常見的打樁方法

-Wl,option:將option傳遞給鏈接器。
option中的每個逗號都要用空格來替換
所以-Wl,--wrap,malloc意味著把--wrap malloc傳遞給鏈接器。

當(dāng)鏈接器看到 --warp ABC (ABC代表任何字符串)選項的時候,會去所有的文件下去尋找 ABC,然后把 ABC 換成 __warp_ABC

之后把所有的 __real_ABC 換成正常的 ABC

包裝函數(shù)

創(chuàng)建mymalloc.c文件,定義包裝函數(shù)

#include <stdio.h>

void *__real_malloc(size_t size);
void __real_free(void *ptr);

//定義malloc 包裝函數(shù)
void *__wrap_malloc(size_t size)
{
    void *ptr = __real_malloc(size);    //調(diào)用標(biāo)準(zhǔn)庫里的malloc
    printf("malloc(%d) = %p\n", (int)size, ptr);
    return ptr;
}

//定義free 包裝函數(shù)
void __wrap_free(void *ptr)
{
    __real_free(ptr);     //調(diào)用標(biāo)準(zhǔn)庫里的free
    printf("free(%p) = %p\n",  ptr);
}

建立程序

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int *p = malloc(32);
    free(p);
    return 0;
}

編譯鏈接

gcc -c mymalloc.c
gcc  -c myprog.c
gcc -Wl,--wrap,malloc -Wl,--wrap,free -o myprog myprog.o mymalloc.o

或者直接

gcc -Wl,--wrap,malloc -Wl,--wrap,free -o myprog myprog.c mymalloc.c

運(yùn)行時打樁

編譯時打樁需要訪問程序的源代碼,而鏈接時需要訪問可重定位目標(biāo)文件。那有沒有一種辦法讓僅僅訪問可執(zhí)行目標(biāo)就能達(dá)到同樣的目的呢?我們可以利用基于動態(tài)鏈接器的LD_PRELOAD環(huán)境變量來實(shí)現(xiàn)。

當(dāng)你將LD_PRELOAD環(huán)境變量設(shè)置為一個共享路徑名的列表(以空格或分號分開),那么在運(yùn)行一個程序時,動態(tài)鏈接器(LD-LINUX.SO)會先搜索列表里的庫,然后才搜素系統(tǒng)其它庫。

利用這個原理,你可以對任何共享庫中的任何函數(shù)打樁,包括libc.so。

建立包裝函數(shù)文件

下面建立mymalloc.c文件,其中定義了malloc和free的包裝函數(shù)。每個包裝函數(shù)中,利用dlsym調(diào)用libc中的標(biāo)準(zhǔn)函數(shù)。

#define  _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

//定義malloc 包裝函數(shù)
void *malloc(size_t size)
{
    void *(*mallocp)(size_t size);
    char *error;

    //獲得libc中malloc函數(shù)的地址
    mallocp = dlsym(RTLD_NEXT, "malloc");
    if((error = dlerror()) != NULL){
        fputs(error, stderr);
        exit(1);
    }

    //利用函數(shù)指針間接調(diào)用libc中的malloc函數(shù)
    char *ptr = mallocp(size);

    printf("malloc()");

    return ptr;
}


//定義free 包裝函數(shù)
void free(void *ptr)
{
    void (*freep)(void *) = NULL;
    char *error;

    if(!ptr) {
        return;
    }

    //獲得libc中free函數(shù)的地址
    freep = dlsym(RTLD_NEXT, "free");
    if((error = dlerror()) != NULL){
        fputs(error, stderr);
        exit(1);
    }

    freep(ptr);  //利用函數(shù)指針間接調(diào)用libc中的free函數(shù)
    printf("free(%p)\n",  ptr);
}

建立自己的程序文件

建立文件myprog.c,并在其中正常調(diào)用malloc函數(shù).

#include <stdio.h>
#include <malloc.h>

int main(void)
{
    int *p = malloc(32);
    free(p);
    return 0;
}

編譯包含包裝函數(shù)的共享庫

  gcc -shared -fpic -o mymalloc.so mymalloc.c -ldl

-fpic:(position independent code)指示生成位置無關(guān)的目標(biāo)文件;編譯共享庫時該參數(shù)為必選!
-shared:指示編譯器生成一個共享目標(biāo)文件。
-ldl:指示鏈接器鏈接到 libdl.so 共享庫。

編譯與運(yùn)行主程序

 gcc -o myprog myprog.c

編譯時打樁,必須要能訪問程序的源代碼,然后重新編譯
鏈接時打樁,必須要能訪問程序的可重定位文件.o,然后重新鏈接。
基于動態(tài)鏈接器的 LD_PRELOAD 環(huán)境變量,我們能用這個機(jī)制,只訪問可執(zhí)行文件.out就可以庫打樁

如果 LD_PRELOAD 環(huán)境變量被設(shè)置為一個共享庫路徑名的列表(空格或者分號分隔),那么當(dāng)你加載和執(zhí)行一個程序,需要解析未定義的引用時,動態(tài)鏈接器會先搜索 LD_PRELOAD庫,然后才搜索任何其他的庫。

通過這個機(jī)制,當(dāng)我們加載和執(zhí)行可執(zhí)行文件時,可以對任何共享庫中的任何函數(shù)打樁,包括libc.so

LD_PRELOAD環(huán)境變量并且運(yùn)行

 LD_PRELOAD="./mymalloc.so" ./myprog

以為這就完事了么,如果按照上面的代碼來操作,最后一步一定會coredump

而且會正好掛在 malloc 的 printf("malloc")那一行

原因:printf 會調(diào)用 printf malloc,當(dāng)場進(jìn)行一個死循環(huán)

另外提個問題:為什么wrap鏈接的時候,我的程序就能正常運(yùn)行,而不是coredump,但是動態(tài)鏈接的時候就會coredump呢?

因?yàn)殒溄拥臅r候,wrap替換是看不到printf內(nèi)部的,這個時候libc參與鏈接,只是把printf這個符號扔給了程序,想看到內(nèi)部,必須等到程序運(yùn)行的時候,程序自己看到了printf,主動找libc.so獲取內(nèi)容的時候才行。所以wrap并不會去替換libc.so里面的東西。

但動態(tài)鏈接不一樣,LD_PRELOAD="./mymalloc.so" ./myprog 這句話的意思是,先不要看別的so,優(yōu)先去看 mymalloc.so,從這里面去找函數(shù)的實(shí)現(xiàn)。然后這個so還會去 libc.so 里面去找printf的實(shí)現(xiàn),這樣就直接套娃了,printf去找malloc,malloc又是優(yōu)先被 mymalloc.so定義的,無限循環(huán)。

情景訓(xùn)練場景一

已有一個 libadd.so,內(nèi)有符號add(int size),同時被main和libadd.so內(nèi)的其他符號引用,不重新編譯liba.so的情況下對add進(jìn)行打樁

搞4個文件:
兩個add_test文件做成libadd.so
一個myprog文件放main函數(shù)
一個my_add文件放wrap

上代碼

libadd_test1.c

#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}

libadd_test2.c

#include <stdio.h>

int add(int a, int b);

int Calculation()
{
    int a = 10;
    int b = 10;

    int ret = add(a , b);
}

myprog.c

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <dlfcn.h>

int add(int a, int b);

int main()
{
    int a = 1;
    int b = 1;

    int ret = add(a, b);

    return 0;
}

my_add.c

#include <stdio.h>

int __real_add(int a, int b);

int __wrap_add(int a, int b)
{
    int ret = __real_add(a, b);
    printf("add result=%d\n", ret);
    return ret;
}

運(yùn)行

生成so

gcc -shared -fpic -o libadd.so libadd_test1.c libadd_test2.c

wrap并鏈接,生成可執(zhí)行程序

gcc -Wl,--wrap,add -o myprog myprog.c my_add.c  -ldl  -rdynamic -L. -ladd

運(yùn)行時需要指定動態(tài)庫的路徑

LD_LIBRARY_PATH=`pwd` ./myprog

如果你鏈接的時候直接指定就鏈這個 libadd.so,那么可以直接運(yùn)行,但是假設(shè)有多個so,最好還是用目錄的方法

gcc -Wl,--wrap,add -o myprog myprog.c my_add.c  -ldl  -rdynamic ./libadd.so
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容