如何利用GCC Labels as Values扩展实现全局标签值构建线程化解释器
GCC Labels as Values扩展:线程化解释器的标签全局化问题
我想利用GCC及其他编译器支持的C/C++ Labels as Values 扩展来编写线程化解释器——这类虚拟机通过直接链接真实机器代码块的地址来定义指令。我的需求是把函数内部的标签值作为全局常量,用于编译或存储VM代码,但示例代码因为扩展不允许在函数外部引用函数内的标签值,无法通过编译。
构想的解释器示例代码如下:
#include <stdio.h> void interpret (void **prog) { void **pc = prog; goto **pc; foo: printf("Foo"); goto nextword; bar: printf("Bar"); goto nextword; nextword: if (*(++pc) != STOP) { printf(" "); goto **pc; } else printf ("\n."); stop: return; } // 这里会编译失败:无法在函数外部引用函数内的标签 void *FOO=&foo, *BAR=&bar, *STOP=&stop; int main (int argc, char **argv) { void *prog[] = {FOO, BAR, FOO, BAR, BAR, FOO, STOP}; interpret(prog); return 0; }
现有替代方案(比如设置函数初始化模式、使用索引数组)存在弊端,特此问询是否有遗漏的可行方法,或是此类实现存在技术层面的阻碍原因。
可行解决方法
1. 静态变量捕获标签地址(首次调用初始化)
在interpret函数内部用静态变量存储标签地址,首次调用函数时完成初始化,再通过全局指针或宏提供外部访问:
#include <stdio.h> static void **FOO_ptr, **BAR_ptr, **STOP_ptr; void interpret (void **prog) { static int initialized = 0; if (!initialized) { // 首次调用时捕获标签地址 FOO_ptr = &&foo; BAR_ptr = &&bar; STOP_ptr = &&stop; initialized = 1; } void **pc = prog; if (!pc) return; // 空参数仅用于初始化 goto **pc; foo: printf("Foo"); goto nextword; bar: printf("Bar"); goto nextword; nextword: if (*(++pc) != STOP_ptr) { printf(" "); goto **pc; } else printf ("\n."); stop: return; } // 用宏简化外部调用 #define FOO FOO_ptr #define BAR BAR_ptr #define STOP STOP_ptr int main (int argc, char **argv) { // 先调用一次完成标签地址初始化 interpret(NULL); void *prog[] = {FOO, BAR, FOO, BAR, BAR, FOO, STOP}; interpret(prog); return 0; }
2. 全局跳转表(函数内部填充)
定义全局结构体存储所有标签地址,在interpret函数内部完成填充,适合指令较多的场景:
#include <stdio.h> // 全局跳转表结构体 typedef struct { void **foo; void **bar; void **stop; } VM_JumpTable; VM_JumpTable vm_jump_table; void interpret (void **prog) { // 每次调用都填充(或加静态判断只填充一次) vm_jump_table.foo = &&foo; vm_jump_table.bar = &&bar; vm_jump_table.stop = &&stop; void **pc = prog; if (!pc) return; goto **pc; foo: printf("Foo"); goto nextword; bar: printf("Bar"); goto nextword; nextword: if (*(++pc) != vm_jump_table.stop) { printf(" "); goto **pc; } else printf ("\n."); stop: return; } #define FOO vm_jump_table.foo #define BAR vm_jump_table.bar #define STOP vm_jump_table.stop int main (int argc, char **argv) { interpret(NULL); void *prog[] = {FOO, BAR, FOO, BAR, BAR, FOO, STOP}; interpret(prog); return 0; }
技术层面的阻碍原因
- 标签作用域限制:GCC的Labels as Values扩展将标签限定在函数级作用域内,编译器不会为函数内标签生成全局符号,禁止跨函数直接引用是为了避免不安全的跨栈帧跳转(比如函数执行完毕栈帧销毁后跳转)。
- 地址初始化时机冲突:全局变量的初始化发生在程序启动阶段,此时函数代码可能还未完成内存重定位,标签的实际地址尚未确定,直接用
&&foo初始化全局变量会得到无效地址。
内容的提问来源于stack exchange,提问作者bobtato
相关产品推荐
相关产品推荐

