为什么在堆栈中定义的C函数指针在传递时无效?

问题描述

我试图将在父块范围内定义的函数指针传递给另一个函数。我在不同的环境中都可以正常工作和遇到段错误。 (我不是C专家)

代码

#include <stdio.h>
#include <stdbool.h>

void test_function(bool (*function_pointer) (int x)) {
        printf("addr passed function_pointer %p\n",function_pointer);
        if (function_pointer(100)) {
                printf("  run: true\n");
        } else {
                printf("  run: false\n");
        }
}

bool function_outside_main(int x) {
        return x < 0;
}

int main(void) {
        // run with function defined globally
        printf("addr function_outside_main %p\n",function_outside_main);
        test_function(function_outside_main);

        // run with function defined in this stack block
        bool function_inside_main(int x) {
                return x > 0;
        }
        printf("addr function_inside_main %p\n",function_inside_main);
        test_function(function_inside_main); // shouldn't the address be valid?
}

在具有GCC版本5.4.0的Ubuntu 16.04.4(在Amazon EC2上)上,它可以与输出配合使用:

addr function_outside_main 0x400620
addr passed function_pointer 0x400620
  run: false
addr function_inside_main 0x7ffc018d5690
addr passed function_pointer 0x7ffc018d5690
  run: true

在具有GCC版本9.3.0的Ubuntu 20.04.1(在Windows WSL下)上,它会出现段错误

addr function_outside_main 0x7ff19c8631dd
addr passed function_pointer 0x7ff19c8631dd
  run: false
addr function_inside_main 0x7ffffc033b20
addr passed function_pointer 0x7ffffc033b20
zsh: segmentation fault (core dumped)  ./a.out

解决方法

类似这样的嵌套函数是gcc扩展,不是C标准的一部分。

gcc通常用于此 1 的实现涉及为嵌套函数创建堆栈上的thunk,因此调用它需要可执行的堆栈支持。较新版本的Linux(和Windows)默认使用不可执行的堆栈,因此会崩溃。

要执行此操作,可以对gcc使用-z execstack选项,也可以在创建二进制文件后使用execstack工具修改二进制文件以指定可执行堆栈。


1 在某些带有-O的gcc版本中,它可以确定何时实际上不需要嵌套函数(它们从不引用包含范围),并且不使用thunk对于这些情况

,

我没有访问WSL(或者说9.3 gcc)的权限,但是我确实注意到了一个警告,在返回内部函数后,可能无法调用内部函数。通常,这对于延迟的函数会很麻烦,但是我怀疑您的编译器已经使用函数结尾折叠了最终语句( test_function(function_inside_main); // shouldn't the address be valid?);那么您可以在该语句之后添加一行:printf("Hello,world\n");,看看它是否可以神奇地解决该问题?

扩展名又可能是定义自己的C类语言的危险。

,

使用此gcc扩展名,不能在其定义的功能范围之外调用它。这是未定义的行为。与取消引用指针在函数作用域之外的局部自动变量非常相似