2

按照我们的教科书(CSAPP book),我一直在尝试拦截对 malloc 和 free 的调用。我遵循了他们的确切代码,以及我在网上找到的几乎相同的代码,但我一直遇到分段错误。我听到我们的教授说了一些关于 printf 的一些事情,它会分配 malloc 并释放内存,所以我认为这是因为我正在拦截 malloc 并且由于我在拦截函数中使用了 printf 函数,它会递归地调用自己。但是我似乎无法找到解决这个问题的方法?我们的教授证明了拦截是有效的(他没有向我们展示代码)并在每次 malloc 发生时打印我们的信息,所以我知道这是可能的。任何人都可以建议一种工作方法吗?

这是我使用的代码,但一无所获:mymalloc.c

#ifdef RUNTIME
// Run-time interposition of malloc and free based on // dynamic linker's (ld-linux.so) LD_PRELOAD mechanism #define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h> #include <dlfcn.h>
void *malloc(size_t size) {
static void *(*mallocp)(size_t size) = NULL; char *error;
void *ptr;
// get address of libc malloc
if (!mallocp) {
mallocp = dlsym(RTLD_NEXT, "malloc"); if ((error = dlerror()) != NULL) {
            fputs(error, stderr);
            exit(EXIT_FAILURE);
         }
}
ptr = mallocp(size);
printf("malloc(%d) = %p\n", (int)size, ptr); return ptr;
}
#endif

测试.c

#include <stdio.h>
#include <stdlib.h>
int main(){
   printf("main\n");
   int* a = malloc(sizeof(int)*5);
   a[0] = 1;
   printf("end\n");
}

我得到的结果:

$ gcc -o test test.c
$ gcc -DRUNTIME -shared -fPIC mymalloc.c -o mymalloc.so
$ LD_PRELOAD=./mymalloc.so ./test
Segmentation Fault

这是我尝试并得到分段错误的代码(来自https://gist.github.com/iamben/4124829):

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

void* malloc(size_t size)
{
        static void* (*rmalloc)(size_t) = NULL;
        void* p = NULL;

        // resolve next malloc
        if(!rmalloc) rmalloc = dlsym(RTLD_NEXT, "malloc");

        // do actual malloc
        p = rmalloc(size);

        // show statistic
        fprintf(stderr, "[MEM | malloc] Allocated: %lu bytes\n", size);

        return p;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR_LEN 128

int main(int argc, const char *argv[])
{
        char *c;
        char *str1 = "Hello ";
        char *str2 = "World";

        //allocate an empty string
        c = malloc(STR_LEN * sizeof(char));
        c[0] = 0x0;

        //and concatenate str{1,2}
        strcat(c, str1);
        strcat(c, str2);

        printf("New str: %s\n", c);


        return 0;
}

来自 git repo 的 makefile 不起作用,所以我手动编译了文件并得到:

$ gcc -shared -fPIC libint.c -o libint.so
$ gcc -o str str.c
$ LD_PRELOAD=./libint.so ./str
Segmentation fault

尽管我复制了教科书代码,但我已经这样做了几个小时,但仍然得到相同的错误结果。我真的很感激任何帮助!

4

1 回答 1

2

printf解决此问题的一种方法是在递归调用 return 时关闭:

static char ACallIsInProgress = 0;
if (!ACallIsInProgress)
{
    ACallIsInProgress = 1;
    printf("malloc(%d) = %p\n", (int)size, ptr);
    ACallIsInProgress = 0;
}
return ptr;

这样,如果printf调用malloc,您的例程将仅调用实际的 malloc(通过mallocp)并返回而不会导致另一个printf. 您将错过有关调用的打印信息mallocprintf但是当使用插入来研究通用程序而不是 C 库时,这通常是可以容忍的。

如果您需要支持多线程,可能需要做一些额外的工作。

printf实现可能只分配一次缓冲区,即第一次使用它。在这种情况下,您可以初始化一个关闭printf类似于上述的标志,printf在例程中调用一次main(可能确保它包含一个很好的格式化任务,导致printf分配一个缓冲区,而不是一个纯字符串),然后设置标志以打开printf呼叫并将其设置为程序的其余部分。

另一种选择是让您的malloc例程根本不使用printf,而是将数据缓存在缓冲区中,稍后由其他例程写入,或者使用 将原始数据写入文件write,稍后由单独的程序解释和格式化该数据。或者原始数据可以通过管道写入一个程序,该程序对其进行格式化和打印,并且不使用您的 interposed malloc.

于 2020-12-13T12:28:08.057 回答