I. With target board unix/-m32/-fPIE/-pie we run into: ... (gdb) p/c (int) array_index("abcdef",2)^M ^M Program received signal SIGSEGV, Segmentation fault.^M 0x56555420 in malloc@plt ()^M The program being debugged was signaled while in a function called from GDB.^M GDB remains in the frame where the signal was received.^M To change this behavior use "set unwindonsignal on".^M Evaluation of the expression containing the function^M (malloc@plt) will be abandoned.^M When the function is done executing, GDB will silently stop.^M (gdb) FAIL: gdb.base/nodebug.exp: p/c (int) array_index("abcdef",2) ... The malloc call happens from gdb due to value_allocate_space_in_inferior. II. Looking at a regular malloc call, we have: ... $ cat test.c #include <stdlib.h> int main (void) { void *p; p = malloc (4); return 0; } ... compiled like so: ... $ gcc -m32 -g -fPIE -pie test.c -save-temps ... we have: ... main: .LFB5: .file 1 "test.c" .loc 1 5 0 .cfi_startproc leal 4(%esp), %ecx .cfi_def_cfa 1, 0 andl $-16, %esp pushl -4(%ecx) pushl %ebp .cfi_escape 0x10,0x5,0x2,0x75,0 movl %esp, %ebp pushl %ebx pushl %ecx .cfi_escape 0xf,0x3,0x75,0x78,0x6 .cfi_escape 0x10,0x3,0x2,0x75,0x7c subl $16, %esp call __x86.get_pc_thunk.ax addl $_GLOBAL_OFFSET_TABLE_, %eax .loc 1 7 0 subl $12, %esp pushl $4 movl %eax, %ebx call malloc@PLT addl $16, %esp movl %eax, -12(%ebp) ... Note that there's an insn before the call that copies $ebx from $eax. This value is immediate used: ... 000003f0 <malloc@plt>: 3f0: ff a3 0c 00 00 00 jmp *0xc(%ebx) 3f6: 68 00 00 00 00 push $0x0 3fb: e9 e0 ff ff ff jmp 3e0 <.plt> ... III. [ Using gdb.in: ... $ cat gdb.in set height 0 set width 0 dir /home/vries/gdb_versions/devel/src/gdb/testsuite/gdb.base file /home/vries/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.base/nodebug/nodebug break inner run nosharedlibrary b malloc p/c (int) array_index("abcdef",2) ... ] In the inferior call case we have: $ gdb -q -ex "set trace-commands on" -x gdb.in +set height 0 +set width 0 +dir /home/vries/gdb_versions/devel/src/gdb/testsuite/gdb.base +file /home/vries/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.base/nodebug/nodebug +break inner Breakpoint 1 at 0x590 +run Breakpoint 1, 0x56555590 in inner () +nosharedlibrary +b malloc Breakpoint 2 at 0x56555420 +p/c (int) array_index("abcdef",2) Breakpoint 2, 0x56555420 in malloc@plt () gdb.in:9: Error in sourced command file: The program being debugged stopped while in a function called from GDB. Evaluation of the expression containing the function (malloc@plt) will be abandoned. When the function is done executing, GDB will silently stop. (gdb) p $ebx +p $ebx $1 = 0 (gdb) ... And this causes the segfault. IV. When dropping the nosharedlibrary, we get instead the malloc from ld.so, which starts like this: ... (gdb) disassemble +disassemble Dump of assembler code for function malloc: => 0xf7feedb0 <+0>: push %edi 0xf7feedb1 <+1>: push %esi 0xf7feedb2 <+2>: call 0xf7ff1d2f <__x86.get_pc_thunk.si> ... It takes care of calling get_pc_thunk for itself, so there's no trouble.
This fixes it: ... diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index f65a07492d2..8ab6b220ff2 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -67,6 +67,7 @@ #include <algorithm> #include <unordered_set> #include "producer.h" +#include "infcall.h" /* Register names. */ @@ -2775,6 +2776,20 @@ i386_thiscall_push_dummy_call (struct gdbarch *gdbarch, struct value *function, if (thiscall) regcache->cooked_write (I386_ECX_REGNUM, value_contents_all (args[0]).data ()); + { + type *ftype; + type *values_type; + CORE_ADDR funaddr = find_function_addr (function, &values_type, &ftype); + if (in_plt_section (funaddr)) + { + struct objfile *objf; + struct value *val = find_function_in_inferior ("__x86.get_pc_thunk.bx", &objf); + val = call_function_by_hand (val, NULL, {}); + CORE_ADDR val2 = value_as_long (val); + store_unsigned_integer (buf, 4, byte_order, val2); + regcache->cooked_write (I386_EBX_REGNUM, buf); + } + } /* MarkK wrote: This "+ 8" is all over the place: (i386_frame_this_id, i386_sigtramp_frame_this_id, ...
https://sourceware.org/pipermail/gdb-patches/2021-November/182984.html
(In reply to Tom de Vries from comment #1) > This fixes it: > ... > diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c > index f65a07492d2..8ab6b220ff2 100644 > --- a/gdb/i386-tdep.c > +++ b/gdb/i386-tdep.c > @@ -67,6 +67,7 @@ > #include <algorithm> > #include <unordered_set> > #include "producer.h" > +#include "infcall.h" > > /* Register names. */ > > @@ -2775,6 +2776,20 @@ i386_thiscall_push_dummy_call (struct gdbarch > *gdbarch, struct > value *function, > if (thiscall) > regcache->cooked_write (I386_ECX_REGNUM, > value_contents_all (args[0]).data ()); > + { > + type *ftype; > + type *values_type; > + CORE_ADDR funaddr = find_function_addr (function, &values_type, &ftype); > + if (in_plt_section (funaddr)) > + { > + struct objfile *objf; > + struct value *val = find_function_in_inferior > ("__x86.get_pc_thunk.bx", &objf); > + val = call_function_by_hand (val, NULL, {}); > + CORE_ADDR val2 = value_as_long (val); > + store_unsigned_integer (buf, 4, byte_order, val2); > + regcache->cooked_write (I386_EBX_REGNUM, buf); > + } > + } > > /* MarkK wrote: This "+ 8" is all over the place: > (i386_frame_this_id, i386_sigtramp_frame_this_id, > ... FTR, this worked completely accidentally. The call to __x86.get_pc_thunk.bx effectively copied the value of the eax register (not set in any way by the thunk), which happened to have the correct value at the point that the inferior was called.
https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=545e49f5ee911bbcf55dc3dbeb49b62103b205d4