了解 ARM ASM 中的函数调用
Understanding function calls in ARM ASM
我正在反汇编本机 Android 库 (armeabi-v7a),这些库很可能是从 C++ 或 C 代码创建的,并且已被删除。
目标是创建一个函数调用树供以后分析。
我在理解反汇编输出时遇到问题。
采用以下由 arm-linux-androideabi-objdump -d libledger.so > output.txt
创建的汇编程序片段
00014988 <_ZSt10__pop_heapIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEENS0_5__ops15_Iter_less_iterEEvT_S9_S9_T0_>:
...
149e0: 4620 mov r0, r4
149e2: f030 fadd bl 44fa0 <_ZNK10__cxxabiv120__si_class_type_info11__do_upcastEPKNS_17__class_type_infoEPKvRNS1_15__upcast_resultE+0x2a7ec>
149e6: e7ff b.n 149e8 <_ZSt10__pop_heapIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEENS0_5__ops15_Iter_less_iterEEvT_S9_S9_T0_+0x60>
149e8: 4628 mov r0, r5
149ea: f030 fad9 bl 44fa0 <_ZNK10__cxxabiv120__si_class_type_info11__do_upcastEPKNS_17__class_type_infoEPKvRNS1_15__upcast_resultE+0x2a7ec>
149ee: f004 fe55 bl 1969c <__cxa_end_cleanup>
149f2: bf00 nop
149f4: b154 cbz r4, 14a0c <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_+0x14>
149f6: 0005 movs r5, r0
000149f8 <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_>:
149f8: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
149fc: 1d07 adds r7, r0, #4
149fe: b085 sub sp, #20
14a00: 4604 mov r4, r0
14a02: 4690 mov r8, r2
14a04: 460e mov r6, r1
14a06: 1b35 subs r5, r6, r4
14a08: 2d43 cmp r5, #67 ; 0x43
14a0a: f340 8095 ble.w 14b38 <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_+0x140>
14a0e: f1b8 0f00 cmp.w r8, #0
14a12: d13f bne.n 14a94 <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_+0x9c>
14a14: 10ad asrs r5, r5, #2
14a16: 4b4a ldr r3, [pc, #296] ; (14b40 <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_+0x148>)
14a18: 1eaf subs r7, r5, #2
14a1a: f10d 090c add.w r9, sp, #12
描述的分支指令 here those of the BL
family resemble most a function call, which is also hinted at 5.3 Subroutine Call。
然后还有通过SWI
的系统调用。
然而,我遇到了一些问题:
- 对于
BL
指令,我能否确定要跳转到的标签始终指向 C 函数的开头?这意味着它们就像调用 C 函数一样。
- 还有哪些其他指令用于 C 函数调用?
- 在上面的示例中,这意味着在
149e2:
处有一个函数调用。我不明白 <XY+0x2a7ec>
部分。
那是不是意味着...
- 被调用的函数开始于
addressoff(XY) + 0x2a7ec
,但不在.dynsym
table中,因此没有便于反汇编程序参考的人性化名称?
- 这是否意味着调用
XY
时在其正文中有一个巨大的偏移量?
- 或者两者都可以?
- 如何识别函数的开始?在我看来,像
00014988 <...>
这样具有完整地址的部分是函数。虽然我担心这只适用于属于动态符号 table 的函数,这可以解释上面的巨大偏移量。
- 在
149f4
也有一个分支,这次是通过CBZ
。这看起来也像是对不同函数的函数调用,并且同样带有偏移量。当 00014988
和 000149f8
都是函数时,这个 CBZ
调用将直接跳转到函数而不是在开头。这是什么意思?
Can I be sure for the BL instructions that the labels to jump to always point to the beginning of a C-function? This would mean that they act like calling a C-function.
是的,大多数时候分支会到函数的开头。
What other instructions are used for C-function calls?
任何可以改变程序计数器的东西。
我看到了 BX, LDR PC,
和 POP
说明。
In the example above it would mean at 149e2: there is a function call. I don't understand the part though. Does that mean that ...
The called function starts at addressoff(XY) + 0x2a7ec, but is not in the .dynsym table and thus has no human-friendly name for the disassembler to refer to?
并非每个地址都与来源行号对齐。偏移量是 map 文件中已知符号的距离,通常是函数名。
Does it mean that XY is called with a huge offset into its body?
没有。分支的目的地与 public 符号有一定的偏移量。很多情况下很多函数都不是public,所以引用映射文件中最近的public符号。
Or are both possible?
见上文。
How do I recognise the start of functions?
没有一般规则。
这是一个过程:
- 打开列表文件(包含汇编语言)。
- 在汇编语言列表中查找偏移量。
- 或者找到基金,然后添加偏移量并查找该地址。
At 149f4 there also is a branch, this time via CBZ. This also looks like a function call into a different function, and again with an offset. When both 00014988 and 000149f8 are functions when this CBZ call would jump directly into the function and not at the beginning. What does this mean?
CBZ
不是 子程序 或 函数 调用。它是另一个地址的分支。预计没有 return。
Branch and Link (BL
) 和 Branch and Exchange (BX
) 指令将 LR
寄存器设置为 return 地址(通常BL
或 BX
之后的下一条指令)。为了return,LR
寄存器中的值被复制到程序计数器(PC
)寄存器中,导致执行转移到LR中的地址。这是来自函数或子例程调用的 return。
我正在反汇编本机 Android 库 (armeabi-v7a),这些库很可能是从 C++ 或 C 代码创建的,并且已被删除。 目标是创建一个函数调用树供以后分析。
我在理解反汇编输出时遇到问题。
采用以下由 arm-linux-androideabi-objdump -d libledger.so > output.txt
00014988 <_ZSt10__pop_heapIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEENS0_5__ops15_Iter_less_iterEEvT_S9_S9_T0_>:
...
149e0: 4620 mov r0, r4
149e2: f030 fadd bl 44fa0 <_ZNK10__cxxabiv120__si_class_type_info11__do_upcastEPKNS_17__class_type_infoEPKvRNS1_15__upcast_resultE+0x2a7ec>
149e6: e7ff b.n 149e8 <_ZSt10__pop_heapIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEENS0_5__ops15_Iter_less_iterEEvT_S9_S9_T0_+0x60>
149e8: 4628 mov r0, r5
149ea: f030 fad9 bl 44fa0 <_ZNK10__cxxabiv120__si_class_type_info11__do_upcastEPKNS_17__class_type_infoEPKvRNS1_15__upcast_resultE+0x2a7ec>
149ee: f004 fe55 bl 1969c <__cxa_end_cleanup>
149f2: bf00 nop
149f4: b154 cbz r4, 14a0c <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_+0x14>
149f6: 0005 movs r5, r0
000149f8 <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_>:
149f8: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
149fc: 1d07 adds r7, r0, #4
149fe: b085 sub sp, #20
14a00: 4604 mov r4, r0
14a02: 4690 mov r8, r2
14a04: 460e mov r6, r1
14a06: 1b35 subs r5, r6, r4
14a08: 2d43 cmp r5, #67 ; 0x43
14a0a: f340 8095 ble.w 14b38 <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_+0x140>
14a0e: f1b8 0f00 cmp.w r8, #0
14a12: d13f bne.n 14a94 <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_+0x9c>
14a14: 10ad asrs r5, r5, #2
14a16: 4b4a ldr r3, [pc, #296] ; (14b40 <_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPSsSt6vectorISsSaISsEEEEiNS0_5__ops15_Iter_less_iterEEvT_S9_T0_T1_+0x148>)
14a18: 1eaf subs r7, r5, #2
14a1a: f10d 090c add.w r9, sp, #12
描述的分支指令 here those of the BL
family resemble most a function call, which is also hinted at 5.3 Subroutine Call。
然后还有通过SWI
的系统调用。
然而,我遇到了一些问题:
- 对于
BL
指令,我能否确定要跳转到的标签始终指向 C 函数的开头?这意味着它们就像调用 C 函数一样。 - 还有哪些其他指令用于 C 函数调用?
- 在上面的示例中,这意味着在
149e2:
处有一个函数调用。我不明白<XY+0x2a7ec>
部分。 那是不是意味着...- 被调用的函数开始于
addressoff(XY) + 0x2a7ec
,但不在.dynsym
table中,因此没有便于反汇编程序参考的人性化名称? - 这是否意味着调用
XY
时在其正文中有一个巨大的偏移量? - 或者两者都可以?
- 被调用的函数开始于
- 如何识别函数的开始?在我看来,像
00014988 <...>
这样具有完整地址的部分是函数。虽然我担心这只适用于属于动态符号 table 的函数,这可以解释上面的巨大偏移量。 - 在
149f4
也有一个分支,这次是通过CBZ
。这看起来也像是对不同函数的函数调用,并且同样带有偏移量。当00014988
和000149f8
都是函数时,这个CBZ
调用将直接跳转到函数而不是在开头。这是什么意思?
Can I be sure for the BL instructions that the labels to jump to always point to the beginning of a C-function? This would mean that they act like calling a C-function.
是的,大多数时候分支会到函数的开头。
What other instructions are used for C-function calls?
任何可以改变程序计数器的东西。
我看到了 BX, LDR PC,
和 POP
说明。
In the example above it would mean at 149e2: there is a function call. I don't understand the part though. Does that mean that ...
The called function starts at addressoff(XY) + 0x2a7ec, but is not in the .dynsym table and thus has no human-friendly name for the disassembler to refer to?
并非每个地址都与来源行号对齐。偏移量是 map 文件中已知符号的距离,通常是函数名。
Does it mean that XY is called with a huge offset into its body?
没有。分支的目的地与 public 符号有一定的偏移量。很多情况下很多函数都不是public,所以引用映射文件中最近的public符号。
Or are both possible?
见上文。
How do I recognise the start of functions?
没有一般规则。
这是一个过程:
- 打开列表文件(包含汇编语言)。
- 在汇编语言列表中查找偏移量。
- 或者找到基金,然后添加偏移量并查找该地址。
At 149f4 there also is a branch, this time via CBZ. This also looks like a function call into a different function, and again with an offset. When both 00014988 and 000149f8 are functions when this CBZ call would jump directly into the function and not at the beginning. What does this mean?
CBZ
不是 子程序 或 函数 调用。它是另一个地址的分支。预计没有 return。
Branch and Link (BL
) 和 Branch and Exchange (BX
) 指令将 LR
寄存器设置为 return 地址(通常BL
或 BX
之后的下一条指令)。为了return,LR
寄存器中的值被复制到程序计数器(PC
)寄存器中,导致执行转移到LR中的地址。这是来自函数或子例程调用的 return。