在 gcc 中,有什么方法可以动态地将函数调用添加到 main() 的开头吗?

问题描述 投票:0回答:2

我在 glibc 基准 malloc 速度测试(

malloc()
)中用我的 fast_malloc()
 实现动态覆盖 
glibc/benchtests/bench-malloc-thread.c
,通过在我的
fast_malloc.c
文件中编写这些函数:

// Override malloc() and free(); see: https://stackoverflow.com/a/262481/4561887

inline void* malloc(size_t num_bytes)
{
    static bool first_call = true;
    if (first_call)
    {
        first_call = false;
        fast_malloc_error_t error = fast_malloc_init();
        assert(error == FAST_MALLOC_ERROR_OK);
    }

    return fast_malloc(num_bytes);
}

inline void free(void* ptr)
{
    fast_free(ptr);
}

请注意,我在

malloc()
包装器中添加了这个低效的内容,以确保
fast_malloc_init()
在第一次调用时首先被调用,以初始化一些内存池。如果可能的话,我想摆脱它并动态地将 init 调用插入到
main()
的开头,而不修改 glibc 代码。这可能吗?

到目前为止,我编写的

malloc()
包装器的缺点是它扭曲了我的基准测试结果,使其看起来我的
fast_malloc()
比实际速度慢,因为 init 函数由
glibc/benchtests/bench-malloc-thread.c
计时,并且我有这个无关的
if (first_call)
,每次 malloc 调用都会检查它。

目前,我在调用

malloc()
可执行文件时动态覆盖
free()
bench-malloc-thread
,如下所示:

LD_PRELOAD='/home/gabriel/GS/dev/fast_malloc/build/libfast_malloc.so' \ 
glibc-build/benchtests/bench-malloc-thread 1

我将添加我的

fast_malloc()
速度测试(使用 this repo): enter image description here

我对此发表的 LinkedIn 帖子:https://www.linkedin.com/posts/gabriel-staples_software-engineering-tradeoffs-activity-6815412255325339648-_c8L.

相关:

  1. [我的存储库分支] https://github.com/ElectricRCAircraftGuy/malloc-benchmarks
  2. [我如何学习如何在 gcc 中生成
    *.so
    动态库] https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
  3. 在 C 中创建 malloc 和 free 的包装函数
c gcc dynamic-library ld-preload
2个回答
3
投票

这可能吗?

是的。您正在构建和

LD_PRELOAD
共享库,共享库可以具有特殊的初始化程序和终结程序函数,它们分别在加载和卸载库时由动态加载器调用。

正如 kaylum 评论的那样,要创建这样的构造函数,您可以使用

__attribute__((constructor))
,如下所示:

__attribute__((constructor))
void fast_malloc_init_ctor()
{
  fast_malloc_error_t error = fast_malloc_init();
  assert(error == FAST_MALLOC_ERROR_OK);
}

// ... the rest of implementation here.

附注

它扭曲了我的基准测试结果,使其看起来像我的 fast_malloc() 比实际速度慢,因为 init 函数是定时的

  1. 您正在与多线程基准进行比较。请注意,您的
    static bool fist_call
    not 线程安全的。实际上,这并不重要,因为
    malloc
    通常在任何线程(主线程除外)存在之前很久就被调用。
  2. 我怀疑这种单一比较实际上会让你的
    fast_malloc()
    变慢。即使删除比较之后,它也可能会变慢——编写一个快速堆分配器需要付出很多努力,聪明的人花了很多人年的时间来优化 GLIBC malloc、TCMalloc 和 jemalloc。

2
投票
main()

函数之前和之后动态注入函数调用。
这是一个完整的、可运行的示例,适合任何想要自己测试的人。在 Linux Ubuntu 20.04 上测试。

此代码是我的

eRCaGuy_hello_world

存储库的一部分。

hello_world_basic.c: #include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C #include <stdint.h> // For `uint8_t`, `int8_t`, etc. #include <stdio.h> // For `printf()` // int main(int argc, char *argv[]) // alternative prototype int main() { printf("This is the start of `main()`.\n"); printf(" Hello world.\n"); printf("This is the end of `main()`.\n"); return 0; }

dynamic_func_call_before_and_after_main.c: #include <assert.h> #include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C #include <stdint.h> // For `uint8_t`, `int8_t`, etc. #include <stdio.h> // For `printf()` #include <stdlib.h> // For `atexit()` /// 3. This function gets attached as a post-main() callback (a sort of program "destructor") /// via the C <stdlib.h> `atexit()` call below void also_called_after_main() { printf("`atexit()`-registered callback functions are also called AFTER `main()`.\n"); } /// 1. Functions with gcc function attribute, `constructor`, get automatically called **before** /// `main()`; see: /// https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes __attribute__((__constructor__)) void called_before_main() { printf("gcc constructors are called BEFORE `main()`.\n"); // 3. Optional way to register a function call for AFTER main(), although // I prefer the simpler gcc `destructor` attribute technique below, instead. int retcode = atexit(also_called_after_main); assert(retcode == 0); // ensure the `atexit()` call to register the callback function succeeds } /// 2. Functions with gcc function attribute, `destructor`, get automatically called **after** /// `main()`; see: /// https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes __attribute__((__destructor__)) void called_after_main() { printf("gcc destructors are called AFTER `main()`.\n"); }

如何构建和运行动态 

lib*.so 共享对象库,并在运行另一个程序时使用

LD_PRELOAD
动态加载它
(请参阅我的 
eRCaGuy_hello_world
存储库中的“dynamic_func_call_before_and_after_main__build_and_run.sh”): # 1. Build the other program (hello_world_basic.c) that has `main()` in it which we want to use mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj hello_world_basic.c \ -o bin/hello_world_basic # 2. Create a .o object file of this program, compiling with Position Independent Code (PIC); see # here: https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html gcc -Wall -Wextra -Werror -O3 -std=c11 -fpic -c dynamic_func_call_before_and_after_main.c \ -o bin/dynamic_func_call_before_and_after_main.o # 3. Link the above PIC object file into a dynamic shared library (`lib*.so` file); link above shows # we must use `-shared` gcc -shared bin/dynamic_func_call_before_and_after_main.o -o \ bin/libdynamic_func_call_before_and_after_main.so # 4. Call the other program with `main()` in it, dynamically injecting this code into that other # program via this code's .so shared object file, and via Linux's `LD_PRELOAD` trick LD_PRELOAD='bin/libdynamic_func_call_before_and_after_main.so' bin/hello_world_basic

样本输出。请注意,我们在“hello_world_basic.c”中找到的 
main()

函数中注入了一些特殊的函数调用: gcc constructors are called BEFORE `main()`. This is the start of `main()`. Hello world. This is the end of `main()`. gcc destructors are called AFTER `main()`. `atexit()`-registered callback functions are also called AFTER `main()`.

参考资料:

如何在 Linux 中构建动态 
lib*.so
库:

https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html

  1. @kaylum 的评论
  2. @雇佣俄罗斯人的回答
  3. @Lundin 的评论
  4. gcc
  5. constructor
  6. destructor 函数属性!:
  7. https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
  8. c 
    atexit()
    func 注册要在 main() 返回或退出后调用的函数!:
  9. https://en.cppreference.com/w/c/program/atexit
  10. 
        
    	
© www.soinside.com 2019 - 2024. All rights reserved.