如何在禁用glibc的情况下编译自定义C语言malloc实现?
解决方法:手动基于brk系统调用实现sbrk函数
因为-nostdlib会完全禁用glibc的所有库函数,而sbrk()本身是glibc对底层brk系统调用的封装,所以你需要自己手动实现sbrk()的逻辑,直接调用系统调用完成内存断点的调整。
核心原理
在x86_64架构下,brk系统调用的编号是12(无需依赖头文件,直接硬编码即可):
- 传入参数为
0时,会返回当前程序的内存断点地址 - 传入具体地址时,会将程序断点调整到该地址,成功返回
0,失败返回-1 sbrk()的本质就是:获取当前断点→计算新断点→调用brk更新→返回旧断点
手动实现sbrk函数(x86_64)
直接用内联汇编编写符合逻辑的sbrk,不需要依赖任何glibc函数:
#include <stdint.h> void *sbrk(intptr_t increment) { static void *current_brk = NULL; void *old_brk; uint64_t sys_ret; // 首次调用先获取当前内存断点 if (current_brk == NULL) { __asm__ volatile ( "mov $12, %%rax\n" // brk系统调用编号 "mov $0, %%rdi\n" // 参数0:获取当前断点 "syscall\n" "mov %%rax, %0\n" : "=r"(sys_ret) : : "%rax", "%rdi", "%rcx", "%r11" // 声明被修改的寄存器 ); current_brk = (void *)sys_ret; } old_brk = current_brk; void *new_brk = (void *)((uintptr_t)current_brk + increment); // 调用brk设置新断点 __asm__ volatile ( "mov $12, %%rax\n" "mov %1, %%rdi\n" // 参数:新的断点地址 "syscall\n" "mov %%rax, %0\n" : "=r"(sys_ret) : "r"(new_brk) : "%rax", "%rdi", "%rcx", "%r11" ); // 处理调用失败的情况 if (sys_ret == (uint64_t)-1) { return (void *)-1; } current_brk = new_brk; return old_brk; }
补充:实现程序入口函数
因为-nostdlib会禁用glibc的_start入口,你需要自己编写一个简单的入口函数来启动程序:
#include <stdint.h> void main(); // x86_64架构的程序入口 void _start() { main(); // 调用exit系统调用退出(编号60) __asm__ volatile ( "mov $60, %%rax\n" "mov $0, %%rdi\n" "syscall\n" ); }
编译注意事项
保持你原有的编译标志即可:
gcc -m64 -fPIC -pedantic -Wall -Wextra -Werror -nostdlib -ggdb3 your_malloc.c -o your_malloc
stddef.h和stdint.h是编译器内置的标准头文件,-nostdlib不会影响它们的使用- 上述代码仅适配x86_64架构,和你的
-m64编译标志匹配
内容的提问来源于stack exchange,提问作者Nayrosk
相关产品推荐
相关产品推荐

