【发布时间】:2021-02-14 03:43:56
【问题描述】:
有没有办法在纯标准 C++ 中以某种方式调用在缓冲区中生成的函数?
假设:
- 我们使用了特定于实现的知识(以及特定于架构的知识)来生成字节序列,以在
char的数组中实现某些功能。 - 此实现与实现的调用约定和 ABI 的其他相关部分正确匹配,因此如果作为 C++ 函数调用,它实际上会正常运行
- 缓冲区被正确标记为可执行文件
也就是说,所有架构/ABI 障碍都已克服,我们需要做的只是调用这段代码。 问题是:是否有一种符合标准的方法来创建指向 this 的函数指针并调用所述函数指针而不会遇到未定义的行为?
据我了解,如果我们可以将指向对象类型的指针转换为函数指针类型,则它是实现定义的。我还认为,如果所述指针将指向与原始指针相同的逻辑地址,则它是实现定义的。障碍是调用:从实现的角度来看,我所做的只是尝试将指向字符数组的指针转换为函数指针,然后调用它。如果说 ptr 是对象类型之一,我知道这将违反严格的别名规则,并且是未定义的行为。但是如果我尝试调用这个函数指针,它是未定义的行为还是仅仅是实现定义的?
【问题讨论】:
-
C++ 不支持 ABI,因此它是 UB。虽然如果你能找到在这种情况下指定行为的特定实现的文档,我相信它可以被认为是实现定义的。
-
POSIX 兼容系统上的 C/C++ 实现需要支持调用从
void*转换的函数指针,以便dlsym工作。此外,工作草案中的措辞是有条件地支持对对象类型的这种转换,并且仅当从错误类型的函数转换为 fptr 时,才定义对 fptr 的调用。当/如果它指向不是函数的东西时,没有这样的语言。 -
从 C++ 标准的角度来看,这是未定义的行为。从您的 ABI 规范或主机操作系统 API 规范(例如 POSIX)的角度来看,它可能是完美定义的行为。从其他一些 ABI 规范(或其他一些主机操作系统的规范)的角度来看,它可能是未定义的行为。
-
C++ 未指定。语言规范说它是 UB。但是,无论如何,几乎 100% 的现实世界软件都在这样做并且工作正常,因为 GetProcAddress( windows ) 和 dlsym ( Linux ) 都返回要转换为可调用函数指针的
void*值。 -
回复:“我们使用了特定于实现的知识”——根据定义,这意味着行为是未定义的。未定义的行为仅仅意味着语言定义没有告诉你你的程序做了什么。这并不意味着坏事会发生。
标签: c++ language-lawyer