我的问题是,我怎样才能得到导致核心转储的地址
在 C 中?
简答:
有两种方法可以解释这个问题。
错误指令的地址是什么?
超出范围的地址是什么?
Elf 核心转储将所有元信息保存在注释中,即
存储在音符片段中。笔记有不同的类型。
要回答 #1,我们需要获取寄存器。看精灵头到
找到程序头表。走程序头表查找
注释表(类型 PT_NOTE)。走笔记表找到笔记
键入 NT_PRSTATUS。这篇笔记的有效载荷是struct
elf_prstatus,可以在 linux/elfcore.h 中找到。中的一个
该结构的字段是所有通用寄存器。抓住
%rip 就完成了。
对于#2,我们做了类似的事情。这次我们要找的笔记
NT_SIGINFO 类型。本笔记的有效载荷是一个 siginfo_t 结构
在 signal.h 中定义。适用信号(SIGILL、SIGFPE、SIGSEGV、
SIGBUS),字段 si_addr 将包含您尝试访问的地址
访问但无法访问。
更多信息如下。在示例核心转储中,rip 是 0x400560,即试图进行非法访问的指令地址。这与其他通用寄存器一起显示。
程序试图访问的内存位于 0x03。这与其余的信号信息一起显示。
长答案:
我认为 BFD 已经有 25 年的历史了,所以我不会使用它来将核心文件的内容转储到 linux 机器上。也许如果您必须编写某种需要使用多种格式的通用代码,但即便如此,我也不确定我今天会这样做。
elf spec 写得很好,根据需要浏览程序头或节头的表格并不难。核心文件中的所有进程元信息都包含在 PT_NOTE 程序段中的一组注释中,只需几行直接的 C 代码即可解析出来。
我编写了一个小程序来从 x86_68 核心文件中获取寄存器并打印一些元数据。我把它放在github。获取note payload的逻辑在这个函数中:
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;
}
该函数被传递一个指向elf文件的指针和一个笔记类型,并返回一个指向相关笔记的有效负载的指针,如果它存在的话。各种可能的笔记类型在 elf.h 中。我在机器上的核心文件中实际看到的注释类型是:
#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 */
这些结构大部分都在 /usr/include/linux 下的头文件中。 xsave 结构是intel manual 第 13 章中描述的几 KB 浮点信息。它有 SSE、AVX 和 MPX,在其中注册。
NT_FILE 负载似乎在标头中没有关联的结构,但它在内核注释 (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...
*/
为 32 位系统解析 elf 文件的更改非常简单。使用相应的 Elf32_XXX 结构并将可变大小的字段向上取整为 4 而不是 8。
过去几天我一直在为这个小程序添加一些东西。目前它处理文件头、段头、通用寄存器、程序状态、程序信息和回溯。我会在有时间的时候添加对其余笔记的支持。这是当前的输出:
$ ./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