ฉันจะวิเคราะห์ไฟล์ core dump ของโปรแกรมด้วย GDB เมื่อมีพารามิเตอร์บรรทัดคำสั่งได้อย่างไร


156

โปรแกรมของฉันทำงานเช่นนี้:

exe -p param1 -i param2 -o param3

มันล้มเหลวและสร้างไฟล์ดัมพ์หลัก, core.pid.

ฉันต้องการวิเคราะห์ไฟล์ core dump โดย

gdb ./exe -p param1 -i param2 -o param3 core.pid

แต่ GDB รับรู้พารามิเตอร์ของไฟล์ EXE เป็นอินพุตของ GDB

ฉันจะวิเคราะห์ไฟล์ core dump ในสถานการณ์นี้ได้อย่างไร


1
คุณแน่ใจหรือว่าexeไม่ใช่สคริปต์เชลล์ (เพื่อตั้งค่าตัวแปรบางอย่าง ฯลฯ ) อย่างเช่นfirefoxอยู่ใน Linux
Basile Starynkevitch

คำตอบ:


182

คุณสามารถใช้ core กับ GDB ได้หลายวิธี แต่การส่งผ่านพารามิเตอร์ซึ่งจะส่งผ่านไปยัง executable to GDB นั้นไม่ใช่วิธีการใช้ไฟล์ core นี่อาจเป็นสาเหตุที่คุณได้รับข้อผิดพลาดนั้น คุณสามารถใช้ไฟล์ core ได้หลายวิธีดังนี้:

gdb <executable> <core-file>หรือgdb <executable> -c <core-file>หรือ

gdb <executable>
...
(gdb) core <core-file>

เมื่อใช้ไฟล์หลักคุณไม่จำเป็นต้องผ่านการขัดแย้ง สถานการณ์ความผิดพลาดแสดงใน GDB (ตรวจสอบกับ GDB เวอร์ชัน 7.1 บน Ubuntu)

ตัวอย่างเช่น:

