在 C 函数中定义一个唯一的全局程序集标签/符号
Posted
技术标签:
【中文标题】在 C 函数中定义一个唯一的全局程序集标签/符号【英文标题】:Define a unique and global assembly label/symbol inside C functions 【发布时间】:2018-10-09 22:07:12 【问题描述】:我想用某种汇编标签/符号来标记特定的 C 行,它不会占用二进制文件中的任何空间,但是通过检查链接器输出映射文件,我将知道所有此类生成标签的出现,并最终知道 C以这种方式“标记”的代码。所以我希望能够定义这样的标签,并使它们成为全局的,并且 used 所以链接器不会把它扔掉 我还需要一些宏魔法来让这些标签在每次预处理 C 代码时都有一个唯一的名称(以确保函数的每个内联实例都有自己的标签 - 否则我猜我会有重复的符号)
例子:
// my build system will pass -DMYFILE_ID for each file, here I am trying to create a unique literal for each inline instance of the function
#define UN(X) #X
#define UNIQUE(X,Y) UN(X##Y)
void my_func(void)
_asm("GLOBAL_LABEL_"UNIQUE(MYFILE_ID,__LINE__)":\n\t")
my_c_code_I_want_to_track();
最后我想要的是在链接器输出符号映射文件中,类似的东西
0xsome_address GLOBAL_LABEL_12_1
0xdifferent_address GLOBAL_LABEL_12_2
0xyeanotheraddress GLOBAL_LABEL_13_1
这基本上应该让我知道 my_c_code_i_want_to_track 实例化的地址
整个想法的灵感来自于组装中的标签实际上是如何放置的“符号”,因此可以检查它们的地址,但它们实际上并不占用自己的空间。
问题: 1.是否可以像这样定义装配标签 2. 如何让这些标签留在输出符号映射文件中并出现 3. UNIQUE 宏有问题,因为我在尝试编译时得到“标签重新定义”
【问题讨论】:
你的问题到底是什么? 用问题定义编辑的问题 不确定内联是如何工作的,它可以很好地重用相同的__LINE__
。至于你的地图文件:你是如何创建的?
除非您明确要求,否则不应丢弃、引用或不引用全局变量。
在剥离二进制文件之前提取地址信息可能会更简单。
【参考方案1】:
您可以在Extended-asm
template 中使用%=
(例如label%=:
)来让编译器生成一个唯一编号,以避免在一个编译单元中多次内联包含 inline-asm 的函数时发生名称冲突。
#define STRINGIFY(x) #x
#define STR(x) STRINGIFY(x)
int foo(int x)
asm("marker" __FILE__ "_line" STR(__LINE__) "_uniqueid%=:" :::);
return x+1;
int caller1(int x)
return foo(x);
int caller2(int x)
return foo(x);
使用gcc -O3
(on Godbolt) 编译为以下汇编:
foo(int):
marker/tmp/compiler-explorer-compiler11899-55-1ki0cth.pehm/example.cpp_line4_uniqueid7:
lea eax, [rdi+1]
ret
caller1(int):
marker/tmp/compiler-explorer-compiler11899-55-1ki0cth.pehm/example.cpp_line4_uniqueid22:
lea eax, [rdi+1]
ret
caller2(int):
marker/tmp/compiler-explorer-compiler11899-55-1ki0cth.pehm/example.cpp_line4_uniqueid41:
lea eax, [rdi+3]
ret
这当然不会组合,因为/
在 GAS 中不是有效的标签字符。
使用只包含可以出现在符号名称中的字符的MYFILE_ID
,这样可以很好地组合,并且您应该能够在nm
输出中看到所有marker
标签。
【讨论】:
【参考方案2】:一个问题是,由于内联,您可能会获得同一个标签的多个副本。将以下属性添加到包含这些标签的函数中:
__attribute__((noinline))
另请注意,您需要将符号标记为全局。让我们把它提取到一个宏中,这样我们就可以很好地格式化而不改变__LINE__
的值:
#define MAKE_LABEL \
__asm__( \
"GLOBAL_LABEL_" UNIQUE(MYFILE_ID, __LINE__) ":" \
"\n\t.global GLOBAL_LABEL_" UNIQUE(MYFILE_ID, __LINE__) \
)
但宏扩展已关闭。不幸的是,我无法向您解释为什么会这样。但这里是正确的宏定义:
#define UN(X) #X
#define UNIQUE2(X,Y) UN(X##Y)
#define UNIQUE(X,Y) UNIQUE2(X,Y)
否则,您将得到 __LINE__
而不是 23。
【讨论】:
GNU C Basicasm
(无操作数)隐含为volatile
。没有输出操作数的扩展 asm
也是如此。
嗯,很有趣。我不知道这一点。
知道如何为此添加类似 __attribute((used)) 的内容,因此即使要求链接器丢弃任何未引用的符号 - 这些标签仍然存在?
@LouisShraga:您具体使用了哪些链接器选项导致了这种情况?
@LouisShraga:您将不得不告诉我们更多有关构建工具链的信息。 __attribute__((used))
的工作方式在不同的系统上是不同的。以上是关于在 C 函数中定义一个唯一的全局程序集标签/符号的主要内容,如果未能解决你的问题,请参考以下文章