]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gas: aarch64: Fixing expression calculation using C64 symbols
authorMatthew Malcomson <matthew.malcomson@arm.com>
Thu, 29 Jul 2021 14:08:33 +0000 (15:08 +0100)
committerMatthew Malcomson <matthew.malcomson@arm.com>
Thu, 29 Jul 2021 14:08:33 +0000 (15:08 +0100)
Expressions involving function symbols should take into account the fact
that the LSB of C64 functions is set (while the LSB of labels is not
set).
The main motivating example of this is `capinit` expressions of the form
  f+((.Ltmp0+1)-f)
where `f` is a function and `.Ltmp0` is a label.
These should result in a CAPINIT relocation of the form
`f + <constant multiple of 4>` since the `+1` on the `.Ltmp0` should
cancel out the LSB that is set on `f`.

This is slightly different to the handling of a set LSB for THUMB
functions, since in THUMB the LSB is not kept when computing relations.
For THUMB expressions using the function are emitted as relocations to
let the linker apply the adjustment.

To implement this, we have two options (we choose the second):
  - Handle the LSB in the target hook `md_optimize_expr` (similar to how
    the arm backend handles expressions involving THUMB functions).
  - Set the LSB on the value assigned to the symbol in `tc_frob_label`.

The approach using the `md_optimize_expr` hook would involve adding one
to the expression that describes an operand, when that operand is a
C64 function symbol with no addend.  Then returning `FALSE` from that
hook in order to let the generic code handle the expression from then
on.

We would only want to do this when there is no addend to avoid applying
this adjustment multiple times in an expression (e.g. when a
subexpression reduces to a function symbol plus addend).
This adjustment would also want to avoid doing this to any expression
that would end up as a relocation involving that function symbol, since
then the artificial adjustment would be propagated to the relocation
(resulting in an expression like `f - 63` where the addend has been
adjusted to account for the LSB in `f`, but the linker will account for
the LSB in `f` itself).
Such avoidance is simple enough for expressions like `O_add` since we
can always avoid them, but it is more awkward to tell for `O_subtract`
expressions where some expressions can be reduced to a constant while
others will end up as a relocation.

Another difficulty with this approach is that the value of an expression
can be different depending on the relative location of the `type`
directive to the expression in the assembly source.  If the directive is
before an expression then the expression will account for the LSB but if
the directive is after the expression it will not.
While behaviour depending on the location of the `type` directive is a
tricky problem and has problems in the Morello LLVM compiler as well,
these behaviours do not match the behaviour of Morello LLVM.

The second approach is to adjust a symbols value in `tc_frob_label` if
it is a C64 function.  This will automatically mean that all symbol
expressions use this LSB correctly.
This approach does still have difficulties with relative locations of
the `type` directive, but here the behaviour matches Morello LLVM.  The
important factor in this case is whether the `type` directive is before
or after the function symbols label.  If the function label is before
the `type` directive then *all* expressions using the function label
will not account for the LSB, otherwise all expressions will utilise it.

There are two known differences with the Morello LLVM behaviour when
taking this approach.

The first is around calculating an expression of the form `operand - f`.
If `operand` is known, then both GAS and LLVM will account for the LSB
of `f`, but if `operand` is not known at the time this expression is
found then GAS will account for the LSB in the final relocation put into
the binary while Morello LLVM will not.  This is a Morello LLVM bug.

The second is that Morello LLVM does not allow expressions of the form
`f > altlabel` while GAS does.  In this case we have chosen to account
for the LSB, so that even if `f` and `altlabel` are defined in the same
place, if `f` is a C64 function symbol and `altlabel` is not then `f >
altlabel` will evaluate to true.

gas/config/tc-aarch64.c
gas/testsuite/gas/aarch64/morello-function-lsb.d [new file with mode: 0644]
gas/testsuite/gas/aarch64/morello-function-lsb.s [new file with mode: 0644]

index 72460349c8d986ba2bfecfb78aec2ca42368cbc5..684058b1da7200de00ecd9ea73653ba59422633d 100644 (file)
@@ -7668,6 +7668,8 @@ aarch64_frob_label (symbolS * sym)
   last_label_seen = sym;
 
   AARCH64_SET_C64 (sym, IS_C64);
+  if (AARCH64_IS_C64 (sym) && S_IS_FUNCTION (sym))
+    *symbol_X_add_number (sym) += 1;
 
   dwarf2_emit_label (sym);
 }