$ ./crash -p param1 -o param2
Segmentation fault (core dumped)
$ gdb ./crash core
GNU gdb (GDB) 7.1-ubuntu
...
Core was generated by `./crash -p param1 -o param2'. <<<<< See this line shows crash scenario
Program terminated with signal 11, Segmentation fault.
#0  __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
    in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

หากคุณต้องการที่จะผ่านพารามิเตอร์เพื่อปฏิบัติการที่จะบั๊กใน GDB --argsใช้

ตัวอย่างเช่น:

$ gdb --args ./crash -p param1 -o param2
GNU gdb (GDB) 7.1-ubuntu
...
(gdb) r
Starting program: /home/@@@@/crash -p param1 -o param2

Program received signal SIGSEGV, Segmentation fault.
__strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
    in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

หน้าคนจะเป็นประโยชน์ในการดูตัวเลือก GDB อื่น ๆ


38

การใช้งาน GDB อย่างง่ายดายเพื่อแก้ไขข้อบกพร่องของไฟล์ coredump:

gdb <executable_path> <coredump_file_path>

ไฟล์ coredump สำหรับ "กระบวนการ" ถูกสร้างเป็นไฟล์ "core.pid"

หลังจากคุณเข้าสู่พรอมต์ GDB (เมื่อดำเนินการตามคำสั่งด้านบน) ให้พิมพ์:

...
(gdb) where

ซึ่งจะทำให้คุณได้รับข้อมูลของสแต็กซึ่งคุณสามารถวิเคราะห์สาเหตุของความผิดพลาด / ความผิดพลาดได้ คำสั่งอื่น ๆเพื่อจุดประสงค์เดียวกันคือ:

...
(gdb) bt full

นี่เป็นเช่นเดียวกับข้างต้น ตามแบบแผนจะแสดงรายการข้อมูลสแต็กทั้งหมด (ซึ่งนำไปสู่ตำแหน่งที่ล้มเหลวในที่สุด)


22

เพียงข้ามพารามิเตอร์ GDB ไม่ต้องการ:

gdb ./exe core.pid

แต่มันไม่ได้ผล คำเตือนเอาต์พุต gdb: ไฟล์หลักอาจไม่ตรงกับไฟล์เรียกทำงานที่ระบุ ไม่สามารถอ่านอิมเมจไฟล์ออบเจกต์ที่ถูกต้องจากหน่วยความจำ
Treper

6
"ไฟล์หลักอาจไม่ตรงกับแฟ้มปฏิบัติการที่ระบุ" คุณปรับเปลี่ยน exe หลังจากที่สร้าง core หรือไม่ คุณอาจสร้างใหม่ด้วยตัวเลือกบรรทัดคำสั่งต่าง ๆ หรือไม่? เป็นสิ่งสำคัญมากที่จะต้องให้ GDB เป็นไบนารีเดียวกันที่แน่นอนซึ่งสร้างคอร์ หากคุณไม่ทำคุณจะได้รับขยะ
ว่าจ้างรัสเซีย

2
ตรวจสอบให้แน่ใจว่าไบนารีที่ส่งผ่านไปยัง gdb ไม่ได้ถูกปล้น คุณสามารถเรียกใช้ 'file <binary name>' ซึ่งแสดงว่าไฟล์ถูกปล้นหรือไม่
Diwakar Sharma

12

objdump+ gdbตัวอย่างที่รันได้น้อยที่สุด

TL; DR:

ตอนนี้สำหรับการตั้งค่าการทดสอบการศึกษาเต็มรูปแบบ:

main.c

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int myfunc(int i) {
    *(int*)(NULL) = i; /* line 7 */
    return i - 1;
}

int main(int argc, char **argv) {
    /* Setup some memory. */
    char data_ptr[] = "string in data segment";
    char *mmap_ptr;
    char *text_ptr = "string in text segment";
    (void)argv;
    mmap_ptr = (char *)malloc(sizeof(data_ptr) + 1);
    strcpy(mmap_ptr, data_ptr);
    mmap_ptr[10] = 'm';
    mmap_ptr[11] = 'm';
    mmap_ptr[12] = 'a';
    mmap_ptr[13] = 'p';
    printf("text addr: %p\n", text_ptr);
    printf("data addr: %p\n", data_ptr);
    printf("mmap addr: %p\n", mmap_ptr);

    /* Call a function to prepare a stack trace. */
    return myfunc(argc);
}

คอมไพล์และรันเพื่อสร้างคอร์:

gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
ulimit -c unlimited
rm -f core
./main.out

เอาท์พุท:

text addr: 0x4007d4
data addr: 0x7ffec6739220
mmap addr: 0x1612010
Segmentation fault (core dumped)

GDB ชี้ให้เราเห็นถึงบรรทัดที่แน่นอนที่เกิดข้อผิดพลาดในการแบ่งส่วนซึ่งเป็นสิ่งที่ผู้ใช้ส่วนใหญ่ต้องการขณะทำการดีบัก:

gdb -q -nh main.out core

แล้ว:

Reading symbols from main.out...done.
[New LWP 27479]
Core was generated by `./main.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
7           *(int*)(NULL) = i;
(gdb) bt
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
#1  0x000000000040072b in main (argc=1, argv=0x7ffec6739328) at main.c:28

ซึ่งชี้ให้เราตรงไปที่รถบั๊กกี้ไลน์ 7

อาร์กิวเมนต์ CLI ถูกเก็บไว้ในไฟล์ core และไม่จำเป็นต้องส่งผ่านอีกครั้ง

ในการตอบคำถามอาร์กิวเมนต์ CLI เฉพาะเราจะเห็นว่าถ้าเราเปลี่ยนอาร์กิวเมนต์ cli เช่นด้วย:

rm -f core
./main.out 1 2

ดังนั้นสิ่งนี้จะปรากฏใน bactrace ก่อนหน้าโดยไม่มีการเปลี่ยนแปลงคำสั่งของเรา:

