【问题标题】:Using LD_PRELOAD on Fedora 25 causes Segmentation Fault在 Fedora 25 上使用 LD_PRELOAD 会导致分段错误
【发布时间】:2017-05-19 18:53:51
【问题描述】:

我在尝试使用很久以前编写的库时发现了一个奇怪的行为。主要问题是,当程序在 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

我发现“fprintf”行导致了 Segfault 问题。于是我把“stdout”文件描述符改成了“stderr”,问题就解决了。

然后我在另一台运行 CentOS 7 的机器上使用“stdout”文件描述符作为“fprintf”的输出测试了相同的代码,并且在使用“stdout”和“stderr”的两种情况下都能正常工作。

通过观察这些结果,我想知道我缺少什么以及为什么会发生这种情况。

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

GNU ld 版本 2.26.1-1.fc25

gcc (GCC) 6.3.1 20161221(红帽 6.3.1-1)

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

GNU ld 版本 2.25.1-22.base.el7

gcc (GCC) 4.8.5 20150623(红帽 4.8.5-11)

【问题讨论】:

    标签: linux gcc glibc ld-preload


    【解决方案1】:

    fprintf 本身在某些情况下可能需要使用malloc。一个可能的原因是stderr 是无缓冲的,而stdout 是行缓冲的。 fprintf(stdout) 可能已经有一个缓冲区,或者它可能会尝试分配一个缓冲区,最终调用你的malloc,它再次调用fprintf,但它不会在同一个FILE* 上重入。

    你可以用一个标志来防止重入,例如(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;
    }
    

    【讨论】:

    • 另一种方法是放弃使用 fprintf,而是使用 snprintf 到本地缓冲区并调用 write() 以输出由 snprintf 生成的字符串
    • @nos 这可能会奏效,但我仍然会担心snprintf 可能涉及分配。
    猜你喜欢
    • 2015-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-18
    • 2021-05-25
    • 1970-01-01
    • 2018-03-05
    • 2016-01-06
    相关资源
    最近更新 更多