在 gcc 中有什么方法可以动态地将函数调用添加到 main() 的开头?
In gcc is there any way to dynamically add a function call to the start of main()?
我通过在我的 fast_malloc.c
文件:
// Override malloc() and free(); see:
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 func 得到由 glibc/benchtests/bench-malloc-thread.c
计时,我有这个无关的 if (first_call)
每次 malloc 调用都会检查它。
目前我在调用 bench-malloc-thread
可执行文件时动态覆盖 malloc()
和 free()
,如下所示:
LD_PRELOAD='/home/gabriel/GS/dev/fast_malloc/build/libfast_malloc.so' \
glibc-build/benchtests/bench-malloc-thread 1
情节我将把我的 fast_malloc()
速度测试添加到(使用 this repo):
LinkedIn post 我做了这个:https://www.linkedin.com/posts/gabriel-staples_software-engineering-tradeoffs-activity-6815412255325339648-_c8L.
相关:
- [我的回购分支]https://github.com/ElectricRCAircraftGuy/malloc-benchmarks
- [我是如何学会如何在 gcc 中生成
*.so
动态库的] https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
- Create a wrapper function for malloc and free in C
Is this possible?
是的。您正在构建和 LD_PRELOAD
ing 一个共享库,共享库可以有特殊的初始化函数和终结函数,动态加载程序分别在加载和卸载库时调用它们。
正如 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.
P.S.
it skews my benchtest results making it look like my fast_malloc() is slower than it really is, because the init func gets timed
- 您正在与多线程基准测试进行比较。请注意,您的
static bool fist_call
不是 线程安全的。实际上这无关紧要,因为 malloc
通常在任何线程(主线程除外)存在之前很久就被调用。
- 我怀疑这种单一比较 实际上 会使您的
fast_malloc()
变慢。它可能 是 即使在你删除比较之后——编写一个快速的堆分配器需要很多努力,聪明的人已经花了很多人年来优化 GLIBC malloc、TCMalloc 和 jemalloc。
如何在另一个可执行文件的 main()
函数之前和之后动态注入函数调用。
这是一个完整的、运行适用于任何想要自行测试的示例。测试于 Linux Ubuntu 20.04.
此代码是我的 eRCaGuy_hello_world 存储库的一部分。
#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
动态加载它(见“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()
函数 before 和 after 中注入了一些特殊的函数调用:
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
- gcc
constructor
和 destructor
函数属性!:
https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
- c
atexit()
func 注册在 main()
returns 或退出后调用的函数!:
https://en.cppreference.com/w/c/program/atexit
我通过在我的 fast_malloc.c
文件:
// Override malloc() and free(); see:
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 func 得到由 glibc/benchtests/bench-malloc-thread.c
计时,我有这个无关的 if (first_call)
每次 malloc 调用都会检查它。
目前我在调用 bench-malloc-thread
可执行文件时动态覆盖 malloc()
和 free()
,如下所示:
LD_PRELOAD='/home/gabriel/GS/dev/fast_malloc/build/libfast_malloc.so' \
glibc-build/benchtests/bench-malloc-thread 1
情节我将把我的 fast_malloc()
速度测试添加到(使用 this repo):
LinkedIn post 我做了这个:https://www.linkedin.com/posts/gabriel-staples_software-engineering-tradeoffs-activity-6815412255325339648-_c8L.
相关:
- [我的回购分支]https://github.com/ElectricRCAircraftGuy/malloc-benchmarks
- [我是如何学会如何在 gcc 中生成
*.so
动态库的] https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html - Create a wrapper function for malloc and free in C
Is this possible?
是的。您正在构建和 LD_PRELOAD
ing 一个共享库,共享库可以有特殊的初始化函数和终结函数,动态加载程序分别在加载和卸载库时调用它们。
正如 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.
P.S.
it skews my benchtest results making it look like my fast_malloc() is slower than it really is, because the init func gets timed
- 您正在与多线程基准测试进行比较。请注意,您的
static bool fist_call
不是 线程安全的。实际上这无关紧要,因为malloc
通常在任何线程(主线程除外)存在之前很久就被调用。 - 我怀疑这种单一比较 实际上 会使您的
fast_malloc()
变慢。它可能 是 即使在你删除比较之后——编写一个快速的堆分配器需要很多努力,聪明的人已经花了很多人年来优化 GLIBC malloc、TCMalloc 和 jemalloc。
如何在另一个可执行文件的 main()
函数之前和之后动态注入函数调用。
这是一个完整的、运行适用于任何想要自行测试的示例。测试于 Linux Ubuntu 20.04.
此代码是我的 eRCaGuy_hello_world 存储库的一部分。
#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
动态加载它(见“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()
函数 before 和 after 中注入了一些特殊的函数调用:
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 - gcc
constructor
和destructor
函数属性!: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes - c
atexit()
func 注册在main()
returns 或退出后调用的函数!: https://en.cppreference.com/w/c/program/atexit