Reading symbols from main.out...done.
[New LWP 21838]
Core was generated by `./main.out 1 2'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
7           *(int*)(NULL) = i; /* line 7 */
(gdb) bt
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
#1  0x0000564583cf2858 in main (argc=3, argv=0x7ffcca4effa8) at main.c:2

argc=3ดังนั้นวิธีการที่ทราบในขณะนี้ ดังนั้นสิ่งนี้ต้องหมายความว่าไฟล์หลักเก็บข้อมูลนั้น ฉันเดาว่ามันแค่เก็บไว้เป็นข้อโต้แย้งmainเหมือนเก็บอาร์กิวเมนต์ของฟังก์ชั่นอื่น ๆ

สิ่งนี้เหมาะสมถ้าคุณพิจารณาว่าดัมพ์หลักต้องถูกเก็บหน่วยความจำทั้งหมดและสถานะการลงทะเบียนของโปรแกรมดังนั้นจึงมีข้อมูลทั้งหมดที่จำเป็นในการกำหนดค่าของฟังก์ชันอาร์กิวเมนต์ในสแต็กปัจจุบัน

ชัดเจนน้อยกว่าคือวิธีการตรวจสอบตัวแปรสภาพแวดล้อม: วิธีรับตัวแปรสภาพแวดล้อมจากแกนดัมพ์ตัวแปรสภาพแวดล้อมก็มีอยู่ในหน่วยความจำด้วยดังนั้น objdump จึงมีข้อมูลนั้น แต่ฉันไม่แน่ใจว่าจะแสดงรายการทั้งหมดในครั้งเดียวอย่างสะดวก ทีละคนดังต่อไปนี้ทำงานทดสอบของฉันแม้ว่า:

p __environ[0]

การวิเคราะห์ Binutils

โดยใช้เครื่องมือ binutils เช่นreadelfและobjdumpเราสามารถถ่ายโอนข้อมูลจำนวนมากที่มีอยู่ในcoreไฟล์เช่นสถานะหน่วยความจำ

ส่วนใหญ่ / ทั้งหมดจะต้องมองเห็นได้ผ่าน GDB แต่เครื่องมือ binutils เหล่านั้นมีวิธีการจำนวนมากซึ่งสะดวกสำหรับการใช้งานบางกรณีในขณะที่ GDB สะดวกกว่าสำหรับการสำรวจเชิงโต้ตอบมากขึ้น

ครั้งแรก:

file core

บอกเราว่าcoreไฟล์นั้นเป็นไฟล์ELF :

core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './main.out'

นี่คือเหตุผลที่เราสามารถตรวจสอบได้โดยตรงด้วยเครื่องมือ binutils ปกติ

ดูอย่างรวดเร็วเกี่ยวกับมาตรฐาน ELFแสดงให้เห็นว่าจริง ๆ แล้วมันมีประเภทของเอลฟ์ที่อุทิศตน:

Elf32_Ehd.e_type == ET_CORE

ข้อมูลรูปแบบเพิ่มเติมสามารถดูได้ที่:

man 5 core

แล้ว:

readelf -Wa core

ให้คำแนะนำบางอย่างเกี่ยวกับโครงสร้างไฟล์ ดูเหมือนว่าหน่วยความจำจะอยู่ในส่วนหัวของโปรแกรมปกติ:

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  NOTE           0x000468 0x0000000000000000 0x0000000000000000 0x000b9c 0x000000     0
  LOAD           0x002000 0x0000000000400000 0x0000000000000000 0x001000 0x001000 R E 0x1000
  LOAD           0x003000 0x0000000000600000 0x0000000000000000 0x001000 0x001000 R   0x1000
  LOAD           0x004000 0x0000000000601000 0x0000000000000000 0x001000 0x001000 RW  0x1000

และยังมีข้อมูลเมตาอีกจำนวนหนึ่งอยู่ในพื้นที่บันทึกย่อโดยprstatusมีพีซี :

Displaying notes found at file offset 0x00000468 with length 0x00000b9c:
  Owner                 Data size       Description
  CORE                 0x00000150       NT_PRSTATUS (prstatus structure)
  CORE                 0x00000088       NT_PRPSINFO (prpsinfo structure)
  CORE                 0x00000080       NT_SIGINFO (siginfo_t data)
  CORE                 0x00000130       NT_AUXV (auxiliary vector)
  CORE                 0x00000246       NT_FILE (mapped files)
    Page size: 4096
                 Start                 End         Page Offset
    0x0000000000400000  0x0000000000401000  0x0000000000000000
        /home/ciro/test/main.out
    0x0000000000600000  0x0000000000601000  0x0000000000000000
        /home/ciro/test/main.out
    0x0000000000601000  0x0000000000602000  0x0000000000000001
        /home/ciro/test/main.out
    0x00007f8d939ee000  0x00007f8d93bae000  0x0000000000000000
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93bae000  0x00007f8d93dae000  0x00000000000001c0
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93dae000  0x00007f8d93db2000  0x00000000000001c0
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93db2000  0x00007f8d93db4000  0x00000000000001c4
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93db8000  0x00007f8d93dde000  0x0000000000000000
        /lib/x86_64-linux-gnu/ld-2.23.so
    0x00007f8d93fdd000  0x00007f8d93fde000  0x0000000000000025
        /lib/x86_64-linux-gnu/ld-2.23.so
    0x00007f8d93fde000  0x00007f8d93fdf000  0x0000000000000026
        /lib/x86_64-linux-gnu/ld-2.23.so
  CORE                 0x00000200       NT_FPREGSET (floating point registers)
  LINUX                0x00000340       NT_X86_XSTATE (x86 XSAVE extended state)

objdump สามารถถ่ายโอนข้อมูลหน่วยความจำทั้งหมดได้อย่างง่ายดายด้วย:

objdump -s core

ซึ่งประกอบด้วย:

Contents of section load1:

 4007d0 01000200 73747269 6e672069 6e207465  ....string in te
 4007e0 78742073 65676d65 6e740074 65787420  xt segment.text 

Contents of section load15:

 7ffec6739220 73747269 6e672069 6e206461 74612073  string in data s
 7ffec6739230 65676d65 6e740000 00a8677b 9c6778cd  egment....g{.gx.

Contents of section load4:

 1612010 73747269 6e672069 6e206d6d 61702073  string in mmap s
 1612020 65676d65 6e740000 11040000 00000000  egment..........

ซึ่งตรงกับค่า stdout ในการทำงานของเรา

สิ่งนี้ถูกทดสอบบน Ubuntu 16.04 amd64, GCC 6.4.0 และ binutils 2.26.1



9

วิธีที่แตกต่างกันเล็กน้อยจะช่วยให้คุณสามารถข้าม GDB ได้ทั้งหมด หากสิ่งที่คุณต้องการคือ backtrace ยูทิลิตี้เฉพาะ Linux 'catchsegv'จะจับ SIGSEGV และแสดง backtrace


3

มันไม่สำคัญว่าไฟล์ที่เรียกทำงานจะมีอาร์กิวเมนต์หรือไม่ ในการรัน GDB บนไบนารีใด ๆ ที่มีไฟล์แกนที่สร้างขึ้นไวยากรณ์จะอยู่ด้านล่าง

Syntax:
gdb <binary name> <generated core file>
Eg:
gdb l3_entity 6290-corefile

ขอยกตัวอย่างด้านล่างเพื่อความเข้าใจมากขึ้น

bash-4.1$ **gdb l3_entity 6290-corefile**

**Core was generated** by `/dir1/dir2/dir3/l3_entity **Program terminated with signal SIGABRT, Aborted.**
#0
#1
#2
#3
#4
#5
#6
#7
#8
#9
#10
(gdb)

