了解 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的系统调用。 然而,我遇到了一些问题:

  1. 对于 BL 指令,我能否确定要跳转到的标签始终指向 C 函数的开头?这意味着它们就像调用 C 函数一样。
  2. 还有哪些其他指令用于 C 函数调用?
  3. 在上面的示例中,这意味着在 149e2: 处有一个函数调用。我不明白 <XY+0x2a7ec> 部分。 那是不是意味着...
    • 被调用的函数开始于addressoff(XY) + 0x2a7ec,但不在.dynsym table中,因此没有便于反汇编程序参考的人性化名称?
    • 这是否意味着调用 XY 时在其正文中有一个巨大的偏移量?
    • 或者两者都可以?
  4. 如何识别函数的开始?在我看来,像 00014988 <...> 这样具有完整地址的部分是函数。虽然我担心这只适用于属于动态符号 table 的函数,这可以解释上面的巨大偏移量。
  5. 149f4也有一个分支,这次是通过CBZ。这看起来也像是对不同函数的函数调用,并且同样带有偏移量。当 00014988000149f8 都是函数时,这个 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?

没有一般规则。
这是一个过程:

  1. 打开列表文件(包含汇编语言)。
  2. 在汇编语言列表中查找偏移量。
  3. 或者找到基金,然后添加偏移量并查找该地址。

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 地址(通常BLBX 之后的下一条指令)。为了return,LR寄存器中的值被复制到程序计数器(PC)寄存器中,导致执行转移到LR中的地址。这是来自函数或子例程调用的 return。