是否可以在 C++ 的编译时计算函数长度?
Is it possible to calculate function length at compile time in C++?
我有这段代码:
constexpr static VOID fStart()
{
auto a = 3;
a++;
}
__declspec(naked)
constexpr static VOID fEnd() {};
static constexpr auto getFSize()
{
return (SIZE_T)((PBYTE)fEnd - (PBYTE)fStart);
}
static constexpr auto fSize = getFSize();
static BYTE func[fSize];
是否可以在不使用任何标准库的情况下在编译期间将“func[fSize]”数组大小声明为“fStart()”的大小?为了稍后将 fStart() 的完整代码复制到此数组中,这是必要的。
标准 C++ 中没有获取函数长度的方法。
您需要使用特定于编译器的方法。
一种方法是让链接器创建一个段,并将您的函数放在该段中。然后使用段的长度。
您也许可以使用一些汇编语言结构来做到这一点;取决于汇编程序和汇编代码。
注意:在嵌入式系统中,移动功能代码是有原因的,例如移动到片上存储器或交换到外部存储器,或者对代码执行校验和。
在某些处理器上以及使用一些已知的编译器和 ABI 约定,您可以执行相反的操作:
在运行时生成机器代码。
对于 Linux 上的 x86/64,我知道 GNU lightning, asmjit, libgccjit 这样做。
elf(5) 格式知道函数的大小。
在 Linux 上,您可以生成 shared libraries (perhaps generate C or C++ code at runtime (like RefPerSys does and GCC MELT did), then compiling it with gcc -fPIC -shared -O
) and later dlopen(3) / dlsym(3) it. And dladdr(3) 非常 有用。您将使用函数指针。
还读了一本关于 linkers and loaders 的书。
但是你通常不能不做一些relocation, unless that machine code is position-independent code就移动机器代码(PIC 通常比普通代码慢 运行)。
相关主题是 garbage collection of code (or even of agents). You need to read the garbage collection handbook and take inspiration from implementations like SBCL。
还请记住,一个好的优化 C++ 编译器允许展开循环、内联扩展函数调用、删除死代码、进行函数克隆等...因此机器代码函数甚至可能不连续:两个C 函数 foo()
和 bar()
可以共享数十条通用机器指令。
在您的 GPGPU 上阅读 Dragon book, and study the source code of GCC (and consider extending it with your GCC plugin). Look also into the assembler code produced by gcc -O2 -Wall -fverbose-asm -S
. Some experimental variants of GCC might be able to generate OpenCL 代码 运行ning(然后,函数结束的概念本身就没有意义)
通过 C 和 C++ 生成的插件,您可以使用 dlclose(3) if you use Ian Taylor's libbacktrace and dladdr
to explore your call stack. In 99% of the cases, it is not worth the trouble, since in practice a Linux process (on current x86-64 laptops in 2021) can do perhaps a million of dlopen(3), as my manydl.c 程序演示小心地删除它们(它生成“随机”C 代码,将其编译成一个独特的 /tmp/generated123.so
,并且 dlopen
那个,并重复多次)。
(在台式机和服务器计算机上)覆盖 机器代码的唯一原因是持久服务器进程每秒生成机器代码。如果这是您的场景,您应该提到它(使用 Java 类加载器生成 JVM 字节码可能更有意义)。
当然,在 16 位微控制器上,情况非常不同。
Is it possible to calculate function length at compile time in C++?
不,因为在运行时间某些功能不再存在。
编译器以某种方式删除了它们。或者克隆它们。或者将它们内联。
对于 C++,standard containers 实际上很重要:会发生大量模板扩展,包括无用代码,您的优化编译器在某些时候必须将其删除。
(想想 2021 年用最近的 GCC 10.2 或 11 编译。到处使用,并链接 gcc -O3 -flto -fwhole-program
:函数 foo23
可能被定义但从未被调用, 然后它不在 ELF 可执行文件中)
下面计算fStart
函数的“字节大小”。但是,无法通过这种方式将大小作为 constexpr
获得,因为转换失去了编译时常量(例如参见 [=13=]),并且两个不相关的函数指针的差异无法在没有某种铸造。
#pragma runtime_checks("", off)
__declspec(code_seg("myFunc$a")) static void fStart()
{ auto a = 3; a++; }
__declspec(code_seg("myFunc$z")) static void fEnd(void)
{ }
#pragma runtime_checks("", restore)
constexpr auto pfnStart = fStart; // ok
constexpr auto pfnEnd = fEnd; // ok
// constexpr auto nStart = (INT_PTR)pfnStart; // error C2131
const auto fnSize = (INT_PTR)pfnEnd - (INT_PTR)pfnStart; // ok
// constexpr auto fnSize = (INT_PTR)pfnEnd - (INT_PTR)pfnStart; // error C2131
我有这段代码:
constexpr static VOID fStart()
{
auto a = 3;
a++;
}
__declspec(naked)
constexpr static VOID fEnd() {};
static constexpr auto getFSize()
{
return (SIZE_T)((PBYTE)fEnd - (PBYTE)fStart);
}
static constexpr auto fSize = getFSize();
static BYTE func[fSize];
是否可以在不使用任何标准库的情况下在编译期间将“func[fSize]”数组大小声明为“fStart()”的大小?为了稍后将 fStart() 的完整代码复制到此数组中,这是必要的。
标准 C++ 中没有获取函数长度的方法。
您需要使用特定于编译器的方法。
一种方法是让链接器创建一个段,并将您的函数放在该段中。然后使用段的长度。
您也许可以使用一些汇编语言结构来做到这一点;取决于汇编程序和汇编代码。
注意:在嵌入式系统中,移动功能代码是有原因的,例如移动到片上存储器或交换到外部存储器,或者对代码执行校验和。
在某些处理器上以及使用一些已知的编译器和 ABI 约定,您可以执行相反的操作:
在运行时生成机器代码。
对于 Linux 上的 x86/64,我知道 GNU lightning, asmjit, libgccjit 这样做。
elf(5) 格式知道函数的大小。
在 Linux 上,您可以生成 shared libraries (perhaps generate C or C++ code at runtime (like RefPerSys does and GCC MELT did), then compiling it with gcc -fPIC -shared -O
) and later dlopen(3) / dlsym(3) it. And dladdr(3) 非常 有用。您将使用函数指针。
还读了一本关于 linkers and loaders 的书。
但是你通常不能不做一些relocation, unless that machine code is position-independent code就移动机器代码(PIC 通常比普通代码慢 运行)。
相关主题是 garbage collection of code (or even of agents). You need to read the garbage collection handbook and take inspiration from implementations like SBCL。
还请记住,一个好的优化 C++ 编译器允许展开循环、内联扩展函数调用、删除死代码、进行函数克隆等...因此机器代码函数甚至可能不连续:两个C 函数 foo()
和 bar()
可以共享数十条通用机器指令。
在您的 GPGPU 上阅读 Dragon book, and study the source code of GCC (and consider extending it with your GCC plugin). Look also into the assembler code produced by gcc -O2 -Wall -fverbose-asm -S
. Some experimental variants of GCC might be able to generate OpenCL 代码 运行ning(然后,函数结束的概念本身就没有意义)
通过 C 和 C++ 生成的插件,您可以使用 dlclose(3) if you use Ian Taylor's libbacktrace and dladdr
to explore your call stack. In 99% of the cases, it is not worth the trouble, since in practice a Linux process (on current x86-64 laptops in 2021) can do perhaps a million of dlopen(3), as my manydl.c 程序演示小心地删除它们(它生成“随机”C 代码,将其编译成一个独特的 /tmp/generated123.so
,并且 dlopen
那个,并重复多次)。
(在台式机和服务器计算机上)覆盖 机器代码的唯一原因是持久服务器进程每秒生成机器代码。如果这是您的场景,您应该提到它(使用 Java 类加载器生成 JVM 字节码可能更有意义)。
当然,在 16 位微控制器上,情况非常不同。
Is it possible to calculate function length at compile time in C++?
不,因为在运行时间某些功能不再存在。
编译器以某种方式删除了它们。或者克隆它们。或者将它们内联。
对于 C++,standard containers 实际上很重要:会发生大量模板扩展,包括无用代码,您的优化编译器在某些时候必须将其删除。
(想想 2021 年用最近的 GCC 10.2 或 11 编译。到处使用,并链接 gcc -O3 -flto -fwhole-program
:函数 foo23
可能被定义但从未被调用, 然后它不在 ELF 可执行文件中)
下面计算fStart
函数的“字节大小”。但是,无法通过这种方式将大小作为 constexpr
获得,因为转换失去了编译时常量(例如参见 [=13=]),并且两个不相关的函数指针的差异无法在没有某种铸造。
#pragma runtime_checks("", off)
__declspec(code_seg("myFunc$a")) static void fStart()
{ auto a = 3; a++; }
__declspec(code_seg("myFunc$z")) static void fEnd(void)
{ }
#pragma runtime_checks("", restore)
constexpr auto pfnStart = fStart; // ok
constexpr auto pfnEnd = fEnd; // ok
// constexpr auto nStart = (INT_PTR)pfnStart; // error C2131
const auto fnSize = (INT_PTR)pfnEnd - (INT_PTR)pfnStart; // ok
// constexpr auto fnSize = (INT_PTR)pfnEnd - (INT_PTR)pfnStart; // error C2131