C/C++的printf会调用malloc()

排查内存问题(或相关的疑难杂症)时,可能一句printf就能让bug出现,或者赶走bug。你可能觉得很神奇,但这并不神奇。

至少我们可以在 Linux-x64 下,通过 malloc hook,来验证当前的编译环境下, printf 确实是调用了 malloc。 而 malloc 底层也不是吃素的, 默认是 glibc 的 ptmalloc 这个内存管理器, 如果本身你的程序把内存控制块写坏了, 继续 malloc 那就容易出现问题, 也就表现为 printf 影响了 bug 的出现。

来看代码。 伸手党可以直接看 godbolt:

https://godbolt.org/z/PPYMW613d

hook.c

c 复制代码
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <malloc.h> // malloc_usable_size
#include <inttypes.h>
#include <pthread.h>

// 颜色定义
#define GREEN "\x1b[32m"
#define YELLOW "\x1b[33m"
#define BLUE "\x1b[34m"
#define RESET "\x1b[0m"

// 线程局部存储防止递归
static __thread int reentrancy_guard = 0;

// 真实函数指针
static void* (*real_malloc)(size_t size) = NULL;
static void (*real_free)(void* ptr) = NULL;
static void* (*real_realloc)(void* ptr, size_t size) = NULL;

// 初始化函数,使用 pthread_once 确保只初始化一次
static pthread_once_t init_once = PTHREAD_ONCE_INIT;

static void init_real_functions() {
    //real_malloc = dlsym(RTLD_NEXT, "malloc");
    real_malloc = (void* (*)(size_t))dlsym(RTLD_NEXT, "malloc");
    real_free = (void (*)(void*))dlsym(RTLD_NEXT, "free");
    real_realloc = (void* (*)(void*, size_t))dlsym(RTLD_NEXT, "realloc");
    if (!real_malloc || !real_free || !real_realloc) {
        const char *error = "Error in `dlsym`\n";
        write(STDERR_FILENO, error, sizeof("Error in `dlsym`\n") - 1);
        _exit(1);
    }
}

// malloc 钩子实现
void* malloc(size_t size) {
    if (reentrancy_guard) {
        return real_malloc ? real_malloc(size) : NULL;
    }

    pthread_once(&init_once, init_real_functions);

    reentrancy_guard++;
    void* ptr = real_malloc(size);
    reentrancy_guard--;

    if (ptr) {
        char buffer[256];
        int len = snprintf(buffer, sizeof(buffer), BLUE "malloc %ld %p\n" RESET, size, ptr);
        write(STDERR_FILENO, buffer, len);
    }

    return ptr;
}

// free 钩子实现
void free(void* ptr) {
    if (reentrancy_guard) {
        if (real_free) real_free(ptr);
        return;
    }

    pthread_once(&init_once, init_real_functions);

    reentrancy_guard++;
    if (ptr) {
        size_t size = malloc_usable_size(ptr);
        char buffer[256];
        int len = snprintf(buffer, sizeof(buffer), GREEN "free %ld %p\n" RESET, size, ptr);
        write(STDERR_FILENO, buffer, len);
    }
    real_free(ptr);
    reentrancy_guard--;
}

// realloc 钩子实现
void* realloc(void* ptr, size_t size) {
    if (reentrancy_guard) {
        return real_realloc ? real_realloc(ptr, size) : NULL;
    }

    pthread_once(&init_once, init_real_functions);

    reentrancy_guard++;
    void* new_ptr = real_realloc(ptr, size);
    reentrancy_guard--;

    if (new_ptr) {
        char buffer[256];
        int len = snprintf(buffer, sizeof(buffer), YELLOW "realloc %ld %p %p\n" RESET, size, ptr, new_ptr);
        write(STDERR_FILENO, buffer, len);
    }

    return new_ptr;
}

编译:

bash 复制代码
gcc -shared -fPIC hook4.c -o hook.so -ldl -O2

使用

cpp 复制代码
int main()
{
    printf("Hello, World");
    return 0;
}
bash 复制代码
g++ main.cpp
LD_PRELOAD=./hook.so ./a.out

输出内容:

bash 复制代码
malloc 4096 0x3a842b0
Hello, World
相关推荐
2401_876221341 分钟前
因数个数、因数和、因数积
c++·算法
十五年专注C++开发4 分钟前
VS2019编译的C++程序,在win10正常运行,在win7上Debug正常运行,Release运行报错0xC0000005,进不了main函数
开发语言·c++·报错c0x0000005
fy zs12 分钟前
网络编程套接字
linux·服务器·网络·c++
fpcc14 分钟前
模板编程—模板和元编程中的错误输出
c++·模板编程
xie_pin_an15 分钟前
C++ 类和对象全解析:从基础语法到高级特性
java·jvm·c++
小温冲冲19 分钟前
C++与QML信号绑定完全指南:实现跨语言无缝通信
c++
GIS 数据栈24 分钟前
【Seggis遥感系统升级】用C++高性能服务Drogon重构软件服务架构|QPS提升300%,性能再升级!
java·开发语言·c++·重构·架构
王老师青少年编程38 分钟前
信奥赛C++提高组csp-s之二分图
数据结构·c++·二分图·csp·信奥赛·csp-s·提高组
柏木乃一39 分钟前
进程(11)进程替换函数详解
linux·服务器·c++·操作系统·exec
Q741_14740 分钟前
C++ 队列 宽度优先搜索 BFS 力扣 429. N 叉树的层序遍历 C++ 每日一题
c++·算法·leetcode·bfs·宽度优先