Analyze the core stack using objdump

@

  • The source code
  • Execute a program
  • Analysis using objdump
  • The source code
  • To run the program
  • objdump

Students who use c++ programming often encounter memory problems such as out-of-bounds memory and repeated release. The most common way to trace such problems is to open the limit of core files, generate core files, and analyze them with GDB. However, in a real production environment. Because the program consumes a large amount of memory, such as search index service, core dump is not practical, so it is generally used to capture the signal in the program, and then print the stack information of the process, and then trace. In the following article, we will trace it in this way. First, we will analyze the problems of objdump and assembly for programs without SO. Then analyze the program with so, how to use objdump to analyze, I hope to help you.

Core analysis of common programs

The source code

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <execinfo.h>

static void print_stack_fs(int sig, FILE * output)
{
    fprintf(output, "--------------------------------------\n");

 char pTime[256];
 //getSafeNow(pTime, 256);
    fprintf(output, "[%s] received signal=%d, thread_id=%ld\n",
      "now", sig, getpid());

    void *array[128]; // 128 stacks at most
    size_t size = backtrace(array, sizeof(array) / sizeof(array[0]));
    if (size > 0 && size < 128) {
        char ** stackLog = backtrace_symbols(array, size);
        if(stackLog) {
            for (size_t i = 0; i < size; i++) {
                fprintf(output,"%s\n", stackLog[i]);
            }
            fflush(output);
            free(stackLog);
        }
    }
}

static void sig_handler(int signo)
{
 if (signo == SIGSEGV ||
        signo == SIGBUS  ||
        signo == SIGABRT ||
        signo == SIGFPE) {

        print_stack_fs(signo, stderr);

  exit(-1);
 }
 else if (signo == SIGTERM || signo == SIGINT) {
  exit(-1);
    }
}

static void sig_register()
{
    struct sigaction sigac;
    sigemptyset(&sigac.sa_mask);
    sigac.sa_handler = sig_handler;
    sigac.sa_flags = 0;

    sigaction(SIGTERM, &sigac, 0);
    sigaction(SIGINT , &sigac, 0);
    sigaction(SIGQUIT, &sigac, 0);
    sigaction(SIGPIPE, &sigac, 0);
    sigaction(SIGBUS , &sigac, 0);
    sigaction(SIGABRT, &sigac, 0);
    sigaction(SIGFPE , &sigac, 0);
    sigaction(SIGSEGV, &sigac, 0);
}



int main(int argc, char *argv[])
{
sig_register();
    int a = 10, b = -2, c = 100;
    char * pstr = 0x00;
    int d = 100;
    *pstr = 0x00;
    return 0;
}
Copy the code

Execute a program

Key address: 0x400Add, the specific virtual space address that points to the code that failed

[now] received signal=11, thread_id=1852
./a.out() [0x4008ab]
./a.out() [0x400985]
/lib64/libc.so.6(+0x362f0) [0x7fbc41a3d2f0]
./a.out() [0x400add]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7fbc41a29445]
./a.out() [0x400769]
Copy the code

Analysis using objdump

-0x18(% RBP) -0x18(% RBP) -0x18(% RBP) -0x18(% RBP) -0x18

321 0000000000400aa1 <main>: 322 400aa1: 55 push %rbp 323 400aa2: 48 89 e5 mov %rsp,%rbp 324 400aa5: 48 83 ec 30 sub $0x30,%rsp 325 400aa9: 89 7d dc mov %edi,-0x24(%rbp) 326 400aac: 48 89 75 d0 mov %rsi,-0x30(%rbp) 327 400ab0: e8 f2 fe ff ff callq 4009a7 <_ZL12sig_registerv> 328 400ab5: C7 45 fc 0a 00 00 00 movl $0xa,-0x4(% RBP) C7 45 f8 fe ffffff movl $0xfffffffe,-0x8(% RBP) C7 45 f4 64 00 00 00 movl $0x64,-0xc(% RBP) 48 c7 45 e8 00 00 00 movq $0x0,-0x18(% RBP) PSTR 332 400ad1: 00 333 400ad2: C7 45 e4 64 00 00 00 movl $0x64,-0x1c(% RBP) 48 8b 45 e8 mov -0x18(% RBP),%rax 336 400ae0: b8 00 00 00 00 mov $0x0,%eax 337 400ae5: b8 00 00 00 00 00 mov $0x0,%eax 337 400ae5: c9 leaveq 338 400ae6: c3 retq 339 400ae7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)Copy the code

Core objdump analysis in so

The source code

  1. max.h
#ifndef __MAX_H__
#define __MAX_H__

int max(int n1, int n2, int n3);

#endif
Copy the code
  1. max.cpp
#include "max.h"

int max(int n1, int n2, int n3)
{
    int max_num = n1;
    max_num = max_num < n2? n2: max_num;
    max_num = max_num < n3? n3: max_num;
    char * pstr = 0x00;
    *pstr = 0x00;
    return max_num;
}
Copy the code
  1. test.cpp
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <execinfo.h>
#include "max.h"

