【问题标题】:objdump produces wrong branch opcode interpretationobjdump 产生错误的分支操作码解释
【发布时间】:2019-08-13 12:12:08
【问题描述】:

查看特定函数的特定目标文件的以下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 被“混淆”。

注意: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

【问题讨论】:

  • 可能是一个错误。对应的源代码是什么?另一种可能性是该指令的操作数会被重定位。 objdump 在反汇编期间评估重定位并在反汇编中考虑它们(但不在转储的机器代码中)。这可以解释差异。
  • 很遗憾我无法提供源代码。另外,b.n 哪个分支到同一函数内的小偏移量是否适用于重定位?
  • 输出是正确的,如果func从3c开始。好的,实际上它只对了一半(下半场)。 func 从哪里开始?
  • @user2162550 除非分支是相对于指令指针的,否则必须重新定位
  • 能否提供更大的反汇编上下文(即从0到至少b4)?

标签: c assembly arm cortex-m objdump


【解决方案1】:

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

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;
}

这可能会反过来产生不准确性,但这应该是极少数情况,也许这对您来说更容易接受。

【讨论】:

  • 仍然存在于gcc-arm-none-eabi-8-2019-q3-update-linux
  • @user2162550 我已经为这个错误提供了更多细节
  • bl 不是 32 位指令,它是两个单独执行的 16 位指令。如果 objdump 错误地将它们解释为那样。如果分支周围的 bl 是向前分支,则 objdump 会产生正确的值,如果向后分支,那么我看到了类似的行为。我认为gas正确生成的分支指令。
  • @old_timer f7ff fffe bl 0
  • @Ctx: old_timer 关于 Thumb bl 是正确的; Thumb 1 需要一种编码 bl 的方法,因此他们将其拆分为两条 16 位指令:一条设置一些内部状态(位移的高位),另一条提供低半部分并跳转。当然,对于处理可变长度指令的工具链,将其视为一条 32 位指令是有意义的。可能在 Thumb2 CPU 内部也是如此,不管它是如何记录的。 (例如ece.uwaterloo.ca/~ece222/ARM/ARM7-TDMI-manual-pt3.pdf 第 40 页:5.19 格式 19:带链接的长分支
猜你喜欢
  • 2021-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-03
  • 1970-01-01
  • 2022-01-24
  • 2020-02-18
相关资源
最近更新 更多