On a dynamic ELF executable, the current kernel loader gives to the interpreter (in the AUXV vector) the AT_PHDR argument as : offset_of_phdr_in_file + first address. It can be wrong for an executable where the program headers are not located in the first loaded segment. An example of such a binary on x86 can be found on http://www.eleves.ens.fr/~godfroy/sample_code_32 The following patch corrects the behaviour. It applies cleanly on 2.6.26.2 (probably 2.6.26.3 too), and was tested on debian x86_64 (both ELF32 and ELF64) as well as on x86. Normal executables, static and PIE continue to work. Signed-off-by: Quentin Godfroy <godfroy@xxxxxxxxxxxxxx> --- diff -ruNp linux-2.6.26.2/fs/binfmt_elf.c linux-2.6.26.2-patch/fs/binfmt_elf.c --- linux-2.6.26.2/fs/binfmt_elf.c 2008-08-06 18:19:01.000000000 +0200 +++ linux-2.6.26.2-patch/fs/binfmt_elf.c 2008-08-09 02:39:03.033179733 +0200 @@ -133,7 +133,7 @@ static int padzero(unsigned long elf_bss static int create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, - unsigned long load_addr, unsigned long interp_load_addr) + unsigned long phdr_addr, unsigned long interp_load_addr) { unsigned long p = bprm->p; int argc = bprm->argc; @@ -193,7 +193,7 @@ create_elf_tables(struct linux_binprm *b NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP); NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE); NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC); - NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff); + NEW_AUX_ENT(AT_PHDR, phdr_addr); NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr)); NEW_AUX_ENT(AT_PHNUM, exec->e_phnum); NEW_AUX_ENT(AT_BASE, interp_load_addr); @@ -529,7 +529,7 @@ static unsigned long randomize_stack_top static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) { struct file *interpreter = NULL; /* to shut gcc up */ - unsigned long load_addr = 0, load_bias = 0; + unsigned long load_addr = 0, load_bias = 0, phdr_addr = 0; int load_addr_set = 0; char * elf_interpreter = NULL; unsigned long error; @@ -685,14 +685,19 @@ static int load_elf_binary(struct linux_ } elf_ppnt = elf_phdata; - for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) + for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) { + if (elf_ppnt->p_type == PT_PHDR) { + phdr_addr = elf_ppnt->p_vaddr; + continue; + } if (elf_ppnt->p_type == PT_GNU_STACK) { if (elf_ppnt->p_flags & PF_X) executable_stack = EXSTACK_ENABLE_X; else executable_stack = EXSTACK_DISABLE_X; - break; + continue; } + } /* Some simple consistency checks for the interpreter */ if (elf_interpreter) { @@ -861,6 +866,10 @@ static int load_elf_binary(struct linux_ end_code += load_bias; start_data += load_bias; end_data += load_bias; + if (phdr_addr) + phdr_addr += load_bias; + else + phdr_addr = load_addr + loc->elf_ex.e_phoff; /* Calling set_brk effectively mmaps the pages that we need * for the bss and break sections. We must do this before @@ -930,7 +939,7 @@ static int load_elf_binary(struct linux_ compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; retval = create_elf_tables(bprm, &loc->elf_ex, - load_addr, interp_load_addr); + phdr_addr, interp_load_addr); if (retval < 0) { send_sig(SIGKILL, current, 0); goto out; -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html