diff --git a/gas/testsuite/gas/aarch64/morello-function-lsb.d b/gas/testsuite/gas/aarch64/morello-function-lsb.d
new file mode 100644 (file)
index 0000000..919403b
--- /dev/null
@@ -0,0 +1,38 @@
+#as: -march=armv8-a+c64
+#objdump: -srt
+
+.*\.o:     file format .*
+
+SYMBOL TABLE:
+0000000000000000 l    d  \.text        0000000000000000 \.text
+0000000000000000 l    d  \.data        0000000000000000 \.data
+0000000000000000 l    d  \.bss 0000000000000000 \.bss
+0000000000000000 l       \.text        0000000000000000 altlabel
+0000000000000064 l       \*ABS\*       0000000000000000 operand
+0000000000000000 l     O \.data        0000000000000010 f\.p
+0000000000000000 g     F \.text        0000000000000013 f
+
+
+RELOCATION RECORDS FOR \[\.text\]:
+OFFSET           TYPE              VALUE 
+0000000000000014 R_AARCH64_ABS32   f-0x0000000000000064
+0000000000000018 R_AARCH64_PREL32  \*ABS\*\+0x000000000000007b
+
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET           TYPE              VALUE 
+0000000000000000 R_MORELLO_CAPINIT  f\+0x000000000000000c
+
+
+Contents of section \.text:
+ 0000 fd7bbf62 fdd3c1c2 01000014 fd7bc122  .*
+ 0010 c053c2c2 00000000 00000000 01000000  .*
+ 0020 ffffffff 00000000 ffffffff 00000000  .*
+ 0030 00000000 ffffffff ffffffff ffffffff  .*
+ 0040 00000000 00000000 ffffffff 00000000  .*
+ 0050 00000000 ffffffff ffffffff ffffffff  .*
+ 0060 00000000 00000000 00000000 ffffffff  .*
+ 0070 ffffffff ffffffff 00000000 00000000  .*
+ 0080 01000000                             .*
+Contents of section \.data:
+ 0000 00000000 00000000 00000000 00000000  .*
diff --git a/gas/testsuite/gas/aarch64/morello-function-lsb.s b/gas/testsuite/gas/aarch64/morello-function-lsb.s
new file mode 100644 (file)
index 0000000..70ca354
--- /dev/null
@@ -0,0 +1,67 @@
+        .text
+        .globl  f
+        .p2align        2
+        .type   f,@function
+f:
+altlabel:       // This label does not have function type, so will not have the LSB set.
+       stp     c29, c30, [csp, #-32]!
+       mov     c29, csp
+        b       .LBB0_1
+.Ltmp0:
+.LBB0_1:
+       ldp     c29, c30, [csp], #32
+       ret     c30
+.Lfunc_end0:
+       // Check that the LSB is not included in the offset when the symbol `f`
+       // ends up in the relocation.  In that case it's the linkers
+       // responsibility to account for the LSB.
+        .word   f - operand
+       // Check that if we emit a relocation that does not include the symbol
+       // `f` we account for the LSB.
+        .word   operand - f
+       // Use `.word 1` markers so is easier to tell what's going on by a
+       // human.
+       .word   1
+       // Ensure we account for the LSB in other valid expressions.
+       // In the output test we require these to evaluate to true or false,
+       // and for true to be represented as 0xffffffff
+       .word   altlabel != f
+       .word   altlabel >= f
+       .word   altlabel <= f
+       .word   altlabel == f
+       .word   altlabel > f
+       .word   altlabel < f
+       .word   f != altlabel
+       .word   f >= altlabel
+       .word   f <= altlabel
+       .word   f == altlabel
+       .word   f > altlabel
+       .word   f < altlabel
+       .word   f != f
+       .word   f >= f
+       .word   f <= f
+       .word   f == f
+       .word   f > f
+       .word   f < f
+       .word   altlabel != altlabel
+       .word   altlabel >= altlabel
+       .word   altlabel <= altlabel
+       .word   altlabel == altlabel
+       .word   altlabel > altlabel
+       .word   altlabel < altlabel
+        .word   1
+       // Ensure we account for the LSB in the standard .size directive.
+        .size   f, .Lfunc_end0-f
+
+        .type   f.p,@object
+        .data
+        .p2align        4
+f.p:
+       // This is a standard directive form. We need to ensure accounts for
+       // the LSB in the subexpression  ((.Ltmp0+1)-f), since otherwise the
+       // resulting relocation is invalid.
+        .capinit f+((.Ltmp0+1)-f)
+        .xword  0
+        .xword  0
+        .size   f.p, 16
+.set operand, 100