记录CSAPP Lecture-13 中的 Library Interpositioning 相关内容

Generating address traces

// int.c
#include <stdio.h>
#include <malloc.h>
int main()
{
    int *p = malloc(32);
    free(p);
    return(0);
}

Compile-time Interpositioning

  • 涉及代码
// mymalloc.c
#ifdef COMPILETIME
#include <stdio.h>
#include <malloc.h>
/* malloc wrapper function */
void *mymalloc(size_t size)
{
    void *ptr = malloc(size);
    printf("malloc(%d)=%p\n",
        (int)size, ptr);
    return ptr;
}
/* free wrapper function */
void myfree(void *ptr)
{
    free(ptr);
    printf("free(%p)\n", ptr);
}
#endif

//malloc.h
#define malloc(size) mymalloc(size)
#define free(ptr) myfree(ptr)
void *mymalloc(size_t size);
void myfree(void *ptr);
  • Makefile
intc: int.c mymalloc.c
	$(CC) $(CFLAGS) -DCOMPILETIME -c mymalloc.c
	$(CC) $(CFLAGS) -I. -o intc int.c mymalloc.o
runc:
	./intc

clean:
	rm -f *~ intr intl intc *.so *.o
  • 下面解析此处涉及到的gcc参数

    • -DCOMPILETIME
      • 原型-Dmacro=..
      • 表示进行预定义#define COMPILETIME
    • -c
      • 编译时不进行连接,即生成.o文件,是二进制形式但是无法运行,内部的跳转函数的跳转地址是0的形式
    • -I.
      • 原型-I dir
      • 用于预处理时添加搜索头文件的目录
      • 可以覆盖掉同名的系统头文件,此处用于覆盖int.c中的#include <malloc.h>
  • 该方法通过编译时把mallocfree替换成我们提供的版本

#ifdef LINKTIME
#include <stdio.h>
void *__real_malloc(size_t size);
void __real_free(void *ptr);

void *__wrap_malloc(size_t size)
{
    void *ptr = __real_malloc(size); /* Call libc malloc */
    printf("malloc(%d) = %p\n", (int)size, ptr);
    return ptr;
}

void __wrap_free(void *ptr)
{
    __real_free(ptr); /* Call libc free */
    printf("free(%p)\n", ptr);
}
#endif
  • Makefile
intl: int.c mymalloc.c
	$(CC) $(CFLAGS) -D LINKTIME -c mymalloc.c
	$(CC) $(CFLAGS) -c int.c
	$(CC) $(CFLAGS) -Wl,--wrap,malloc -Wl,--wrap,free -o intl int.o mymalloc.o

runl:
	./intl
  • 下面解析此处涉及到的gcc参数
    • -Wl
      • 原型-Wl,option,用于给`linker传递选项,具体文档
        • 此处的逗号会被linker理解为空格
        • -Wl,--wrap,symbol实际为ld --wrap symbol
          • 会把对symbol的引用解析为__wrap_symbol
          • 而对__real_malloc的引用则会解析为真实的symbol
        • 从而使得int.c中的malloc指向我们重定义的__wrap_malloc, 而mymalloc.c中实际调用的是真实的malloc(通过__real_malloc)

Load/Run-time Interpositioning

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

/* malloc wrapper function */
void *malloc(size_t size)
{
    void *(*mallocp)(size_t size);
    char *error;

    mallocp = dlsym(RTLD_NEXT, "malloc"); /* Get address of libc malloc */
    if ((error = dlerror()) != NULL) {
        fputs(error, stderr);
        exit(1);
    }
    void *ptr = mallocp(size); /* Call libc malloc */
    fprintf(stderr, "malloc(%d) = %p\n", (int)size, ptr);
    return ptr;
}

/* free wrapper function */
void free(void *ptr)
{
    void (*freep)(void *) = NULL;
    char *error;

    if (!ptr)
        return;

    freep = dlsym(RTLD_NEXT, "free"); /* Get address of libc free */
    if ((error = dlerror()) != NULL) {
        fputs(error, stderr);
        exit(1);
    }
    // printf("free(%p)\n", ptr)
    fprintf(stderr, "free(%p)\n", ptr);
    freep(ptr); /* Call libc free */
}
#endif
  • 要使用RTLD_NEXT ,需要宏定义define _GNU_SOURCE, 因为其不是posix定义的

  • 此处通过调用dlsym来获得原始库中的mallocfree函数,RTLD_NEXT表示从下一个库中解析函数符号,而非当前这个调用dlsym的库

  • 此处不能使用printffprintf(stdout, ...), 因为当对标准输出流进行写操作时,会通过malloc创建一个缓冲区,导致出现递归调用malloc的情况,此处要访问无缓冲的标准错误流才能正常定义我们的malloc

  • Makefile

intr: int.c mymalloc.c
	$(CC) $(CFLAGS) -DRUNTIME -shared -fpic -o mymalloc.so mymalloc.c -ldl
	$(CC) $(CFLAGS) -o intr int.c

runr:
	(LD_PRELOAD="./mymalloc.so" ./intr)
  • 下面解析此处涉及到的gcc参数
    • -shared
      • 用于生成共享库(.so)
      • 一般需要和-fpic一起作用
    • -fpic
      • 生成能在共享库中被使用的位置独立的代码,即当被装载时可以放在内存的任意位置
    • -ldl
      • 原型-llibrary
      • 在连接的时候寻找以参数library为名的库(liblibrary.soliblibrary.a)(同名则倾向于使用动态共享库),此处表用连接时需要导入libdl.so
      • 此处要放在mymalloc.so之后, 即编译时使用的函数例如dlsym等要在libdl.so中查找到相关依赖
    • -rdynamic
      • 代表给连接器传--export-dynamic参数
      • 这是调用dlopen可能要使用的参数,代表把所有的符号(包括模块内部的符号)都加入动态符号表(dynamic symbol table, 是在运行时对所有动态对象可见的对象),
      • 为了让主程序内定义的全局函数对库函数可见, 尤其需要传入主程序中的函数作为回调函数时需要该选项(参考了该文)
  • 运行时需要给出LD_PRELOAD,为bash语法, 动态链接器会先在LD_PRELOAD中寻找未定义的引用, 此处可以替换int.c中的malloc变为使用我们自定义的,又因为我们的mymalloc.malloc实际实现时调用的是下一个库(即libc.so)中的malloc,所以不会出现递归调用
posted @ 2021-01-23 20:52  Muxv  阅读(175)  评论(0)    收藏  举报