我已经获得了 RTL 格式的 gcc 控制流图转储,并使用 graphviz 对其进行了可视化。然而,目前还不清楚哪些跳转/调用是直接的,哪些是间接的。有什么建议可以区分它们吗?
问问题
356 次
1 回答
2
直接调用通常包含一个符号引用作为 RTL 的一部分,因为编译器确切地知道调用目标。对于间接调用,存在使用一个或多个寄存器的内存引用,但不存在符号。因此,内存 RTL 子表达式类似于我们在 mov 指令中看到的标准内存读写格式。
例如,Linux 内核 3.19 中函数do_early_param中的第一条调用指令是直接调用:
callq ffffffffc1657839 <strcmp>
各自的 RTL 是
(call_insn/i:TI 19 18 134 4 (set (reg:SI 0 ax)
(call (mem:QI (symbol_ref:DI ("strcmp") [flags 0x41] <function_decl 0x2b2f6f0e1a00 strcmp>) [0 __builtin_strcmp S1 A8])
(const_int 0 [0]))) init/main.c:436 631 {*call_value}
(expr_list:REG_DEAD (reg:DI 5 di)
(expr_list:REG_DEAD (reg:DI 4 si)
(expr_list:REG_EH_REGION (const_int 0 [0])
(nil))))
(expr_list:REG_FRAME_RELATED_EXPR (use (reg:DI 5 di))
(expr_list:REG_FRAME_RELATED_EXPR (use (reg:DI 4 si))
(nil))))
稍后,我们有一个间接调用:
callq *0x8(%rbx)
这转化为
(call_insn:TI 38 37 140 7 (set (reg:SI 0 ax)
(call (mem:QI (mem/f:DI (plus:DI (reg/v/f:DI 3 bx [orig:60 p ] [60])
(const_int 8 [0x8])) [0 MEM[base: p_1, offset: 8B]+0 S8 A64]) [0 *D.45869_10 S1 A8])
(const_int 0 [0]))) init/main.c:439 631 {*call_value}
(expr_list:REG_DEAD (reg:DI 5 di)
(nil))
(expr_list:REG_FRAME_RELATED_EXPR (use (reg:DI 5 di))
(nil)))
(没有 symbol_ref)。对于 JMP 指令,请记住间接 JMP 实际上可能是尾调用,在 RTL 中显示为 CALL_P 指令。例如,在函数mtrr_bp_init我们有
jmpq *%rax
在 RTL 中是
(call_insn/j:TI 144 143 145 25 (call (mem:QI (reg/f:DI 0 ax [orig:151 mtrr_if.32_31->set_all ] [151]) [0 *D.30602_32 S1 A8])
(const_int 0 [0])) arch/x86/kernel/cpu/mtrr/main.c:744 623 {*sibcall}
(expr_list:REG_DEAD (reg/f:DI 0 ax [orig:151 mtrr_if.32_31->set_all ] [151])
(nil))
(nil))
除此之外,通常间接 jmp 将显示为 PARALLEL RTL 表达式。例如,在函数update_mp_table我们看到
jmpq *-0x3eff7188(,%rax,8)
在 RTL 中是:
(jump_insn:TI 173 170 174 22 (parallel [
(set (pc)
(mem/u/c:DI (plus:DI (mult:DI (reg:DI 0 ax [orig:182 *mpt_138 ] [182])
(const_int 8 [0x8]))
(label_ref:DI 175)) [0 S8 A8]))
(use (label_ref 175))
]) arch/x86/kernel/mpparse.c:741 613 {*tablejump_1}
(expr_list:REG_DEAD (reg:DI 0 ax [orig:182 *mpt_138 ] [182])
(insn_list:REG_LABEL_OPERAND 175 (nil)))
-> 175)
于 2015-09-28T18:33:41.713 回答