objdump 产生错误的分支操作码解释

objdump produces wrong branch opcode interpretation

查看特定函数的特定目标文件的以下 objdump 行 (func):

3c:   e03a            b.n     78 <func+0x78>

现在,目标系统 (ARMv6-M) 中的操作码 e03a 表示跳转到 PC + 0x78 的位置。正确的解释是:

3c:   e03a            b.n     B4 <func+0xB4>

每个其他函数和文件都包含正确的 b.n 解释,并在其 objdump 转储中进行正确的值计算。由于某种原因,只有这个函数导致 objdump 成为 "confused".

注意:func0x0 开始。

我想不出造成这种情况的任何原因。而且由于我有解析和使用 objdump 转储的工具,这给我带来了很大的问题。 有什么合理的理由吗?

工具链:gcc-arm-none-eabi-4_9-2015q3

平台运行这个工具链:Ubuntu 16.04.2 LTS


编辑: 我正在附加部分转储:

Disassembly of section i.func:

00000000 <func>:
   0:   b531        push    {r0, r4, r5, lr}
   2:   b088        sub sp, #32
   4:   2100        movs    r1, #0
   6:   9106        str r1, [sp, #24]
   8:   482c        ldr r0, [pc, #176]  ; (bc <func+0xbc>)
   a:   6800        ldr r0, [r0, #0]
   c:   6840        ldr r0, [r0, #4]
   e:   9103        str r1, [sp, #12]
  10:   1c40        adds    r0, r0, #1
  12:   9002        str r0, [sp, #8]
  14:   492a        ldr r1, [pc, #168]  ; (c0 <func+0xc0>)
  16:   2000        movs    r0, #0
  18:   9104        str r1, [sp, #16]
  1a:   9005        str r0, [sp, #20]
  1c:   a802        add r0, sp, #8
  1e:   f7ff fffe   bl  0 <random_func>
  22:   f7ff fffe   bl  0 <random_func2>
  26:   4604        mov r4, r0
  28:   4d26        ldr r5, [pc, #152]  ; (c4 <func+0xc4>)
  2a:   42ac        cmp r4, r5
  2c:   d007        beq.n   3e <func+0x3e>
  2e:   a326        add r3, pc, #152    ; (adr r3, c8 <func+0xc8>)
  30:   22ee        movs    r2, #238    ; 0xee
  32:   492c        ldr r1, [pc, #176]  ; (e4 <func+0xe4>)
  34:   2000        movs    r0, #0
  36:   9400        str r4, [sp, #0]
  38:   f7ff fffe   bl  0 <log_func>
  3c:   e03a        b.n 78 <func+0x78>   <---- PROBLEM IS HERE
  3e:   f7ff fffe   bl  0 <func>
  42:   9006        str r0, [sp, #24]
  44:   f3bf 8f5f   dmb sy
  48:   a808        add r0, sp, #32
  4a:   7800        ldrb    r0, [r0, #0]
  4c:   2800        cmp r0, #0
  4e:   d00f        beq.n   70 <func+0x70>
  50:   9806        ldr r0, [sp, #24]
  52:   2803        cmp r0, #3
  54:   d016        beq.n   84 <func+0x84>
  56:   f7ff fffe   bl  0 <some_hw_func>
  5a:   4604        mov r4, r0
  5c:   42ac        cmp r4, r5
  5e:   d01a        beq.n   96 <func+0x96>
  60:   a321        add r3, pc, #132    ; (adr r3, e8 <func+0xe8>)
  62:   22fa        movs    r2, #250    ; 0xfa
  64:   491f        ldr r1, [pc, #124]  ; (e4 <func+0xe4>)
  66:   2000        movs    r0, #0
  68:   9400        str r4, [sp, #0]
  6a:   f7ff fffe   bl  0 <log_func>
  6e:   e021        b.n 46 <random_delay+0x46>  <--- ALSO HERE SAME PROBLEM
  70:   f7ff fffe   bl  0 <random_delay>
  74:   2800        cmp r0, #0
  76:   d003        beq.n   80 <func+0x80>
  78:   a808        add r0, sp, #32
  7a:   7800        ldrb    r0, [r0, #0]
  7c:   2800        cmp r0, #0
  7e:   d018        beq.n   b2 <func+0xb2>
  80:   f7ff fffe   bl  0 <some_hw_func2>
  84:   f7ff fffe   bl  0 <random_delay>
  88:   2800        cmp r0, #0
  8a:   d002        beq.n   92 <func+0x92>
  8c:   9806        ldr r0, [sp, #24]
  8e:   2803        cmp r0, #3
  90:   d00f        beq.n   b2 <func+0xb2>
  92:   f7ff fffe   bl  0 <some_hw_func2>
  96:   f7ff fffe   bl  0 <func>
  9a:   4604        mov r4, r0
  9c:   42ac        cmp r4, r5
  9e:   d008        beq.n   b2 <func+0xb2>
  a0:   22ff        movs    r2, #255    ; 0xff
  a2:   a318        add r3, pc, #96 ; (adr r3, 104 <func+0x104>)
  a4:   3201        adds    r2, #1
  a6:   490f        ldr r1, [pc, #60]   ; (e4 <func+0xe4>)
  a8:   2000        movs    r0, #0
  aa:   9400        str r4, [sp, #0]
  ac:   f7ff fffe   bl  0 <log_func>
  b0:   e000        b.n b4 <func+0xb4>
  b2:   462c        mov r4, r5
  b4:   4620        mov r0, r4

看起来像一个错误;每次跳跃都在跳跃之间,像这里一样受到重新定位

38:   f7ff fffe   bl  0 <log_func>
3c:   e03a        b.n 78 <func+0x78>   <---- PROBLEM IS HERE
3e:   f7ff fffe   bl  0 <func>

或此处

6a:   f7ff fffe   bl  0 <log_func>
6e:   e021        b.n 46 <random_delay+0x46>
70:   f7ff fffe   bl  0 <random_delay>

计算错误

这没有正当理由;向错误跟踪系统报告 http://www.sourceware.org/bugzilla/ 可能是合适的(经过验证,最新版本也存在此错误)

编辑:我有时间深入研究这个错误。

问题是,如果 before 指令 b.n 是任何 32 位指令,而 after 指令b.n 受重定位影响,objdump 错误地假定 b.n 指令具有与之关联的重定位,并将相对 pc 设置为 0 以进行偏移计算。

来自 binutils/objdump.c 的代码部分是罪魁祸首:

              bfd_signed_vma distance_to_rel;

              distance_to_rel = (**relppp)->address
                - (rel_offset + addr_offset);

              /* Check to see if the current reloc is associated with
                 the instruction that we are about to disassemble.  */
              if (distance_to_rel == 0
                  /* FIXME: This is wrong.  We are trying to catch
                     relocs that are addressed part way through the
                     current instruction, as might happen with a packed
                     VLIW instruction.  Unfortunately we do not know the
                     length of the current instruction since we have not
                     disassembled it yet.  Instead we take a guess based
                     upon the length of the previous instruction.  The
                     proper solution is to have a new target-specific
                     disassembler function which just returns the length
                     of an instruction at a given address without trying
                     to display its disassembly. */
                  || (distance_to_rel > 0
                      && distance_to_rel < (bfd_signed_vma) (previous_octets/ opb)))
                {
                  inf->flags |= INSN_HAS_RELOC;
                  aux->reloc = **relppp;
                }

评论说明了一切:此解析器根据前一条 32 位指令猜测下一条指令是 也是 32 位(它不是!)。重定位的目标是 3e,反汇编器假定下一条指令是从 3c3f,因此 b.n 被标记为 INSN_HAS_RELOC,这反过来会导致不正确的偏移量计算。看来,这样子修起来不容易啊。

但是,您可以尝试像这样修补您的 objdump:

if (distance_to_rel == 0) {
              inf->flags |= INSN_HAS_RELOC;
              aux->reloc = **relppp;
}

这可能会反过来产生不准确的情况,但这种情况应该很少见,也许您更容易接受。