aboutsummaryrefslogtreecommitdiffstats
path: root/fs/binfmt_elf.c
diff options
context:
space:
mode:
authorDaisuke HATAYAMA <d.hatayama@jp.fujitsu.com>2010-03-05 16:44:10 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2010-03-06 14:26:46 -0500
commit8d9032bbe4671dc481261ccd4e161cd96e54b118 (patch)
treea31d22f488f7d6789259da68c53cb2727a925fa8 /fs/binfmt_elf.c
parent93eb211e6c9ff6054fcf9c5b9e344d8d9ad29175 (diff)
elf coredump: add extended numbering support
The current ELF dumper implementation can produce broken corefiles if program headers exceed 65535. This number is determined by the number of vmas which the process have. In particular, some extreme programs may use more than 65535 vmas. (If you google max_map_count, you can find some users facing this problem.) This kind of program never be able to generate correct coredumps. This patch implements ``extended numbering'' that uses sh_info field of the first section header instead of e_phnum field in order to represent upto 4294967295 vmas. This is supported by AMD64-ABI(http://www.x86-64.org/documentation.html) and Solaris(http://docs.sun.com/app/docs/doc/817-1984/). Of course, we are preparing patches for gdb and binutils. Signed-off-by: Daisuke HATAYAMA <d.hatayama@jp.fujitsu.com> Cc: "Luck, Tony" <tony.luck@intel.com> Cc: Jeff Dike <jdike@addtoit.com> Cc: David Howells <dhowells@redhat.com> Cc: Greg Ungerer <gerg@snapgear.com> Cc: Roland McGrath <roland@redhat.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Andi Kleen <andi@firstfloor.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: <linux-arch@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/binfmt_elf.c')
-rw-r--r--fs/binfmt_elf.c66
1 files changed, 63 insertions, 3 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 6fc49b6ed936..78de530cfb02 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1838,6 +1838,34 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
1838 return gate_vma; 1838 return gate_vma;
1839} 1839}
1840 1840
1841static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
1842 elf_addr_t e_shoff, int segs)
1843{
1844 elf->e_shoff = e_shoff;
1845 elf->e_shentsize = sizeof(*shdr4extnum);
1846 elf->e_shnum = 1;
1847 elf->e_shstrndx = SHN_UNDEF;
1848
1849 memset(shdr4extnum, 0, sizeof(*shdr4extnum));
1850
1851 shdr4extnum->sh_type = SHT_NULL;
1852 shdr4extnum->sh_size = elf->e_shnum;
1853 shdr4extnum->sh_link = elf->e_shstrndx;
1854 shdr4extnum->sh_info = segs;
1855}
1856
1857static size_t elf_core_vma_data_size(struct vm_area_struct *gate_vma,
1858 unsigned long mm_flags)
1859{
1860 struct vm_area_struct *vma;
1861 size_t size = 0;
1862
1863 for (vma = first_vma(current, gate_vma); vma != NULL;
1864 vma = next_vma(vma, gate_vma))
1865 size += vma_dump_size(vma, mm_flags);
1866 return size;
1867}
1868
1841/* 1869/*
1842 * Actual dumper 1870 * Actual dumper
1843 * 1871 *
@@ -1857,6 +1885,9 @@ static int elf_core_dump(struct coredump_params *cprm)
1857 unsigned long mm_flags; 1885 unsigned long mm_flags;
1858 struct elf_note_info info; 1886 struct elf_note_info info;
1859 struct elf_phdr *phdr4note = NULL; 1887 struct elf_phdr *phdr4note = NULL;
1888 struct elf_shdr *shdr4extnum = NULL;
1889 Elf_Half e_phnum;
1890 elf_addr_t e_shoff;
1860 1891
1861 /* 1892 /*
1862 * We no longer stop all VM operations. 1893 * We no longer stop all VM operations.
@@ -1885,12 +1916,19 @@ static int elf_core_dump(struct coredump_params *cprm)
1885 if (gate_vma != NULL) 1916 if (gate_vma != NULL)
1886 segs++; 1917 segs++;
1887 1918
1919 /* for notes section */
1920 segs++;
1921
1922 /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid
1923 * this, kernel supports extended numbering. Have a look at
1924 * include/linux/elf.h for further information. */
1925 e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
1926
1888 /* 1927 /*
1889 * Collect all the non-memory information about the process for the 1928 * Collect all the non-memory information about the process for the
1890 * notes. This also sets up the file header. 1929 * notes. This also sets up the file header.
1891 */ 1930 */
1892 if (!fill_note_info(elf, segs + 1, /* including notes section */ 1931 if (!fill_note_info(elf, e_phnum, &info, cprm->signr, cprm->regs))
1893 &info, cprm->signr, cprm->regs))
1894 goto cleanup; 1932 goto cleanup;
1895 1933
1896 has_dumped = 1; 1934 has_dumped = 1;
@@ -1900,7 +1938,7 @@ static int elf_core_dump(struct coredump_params *cprm)
1900 set_fs(KERNEL_DS); 1938 set_fs(KERNEL_DS);
1901 1939
1902 offset += sizeof(*elf); /* Elf header */ 1940 offset += sizeof(*elf); /* Elf header */
1903 offset += (segs + 1) * sizeof(struct elf_phdr); /* Program headers */ 1941 offset += segs * sizeof(struct elf_phdr); /* Program headers */
1904 foffset = offset; 1942 foffset = offset;
1905 1943
1906 /* Write notes phdr entry */ 1944 /* Write notes phdr entry */
@@ -1926,6 +1964,19 @@ static int elf_core_dump(struct coredump_params *cprm)
1926 */ 1964 */
1927 mm_flags = current->mm->flags; 1965 mm_flags = current->mm->flags;
1928 1966
1967 offset += elf_core_vma_data_size(gate_vma, mm_flags);
1968 offset += elf_core_extra_data_size();
1969 e_shoff = offset;
1970
1971 if (e_phnum == PN_XNUM) {
1972 shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL);
1973 if (!shdr4extnum)
1974 goto end_coredump;
1975 fill_extnum_info(elf, shdr4extnum, e_shoff, segs);
1976 }
1977
1978 offset = dataoff;
1979
1929 size += sizeof(*elf); 1980 size += sizeof(*elf);
1930 if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf))) 1981 if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf)))
1931 goto end_coredump; 1982 goto end_coredump;
@@ -2003,11 +2054,20 @@ static int elf_core_dump(struct coredump_params *cprm)
2003 if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit)) 2054 if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit))
2004 goto end_coredump; 2055 goto end_coredump;
2005 2056
2057 if (e_phnum == PN_XNUM) {
2058 size += sizeof(*shdr4extnum);
2059 if (size > cprm->limit
2060 || !dump_write(cprm->file, shdr4extnum,
2061 sizeof(*shdr4extnum)))
2062 goto end_coredump;
2063 }
2064
2006end_coredump: 2065end_coredump:
2007 set_fs(fs); 2066 set_fs(fs);
2008 2067
2009cleanup: 2068cleanup:
2010 free_note_info(&info); 2069 free_note_info(&info);
2070 kfree(shdr4extnum);
2011 kfree(phdr4note); 2071 kfree(phdr4note);
2012 kfree(elf); 2072 kfree(elf);
2013out: 2073out: