gdb/elfread.c: Add plt symbol check for _PROCEDURE_LINKAGE_TABLE_
In the current code, when execute the following test on LoongArch:
$ make check-gdb TESTS="gdb.base/gnu-ifunc.exp"
=== gdb Summary ===
# of expected passes 111
# of unexpected failures 62
According to IFUNC's working process [1]. first time the IFUNC function
is called, the dynamic linker will not simply fill the .got.plt entry
with the actual address of IFUNC symbol, it will call the IFUNC resolver
function and take the return address, uses it as the sym-bound address
and puts it in the .got.plt entry. Initial address in .got.plt entry is
not a real function addresss. Depending on the compiler implementation,
some different addresses will be filled in. Most architectures will use
a .plt entry address to fill in the corresponding .got.plt entry.
In gdb, elf_gnu_ifunc_resolve_addr() will be called to return a real
IFUNC function addresss. First check to see if the real address for
the IFUNC symbol has been resolved by the following function:
elf_gnu_ifunc_resolve_name (const char *name, CORE_ADDR *addr_p)
{
if (elf_gnu_ifunc_resolve_by_cache (name, addr_p))
return true;
if (elf_gnu_ifunc_resolve_by_got (name, addr_p))
return true;
return false;
}
in elf_gnu_ifunc_resolve_by_got(), it gets the contents of the
.got.plt entry and determines if the contents is the correct address
by calling elf_gnu_ifunc_record_cache(). Based on the IFUNC working
principle analysis above, the address filled in the .got.plt entry is
not the actual target function address initially, it would be a .plt
entry address corresponding symbol like *@plt. In this case, gdb just
go back to execute the resolver function and puts the return address
in the .got.plt entry. After that, gdb can get a real ifun address via
.got.plt entry.
On LoongArch, initially, each address filled in the .got.plt entries
is the first .plt entry address. Some architectures such as LoongArch
define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt
section. This symbol is the first plt entry, so gdb needs to check
this symbol in elf_gnu_ifunc_record_cache().
On LoongArch .got.plt and .plt section as follow:
$objdump -D gdb/testsuite/outputs/gdb.base/gnu-ifunc/gnu-ifunc-0-0-0
...
0000000120010008 <.got.plt>:
120010008:
ffffffff 0xffffffff
12001000c:
ffffffff 0xffffffff
...
120010018:
20004000 ll.w $zero, $zero, 64(0x40)
12001001c:
00000001 0x00000001
120010020:
20004000 ll.w $zero, $zero, 64(0x40)
120010024:
00000001 0x00000001
120010028:
20004000 ll.w $zero, $zero, 64(0x40)
12001002c:
00000001 0x00000001
120010030:
20004000 ll.w $zero, $zero, 64(0x40)
120010034:
00000001 0x00000001
...
Disassembly of section .plt:
0000000120004000 <_PROCEDURE_LINKAGE_TABLE_>:
120004000:
1c00018e pcaddu12i $t2, 12(0xc)
120004004:
0011bdad sub.d $t1, $t1, $t3
120004008:
28c021cf ld.d $t3, $t2, 8(0x8)
12000400c:
02ff51ad addi.d $t1, $t1, -44(0xfd4)
120004010:
02c021cc addi.d $t0, $t2, 8(0x8)
120004014:
004505ad srli.d $t1, $t1, 0x1
120004018:
28c0218c ld.d $t0, $t0, 8(0x8)
12000401c:
4c0001e0 jirl $zero, $t3, 0
0000000120004020 <__libc_start_main@plt>:
120004020:
1c00018f pcaddu12i $t3, 12(0xc)
120004024:
28ffe1ef ld.d $t3, $t3, -8(0xff8)
120004028:
4c0001ed jirl $t1, $t3, 0
12000402c:
03400000 andi $zero, $zero, 0x0
0000000120004030 <abort@plt>:
120004030:
1c00018f pcaddu12i $t3, 12(0xc)
120004034:
28ffc1ef ld.d $t3, $t3, -16(0xff0)
120004038:
4c0001ed jirl $t1, $t3, 0
12000403c:
03400000 andi $zero, $zero, 0x0
0000000120004040 <gnu_ifunc@plt>:
120004040:
1c00018f pcaddu12i $t3, 12(0xc)
120004044:
28ffa1ef ld.d $t3, $t3, -24(0xfe8)
120004048:
4c0001ed jirl $t1, $t3, 0
12000404c:
03400000 andi $zero, $zero, 0x0
...
With this patch:
$make check-gdb TESTS="gdb.base/gnu-ifunc.exp"
=== gdb Summary ===
#of expected passes 173
[1] https://sourceware.org/glibc/wiki/GNU_IFUNC
Signed-off-by: Hui Li <lihui@loongson.cn>