Як я аналізую основний дамп-файл програми з GDB, коли він має параметри командного рядка?


156

Моя програма працює так:

exe -p param1 -i param2 -o param3

Він впав і був створений файл дампа ядра, core.pid.

Я хочу проаналізувати основний дамп-файл за

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

Але GDB розпізнає параметри файлу EXE як вхідні дані GDB.

Як я аналізую основний дамп-файл у цій ситуації?


1
Ви впевнені, що exeце не скрипт оболонки (для встановлення деяких змінних тощо), як, наприклад, firefoxє в Linux?
Базиль Старинкевич

Відповіді:


182

Ви можете використовувати ядро ​​з GDB різними способами, але передача параметрів, які слід передавати виконуваному GDB, не є способом використання основного файлу. Це також може бути причиною помилки. Ви можете використовувати основний файл такими способами:

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)

Сторінки "Man" допоможуть переглянути інші параметри GDB.


38

Просте використання GDB для налагодження файлів спільного завантаження:

gdb <executable_path> <coredump_file_path>

Файл coredump для "процесу" створюється як файл "core.pid".

Після потрапляння всередину запиту GDB (при виконанні вищевказаної команди) введіть:

...
(gdb) where

Це допоможе вам отримати інформацію про стек, де ви зможете проаналізувати причину аварії / несправності. Інші команди для тих же цілей:

...
(gdb) bt full

Це те саме, що і вище. За умовами, він перераховує всю інформацію про стек (що в кінцевому підсумку призводить до місця збоїв).


22

Просто пропустіть параметри. GDB їм не потрібен:

gdb ./exe core.pid

Але це не працює. Попередження про вихід gdb: основний файл може не відповідати заданому виконуваному файлу. Не вдалося прочитати дійсне зображення об’єктного файлу з пам'яті.
Трепер

6
"Основний файл може не відповідати вказаному виконуваному файлу". Ви модифікували exe після того, як він створив ядро? Ви, можливо, перебудували його за допомогою різних варіантів командного рядка? Дуже важливо надати GDB саме такий бінарний файл, який виробляв ядро. Якщо цього не зробити, ви вийдете сміття.
працевлаштовано росіянина

2
Також переконайтесь, що двійковий файл, переданий gdb, не позбавлений. Ви можете запустити "файл <двійкове ім'я>", який показує, що він видалений чи ні.
Дівакар Шарма

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 зберігаються в основному файлі і їх не потрібно передавати знову

Щоб відповісти на конкретні запитання аргументів CLI, ми бачимо, що якщо ми змінимо аргументи cli, наприклад, за допомогою:

rm -f core
./main.out 1 2

то це відображається в попередній бактрейсі без змін наших команд:

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, як readelfі objdump, ми можемо масово скидати інформацію, що міститься вcore файлі, наприклад стан пам'яті.

Більшість / усе це також повинно бути видно через GDB, але ці інструменти бінутилів пропонують більш об'ємний підхід, зручний для певних випадків використання, тоді як GDB зручніший для більш інтерактивного дослідження.

Перший:

file core

говорить нам, що coreфайл - це фактично файл ELF :

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

саме тому ми можемо перевірити його безпосередньо за допомогою звичайних інструментів для бінусів.

Швидкий погляд на стандарт ELF показує, що насправді існує тип 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 та бінутилах 2.26.1.




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

Ви можете проаналізувати основний дамп-файл за допомогою команди "gdb".

 gdb - The GNU Debugger

 syntax:

 # gdb executable-file core-file

 example: # gdb out.txt core.xxx 

1
out.txt - це виконуваний файл? Це здається оманливим розширенням файлу.
Алан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.