#linux #debugging
#linux #отладка #coredump
Вопрос:
Размер дампа ядра можно ограничить с помощью ограничений (ulimit, rlimit и т. Д.).
Мне интересно, как это на самом деле реализовано — например: достаточно ли умна генерация дампа ядра, чтобы расставить приоритеты в стеке? Память, на которую ссылаются указатели локальных переменных? Или это буквально все адресное пространство процесса, усеченное на N байтов?
Комментарии:
1. Это вопрос программирования? Если нет, это может быть лучше подходит для unix.stackexchange.com
2. Сложный вызов, но я думаю, что программисты с большей вероятностью знают фактическую структуру дампа ядра, чем системные администраторы unix.
Ответ №1:
Поскольку я недавно столкнулся с усечением дампа ядра в каком-то процессе, я могу поделиться своим опытом по этому поводу. Ядро Linux сообщает о дампе ядра в контексте процесса сбоя. В исходном коде ядра Linux перенос дампа ядра из ядра в область пользовательского пространства выполняется несколькими вызовами dump_emit() в fs/coredump.c:
/*
* Core dumping helper functions. These are the only things you should
* do on a core-file: use only these functions to write out all the
* necessary info.
*/
int dump_emit(struct coredump_params *cprm, const void *addr, int nr)
{
struct file *file = cprm->file;
loff_t pos = file->f_pos;
ssize_t n;
if (cprm->written nr > cprm->limit)
return 0;
while (nr) {
if (dump_interrupted())
return 0;
n = __kernel_write(file, addr, nr, amp;pos);
if (n <= 0)
return 0;
file->f_pos = pos;
cprm->written = n;
cprm->pos = n;
nr -= n;
}
return 1;
}
EXPORT_SYMBOL(dump_emit);
Приведенная выше функция проверяет две основные вещи:
- Является ли текущий размер дампа ядра все еще ниже заданного предела для процесса?
if (cprm->written nr > cprm->limit)
- Есть ли ожидающий сигнал для текущего процесса?
if (dump_interrupted())
Если одна из вышеуказанных проверок завершается неудачей, передача дампа ядра из ядра в пространство пользователя прерывается, и, таким образом, результирующий файл ядра усекается в любой момент. В ядре, настроенном на ELF, вышеупомянутая служба, например, вызывается elf_core_dump() из fs/binfmt_elf.c:
/*
* Actual dumper
*
* This is a two-pass process; first we find the offsets of the bits,
* and then they are actually written out. If we run out of core limit
* we just truncate.
*/
static int elf_core_dump(struct coredump_params *cprm)
{
int has_dumped = 0;
mm_segment_t fs;
int segs, i;
size_t vma_data_size = 0;
struct vm_area_struct *vma, *gate_vma;
struct elfhdr *elf = NULL;
loff_t offset = 0, dataoff;
struct elf_note_info info = { };
struct elf_phdr *phdr4note = NULL;
struct elf_shdr *shdr4extnum = NULL;
Elf_Half e_phnum;
elf_addr_t e_shoff;
elf_addr_t *vma_filesz = NULL;
/*
* We no longer stop all VM operations.
*
* This is because those proceses that could possibly change map_count
* or the mmap / vma pages are now blocked in do_exit on current
* finishing this core dump.
*
* Only ptrace can touch these memory addresses, but it doesn't change
* the map_count or the pages allocated. So no possibility of crashing
* exists while dumping the mm->vm_next areas to the core file.
*/
/* alloc memory for large data structures: too large to be on stack */
elf = kmalloc(sizeof(*elf), GFP_KERNEL);
if (!elf)
goto out;
/*
* The number of segs are recored into ELF header as 16bit value.
* Please check DEFAULT_MAX_MAP_COUNT definition when you modify here.
*/
segs = current->mm->map_count;
segs = elf_core_extra_phdrs();
gate_vma = get_gate_vma(current->mm);
if (gate_vma != NULL)
segs ;
/* for notes section */
segs ;
/* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid
* this, kernel supports extended numbering. Have a look at
* include/linux/elf.h for further information. */
e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
/*
* Collect all the non-memory information about the process for the
* notes. This also sets up the file header.
*/
if (!fill_note_info(elf, e_phnum, amp;info, cprm->siginfo, cprm->regs))
goto cleanup;
has_dumped = 1;
fs = get_fs();
set_fs(KERNEL_DS);
offset = sizeof(*elf); /* Elf header */
offset = segs * sizeof(struct elf_phdr); /* Program headers */
/* Write notes phdr entry */
{
size_t sz = get_note_info_size(amp;info);
sz = elf_coredump_extra_notes_size();
phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL);
if (!phdr4note)
goto end_coredump;
fill_elf_note_phdr(phdr4note, sz, offset);
offset = sz;
}
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
if (segs - 1 > ULONG_MAX / sizeof(*vma_filesz))
goto end_coredump;
vma_filesz = vmalloc((segs - 1) * sizeof(*vma_filesz));
if (!vma_filesz)
goto end_coredump;
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma)) {
unsigned long dump_size;
dump_size = vma_dump_size(vma, cprm->mm_flags);
vma_filesz[i ] = dump_size;
vma_data_size = dump_size;
}
offset = vma_data_size;
offset = elf_core_extra_data_size();
e_shoff = offset;
if (e_phnum == PN_XNUM) {
shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL);
if (!shdr4extnum)
goto end_coredump;
fill_extnum_info(elf, shdr4extnum, e_shoff, segs);
}
offset = dataoff;
if (!dump_emit(cprm, elf, sizeof(*elf)))
goto end_coredump;
if (!dump_emit(cprm, phdr4note, sizeof(*phdr4note)))
goto end_coredump;
/* Write program headers for segments dump */
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma)) {
struct elf_phdr phdr;
phdr.p_type = PT_LOAD;
phdr.p_offset = offset;
phdr.p_vaddr = vma->vm_start;
phdr.p_paddr = 0;
phdr.p_filesz = vma_filesz[i ];
phdr.p_memsz = vma->vm_end - vma->vm_start;
offset = phdr.p_filesz;
phdr.p_flags = vma->vm_flags amp; VM_READ ? PF_R : 0;
if (vma->vm_flags amp; VM_WRITE)
phdr.p_flags |= PF_W;
if (vma->vm_flags amp; VM_EXEC)
phdr.p_flags |= PF_X;
phdr.p_align = ELF_EXEC_PAGESIZE;
if (!dump_emit(cprm, amp;phdr, sizeof(phdr)))
goto end_coredump;
}
if (!elf_core_write_extra_phdrs(cprm, offset))
goto end_coredump;
/* write out the notes section */
if (!write_note_info(amp;info, cprm))
goto end_coredump;
if (elf_coredump_extra_notes_write(cprm))
goto end_coredump;
/* Align to page */
if (!dump_skip(cprm, dataoff - cprm->pos))
goto end_coredump;
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma)) {
unsigned long addr;
unsigned long end;
end = vma->vm_start vma_filesz[i ];
for (addr = vma->vm_start; addr < end; addr = PAGE_SIZE) {
struct page *page;
int stop;
page = get_dump_page(addr);
if (page) {
void *kaddr = kmap(page);
stop = !dump_emit(cprm, kaddr, PAGE_SIZE);
kunmap(page);
put_page(page);
} else
stop = !dump_skip(cprm, PAGE_SIZE);
if (stop)
goto end_coredump;
}
}
dump_truncate(cprm);
if (!elf_core_write_extra_data(cprm))
goto end_coredump;
if (e_phnum == PN_XNUM) {
if (!dump_emit(cprm, shdr4extnum, sizeof(*shdr4extnum)))
goto end_coredump;
}
end_coredump:
set_fs(fs);
cleanup:
free_note_info(amp;info);
kfree(shdr4extnum);
vfree(vma_filesz);
kfree(phdr4note);
kfree(elf);
out:
return has_dumped;
}