在Fedora 25上使用LD_PRELOAD会导致分段错误

时间:2017-05-19 18:53:51

标签: linux gcc glibc ld-preload

在我尝试使用我很久以前写过的库时,我发现了一种奇怪的行为。主要问题是,当程序在 Fedora 25 上执行并使用LD_PRELOAD链接到我的库时,系统会引发分段错误。我已经为我的旧图书馆做了一小部分样本,以便轻松了解问题。

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

extern void *__libc_malloc(size_t size);

void *malloc(size_t size) 
{
  void *ptr = __libc_malloc(size);
  fprintf(stdout, "Malloc(%d)->(%p)\n", (int) size, ptr);
  return ptr;
}

此代码是使用以下参数编译的:

gcc -c -fPIC -O3 -Wall -o libtest.o libtest.c
gcc -shared -o libtest.so libtest.o

程序执行如下:

LD_PRELOAD=./libtest.so ./progtest

我发现&#34; fprintf &#34;线路导致了Segfault问题。所以我改变了&#34; 标准输出&#34;文件描述符到&#34; stderr &#34;,问题就消失了。

然后我使用&#34; 标准输出&#34;测试了相同的代码。文件描述符作为&#34; fprintf &#34;的输出在运行 CentOS 7 的另一台机器上,使用&#34; 标准输出&#34;它在两种情况下均正常工作和&#34; stderr &#34;。

通过观察这些结果,我想知道我错过了什么以及为什么会这样。

Fedora 25上安装的GLibc和GCC版本:

  

GNU ld version 2.26.1-1.fc25

     

gcc(GCC)6.3.1 20161221(Red Hat 6.3.1-1)

在CentOS 7上安装的GLibc和GCC版本:

  

GNU ld version 2.25.1-22.base.el7

     

gcc(GCC)4.8.5 20150623(Red Hat 4.8.5-11)

1 个答案:

答案 0 :(得分:2)

在某些情况下,

fprintf本身可能需要使用malloc。一个可能的原因是stderr是无缓冲的,而stdout是行缓冲的。 fprintf(stdout)可能已经有一个缓冲区,或者它可能会尝试分配一个缓冲区,最后会调用malloc,再次调用fprintf,但它不能重新进入FILE* { {1}}。

您可以使用标志防止重入,例如(C11):

#include <stdbool.h>
#include <threads.h>
thread_local bool inside_malloc = false;
void *malloc(size_t size) {
    void *ptr = __libc_malloc(size);
    if (!inside_malloc) {
        inside_malloc = true;
        fprintf(stdout, "Malloc(%zd)->(%p)\n", size, ptr);
        inside_malloc = false;
    }
    return ptr;
}