diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/binfmt_elf.c | 324 |
1 files changed, 194 insertions, 130 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index b8bca1ebc1a0..4510429b973e 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c | |||
@@ -1395,7 +1395,8 @@ static int writenote(struct memelfnote *men, struct file *file, | |||
1395 | if (!dump_seek(file, (off))) \ | 1395 | if (!dump_seek(file, (off))) \ |
1396 | goto end_coredump; | 1396 | goto end_coredump; |
1397 | 1397 | ||
1398 | static void fill_elf_header(struct elfhdr *elf, int segs) | 1398 | static void fill_elf_header(struct elfhdr *elf, int segs, |
1399 | u16 machine, u32 flags, u8 osabi) | ||
1399 | { | 1400 | { |
1400 | memcpy(elf->e_ident, ELFMAG, SELFMAG); | 1401 | memcpy(elf->e_ident, ELFMAG, SELFMAG); |
1401 | elf->e_ident[EI_CLASS] = ELF_CLASS; | 1402 | elf->e_ident[EI_CLASS] = ELF_CLASS; |
@@ -1405,12 +1406,12 @@ static void fill_elf_header(struct elfhdr *elf, int segs) | |||
1405 | memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); | 1406 | memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); |
1406 | 1407 | ||
1407 | elf->e_type = ET_CORE; | 1408 | elf->e_type = ET_CORE; |
1408 | elf->e_machine = ELF_ARCH; | 1409 | elf->e_machine = machine; |
1409 | elf->e_version = EV_CURRENT; | 1410 | elf->e_version = EV_CURRENT; |
1410 | elf->e_entry = 0; | 1411 | elf->e_entry = 0; |
1411 | elf->e_phoff = sizeof(struct elfhdr); | 1412 | elf->e_phoff = sizeof(struct elfhdr); |
1412 | elf->e_shoff = 0; | 1413 | elf->e_shoff = 0; |
1413 | elf->e_flags = ELF_CORE_EFLAGS; | 1414 | elf->e_flags = flags; |
1414 | elf->e_ehsize = sizeof(struct elfhdr); | 1415 | elf->e_ehsize = sizeof(struct elfhdr); |
1415 | elf->e_phentsize = sizeof(struct elf_phdr); | 1416 | elf->e_phentsize = sizeof(struct elf_phdr); |
1416 | elf->e_phnum = segs; | 1417 | elf->e_phnum = segs; |
@@ -1517,6 +1518,16 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, | |||
1517 | return 0; | 1518 | return 0; |
1518 | } | 1519 | } |
1519 | 1520 | ||
1521 | static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm) | ||
1522 | { | ||
1523 | elf_addr_t *auxv = (elf_addr_t *) mm->saved_auxv; | ||
1524 | int i = 0; | ||
1525 | do | ||
1526 | i += 2; | ||
1527 | while (auxv[i - 2] != AT_NULL); | ||
1528 | fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv); | ||
1529 | } | ||
1530 | |||
1520 | /* Here is the structure in which status of each thread is captured. */ | 1531 | /* Here is the structure in which status of each thread is captured. */ |
1521 | struct elf_thread_status | 1532 | struct elf_thread_status |
1522 | { | 1533 | { |
@@ -1569,6 +1580,174 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) | |||
1569 | return sz; | 1580 | return sz; |
1570 | } | 1581 | } |
1571 | 1582 | ||
1583 | struct elf_note_info { | ||
1584 | struct memelfnote *notes; | ||
1585 | struct elf_prstatus *prstatus; /* NT_PRSTATUS */ | ||
1586 | struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ | ||
1587 | struct list_head thread_list; | ||
1588 | elf_fpregset_t *fpu; | ||
1589 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1590 | elf_fpxregset_t *xfpu; | ||
1591 | #endif | ||
1592 | int thread_status_size; | ||
1593 | int numnote; | ||
1594 | }; | ||
1595 | |||
1596 | static int fill_note_info(struct elfhdr *elf, int phdrs, | ||
1597 | struct elf_note_info *info, | ||
1598 | long signr, struct pt_regs *regs) | ||
1599 | { | ||
1600 | #define NUM_NOTES 6 | ||
1601 | struct list_head *t; | ||
1602 | struct task_struct *g, *p; | ||
1603 | |||
1604 | info->notes = NULL; | ||
1605 | info->prstatus = NULL; | ||
1606 | info->psinfo = NULL; | ||
1607 | info->fpu = NULL; | ||
1608 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1609 | info->xfpu = NULL; | ||
1610 | #endif | ||
1611 | INIT_LIST_HEAD(&info->thread_list); | ||
1612 | |||
1613 | info->notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote), | ||
1614 | GFP_KERNEL); | ||
1615 | if (!info->notes) | ||
1616 | return 0; | ||
1617 | info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL); | ||
1618 | if (!info->psinfo) | ||
1619 | return 0; | ||
1620 | info->prstatus = kmalloc(sizeof(*info->prstatus), GFP_KERNEL); | ||
1621 | if (!info->prstatus) | ||
1622 | return 0; | ||
1623 | info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL); | ||
1624 | if (!info->fpu) | ||
1625 | return 0; | ||
1626 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1627 | info->xfpu = kmalloc(sizeof(*info->xfpu), GFP_KERNEL); | ||
1628 | if (!info->xfpu) | ||
1629 | return 0; | ||
1630 | #endif | ||
1631 | |||
1632 | info->thread_status_size = 0; | ||
1633 | if (signr) { | ||
1634 | struct elf_thread_status *tmp; | ||
1635 | rcu_read_lock(); | ||
1636 | do_each_thread(g, p) | ||
1637 | if (current->mm == p->mm && current != p) { | ||
1638 | tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC); | ||
1639 | if (!tmp) { | ||
1640 | rcu_read_unlock(); | ||
1641 | return 0; | ||
1642 | } | ||
1643 | tmp->thread = p; | ||
1644 | list_add(&tmp->list, &info->thread_list); | ||
1645 | } | ||
1646 | while_each_thread(g, p); | ||
1647 | rcu_read_unlock(); | ||
1648 | list_for_each(t, &info->thread_list) { | ||
1649 | struct elf_thread_status *tmp; | ||
1650 | int sz; | ||
1651 | |||
1652 | tmp = list_entry(t, struct elf_thread_status, list); | ||
1653 | sz = elf_dump_thread_status(signr, tmp); | ||
1654 | info->thread_status_size += sz; | ||
1655 | } | ||
1656 | } | ||
1657 | /* now collect the dump for the current */ | ||
1658 | memset(info->prstatus, 0, sizeof(*info->prstatus)); | ||
1659 | fill_prstatus(info->prstatus, current, signr); | ||
1660 | elf_core_copy_regs(&info->prstatus->pr_reg, regs); | ||
1661 | |||
1662 | /* Set up header */ | ||
1663 | fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS, ELF_OSABI); | ||
1664 | |||
1665 | /* | ||
1666 | * Set up the notes in similar form to SVR4 core dumps made | ||
1667 | * with info from their /proc. | ||
1668 | */ | ||
1669 | |||
1670 | fill_note(info->notes + 0, "CORE", NT_PRSTATUS, | ||
1671 | sizeof(*info->prstatus), info->prstatus); | ||
1672 | fill_psinfo(info->psinfo, current->group_leader, current->mm); | ||
1673 | fill_note(info->notes + 1, "CORE", NT_PRPSINFO, | ||
1674 | sizeof(*info->psinfo), info->psinfo); | ||
1675 | |||
1676 | info->numnote = 2; | ||
1677 | |||
1678 | fill_auxv_note(&info->notes[info->numnote++], current->mm); | ||
1679 | |||
1680 | /* Try to dump the FPU. */ | ||
1681 | info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, | ||
1682 | info->fpu); | ||
1683 | if (info->prstatus->pr_fpvalid) | ||
1684 | fill_note(info->notes + info->numnote++, | ||
1685 | "CORE", NT_PRFPREG, sizeof(*info->fpu), info->fpu); | ||
1686 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1687 | if (elf_core_copy_task_xfpregs(current, info->xfpu)) | ||
1688 | fill_note(info->notes + info->numnote++, | ||
1689 | "LINUX", ELF_CORE_XFPREG_TYPE, | ||
1690 | sizeof(*info->xfpu), info->xfpu); | ||
1691 | #endif | ||
1692 | |||
1693 | return 1; | ||
1694 | |||
1695 | #undef NUM_NOTES | ||
1696 | } | ||
1697 | |||
1698 | static size_t get_note_info_size(struct elf_note_info *info) | ||
1699 | { | ||
1700 | int sz = 0; | ||
1701 | int i; | ||
1702 | |||
1703 | for (i = 0; i < info->numnote; i++) | ||
1704 | sz += notesize(info->notes + i); | ||
1705 | |||
1706 | sz += info->thread_status_size; | ||
1707 | |||
1708 | return sz; | ||
1709 | } | ||
1710 | |||
1711 | static int write_note_info(struct elf_note_info *info, | ||
1712 | struct file *file, loff_t *foffset) | ||
1713 | { | ||
1714 | int i; | ||
1715 | struct list_head *t; | ||
1716 | |||
1717 | for (i = 0; i < info->numnote; i++) | ||
1718 | if (!writenote(info->notes + i, file, foffset)) | ||
1719 | return 0; | ||
1720 | |||
1721 | /* write out the thread status notes section */ | ||
1722 | list_for_each(t, &info->thread_list) { | ||
1723 | struct elf_thread_status *tmp = | ||
1724 | list_entry(t, struct elf_thread_status, list); | ||
1725 | |||
1726 | for (i = 0; i < tmp->num_notes; i++) | ||
1727 | if (!writenote(&tmp->notes[i], file, foffset)) | ||
1728 | return 0; | ||
1729 | } | ||
1730 | |||
1731 | return 1; | ||
1732 | } | ||
1733 | |||
1734 | static void free_note_info(struct elf_note_info *info) | ||
1735 | { | ||
1736 | while (!list_empty(&info->thread_list)) { | ||
1737 | struct list_head *tmp = info->thread_list.next; | ||
1738 | list_del(tmp); | ||
1739 | kfree(list_entry(tmp, struct elf_thread_status, list)); | ||
1740 | } | ||
1741 | |||
1742 | kfree(info->prstatus); | ||
1743 | kfree(info->psinfo); | ||
1744 | kfree(info->notes); | ||
1745 | kfree(info->fpu); | ||
1746 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1747 | kfree(info->xfpu); | ||
1748 | #endif | ||
1749 | } | ||
1750 | |||
1572 | static struct vm_area_struct *first_vma(struct task_struct *tsk, | 1751 | static struct vm_area_struct *first_vma(struct task_struct *tsk, |
1573 | struct vm_area_struct *gate_vma) | 1752 | struct vm_area_struct *gate_vma) |
1574 | { | 1753 | { |
@@ -1604,29 +1783,15 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma, | |||
1604 | */ | 1783 | */ |
1605 | static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit) | 1784 | static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit) |
1606 | { | 1785 | { |
1607 | #define NUM_NOTES 6 | ||
1608 | int has_dumped = 0; | 1786 | int has_dumped = 0; |
1609 | mm_segment_t fs; | 1787 | mm_segment_t fs; |
1610 | int segs; | 1788 | int segs; |
1611 | size_t size = 0; | 1789 | size_t size = 0; |
1612 | int i; | ||
1613 | struct vm_area_struct *vma, *gate_vma; | 1790 | struct vm_area_struct *vma, *gate_vma; |
1614 | struct elfhdr *elf = NULL; | 1791 | struct elfhdr *elf = NULL; |
1615 | loff_t offset = 0, dataoff, foffset; | 1792 | loff_t offset = 0, dataoff, foffset; |
1616 | int numnote; | ||
1617 | struct memelfnote *notes = NULL; | ||
1618 | struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */ | ||
1619 | struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */ | ||
1620 | struct task_struct *g, *p; | ||
1621 | LIST_HEAD(thread_list); | ||
1622 | struct list_head *t; | ||
1623 | elf_fpregset_t *fpu = NULL; | ||
1624 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1625 | elf_fpxregset_t *xfpu = NULL; | ||
1626 | #endif | ||
1627 | int thread_status_size = 0; | ||
1628 | elf_addr_t *auxv; | ||
1629 | unsigned long mm_flags; | 1793 | unsigned long mm_flags; |
1794 | struct elf_note_info info; | ||
1630 | 1795 | ||
1631 | /* | 1796 | /* |
1632 | * We no longer stop all VM operations. | 1797 | * We no longer stop all VM operations. |
@@ -1644,52 +1809,6 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un | |||
1644 | elf = kmalloc(sizeof(*elf), GFP_KERNEL); | 1809 | elf = kmalloc(sizeof(*elf), GFP_KERNEL); |
1645 | if (!elf) | 1810 | if (!elf) |
1646 | goto cleanup; | 1811 | goto cleanup; |
1647 | prstatus = kmalloc(sizeof(*prstatus), GFP_KERNEL); | ||
1648 | if (!prstatus) | ||
1649 | goto cleanup; | ||
1650 | psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); | ||
1651 | if (!psinfo) | ||
1652 | goto cleanup; | ||
1653 | notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote), GFP_KERNEL); | ||
1654 | if (!notes) | ||
1655 | goto cleanup; | ||
1656 | fpu = kmalloc(sizeof(*fpu), GFP_KERNEL); | ||
1657 | if (!fpu) | ||
1658 | goto cleanup; | ||
1659 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1660 | xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL); | ||
1661 | if (!xfpu) | ||
1662 | goto cleanup; | ||
1663 | #endif | ||
1664 | |||
1665 | if (signr) { | ||
1666 | struct elf_thread_status *tmp; | ||
1667 | rcu_read_lock(); | ||
1668 | do_each_thread(g,p) | ||
1669 | if (current->mm == p->mm && current != p) { | ||
1670 | tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC); | ||
1671 | if (!tmp) { | ||
1672 | rcu_read_unlock(); | ||
1673 | goto cleanup; | ||
1674 | } | ||
1675 | tmp->thread = p; | ||
1676 | list_add(&tmp->list, &thread_list); | ||
1677 | } | ||
1678 | while_each_thread(g,p); | ||
1679 | rcu_read_unlock(); | ||
1680 | list_for_each(t, &thread_list) { | ||
1681 | struct elf_thread_status *tmp; | ||
1682 | int sz; | ||
1683 | |||
1684 | tmp = list_entry(t, struct elf_thread_status, list); | ||
1685 | sz = elf_dump_thread_status(signr, tmp); | ||
1686 | thread_status_size += sz; | ||
1687 | } | ||
1688 | } | ||
1689 | /* now collect the dump for the current */ | ||
1690 | memset(prstatus, 0, sizeof(*prstatus)); | ||
1691 | fill_prstatus(prstatus, current, signr); | ||
1692 | elf_core_copy_regs(&prstatus->pr_reg, regs); | ||
1693 | 1812 | ||
1694 | segs = current->mm->map_count; | 1813 | segs = current->mm->map_count; |
1695 | #ifdef ELF_CORE_EXTRA_PHDRS | 1814 | #ifdef ELF_CORE_EXTRA_PHDRS |
@@ -1700,42 +1819,16 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un | |||
1700 | if (gate_vma != NULL) | 1819 | if (gate_vma != NULL) |
1701 | segs++; | 1820 | segs++; |
1702 | 1821 | ||
1703 | /* Set up header */ | ||
1704 | fill_elf_header(elf, segs + 1); /* including notes section */ | ||
1705 | |||
1706 | has_dumped = 1; | ||
1707 | current->flags |= PF_DUMPCORE; | ||
1708 | |||
1709 | /* | 1822 | /* |
1710 | * Set up the notes in similar form to SVR4 core dumps made | 1823 | * Collect all the non-memory information about the process for the |
1711 | * with info from their /proc. | 1824 | * notes. This also sets up the file header. |
1712 | */ | 1825 | */ |
1826 | if (!fill_note_info(elf, segs + 1, /* including notes section */ | ||
1827 | &info, signr, regs)) | ||
1828 | goto cleanup; | ||
1713 | 1829 | ||
1714 | fill_note(notes + 0, "CORE", NT_PRSTATUS, sizeof(*prstatus), prstatus); | 1830 | has_dumped = 1; |
1715 | fill_psinfo(psinfo, current->group_leader, current->mm); | 1831 | current->flags |= PF_DUMPCORE; |
1716 | fill_note(notes + 1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); | ||
1717 | |||
1718 | numnote = 2; | ||
1719 | |||
1720 | auxv = (elf_addr_t *)current->mm->saved_auxv; | ||
1721 | |||
1722 | i = 0; | ||
1723 | do | ||
1724 | i += 2; | ||
1725 | while (auxv[i - 2] != AT_NULL); | ||
1726 | fill_note(¬es[numnote++], "CORE", NT_AUXV, | ||
1727 | i * sizeof(elf_addr_t), auxv); | ||
1728 | |||
1729 | /* Try to dump the FPU. */ | ||
1730 | if ((prstatus->pr_fpvalid = | ||
1731 | elf_core_copy_task_fpregs(current, regs, fpu))) | ||
1732 | fill_note(notes + numnote++, | ||
1733 | "CORE", NT_PRFPREG, sizeof(*fpu), fpu); | ||
1734 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1735 | if (elf_core_copy_task_xfpregs(current, xfpu)) | ||
1736 | fill_note(notes + numnote++, | ||
1737 | "LINUX", ELF_CORE_XFPREG_TYPE, sizeof(*xfpu), xfpu); | ||
1738 | #endif | ||
1739 | 1832 | ||
1740 | fs = get_fs(); | 1833 | fs = get_fs(); |
1741 | set_fs(KERNEL_DS); | 1834 | set_fs(KERNEL_DS); |
@@ -1748,12 +1841,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un | |||
1748 | /* Write notes phdr entry */ | 1841 | /* Write notes phdr entry */ |
1749 | { | 1842 | { |
1750 | struct elf_phdr phdr; | 1843 | struct elf_phdr phdr; |
1751 | int sz = 0; | 1844 | size_t sz = get_note_info_size(&info); |
1752 | |||
1753 | for (i = 0; i < numnote; i++) | ||
1754 | sz += notesize(notes + i); | ||
1755 | |||
1756 | sz += thread_status_size; | ||
1757 | 1845 | ||
1758 | sz += elf_coredump_extra_notes_size(); | 1846 | sz += elf_coredump_extra_notes_size(); |
1759 | 1847 | ||
@@ -1798,23 +1886,12 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un | |||
1798 | #endif | 1886 | #endif |
1799 | 1887 | ||
1800 | /* write out the notes section */ | 1888 | /* write out the notes section */ |
1801 | for (i = 0; i < numnote; i++) | 1889 | if (!write_note_info(&info, file, &foffset)) |
1802 | if (!writenote(notes + i, file, &foffset)) | 1890 | goto end_coredump; |
1803 | goto end_coredump; | ||
1804 | 1891 | ||
1805 | if (elf_coredump_extra_notes_write(file, &foffset)) | 1892 | if (elf_coredump_extra_notes_write(file, &foffset)) |
1806 | goto end_coredump; | 1893 | goto end_coredump; |
1807 | 1894 | ||
1808 | /* write out the thread status notes section */ | ||
1809 | list_for_each(t, &thread_list) { | ||
1810 | struct elf_thread_status *tmp = | ||
1811 | list_entry(t, struct elf_thread_status, list); | ||
1812 | |||
1813 | for (i = 0; i < tmp->num_notes; i++) | ||
1814 | if (!writenote(&tmp->notes[i], file, &foffset)) | ||
1815 | goto end_coredump; | ||
1816 | } | ||
1817 | |||
1818 | /* Align to page */ | 1895 | /* Align to page */ |
1819 | DUMP_SEEK(dataoff - foffset); | 1896 | DUMP_SEEK(dataoff - foffset); |
1820 | 1897 | ||
@@ -1865,22 +1942,9 @@ end_coredump: | |||
1865 | set_fs(fs); | 1942 | set_fs(fs); |
1866 | 1943 | ||
1867 | cleanup: | 1944 | cleanup: |
1868 | while (!list_empty(&thread_list)) { | ||
1869 | struct list_head *tmp = thread_list.next; | ||
1870 | list_del(tmp); | ||
1871 | kfree(list_entry(tmp, struct elf_thread_status, list)); | ||
1872 | } | ||
1873 | |||
1874 | kfree(elf); | 1945 | kfree(elf); |
1875 | kfree(prstatus); | 1946 | free_note_info(&info); |
1876 | kfree(psinfo); | ||
1877 | kfree(notes); | ||
1878 | kfree(fpu); | ||
1879 | #ifdef ELF_CORE_COPY_XFPREGS | ||
1880 | kfree(xfpu); | ||
1881 | #endif | ||
1882 | return has_dumped; | 1947 | return has_dumped; |
1883 | #undef NUM_NOTES | ||
1884 | } | 1948 | } |
1885 | 1949 | ||
1886 | #endif /* USE_ELF_CORE_DUMP */ | 1950 | #endif /* USE_ELF_CORE_DUMP */ |