【问题标题】:Library interpositioning库插入
【发布时间】:2020-12-13 11:37:32
【问题描述】:

按照我们的教科书(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

test.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

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

【问题讨论】:

  • 尝试fprintf(stderr, ...) 避免递归。 stderr 没有缓冲。
  • @chqrlie 我已经尝试过了,但仍然出现同样的错误...... gcc 编译语句可能有问题吗??
  • 你也可以尝试 sprintf(buf, ...) 到一个本地数组和 write(2, buf, strlen(buf))
  • 如果你只是从你的malloc 写一个带有write() 的常量字符串,它是否有效?
  • 如果只想跟踪malloc/free,可以使用环境变量。见man malloc

标签: c dlsym library-interposition


【解决方案1】:

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

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。您会错过有关 printf 所做的对 malloc 的调用的打印信息,但在使用插入来研究通用程序而不是 C 库时,这通常是可以容忍的。

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

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

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

【讨论】:

  • 感谢您的回答!然而,我想知道静态字符是如何工作的。如果char为0,则进入if条件,返回ptr。那么下面的语句(其中 0 分配给 char)将如何执行?它的目的是什么??
  • 在调试时,我发现问题实际上不是 printf 函数,而是 dlsym 函数(我在 dlsym 之前和之后尝试了 write() 函数(写入字符串),和打印字符串的无限循环)。这怎么可能??你有什么想法吗?
  • 很遗憾printfmalloc 分配内存来执行如此简单的格式化!
  • @juimdpp:仅供参考,我编辑了答案以更改代码——我起初没有注意到returnprintf 在同一行。我已将其移到 if 语句之外。
  • @juimdpp:如果dlsym 正在调用malloc,您可能需要将mallocp 的初始化移出malloc 例程。
猜你喜欢
  • 2013-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-14
  • 2019-09-25
相关资源
最近更新 更多