0
votes

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?

1
you should not use printf in the signal handler - Seek Addo
@SeekAddo, OK. I can change it to logging information to a file. But how do I solve the main question. - drdot
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
@BetaRunner, Thank you for the explanation! - drdot

1 Answers

2
votes

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.