获取使用C引起分段故障,从核心转储地址 [英] Get the address that caused segmentation fault from core dump using C

查看:596
本文介绍了获取使用C引起分段故障,从核心转储地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图写一个C程序,可以解析核心转储文件。我的问题是,我如何才能在C导致核心转储的地址?我知道有一个可以用gdb从这个答案得到地址:

<一个href=\"http://stackoverflow.com/questions/3003339/how-can-i-get-gdb-to-tell-me-what-address-caused-a-segfault\">How我能得到GDB告诉我什么地址造成了段错误?

不过,我想直接检索C.任何信息将是非常美联社preciated的地址。谢谢!

注意:我知道如何解析核心转储作为一个小精灵。但我不知道怎么弄导致段错误的地址。


解决方案

  

我的问题是,我如何才能导致核心转储的地址
   用C?


简短的回答:

有两种方式间preT这个问题。


  1. 什么是错误指令的地址?


  2. 那是什么是出界的地址?


精灵核心转储保存所有的元信息中指出,这是
存储在注释段。该票据是不同类型的。

要回答#1,我们需要抓住的寄存器。看看精灵头
找到该程序头表。走程序头表找
注释台(类型PT_NOTE)。走的说明表,找到一个音符
键入NT_PRSTATUS。本说明的有效载荷是一个结构
elf_prstatus
,它可以在linux / elfcore.h找到。其中一个
这个结构的领域是所有的通用寄存器。抓
%裂口和你做。

有关#2,我们做类似的事情。这一次,我们正在寻找一个音符
类型NT_SIGINFO。本说明的有效载荷是一个siginfo_t结构
在signal.h中定义。对于适用的信号(SIGILL,SIGFPE,SIGSEGV,
SIGBUS),现场si_addr将包含你试图地址
访问,但不能。

更多信息如下。在这个例子中核心转储,RIP是0x400560,即试图做一个非法访问的指令地址。这显示在与通用寄存器的其余部分。

程序试图访问的内存是0×03。此显示用的信号信息的其余部分。

长的答案:

我觉得BFD拥有25年就可以了克鲁夫特的,所以我不会用它只是倾倒在Linux中一个核心文件的内容。也许如果你不得不写一些通用code的需要与一堆格式的工作,但即使如此,我不知道这就是我怎么会走到今天。

借助精灵规范是pretty写得很好,它不难刚刚经历节目标题或部分标题的表走需要。所有的核心文件的进程元信息包含在可以在传统的C code的短短几行被解析出PT_NOTE程序段的一套纸币。

我写了一个小程序来抓取寄存器出x86_68核心文件和打印一些元数据。我把它放在 github上。为获得一记有效载荷的逻辑是这样的功能:

 无效* get_note(void *的副总裁,诠释nt_type){
    Elf64_Ehdr *诶= VP;
    的for(int i = 0; I&LT;&EH-GT; e_phnum; ++ I){
        Elf64_Phdr * pH值=(VP + EH-GT&; e_phoff + I * EH-GT&; e_phentsize);
        如果(PH-GT&;!p_type = PT_NOTE){
            继续;
        }
        无效* note_table =(VP + PH-GT&; P_offset的);
        无效* note_table_end =(note_table + PH-GT&; p_filesz);
        Elf64_Nhdr * current_note = note_table;
        而(current_note≤(Elf64_Nhdr *)note_table_end){
            无效* note_end = current_note;
            note_end + = 3 * sizeof的(Elf64_Word);
            note_end + = roundup8(current_note-&GT; n_namesz);
            如果(current_note-&GT; n_type == nt_type){
                返回note_end;
            }
            note_end + = roundup8(current_note-&GT; n_descsz);
            current_note = note_end;
        }
    }
    返回0;
}

该功能交给一个指向ELF文件和笔记型并返回一个指针相关联的注释的有效载荷,如果它存在。各种可能的纸币类型在elf.h.该说明的类型我居然看到在我的机器上的核心文件是:

 的#define NT_PRSTATUS 1 / *包含prstatus结构*副本/
