https://sourceware.org/bugzilla/show_bug.cgi?id=34350

            Bug ID: 34350
           Summary: powerpc: PC-relative relocations against IFUNCs broken
                    in various ways
           Product: binutils
           Version: 2.47 (HEAD)
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ld
          Assignee: unassigned at sourceware dot org
          Reporter: jrtc27 at jrtc27 dot com
                CC: maskray at sourceware dot org
  Target Milestone: ---

[Note, this was tested using Debian's 2.46.50.20260617-1, which I believe does
not have any patches that would affect this, and whilst I don't know the
precise revision that corresponds to, I assume any recent-ish binutils will
behave the same, as I don't see any recent changes to elf32-ppc.c that would
matter]

Consider the reduced:

```
$ cat pcrel.s
.balign 4
.type ifunc, %gnu_indirect_function
ifunc:
  blr

.rodata
.balign 4
.4byte ifunc - .
$ as -a32 pcrel.s -o pcrel.o
```

Due to the PC-relative (data) reference to ifunc, we cannot just emit an
R_PPC_IRELATIVE for it, and must instead emit a canonical PLT, which then
replaces ifunc as an STT_FUNC symbol (in order to be canonical for all other
address-significant relocations). However, this is broken for all of PDEs, PIEs
and DSOs, although PDEs are slightly less broken.

Starting with PDEs:

```
$ ld -m elf32ppclinux pcrel.o -o pcrel.pde
$ readelf -x.rodata pcrel.pde

Hex dump of section '.rodata':
  0x10000094 fffffff0                            ....

$ objdump -d pcrel.pde | grep -A3 10000084
10000084:       3d 60 10 01     lis     r11,4097
10000088:       81 6b 00 a0     lwz     r11,160(r11)
1000008c:       7d 69 03 a6     mtctr   r11
10000090:       4e 80 04 20     bctr
```

At first that looks good, it's referencing a canonical PLT-like thing, and the
address being loaded from is indeed the GOT (TOC? and called .iplt because
PowerPC...) entry for ifunc that has an R_PPC_IRELATIVE against it. However,
the ifunc symbol has not been altered to point at that, instead pointing at the
actual resolver, so any other references will get the underlying resolved
function pointer, not the stub, and so this is not a *canonical* PLT, breaking
address equality:

```
$ readelf -Ws pcrel.pde | grep ifunc
     6: 10000080     0 IFUNC   LOCAL  DEFAULT    2 ifunc
```

Moving onto PIEs and DSOs, which behave the same, things get worse. As above,
we don't replace ifunc with an STT_FUNC symbol. However, we also don't even
emit a stub to point the relocation at; instead, it just ends up referring to
the resolver itself as if it were a normal function, and so calling it (when
you add "PC" back on) returns the pointer to the actual function you were
trying to call, not call it:

```
$ ld -m elf32ppclinux pcrel.o -o pcrel.pie -pie
$ readelf -x.rodata pcrel.pie

Hex dump of section '.rodata':
  0x00000138 fffffffc                            ....

$ objdump -d pcrel.pie | grep -A2 00000134
00000134 <ifunc>:
 134:   4e 80 00 20     blr

$ readelf -WSs pcrel.pie | grep 00000134
  [ 5] .text             PROGBITS        00000134 000134 000004 00  AX  0   0 
4
     5: 00000134     0 SECTION LOCAL  DEFAULT    5 .text
    11: 00000134     0 IFUNC   LOCAL  DEFAULT    5 ifunc
```

```
$ ld -m elf32ppclinux pcrel.o -o pcrel.so -shared
$ readelf -x.rodata pcrel.so

Hex dump of section '.rodata':
  0x000000e4 fffffffc                            ....

$ objdump -d pcrel.so | grep -A2 000000e0
000000e0 <ifunc>:
  e0:   4e 80 00 20     blr

$ readelf -WSs pcrel.so | grep 000000e0
  [ 4] .text             PROGBITS        000000e0 0000e0 000004 00  AX  0   0 
4
     4: 000000e0     0 SECTION LOCAL  DEFAULT    4 .text
    10: 000000e0     0 IFUNC   LOCAL  DEFAULT    4 ifunc
```

Playing around with PowerPC64 ELFv1 things also seem broken (I have not yet
tried v2), but I'm not currently interested in understanding its ABI for
canonical IFUNC PLTs and don't care to remind myself of all the details of both
64-bit ABIs when it comes to defining and implementing functions thanks to all
the descriptor and entry point complications. I have, however, observed
PowerPC64 ELFv1 give different function pointer values for the same IFUNC in
the same program depending on whether the value comes from a static storage
duration variable or "dynamically" within a function (i.e. from the GOT/TOC, I
assume).


As for why I care about this: LLD is a bit lazier about when it chooses to use
canonical PLTs for IFUNCs (it's a bit simpler, as you don't have to consider
whether the specific relocation in question can be replaced with an IRELATIVE),
and does it in less-contrived cases that happen in the real world (currently,
even .rela.got2 ADDR32 relocations count as direct relocations like PC-relative
ones). I believe its PDE canonical PLT for IFUNCs works fine, but not its
PIE/DSO one, as the latter uses r30 to index the GOT without initialising it,
and there's no guarantee where that points when reached via an indirect call. I
was hoping to probe binutils to see what its non-PDE canonical PLT is that
avoids this issue, but I have yet to coax binutils into emitting one, and am
wondering if it's even implemented at all. I imagine the canonical PLT will end
up needing to double in size and first initialise r30...

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to