GCC:如何在 MCU 上完全禁用堆使用?

GCC: How to disable heap usage entirely on an MCU?

我有一个 运行 在基于 ARM Cortex-M 的 MCU 上使用 C 和 C++ 编写的应用程序。我使用 gccg++ 来编译它,并希望完全禁用任何堆使用。

在 MCU 启动文件中,堆大小已设置为 0。除此之外,我还想禁止在代码中意外使用堆。

换句话说,我希望链接器(and/or 编译器)在 malloccallocfree 函数或使用了 newnew[]deletedelete[] 运算符。

到目前为止,我已经尝试了 -nostdlib,这给了我 undefined reference to _start 这样的问题。我也试过 -nodefaultlibs 但当我尝试调用 malloc 时那个人仍然没有抱怨。正确的做法是什么?

备注:

我不确定这是最好的方法,但是您可以使用 ld--wrap 标志(可以使用 -Wl 通过 gcc ).

想法是 --wrap 允许您请求 ld 将 "real" 符号重定向到您的自定义符号;例如,如果您执行 --wrap=malloc,那么 ld 将查找要调用的 __wrap_malloc 函数,而不是原来的 `malloc.

现在,如果你 --wrap=malloc 没有定义 __wrap_malloc 如果没有人使用它,你将逃脱它,但如果有人引用 malloc 你会得到一个链接错误。

$ cat test-nomalloc.c 
#include <stdlib.h>

int main() {
#ifdef USE_MALLOC
    malloc(10);
#endif
    return 0;
}
$ gcc test-nomalloc.c -Wl,--wrap=malloc
$ gcc test-nomalloc.c -DUSE_MALLOC -Wl,--wrap=malloc
/tmp/ccIEUu9v.o: In function `main':
test-nomalloc.c:(.text+0xa): undefined reference to `__wrap_malloc'
collect2: error: ld returned 1 exit status

对于 new,您可以使用损坏的名称 _Znwm (operator new(unsigned long)) 和 _Znam (operator new[](unsigned long)),这应该是每个 new应该归结到最后。

(作为答案发布,因为它不适合评论)

如果 OS 您 运行 支持 the use of LD_PRELOAD,此代码应检测使用堆的尝试:

/* remove the LD_PRELOAD from the environment so it
   doesn't kill any child process the app may spawn */
static void lib_init(void) __attribute__((constructor));
static void lib_init( void )
{
    unsetenv( "LD_PRELOAD" );
}

void *malloc( size_t bytes )
{
    kill( getpid(), SIGSEGV );
    return( NULL );
}

void *calloc( size_t n, size_t bytes )
{
    kill( getpid(), SIGSEGV );
    return( NULL );
}

void *realloc( void *ptr, size_t bytes )
{
    kill( getpid(), SIGSEGV );
    return( NULL );
}

void *valloc( size_t bytes )
{
    kill( getpid(), SIGSEGV );
    return( NULL );
}

void *memalign( size_t alignment, size_t bytes )
{
    kill( getpid(), SIGSEGV );
    return( NULL );
}

int posix_memalign( void **ptr, size_t alignment, size_t bytes )
{
    *ptr = NULL;
    kill( getpid(), SIGSEGV );
    return( -1 );
}

假设 new 是使用 malloc() 实现的,而 delete 是使用 free() 实现的,这将捕获所有堆使用情况并为您提供一个带有堆栈跟踪的核心文件,假设启用了核心文件。

添加正确的头文件,编译文件:

gcc [-m32|-m64] -shared heapdetect.c -o heapdetect.so

运行 您的应用:

LD_PRELOAD=/path/to/heapdetect.so /your/app/here args ...