1

From source code of Redis, in file src/debug.c, it use the backtrace() to log the call stack. within these operation, I noticed the getMcontextEip(), it seems like that in Linux:

  static void *getMcontextEip(ucontext_t *uc) {
    /* Linux */
  #if defined(__i386__)
    return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
  #elif defined(__X86_64__) || defined(__x86_64__)
    return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
  #elif defined(__ia64__)                     /* Linux IA64 */
    return (void*) uc->uc_mcontext.sc_ip;
  #endif
  }

the mechanism behind all of these is: while there was a signal(i.e., SIGFPE), it will be captured and try to log the call stack to file:

void log_stack_trace(ucontext_t *uc) {
    void *trace[100];
    int fd = open(file);
    int trace_size = backtrace(trace, 100); /* get call stack */

    /* overwrite sigaction with caller's address */
    if (getMcontextEip(uc) != NULL)
        trace[1] = getMcontextEip(uc);

    backtrace_symbols_fd(trace, trace_size, fd); /* log to file */
}

from the comment, we know it was designed to overwrite sigaction with caller's address, but is there any hint to do this? I have simulated a SIGFPE signal and debugged it in GDB, the ucontext_t seems like that:

(gdb) p *uc
$6 = {
  uc_flags = 0, 
  uc_link = 0x0, 
  uc_stack = {
    ss_sp = 0x0, 
    ss_flags = 2, 
    ss_size = 0
  }, 
  uc_mcontext = {
    gregs = {51, 0, 123, 123, 0, 0, -1073745320, -1073745376, -1208258560, 0, -1073745852, 5, 0, 0, 134514547, 115, 2163270, -1073745376, 123}, 
    fpregs = 0xbfffefb0, 
    oldmask = 0, 
    cr2 = 0
  }, 
  uc_sigmask = {
    __val = {0, 0, 44472, 8441088, 0, 0, 4294902655, 4294901760, 4294967295, 0 <repeats 23 times>}
  }, 
  __fpregs_mem = {
    cw = 0, 
    sw = 0, 
    tag = 895, 
    ipoff = 0, 
    cssel = 0, 
    dataoff = 0, 
    datasel = 0, 
    _st = {{
        significand = {0, 0, 8064, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }}, 
    status = 0
  }
}

In getMcontextEip, it just return the uc->uc_mcontext.gregs[14] on i386 platform, why do these? and why 14 and not others(there were 19 elements)?

coanor
  • 3,746
  • 4
  • 50
  • 67

1 Answers1

1

I believe "14" is the EIP register, so the current instruction pointer (aka program counter) which is the starting point you would need to know where you were in the call stack when the signal was received.

You might find useful to see how V8 uses this for its sampling profiler: http://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/sampler.cc?spec=svn16109&r=16109

Maks
  • 7,562
  • 6
  • 43
  • 65