diff options
author | Roland McGrath <roland@redhat.com> | 2008-01-30 07:31:45 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:31:45 -0500 |
commit | 4206d3aa1978e44f58bfa4e1c9d8d35cbf19c187 (patch) | |
tree | 53de158bec7e6890ce9a5772f3d04f23e9a29803 /fs/binfmt_elf.c | |
parent | 3aba481fc94d83ff630d4b7cd2f7447010c4c6df (diff) |
elf core dump: notes user_regset
This modifies the ELF core dump code under #ifdef CORE_DUMP_USE_REGSET.
It changes nothing when this macro is not defined. When it's #define'd
by some arch header (e.g. asm/elf.h), the arch must support the
user_regset (linux/regset.h) interface for reading thread state.
This provides an alternate version of note segment writing that is based
purely on the user_regset interfaces. When CORE_DUMP_USE_REGSET is set,
the arch need not define macros such as ELF_CORE_COPY_REGS and ELF_ARCH.
All that information is taken from the user_regset data structures.
The core dumps come out exactly the same if arch's definitions for its
user_regset details are correct.
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'fs/binfmt_elf.c')
-rw-r--r-- | fs/binfmt_elf.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 4510429b973e..786ee275ec0a 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c | |||
@@ -1528,6 +1528,228 @@ static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm) | |||
1528 | fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv); | 1528 | fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv); |
1529 | } | 1529 | } |
1530 | 1530 | ||
1531 | #ifdef CORE_DUMP_USE_REGSET | ||
1532 | #include <linux/regset.h> | ||
1533 | |||
1534 | struct elf_thread_core_info { | ||
1535 | struct elf_thread_core_info *next; | ||
1536 | struct task_struct *task; | ||
1537 | struct elf_prstatus prstatus; | ||
1538 | struct memelfnote notes[0]; | ||
1539 | }; | ||
1540 | |||
1541 | struct elf_note_info { | ||
1542 | struct elf_thread_core_info *thread; | ||
1543 | struct memelfnote psinfo; | ||
1544 | struct memelfnote auxv; | ||
1545 | size_t size; | ||
1546 | int thread_notes; | ||
1547 | }; | ||
1548 | |||
1549 | static int fill_thread_core_info(struct elf_thread_core_info *t, | ||
1550 | const struct user_regset_view *view, | ||
1551 | long signr, size_t *total) | ||
1552 | { | ||
1553 | unsigned int i; | ||
1554 | |||
1555 | /* | ||
1556 | * NT_PRSTATUS is the one special case, because the regset data | ||
1557 | * goes into the pr_reg field inside the note contents, rather | ||
1558 | * than being the whole note contents. We fill the reset in here. | ||
1559 | * We assume that regset 0 is NT_PRSTATUS. | ||
1560 | */ | ||
1561 | fill_prstatus(&t->prstatus, t->task, signr); | ||
1562 | (void) view->regsets[0].get(t->task, &view->regsets[0], | ||
1563 | 0, sizeof(t->prstatus.pr_reg), | ||
1564 | &t->prstatus.pr_reg, NULL); | ||
1565 | |||
1566 | fill_note(&t->notes[0], "CORE", NT_PRSTATUS, | ||
1567 | sizeof(t->prstatus), &t->prstatus); | ||
1568 | *total += notesize(&t->notes[0]); | ||
1569 | |||
1570 | /* | ||
1571 | * Each other regset might generate a note too. For each regset | ||
1572 | * that has no core_note_type or is inactive, we leave t->notes[i] | ||
1573 | * all zero and we'll know to skip writing it later. | ||
1574 | */ | ||
1575 | for (i = 1; i < view->n; ++i) { | ||
1576 | const struct user_regset *regset = &view->regsets[i]; | ||
1577 | if (regset->core_note_type && | ||
1578 | (!regset->active || regset->active(t->task, regset))) { | ||
1579 | int ret; | ||
1580 | size_t size = regset->n * regset->size; | ||
1581 | void *data = kmalloc(size, GFP_KERNEL); | ||
1582 | if (unlikely(!data)) | ||
1583 | return 0; | ||
1584 | ret = regset->get(t->task, regset, | ||
1585 | 0, size, data, NULL); | ||
1586 | if (unlikely(ret)) | ||
1587 | kfree(data); | ||
1588 | else { | ||
1589 | if (regset->core_note_type != NT_PRFPREG) | ||
1590 | fill_note(&t->notes[i], "LINUX", | ||
1591 | regset->core_note_type, | ||
1592 | size, data); | ||
1593 | else { | ||
1594 | t->prstatus.pr_fpvalid = 1; | ||
1595 | fill_note(&t->notes[i], "CORE", | ||
1596 | NT_PRFPREG, size, data); | ||
1597 | } | ||
1598 | *total += notesize(&t->notes[i]); | ||
1599 | } | ||
1600 | } | ||
1601 | } | ||
1602 | |||
1603 | return 1; | ||
1604 | } | ||
1605 | |||
1606 | static int fill_note_info(struct elfhdr *elf, int phdrs, | ||
1607 | struct elf_note_info *info, | ||
1608 | long signr, struct pt_regs *regs) | ||
1609 | { | ||
1610 | struct task_struct *dump_task = current; | ||
1611 | const struct user_regset_view *view = task_user_regset_view(dump_task); | ||
1612 | struct elf_thread_core_info *t; | ||
1613 | struct elf_prpsinfo *psinfo; | ||
1614 | struct task_struct *g, *p; | ||
1615 | unsigned int i; | ||
1616 | |||
1617 | info->size = 0; | ||
1618 | info->thread = NULL; | ||
1619 | |||
1620 | psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); | ||
1621 | fill_note(&info->psinfo, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); | ||
1622 | |||
1623 | if (psinfo == NULL) | ||
1624 | return 0; | ||
1625 | |||
1626 | /* | ||
1627 | * Figure out how many notes we're going to need for each thread. | ||
1628 | */ | ||
1629 | info->thread_notes = 0; | ||
1630 | for (i = 0; i < view->n; ++i) | ||
1631 | if (view->regsets[i].core_note_type != 0) | ||
1632 | ++info->thread_notes; | ||
1633 | |||
1634 | /* | ||
1635 | * Sanity check. We rely on regset 0 being in NT_PRSTATUS, | ||
1636 | * since it is our one special case. | ||
1637 | */ | ||
1638 | if (unlikely(info->thread_notes == 0) || | ||
1639 | unlikely(view->regsets[0].core_note_type != NT_PRSTATUS)) { | ||
1640 | WARN_ON(1); | ||
1641 | return 0; | ||
1642 | } | ||
1643 | |||
1644 | /* | ||
1645 | * Initialize the ELF file header. | ||
1646 | */ | ||
1647 | fill_elf_header(elf, phdrs, | ||
1648 | view->e_machine, view->e_flags, view->ei_osabi); | ||
1649 | |||
1650 | /* | ||
1651 | * Allocate a structure for each thread. | ||
1652 | */ | ||
1653 | rcu_read_lock(); | ||
1654 | do_each_thread(g, p) | ||
1655 | if (p->mm == dump_task->mm) { | ||
1656 | t = kzalloc(offsetof(struct elf_thread_core_info, | ||
1657 | notes[info->thread_notes]), | ||
1658 | GFP_ATOMIC); | ||
1659 | if (unlikely(!t)) { | ||
1660 | rcu_read_unlock(); | ||
1661 | return 0; | ||
1662 | } | ||
1663 | t->task = p; | ||
1664 | if (p == dump_task || !info->thread) { | ||
1665 | t->next = info->thread; | ||
1666 | info->thread = t; | ||
1667 | } else { | ||
1668 | /* | ||
1669 | * Make sure to keep the original task at | ||
1670 | * the head of the list. | ||
1671 | */ | ||
1672 | t->next = info->thread->next; | ||
1673 | info->thread->next = t; | ||
1674 | } | ||
1675 | } | ||
1676 | while_each_thread(g, p); | ||
1677 | rcu_read_unlock(); | ||
1678 | |||
1679 | /* | ||
1680 | * Now fill in each thread's information. | ||
1681 | */ | ||
1682 | for (t = info->thread; t != NULL; t = t->next) | ||
1683 | if (!fill_thread_core_info(t, view, signr, &info->size)) | ||
1684 | return 0; | ||
1685 | |||
1686 | /* | ||
1687 | * Fill in the two process-wide notes. | ||
1688 | */ | ||
1689 | fill_psinfo(psinfo, dump_task->group_leader, dump_task->mm); | ||
1690 | info->size += notesize(&info->psinfo); | ||
1691 | |||
1692 | fill_auxv_note(&info->auxv, current->mm); | ||
1693 | info->size += notesize(&info->auxv); | ||
1694 | |||
1695 | return 1; | ||
1696 | } | ||
1697 | |||
1698 | static size_t get_note_info_size(struct elf_note_info *info) | ||
1699 | { | ||
1700 | return info->size; | ||
1701 | } | ||
1702 | |||
1703 | /* | ||
1704 | * Write all the notes for each thread. When writing the first thread, the | ||
1705 | * process-wide notes are interleaved after the first thread-specific note. | ||
1706 | */ | ||
1707 | static int write_note_info(struct elf_note_info *info, | ||
1708 | struct file *file, loff_t *foffset) | ||
1709 | { | ||
1710 | bool first = 1; | ||
1711 | struct elf_thread_core_info *t = info->thread; | ||
1712 | |||
1713 | do { | ||
1714 | int i; | ||
1715 | |||
1716 | if (!writenote(&t->notes[0], file, foffset)) | ||
1717 | return 0; | ||
1718 | |||
1719 | if (first && !writenote(&info->psinfo, file, foffset)) | ||
1720 | return 0; | ||
1721 | if (first && !writenote(&info->auxv, file, foffset)) | ||
1722 | return 0; | ||
1723 | |||
1724 | for (i = 1; i < info->thread_notes; ++i) | ||
1725 | if (t->notes[i].data && | ||
1726 | !writenote(&t->notes[i], file, foffset)) | ||
1727 | return 0; | ||
1728 | |||
1729 | first = 0; | ||
1730 | t = t->next; | ||
1731 | } while (t); | ||
1732 | |||
1733 | return 1; | ||
1734 | } | ||
1735 | |||
1736 | static void free_note_info(struct elf_note_info *info) | ||
1737 | { | ||
1738 | struct elf_thread_core_info *threads = info->thread; | ||
1739 | while (threads) { | ||
1740 | unsigned int i; | ||
1741 | struct elf_thread_core_info *t = threads; | ||
1742 | threads = t->next; | ||
1743 | WARN_ON(t->notes[0].data && t->notes[0].data != &t->prstatus); | ||
1744 | for (i = 1; i < info->thread_notes; ++i) | ||
1745 | kfree(t->notes[i].data); | ||
1746 | kfree(t); | ||
1747 | } | ||
1748 | kfree(info->psinfo.data); | ||
1749 | } | ||
1750 | |||
1751 | #else | ||
1752 | |||
1531 | /* Here is the structure in which status of each thread is captured. */ | 1753 | /* Here is the structure in which status of each thread is captured. */ |
1532 | struct elf_thread_status | 1754 | struct elf_thread_status |
1533 | { | 1755 | { |
@@ -1748,6 +1970,8 @@ static void free_note_info(struct elf_note_info *info) | |||
1748 | #endif | 1970 | #endif |
1749 | } | 1971 | } |
1750 | 1972 | ||
1973 | #endif | ||
1974 | |||
1751 | static struct vm_area_struct *first_vma(struct task_struct *tsk, | 1975 | static struct vm_area_struct *first_vma(struct task_struct *tsk, |
1752 | struct vm_area_struct *gate_vma) | 1976 | struct vm_area_struct *gate_vma) |
1753 | { | 1977 | { |