Skip to content

Commit a12c2b3

Browse files
committed
Now handle binaries that are missing DT_INIT.
In this case, E9Patch will use DT_INIT_ARRAY instead. Fixes #27
1 parent 60c0593 commit a12c2b3

1 file changed

Lines changed: 105 additions & 15 deletions

File tree

src/e9patch/e9elf.cpp

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,83 @@ size_t emitLoaderMap(uint8_t *data, intptr_t addr, size_t len, off_t offset,
365365
return size;
366366
}
367367

368+
/*
369+
* Get the offset for an address.
370+
*/
371+
static off_t addrToOffset(const Binary *B, intptr_t lo, intptr_t hi)
372+
{
373+
const Elf64_Phdr *phdrs =
374+
(const Elf64_Phdr *)(B->patched.bytes + B->elf.ehdr->e_phoff);
375+
size_t phnum = (size_t)B->elf.ehdr->e_phnum;
376+
for (unsigned i = 0; i < phnum; i++)
377+
{
378+
const Elf64_Phdr *phdr = phdrs + i;
379+
switch (phdr->p_type)
380+
{
381+
case PT_LOAD: case PT_GNU_RELRO:
382+
break;
383+
default:
384+
continue;
385+
}
386+
intptr_t base = (intptr_t)phdr->p_vaddr;
387+
size_t size = (size_t)phdr->p_filesz;
388+
off_t offset = (off_t)phdr->p_offset;
389+
if (lo >= base && hi < base + (ssize_t)size)
390+
return offset + (lo - base);
391+
}
392+
return -1;
393+
}
394+
395+
/*
396+
* Replace an init/fini function.
397+
*/
398+
intptr_t replaceInitFini(Binary *B, Elf64_Dyn *dyn_fn,
399+
const Elf64_Dyn *dyn_fn_array, size_t dyn_fn_arraysz,
400+
const Elf64_Dyn *dyn_rela, size_t dyn_relasz, intptr_t entry)
401+
{
402+
uint8_t *data = B->patched.bytes;
403+
if (dyn_fn != nullptr)
404+
{
405+
intptr_t old = (intptr_t)dyn_fn->d_un.d_ptr;
406+
dyn_fn->d_un.d_ptr = (Elf64_Addr)entry;
407+
return old;
408+
}
409+
else if (dyn_fn_array != nullptr && dyn_fn_arraysz > 0)
410+
{
411+
intptr_t ptr = (intptr_t)dyn_fn_array->d_un.d_ptr;
412+
intptr_t offset = addrToOffset(B, ptr, ptr);
413+
if (offset < 0)
414+
return INTPTR_MIN;
415+
Elf64_Addr *fns = (Elf64_Addr *)(data + offset);
416+
intptr_t old = (intptr_t)fns[0];
417+
fns[0] = (Elf64_Addr)entry;
418+
if (dyn_rela != nullptr && dyn_relasz > 0)
419+
{
420+
// fns[0] may be subject to a relocation:
421+
intptr_t rela_ptr = (intptr_t)dyn_rela->d_un.d_ptr;
422+
intptr_t rela_offset = addrToOffset(B, rela_ptr,
423+
rela_ptr + dyn_relasz);
424+
if (rela_offset < 0)
425+
return old;
426+
size_t rela_size = dyn_relasz / sizeof(Elf64_Rela);
427+
Elf64_Rela *rela = (Elf64_Rela *)(B->patched.bytes + rela_offset);
428+
for (size_t i = 0; i < rela_size; i++)
429+
{
430+
if (ELF64_R_TYPE(rela[i].r_info) != R_X86_64_RELATIVE)
431+
continue;
432+
if ((intptr_t)rela[i].r_offset == ptr)
433+
{
434+
old = rela[i].r_addend;
435+
rela[i].r_addend = (Elf64_Addr)entry;
436+
break;
437+
}
438+
}
439+
}
440+
return old;
441+
}
442+
return INTPTR_MIN;
443+
}
444+
368445
/*
369446
* Emit the (modified) ELF binary.
370447
*/
@@ -575,7 +652,10 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
575652

576653
// Step (5): Modify the entry/fini addresses.
577654
Elf64_Phdr *phdr = B->elf.phdr_dynamic;
578-
Elf64_Dyn *dyn_init = nullptr, *dyn_fini = nullptr;
655+
Elf64_Dyn *dyn_init = nullptr, *dyn_init_array = nullptr,
656+
*dyn_fini = nullptr, *dyn_fini_array = nullptr,
657+
*dyn_rela = nullptr;
658+
size_t dyn_init_arraysz = 0, dyn_fini_arraysz = 0, dyn_relasz = 0;
579659
if (phdr != nullptr)
580660
{
581661
config_elf->dynamic = (intptr_t)phdr->p_vaddr;
@@ -588,11 +668,21 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
588668
switch (dynamic[i].d_tag)
589669
{
590670
case DT_INIT:
591-
dyn_init = dynamic + i;
592-
break;
671+
dyn_init = dynamic + i; break;
672+
case DT_INIT_ARRAY:
673+
dyn_init_array = dynamic + i; break;
674+
case DT_INIT_ARRAYSZ:
675+
dyn_init_arraysz = (size_t)dynamic[i].d_un.d_val; break;
593676
case DT_FINI:
594-
dyn_fini = dynamic + i;
595-
break;
677+
dyn_fini = dynamic + i; break;
678+
case DT_FINI_ARRAY:
679+
dyn_fini_array = dynamic + i; break;
680+
case DT_FINI_ARRAYSZ:
681+
dyn_fini_arraysz = (size_t)dynamic[i].d_un.d_val; break;
682+
case DT_RELA:
683+
dyn_rela = dynamic + i; break;
684+
case DT_RELASZ:
685+
dyn_relasz = (size_t)dynamic[i].d_un.d_val; break;
596686
default:
597687
break;
598688
}
@@ -609,22 +699,22 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
609699
break;
610700
}
611701
case MODE_ELF_DSO:
612-
if (dyn_init == nullptr)
613-
error("failed to replace DT_INIT entry; no DT_INIT entry "
614-
"was found");
615-
config->entry = (intptr_t)dyn_init->d_un.d_ptr;
616-
dyn_init->d_un.d_ptr = (Elf64_Addr)entry;
702+
config->entry = replaceInitFini(B, dyn_init, dyn_init_array,
703+
dyn_init_arraysz, dyn_rela, dyn_relasz, entry);
704+
if (config->entry == INTPTR_MIN)
705+
error("failed to replace entry point; no DT_INIT or "
706+
"DT_INIT_ARRAY entry found");
617707
break;
618708
default:
619709
error("invalid mode");
620710
}
621711
if (fini != 0x0)
622712
{
623-
if (dyn_fini == nullptr)
624-
error("failed to replace DT_FINI entry; no DT_FINI entry was "
625-
"found");
626-
config->fini = (intptr_t)dyn_fini->d_un.d_ptr;
627-
dyn_fini->d_un.d_ptr = (Elf64_Addr)fini;
713+
config->fini = replaceInitFini(B, dyn_fini, dyn_fini_array,
714+
dyn_fini_arraysz, dyn_rela, dyn_relasz, fini);
715+
if (config->fini == INTPTR_MIN)
716+
error("failed to replace finalization point; no DT_FINI or "
717+
"DT_FINI_ARRAY entry found");
628718
}
629719

630720
// Step (6): Modify the PHDR to load the loader.

0 commit comments

Comments
 (0)