static void print_stack_fs(int sig, FILE * output)
{
    fprintf(output, "--------------------------------------\n");

 char pTime[256];
 //getSafeNow(pTime, 256);
    fprintf(output, "[%s] received signal=%d, thread_id=%ld\n",
      "now", sig, getpid());

    void *array[128]; // 128 stacks at most
    size_t size = backtrace(array, sizeof(array) / sizeof(array[0]));
    if (size > 0 && size < 128) {
        char ** stackLog = backtrace_symbols(array, size);
        if(stackLog) {
            for (size_t i = 0; i < size; i++) {
                fprintf(output,"%s\n", stackLog[i]);
            }
            fflush(output);
            free(stackLog);
        }
    }
}

static void sig_handler(int signo)
{
 if (signo == SIGSEGV ||
        signo == SIGBUS  ||
        signo == SIGABRT ||
        signo == SIGFPE) {

        print_stack_fs(signo, stderr);

  exit(-1);
 }
 else if (signo == SIGTERM || signo == SIGINT) {
  exit(-1);
    }
}

static void sig_register()
{
    struct sigaction sigac;
    sigemptyset(&sigac.sa_mask);
    sigac.sa_handler = sig_handler;
    sigac.sa_flags = 0;

    sigaction(SIGTERM, &sigac, 0);
    sigaction(SIGINT , &sigac, 0);
    sigaction(SIGQUIT, &sigac, 0);
    sigaction(SIGPIPE, &sigac, 0);
    sigaction(SIGBUS , &sigac, 0);
    sigaction(SIGABRT, &sigac, 0);
    sigaction(SIGFPE , &sigac, 0);
    sigaction(SIGSEGV, &sigac, 0);
}



int main(int argc, char *argv[])
{
 sig_register();
    int a = 10, b = -2, c = 100;
    int d = 100;
    printf("max among 10, -2 and 100 is %d.\n", max(a, b, c));
    return 0;
}
Copy the code

To run the program

/libmax.so(_Z3maxiii+0x45) [0x7FB914D6868a]

[now] received signal=11, thread_id=1893
./a.out() [0x4009fb]
./a.out() [0x400ad5]
/lib64/libc.so.6(+0x362f0) [0x7fb9141b12f0]
./libmax.so(_Z3maxiii+0x45) [0x7fb914d6868a]
./a.out() [0x400c33]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7fb91419d445]
./a.out() [0x4008b9]
Copy the code

objdump

Objdump -d libmax. So = _Z3maxiii = 645 + 0x45 Movq $0x0,-0x10(% RBP), movq $0x0,-0x10(% RBP)

106 0000000000000645 <_Z3maxiii>: 107 645: 55 push %rbp 108 646: 48 89 e5 mov %rsp,%rbp 109 649: 89 7d EC mov %edi,-0x14(% RBP) // 3 7d EC mov %edi,-0x14(% RBP) // 89 55 e4 mov %edx,-0x1c(% RBP) // 3 112 652:8b 45 ec mov -0x14(% RBP),% eAX 113 655: 89 45 fc mov %eax,-0x4(%rbp) 114 658: 8b 45 fc mov -0x4(%rbp),%eax 115 65b: 3b 45 e8 cmp -0x18(%rbp),%eax 116 65e: 7d 05 jge 665 <_Z3maxiii+0x20> 117 660: 8b 45 e8 mov -0x18(%rbp),%eax 118 663: eb 03 jmp 668 <_Z3maxiii+0x23> 119 665: 8b 45 fc mov -0x4(%rbp),%eax 120 668: 89 45 fc mov %eax,-0x4(%rbp) 121 66b: 8b 45 fc mov -0x4(%rbp),%eax 122 66e: 3b 45 e4 cmp -0x1c(%rbp),%eax 123 671: 7d 05 jge 678 <_Z3maxiii+0x33> 124 673: 8b 45 e4 mov -0x1c(%rbp),%eax 125 676: eb 03 jmp 67b <_Z3maxiii+0x36> 126 678: 8b 45 fc mov -0x4(%rbp),%eax 127 67b: 89 45 fc mov %eax,-0x4(%rbp) 128 67e: 48 c7 45 f0 00 00 00 movq $0x0,-0x10(%rbp) // pstr 129 685: 00 130 686: 48 8b 45 f0 mov -0x10(%rbp),%rax 131 68a: C6 00 00 movb $0x0,(%rax) // assign 0 to PSTR 132 68d: 8b 45 fc mov -0x4(% RBP),% eAX 133 690: 5d pop % RBPCopy the code

Use addr2line to locate the number of lines in the problem

[dubaokun@localhost so]$ addr2line -e libmax.so -ifC 68a
max(int, int, int)
/home/dubaokun/github/code/engine_code/compile/objdump/so/max.cpp:9 (discriminator 3)
Copy the code

conclusion

The above procedure is relatively simple, the procedure in the actual work is more complex, but the complexity is from the foundation, we can think carefully, carefully study, for assembly code to have a certain understanding.