]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
morello/disassembler: Fixed objdump of adrp inst with negative offset
authorBenjamin Teoh <Benjamin.Teoh@arm.com>
Wed, 6 Dec 2023 17:20:40 +0000 (17:20 +0000)
committerAlex Coplan <alex.coplan@arm.com>
Wed, 6 Dec 2023 18:40:59 +0000 (18:40 +0000)
When an adrp instruction references a symbol that is more than a page
in memory behind the instruction, it would have a negative offset.
An example of this is:

foo:
        nop
        .zero 4096
        adrp c0, foo

where adrp references 'foo' that is more than a page in memory behind
it.

In the case where the offset is negative, when translating from its
binary format, the offset was seen as an unsigned integer, which
caused a spurious high bit set in the resolved address in the adrp
instruction like in:

0000000000400078 <foo>:
 400078:   d503201f   nop
        ...
 40107c:   f0ffffe0   adrp    c0, 100400000 <__bss_end__+0xfffeeff8>

There was an issue with how the imm field of the adrp instruction was
extracted in `aarch64_ext_imm`. The value was not sign extended
correctly for capability mode targets. This was caused by the imm
field having its `P` bit being removed before the sign extension,
which is exclusive to 64-bit capability targets. This was remedied
by shortening the width of the imm field before sign extending the
imm value, resulting in:

0000000000400078 <foo>:
 400078:   d503201f   nop
        ...
 40107c:   f0ffffe0   adrp    c0, 400000 <foo-0x78>

binutils/testsuite/binutils-all/aarch64/disas-adrp-neg-ofs.d [new file with mode: 0644]
binutils/testsuite/binutils-all/aarch64/disas-adrp-neg-ofs.s [new file with mode: 0644]
opcodes/aarch64-dis.c

diff --git a/binutils/testsuite/binutils-all/aarch64/disas-adrp-neg-ofs.d b/binutils/testsuite/binutils-all/aarch64/disas-adrp-neg-ofs.d
new file mode 100644 (file)
index 0000000..9f2e2a5
--- /dev/null
@@ -0,0 +1,15 @@
+#name: disas-adrp-neg-ofs
+#source: disas-adrp-neg-ofs.s
+#as: -march=morello+c64
+#ld: --section-start .text=0x400000
+#objdump: -D
+
+.*: +file format .*aarch64.*
+
+
+Disassembly of section .text:
+
+0000000000400000 <_start>:
+  400000:      d503201f        nop
+       ...
+  401004:      f0ffffe0        adrp    c0, 400000 <_start>
diff --git a/binutils/testsuite/binutils-all/aarch64/disas-adrp-neg-ofs.s b/binutils/testsuite/binutils-all/aarch64/disas-adrp-neg-ofs.s
new file mode 100644 (file)
index 0000000..875a9e2
--- /dev/null
@@ -0,0 +1,5 @@
+       .global _start
+_start:
+       nop
+       .zero 4096
+       adrp c0, _start
index a1778b4d2b42ec53a820eccf6dc74830615dd03e..32a40f0841c4605940aa3b80822b0da6264047d2 100644 (file)
@@ -676,6 +676,7 @@ aarch64_ext_imm (const aarch64_operand *self, aarch64_opnd_info *info,
                 aarch64_operand_error *errors ATTRIBUTE_UNUSED)
 {
   uint64_t imm;
+  unsigned width_ofs = 1;
 
   imm = extract_all_fields (self, code);
 
@@ -688,10 +689,11 @@ aarch64_ext_imm (const aarch64_operand *self, aarch64_opnd_info *info,
        return FALSE;
 
       imm &= (1UL << 20) - 1;
+      width_ofs++;
     }
 
   if (operand_need_sign_extension (self))
-    imm = sign_extend (imm, get_operand_fields_width (self) - 1);
+    imm = sign_extend (imm, get_operand_fields_width (self) - width_ofs);
 
   if (operand_need_shift_by_two (self))
     imm <<= 2;