0

I wrote a simple program to understand the control flow transfer of a signal handler. The following program will let the child to repetitively write to .text region and trigger the handler.

#include "csapp.h" // just include standard headers                             

extern char etext;                                                              

void handler() {                                                                
  printf("pid = %d, in handler\n", getpid());                                   
  return;                                                                       
}                                                                               

int main(int argc, char **argv) {                                               
  if (signal(SIGSEGV, handler) == SIG_ERR) {                                    
    perror("signal error");                                                     
  }                                                                             

  if (fork() == 0) {                                                            
    printf("from Child before\n");                                              
    etext = 'a';                                                                
    printf("from Child after\n");                                               
  }                                                                             

  return EXIT_SUCCESS;                                                          
}             

However, I want to verify this using gdb. Disassembling the main gives the assembly below. I created a break point using command below so gdb can break at the point right before it write to the .text region.

(gdb) b *0x00000000004006fb

Then I run the program in gdb. But the gdb wont stop and keeps executing the signal handler.

   0x00000000004006ba <+0>: push   %rbp
   0x00000000004006bb <+1>: mov    %rsp,%rbp
   0x00000000004006be <+4>: sub    $0x10,%rsp
   0x00000000004006c2 <+8>: mov    %edi,-0x4(%rbp)
   0x00000000004006c5 <+11>:    mov    %rsi,-0x10(%rbp)
   0x00000000004006c9 <+15>:    mov    $0x40069d,%esi
   0x00000000004006ce <+20>:    mov    $0xb,%edi
   0x00000000004006d3 <+25>:    callq  0x400570 <signal@plt>
   0x00000000004006d8 <+30>:    cmp    $0xffffffffffffffff,%rax
   0x00000000004006dc <+34>:    jne    0x4006e8 <main+46>
   0x00000000004006de <+36>:    mov    $0x4007ba,%edi
   0x00000000004006e3 <+41>:    callq  0x400590 <perror@plt>
   0x00000000004006e8 <+46>:    callq  0x4005a0 <fork@plt>
   0x00000000004006ed <+51>:    test   %eax,%eax
   0x00000000004006ef <+53>:    jne    0x40070c <main+82>
   0x00000000004006f1 <+55>:    mov    $0x4007c7,%edi
   0x00000000004006f6 <+60>:    callq  0x400530 <puts@plt>
   0x00000000004006fb <+65>:    movb   $0x61,0x9b(%rip)        # 0x40079d
   0x0000000000400702 <+72>:    mov    $0x4007d9,%edi
   0x0000000000400707 <+77>:    callq  0x400530 <puts@plt>
   0x000000000040070c <+82>:    mov    $0x0,%eax
   0x0000000000400711 <+87>:    leaveq 
   0x0000000000400712 <+88>:    retq   

Questions: 1.Why gdb wont break at the address I specify?

2.How can I use gdb so that I know the exact address the instruction pointer points to when it returns from the signal handler?

drdot
  • 3,215
  • 9
  • 46
  • 81
  • 1
    you should not use printf in the signal handler – Seek Addo Jun 04 '17 at 16:35
  • @SeekAddo, OK. I can change it to logging information to a file. But how do I solve the main question. – drdot Jun 04 '17 at 16:40
  • Possible duplicate of [How can I get GDB to tell me what address caused a segfault?](https://stackoverflow.com/questions/3003339/how-can-i-get-gdb-to-tell-me-what-address-caused-a-segfault) – Mark Plotnick Jun 04 '17 at 16:57
  • 1
    You must avoid printf in the handler, especially in your case. From manual : [...] Suppose that the main program is in the middle of a call to a stdio function such as printf(3) where the buffer and associated variables have been partially updated. If, at that moment, the program is interrupted by a signal handler that also calls printf(3), then the second call to printf(3) will operate on inconsistent data, with unpredictable results. – simo-r Jun 04 '17 at 17:32
  • @BetaRunner, Thank you for the explanation! – drdot Jun 04 '17 at 18:31
  • @MarkPlotnick, that solution does not work because if I cannot break in gdb properly. Read my question then you will understand, it keeps looping. – drdot Jun 04 '17 at 22:51
  • @BetaRunner, do you have any insight on the actual question that I asked? – drdot Jun 04 '17 at 22:51
  • Does your program stop when the signal occurs and does gdb let you type commands at that point? Also, when you set that breakpoint, was gdb debugging the parent or was it debugging the child? – Mark Plotnick Jun 05 '17 at 08:05
  • @MarkPlotnick, the program keeps printing the lines in the signal handler which is the same behavior if I invoke the program without gdb. I cannot type anything since it keeps printing to stdout. I think you get a good point. Gdb is debugging the parent because I do gdb ./a.out. How can I debug the child? – drdot Jun 05 '17 at 21:57
  • [set follow-fork-mode child](https://sourceware.org/gdb/onlinedocs/gdb/Forks.html) – Mark Plotnick Jun 05 '17 at 22:15

1 Answers1

2

Why gdb wont break at the address I specify?

Because you are debugging the wrong (parent) process.

The instruction you want to stop on is only executed in the child, and you aren't debugging the child.

To debug the child, use set follow-fork-mode child.

How can I use gdb so that I know the exact address the instruction pointer points to when it returns from the signal handler?

Put a breakpoint on the ret instruction in the signal handler, and do x/a $rsp when that breakpoint is hit.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362