#定义NT_F preGSET 2 / *包含f的pregset结构*副本/
#定义NT_PRPSINFO 3 / *包含prpsinfo结构*副本/
#定义NT_AUXV 6 / *包含auxv阵副本* /
#定义NT_X86_XSTATE 0x202 / * 86扩展状态使用xsave * /
#定义NT_SIGINFO 0x53494749 / *包含siginfo_t副本,
                                   大小可能会增加* /
#定义NT_FILE 0x46494c45 / *包含有关映射信息
                                   文件* /

大多数这些结构都在头文件在/ usr / include中/ Linux操作系统。该xsave结构中的<一个13章描述了浮点资料一对夫妇KB href=\"http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf\"相对=nofollow>英特尔手动。它具有SSE,AVX和MPX,在其注册。

的NT_FILE净荷似乎不具有在头相关联的结构,但它在内核评论(FS / binfmt_elf.c)被描述

  / *
 * NT_FILE注意格式:
 *
 *长计数 - 有多少个文件映射
 *长PAGE_SIZE - 单位file_ofs
 *第[count]元素的数组
 *长启动
 *长端
 *长file_ofs
 *其次是ASCII文件名COUNTFILE1NULFILE2NUL ...
 * /

解析ELF文件对于32位系统的变化是pretty微不足道。使用相应的Elf32_XXX结构和可变大小字段围捕4而不是8。

我已经将东西给这个小程序过去几天。目前,它的文件头,片段时,通用寄存器,程序状态,程序信息,并回溯。当我有时间,我会增加对票据的REST支持。下面是电流输出:

  $ ./read_pc -biprst核心