จากผลลัพธ์ข้างต้นคุณสามารถคาดเดาบางสิ่งเกี่ยวกับคอร์ไม่ว่าจะเป็นการเข้าถึง NULL, SIGABORT, ฯลฯ

ตัวเลขเหล่านี้ # 0 ถึง # 10 เป็นเฟรมสแต็กของ GDB เฟรมสแต็กเหล่านี้ไม่ใช่ของไบนารีของคุณ ในเฟรมด้านบน 0 - 10 ถ้าคุณสงสัยว่ามีอะไรผิดพลาดให้เลือกเฟรมนั้น

(gdb) frame 8

ตอนนี้เพื่อดูรายละเอียดเพิ่มเติมเกี่ยวกับมัน:

(gdb) list +

หากต้องการตรวจสอบปัญหาเพิ่มเติมคุณสามารถพิมพ์ค่าตัวแปรที่น่าสงสัยได้ที่นี่ ณ เวลานี้

(gdb) print thread_name

0

เพียงพิมพ์คำสั่ง:

$ gdb <Binary> <codeDump>

หรือ

$ gdb <binary>

$ gdb) core <coreDump>

ไม่จำเป็นต้องระบุอาร์กิวเมนต์บรรทัดคำสั่งใด ๆ การถ่ายโอนข้อมูลรหัสสร้างขึ้นเนื่องจากการออกกำลังกายก่อนหน้านี้


-1

คุณสามารถวิเคราะห์ไฟล์ core dump โดยใช้คำสั่ง "gdb"

 gdb - The GNU Debugger

 syntax:

 # gdb executable-file core-file

 example: # gdb out.txt core.xxx 

1
out.txt เป็นไฟล์ปฏิบัติการหรือไม่ ดูเหมือนว่าจะเป็นนามสกุลไฟล์ที่ทำให้เข้าใจผิด
Alan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.