Here is a patch that is makes fs/binfmt_elf.c more careful in
distinguishing betweeen the ELF executable "page size" and the Linux
kernel's page size. So far, binfmt_elf.c always aligned everything
according to ELF_EXEC_PAGESIZE, but this doesn't work right if the ELF
executable's page size is smaller than the host's page size. This
happens, for example, when loading an IA-32 binary on an IA-64 host
with a page size >4KB. Of course, it's not possible to use the normal
mmap() in this case either. Thus, the patch also provides the means
to use an alternate mapping function via macro "elf_map".
I believe this patch is safe for all platforms, but I'm not 100% sure,
so I thought it would be good to put it up for testing/discussion.
--david
diff -urN linux-2.4.0-test4/fs/binfmt_elf.c linux-2.4.0-test4-lia/fs/binfmt_elf.c
--- linux-2.4.0-test4/fs/binfmt_elf.c Tue Jul 11 15:43:45 2000
+++ linux-2.4.0-test4-lia/fs/binfmt_elf.c Thu Jul 13 15:54:09 2000
@@ -41,6 +41,7 @@
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs);
static int load_elf_library(struct file*);
+static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int);
extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);
extern void dump_thread(struct pt_regs *, struct user *);
@@ -59,9 +60,15 @@
#define elf_core_dump NULL
#endif
-#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_EXEC_PAGESIZE-1))
-#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_EXEC_PAGESIZE-1))
-#define ELF_PAGEALIGN(_v) (((_v) + ELF_EXEC_PAGESIZE - 1) & ~(ELF_EXEC_PAGESIZE - 1))
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+# define ELF_MIN_ALIGN ELF_EXEC_PAGESIZE
+#else
+# define ELF_MIN_ALIGN PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))
+#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))
static struct linux_binfmt elf_format = {
NULL, THIS_MODULE, load_elf_binary, load_elf_library, elf_core_dump, ELF_EXEC_PAGESIZE
@@ -89,7 +96,7 @@
nbyte = ELF_PAGEOFFSET(elf_bss);
if (nbyte) {
- nbyte = ELF_EXEC_PAGESIZE - nbyte;
+ nbyte = ELF_MIN_ALIGN - nbyte;
clear_user((void *) elf_bss, nbyte);
}
}
@@ -198,6 +205,22 @@
return sp;
}
+#ifndef elf_map
+
+static inline unsigned long
+elf_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type)
+{
+ unsigned long map_addr;
+
+ down(¤t->mm->mmap_sem);
+ map_addr = do_mmap(filep, ELF_PAGESTART(addr),
+ eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type,
+ eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));
+ up(¤t->mm->mmap_sem);
+ return(map_addr);
+}
+
+#endif /* !elf_map */
/* This is much more generalized than the library routine read function,
so we keep this separate. Technically the library read function
@@ -235,7 +258,7 @@
/* Now read in all of the header information */
size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;
- if (size > ELF_EXEC_PAGESIZE)
+ if (size > ELF_MIN_ALIGN)
goto out;
elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
if (!elf_phdata)
@@ -261,16 +284,7 @@
if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
elf_type |= MAP_FIXED;
- down(¤t->mm->mmap_sem);
- map_addr = do_mmap(interpreter,
- load_addr + ELF_PAGESTART(vaddr),
- eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr),
- elf_prot,
- elf_type,
- eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));
- up(¤t->mm->mmap_sem);
- if (map_addr > -1024UL) /* Real error */
- goto out_close;
+ map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type);
if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
load_addr = map_addr - ELF_PAGESTART(vaddr);
@@ -304,7 +318,7 @@
* last bss page.
*/
padzero(elf_bss);
- elf_bss = ELF_PAGESTART(elf_bss + ELF_EXEC_PAGESIZE - 1); /* What we have mapped so far */
+ elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1); /* What we have mapped so far */
/* Map the last of the bss segment */
if (last_bss > elf_bss)
@@ -356,7 +370,7 @@
flush_icache_range((unsigned long)addr,
(unsigned long)addr + text_data);
- do_brk(ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1),
+ do_brk(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1),
interp_ex->a_bss);
elf_entry = interp_ex->a_entry;
@@ -607,13 +635,7 @@
elf_flags |= MAP_FIXED;
}
- down(¤t->mm->mmap_sem);
- error = do_mmap(bprm->file, ELF_PAGESTART(load_bias + vaddr),
- (elf_ppnt->p_filesz +
- ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
- elf_prot, elf_flags, (elf_ppnt->p_offset -
- ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
- up(¤t->mm->mmap_sem);
+ error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
if (!load_addr_set) {
load_addr_set = 1;
@@ -785,7 +807,7 @@
/* Now read in all of the header information */
j = sizeof(struct elf_phdr) * elf_ex.e_phnum;
- if (j > ELF_EXEC_PAGESIZE)
+ if (j > ELF_MIN_ALIGN)
goto out;
error = -ENOMEM;
@@ -824,8 +846,7 @@
elf_bss = k;
padzero(elf_bss);
- len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr +
- ELF_EXEC_PAGESIZE - 1);
+ len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr + ELF_MIN_ALIGN - 1);
bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
if (bss > len)
do_brk(len, bss - len);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/
This archive was generated by hypermail 2b29 : Sat Jul 15 2000 - 21:00:21 EST