carregando arquivo ELF em C no espaço do usuário

Eu estou tentando carregar um arquivo ELF compilado com "gcc -m32 test.c -o test.exe" no Linux em um ambiente x86 de 64 bits. Eu estou tentando carregar esse arquivo de 32 bits (test.exe) dentro de um carregador de ELF de espaço do usuário que tem a seguinte lógica de núcleo (32bit ELF).

O problema é que a chamada para o endereço inicial retornado resulta em um dump de núcleo de falha de segmentação. Aqui está o código:

void *image_load (char *elf_start, unsigned int size)
{
    Elf32_Ehdr      *hdr    = NULL;
    Elf32_Phdr      *phdr   = NULL;
    unsigned char   *start  = NULL;
    Elf32_Addr      taddr   = 0;
    Elf32_Addr      offset  = 0;
    int i = 0;
    unsigned char *exec = NULL;
    Elf32_Addr      estart = 0;

    hdr = (Elf32_Ehdr *) elf_start;

    if(!is_image_valid(hdr)) {
        printk("image_load:: invalid ELF image\n");
        return 0;
    }

    exec = (unsigned char *)mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
                      MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);

    if(!exec) {
        printk("image_load:: error allocating memory\n");
        return 0;
    }

    // Start with clean memory.
    memset(exec,0x0,size);

    phdr = (Elf32_Phdr *)(elf_start + hdr->e_phoff);

    for(i=0; i < hdr->e_phnum; ++i) {
            if(phdr[i].p_type != PT_LOAD) {
                    continue;
            }
            if(phdr[i].p_filesz > phdr[i].p_memsz) {
                    printk("image_load:: p_filesz > p_memsz\n");
                    munmap(exec, size);
                    return 0;
            }
            if(!phdr[i].p_filesz) {
                    continue;
            }

            // p_filesz can be smaller than p_memsz,
            // the difference is zeroe'd out.
            start = (unsigned char *) (elf_start + phdr[i].p_offset);
            // taddr = phdr[i].p_vaddr + (Elf32_Addr)exec;
            if(!estart) {
                estart = phdr[i].p_paddr;
            }
            taddr = (Elf32_Addr)exec + offset + (phdr[i].p_paddr - estart);
            memmove((unsigned char *)taddr,
                    (unsigned char *)start,phdr[i].p_filesz);
            offset += (phdr[i].p_memsz + (phdr[i].p_paddr - estart));

            if(!(phdr[i].p_flags & PF_W)) {
                    // Read-only.
                    mprotect((unsigned char *) taddr, 
                              phdr[i].p_memsz,
                              PROT_READ);
            }

            if(phdr[i].p_flags & PF_X) {
                    // Executable.
                    mprotect((unsigned char *) taddr, 
                            phdr[i].p_memsz,
                            PROT_EXEC);
            }
    }

    return (void *)((hdr->e_entry - estart) + (Elf32_Addr)exec);

}/* image_load */

...
    int (*main)(int, char **)=image_load(...);
    main(argc,argv); // Crash...
...

questionAnswers(3)

yourAnswerToTheQuestion