通用寄存器:
R15 R14 0x000000000000000000 0x000000000000000000
R13 R12 0x0000007ffc20d36a50 0x000000000000400430
RBP 0x0000007ffc20d36950 RBX 0x000000000000000000
R11 R10 0x000000000000000246 0x000000000000000000
R9 0x000000000000000002 R8 0x000000000000000000
RAX 0x000000000000000003 RCX 0x00000000007ffffffe
RDX 0x0000007f5817523780 RSI 0x000000000000000001
RDI 0x000000000000000001 SS 0x00000000000000002b
撕裂0x000000000000400560 CS 0x000000000000000033
EFLAGS 0x000000000000010246 RSP 0x0000007ffc20d36950
fs_bas​​e 0x0000007f5817723700 gs_bas​​e 0x000000000000000000
DS 0x000000000000000000 ES 0x000000000000000000
FS 0x000000000000000000 GS 0x000000000000000000
orig_rax 0x00ffffffffffffffff程序状态:
SIGNO 11信号code 0 0错误号
cursig 11 sigpend 000000000000000000 sigheld 000000000000000000
PID 27547 PPID 26600 PGRP 27547 SID 26600
UTIME:0.000000 0.000000 STIME
cutime:0.000000 0.000000 cstime
fpvalid:1
信号的相关信息:
SIGNO:11错误号0 code 1
地址0x3中addr_lsb 0 addr_bnd((无),(无))
进程信息:
状态0(R)僵尸0 0不错标志0x400600
UID 1000 GID 1000 PID 27547 PPID 26600 PGRP 27547 SID 26600
FNAME:富
ARGS:./foo
回溯:
撕裂= 0x000000000000400560
撕裂= 0x000000000000400591
撕裂= 0x0000000000004005a1
程序头:
   型胶印的Virt地址PhysAddr
             FileSiz MEMSIZE标记对齐
 注0x00000000000004a0 0x0000000000000000 0000000000000000
           0x0000000000000b98 0x0000000000000000 0x000000处
 LOAD 0x0000000000002000 0x0000000000400000 0000000000000000
           0x0000000000001000 0x0000000000001000 - [R点¯x0x001000
 LOAD 0x0000000000003000 0x0000000000600000 0000000000000000
           0x0000000000001000 0x0000000000001000点¯x0x001000
 LOAD 0x0000000000004000 0x0000000000601000 0000000000000000
           0x0000000000001000 0x0000000000001000 WX 0x001000
 LOAD 0x0000000000005000 0x00000000018bf000 0000000000000000
           0x0000000000021000 0x0000000000021000 WX 0x001000
 LOAD 0x0000000000026000 0x00007f581715e000 0000000000000000
           0x0000000000001000 0x00000000001c0000 - [R点¯x0x001000
 LOAD 0x0000000000027000 0x00007f581731e000 0000000000000000
           0x0000000000000000 0x00000000001ff000 0x001000
 LOAD 0x0000000000027000 0x00007f581751d000 0000000000000000
           0x0000000000004000 0x0000000000004000点¯x0x001000
 LOAD 0x000000000002b000 0x00007f5817521000 0000000000000000
           0x0000000000002000 0x0000000000002000 WX 0x001000
 LOAD 0x000000000002d000 0x00007f5817523000 0000000000000000
           0x0000000000004000 0x0000000000004000 WX 0x001000
 LOAD 0x0000000000031000 0x00007f5817527000 0000000000000000
           0x0000000000001000 0x0000000000026000 - [R点¯x0x001000
 LOAD 0x0000000000032000 0x00007f5817722000 0000000000000000
           0x0000000000003000 0x0000000000003000 WX 0x001000
 LOAD 0x0000000000035000 0x00007f581774a000 0000000000000000
           0x0000000000002000 0x0000000000002000 WX 0x001000
 LOAD 0x0000000000037000 0x00007f581774c000 0000000000000000
           0x0000000000001000 0x0000000000001000点¯x0x001000
 LOAD 0x0000000000038000 0x00007f581774d000 0000000000000000
           0x0000000000001000 0x0000000000001000 WX 0x001000
 LOAD 0x0000000000039000 0x00007f581774e000 0000000000000000
           0x0000000000001000 0x0000000000001000 WX 0x001000
 LOAD 0x000000000003a000 0x00007ffc20d16000 0000000000000000
           0x0000000000022000 0x0000000000022000 WX 0x001000
 LOAD 0x000000000005c000 0x00007ffc20d9c000 0000000000000000
           0x0000000000002000 0x0000000000002000点¯x0x001000
 LOAD 0x000000000005e000 0x00007ffc20d9e000 0000000000000000
           0x0000000000002000 0x0000000000002000 - [R点¯x0x001000
 LOAD 0x0000000000060000 0xffffffffff600000 0000000000000000
           0x0000000000001000 0x0000000000001000 - [R点¯x0x001000
所有工作

I am trying to write a C program that can parse core dump files. My question is, how can I get the address that caused the core dump in C? I know one can get the address using gdb from this answer:

How can I get GDB to tell me what address caused a segfault?

But I would like to directly retrieve the address in C. Any information would be highly appreciated. Thanks!

Notice: I know how to parse core dump as an elf. But I don't know how to get the address that caused the segfault.

解决方案

My question is, how can I get the address that caused the core dump in C?

Short answer:

There are two ways to interpret this question.

  1. What was address of the faulting instruction?

  2. What was the address that was out of bounds?

Elf core dumps keep all of the meta information in notes, which are stored in a note segment. The notes are of different types.

To answer #1, we need to grab the registers. Look at the elf header to find the program header table. Walk the program header table to find the note table (type PT_NOTE). Walk the note table to find a note of type NT_PRSTATUS. The payload of this note is a struct elf_prstatus, which can be found in linux/elfcore.h. One of the fields of this struct is all of the general purpose registers. Grab %rip and you are done.

For #2, we do something similar. This time we are looking for a note of type NT_SIGINFO. The payload of this note is a siginfo_t structure defined in signal.h. For applicable signals (SIGILL, SIGFPE, SIGSEGV, SIGBUS), the field si_addr will contain the address you tried to access but couldn't.

More information is below. In the example core dump, rip is 0x400560, the instruction address that tried to do an illegal access. This is displayed with the rest of the general purpose registers.

The memory the program tried to access is at 0x03. This is displayed with the rest of the signal information.

The long answer:

I think BFD has 25 years of cruft on it, so I wouldn't use it just to dump the contents of a core file on a linux box. Maybe if you had to write some kind of general purpose code that needs to work with a bunch of formats, but even then I'm not sure that's how I would go today.

The elf spec is pretty well written and it is not hard to just walk through the tables of program headers or section headers as needed. All of the process meta information in a core file is contained in a set of notes in a PT_NOTE program segment that can be parsed out in just a few lines of straight C code.

I wrote a little program to grab the registers out of a x86_68 core file and print some of the meta data. I put it on github. The logic for getting a note payload is in this function:

void *get_note(void *vp, int nt_type){
    Elf64_Ehdr *eh=vp;
    for(int i=0; i<eh->e_phnum; ++i){
        Elf64_Phdr *ph=(vp+eh->e_phoff+i*eh->e_phentsize);
        if(ph->p_type!=PT_NOTE){
            continue;
        }
        void *note_table=(vp + ph->p_offset);
        void *note_table_end=(note_table+ph->p_filesz);
        Elf64_Nhdr *current_note=note_table;
        while(current_note<(Elf64_Nhdr *)note_table_end){
            void *note_end=current_note;
            note_end += 3*sizeof(Elf64_Word);
            note_end += roundup8(current_note->n_namesz);
            if(current_note->n_type==nt_type){
                return note_end;
            }
            note_end += roundup8(current_note->n_descsz);
            current_note=note_end;          
        }
    }
    return 0;
}

The function is handed a pointer to the elf file and a note type and returns a pointer the payload of the associated note, if it exists. The various possible note types are in elf.h. The note types I actually see in core files on my machine are:

#define NT_PRSTATUS 1       /* Contains copy of prstatus struct */
#define NT_FPREGSET 2       /* Contains copy of fpregset struct */
#define NT_PRPSINFO 3       /* Contains copy of prpsinfo struct */
#define NT_AUXV     6       /* Contains copy of auxv array */
#define NT_X86_XSTATE   0x202       /* x86 extended state using xsave */
#define NT_SIGINFO  0x53494749  /* Contains copy of siginfo_t,
                                   size might increase */
#define NT_FILE     0x46494c45  /* Contains information about mapped
                                   files */

Most of these structures are in headers under /usr/include/linux. The xsave structure is a couple KB of floating point information described in Ch 13 of the intel manual. It has the SSE, AVX, and MPX, registers in it.

The NT_FILE payload doesn't seem to have an associated struct in a header, but it is described in a kernel comment (fs/binfmt_elf.c):

/*
 * Format of NT_FILE note:
 *
 * long count     -- how many files are mapped
 * long page_size -- units for file_ofs
 * array of [COUNT] elements of
 *   long start
 *   long end
 *   long file_ofs
 * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
 */

The changes for parsing the elf file for a 32 bit system are pretty trivial. Use the corresponding Elf32_XXX structures and round up by 4 instead of 8 for the variable sized fields.

I've been adding stuff to this little program last couple of days. Currently it does the file header, segment headers, general registers, program status, program info and a backtrace. I'll add support for the rest of the notes as I get time. Here is the current output:

 $ ./read_pc -biprst core
General Registers: 
r15     0x000000000000000000  r14     0x000000000000000000  
r13     0x0000007ffc20d36a50  r12     0x000000000000400430  
rbp     0x0000007ffc20d36950  rbx     0x000000000000000000  
r11     0x000000000000000246  r10     0x000000000000000000  
r9      0x000000000000000002  r8      0x000000000000000000  
rax     0x000000000000000003  rcx     0x00000000007ffffffe  
rdx     0x0000007f5817523780  rsi     0x000000000000000001  
rdi     0x000000000000000001  ss      0x00000000000000002b  
rip     0x000000000000400560  cs      0x000000000000000033  
eflags  0x000000000000010246  rsp     0x0000007ffc20d36950  
fs_base 0x0000007f5817723700  gs_base 0x000000000000000000  
ds      0x000000000000000000  es      0x000000000000000000  
fs      0x000000000000000000  gs      0x000000000000000000  
orig_rax 0x00ffffffffffffffff  

Program status: 
signo 11 signal code 0 errno 0
cursig 11 sigpend 000000000000000000 sigheld 000000000000000000
pid 27547 ppid 26600 pgrp 27547 sid 26600
utime: 0.000000 stime 0.000000
cutime: 0.000000 cstime 0.000000
fpvalid: 1


Signal Information: 
signo: 11 errno 0 code 1
addr 0x3 addr_lsb 0 addr_bnd ((nil), (nil))


Process Information:
state 0 (R) zombie 0 nice 0 flags 0x400600
uid 1000 gid 1000 pid 27547 ppid 26600 pgrp 27547 sid 26600
fname: foo
args: ./foo 


Backtrace: 
rip = 0x000000000000400560
rip = 0x000000000000400591
rip = 0x0000000000004005a1


Program Headers:
   Type      Offset             Virt Addr          PhysAddr          
             FileSiz            MemSize              Flags  Align    
 NOTE      0x00000000000004a0 0x0000000000000000 0000000000000000
           0x0000000000000b98 0x0000000000000000         0x000000
 LOAD      0x0000000000002000 0x0000000000400000 0000000000000000
           0x0000000000001000 0x0000000000001000 R X     0x001000
 LOAD      0x0000000000003000 0x0000000000600000 0000000000000000
           0x0000000000001000 0x0000000000001000   X     0x001000
 LOAD      0x0000000000004000 0x0000000000601000 0000000000000000
           0x0000000000001000 0x0000000000001000  WX     0x001000
 LOAD      0x0000000000005000 0x00000000018bf000 0000000000000000
           0x0000000000021000 0x0000000000021000  WX     0x001000
 LOAD      0x0000000000026000 0x00007f581715e000 0000000000000000
           0x0000000000001000 0x00000000001c0000 R X     0x001000
 LOAD      0x0000000000027000 0x00007f581731e000 0000000000000000
           0x0000000000000000 0x00000000001ff000         0x001000
 LOAD      0x0000000000027000 0x00007f581751d000 0000000000000000
           0x0000000000004000 0x0000000000004000   X     0x001000
 LOAD      0x000000000002b000 0x00007f5817521000 0000000000000000
           0x0000000000002000 0x0000000000002000  WX     0x001000
 LOAD      0x000000000002d000 0x00007f5817523000 0000000000000000
           0x0000000000004000 0x0000000000004000  WX     0x001000
 LOAD      0x0000000000031000 0x00007f5817527000 0000000000000000
           0x0000000000001000 0x0000000000026000 R X     0x001000
 LOAD      0x0000000000032000 0x00007f5817722000 0000000000000000
           0x0000000000003000 0x0000000000003000  WX     0x001000
 LOAD      0x0000000000035000 0x00007f581774a000 0000000000000000
           0x0000000000002000 0x0000000000002000  WX     0x001000
 LOAD      0x0000000000037000 0x00007f581774c000 0000000000000000
           0x0000000000001000 0x0000000000001000   X     0x001000
 LOAD      0x0000000000038000 0x00007f581774d000 0000000000000000
           0x0000000000001000 0x0000000000001000  WX     0x001000
 LOAD      0x0000000000039000 0x00007f581774e000 0000000000000000
           0x0000000000001000 0x0000000000001000  WX     0x001000
 LOAD      0x000000000003a000 0x00007ffc20d16000 0000000000000000
           0x0000000000022000 0x0000000000022000  WX     0x001000
 LOAD      0x000000000005c000 0x00007ffc20d9c000 0000000000000000
           0x0000000000002000 0x0000000000002000   X     0x001000
 LOAD      0x000000000005e000 0x00007ffc20d9e000 0000000000000000
           0x0000000000002000 0x0000000000002000 R X     0x001000
 LOAD      0x0000000000060000 0xffffffffff600000 0000000000000000
           0x0000000000001000 0x0000000000001000 R X     0x001000
All worked

这篇关于获取使用C引起分段故障,从核心转储地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