Bug 28467 - [m32][pie] FAIL: gdb.base/nodebug.exp: p/c (int) array_index("abcdef",2)
Summary: [m32][pie] FAIL: gdb.base/nodebug.exp: p/c (int) array_index("abcdef",2)
Status: RESOLVED FIXED
Alias: None
Product: gdb
Classification: Unclassified
Component: tdep (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: 12.1
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-10-18 14:24 UTC by Tom de Vries
Modified: 2021-12-07 07:35 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tom de Vries 2021-10-18 14:24:15 UTC
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.
Comment 1 Tom de Vries 2021-10-30 10:50:52 UTC
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,
...
Comment 3 Tom de Vries 2021-11-01 12:05:01 UTC
(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.