Bug 1525510 - Add a trampoline to call original_init when it's not possible directly. r=froydnj

Differential Revision: https://phabricator.services.mozilla.com/D18807

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Mike Hommey 2019-02-07 16:23:49 +00:00
parent 1d2102e8c5
commit 247022a791
2 changed files with 49 additions and 0 deletions

View File

@ -93,6 +93,7 @@ class ElfRelHackCode_Section : public ElfSection {
parent(e),
relhack_section(relhack_section),
init(init),
init_trampoline(nullptr),
mprotect_cb(mprotect_cb),
sysconf_cb(sysconf_cb) {
std::string file(rundir);
@ -148,6 +149,22 @@ class ElfRelHackCode_Section : public ElfSection {
// Get all relevant sections from the injected code object.
add_code_section(sym->value.getSection());
// If the original init function is located too far away, we're going to
// need to use a trampoline. See comment in inject.c.
// Theoretically, we should check for (init - instr) > 0xffffff, where instr
// is the virtual address of the instruction that calls the original init,
// but we don't have it at this point, so punt to just init.
if (init > 0xffffff && parent.getMachine() == EM_ARM) {
Elf_SymValue *trampoline = symtab->lookup("init_trampoline");
if (!trampoline) {
throw std::runtime_error(
"Couldn't find an 'init_trampoline' symbol in the injected code");
}
init_trampoline = trampoline->value.getSection();
add_code_section(init_trampoline);
}
// Adjust code sections offsets according to their size
std::vector<ElfSection *>::iterator c = code.begin();
(*c)->getShdr().sh_addr = 0;
@ -365,6 +382,12 @@ class ElfRelHackCode_Section : public ElfSection {
ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious();
addr = ehdr->getAddr();
} else if (strcmp(name, "original_init") == 0) {
if (init_trampoline) {
addr = init_trampoline->getAddr();
} else {
addr = init;
}
} else if (strcmp(name, "real_original_init") == 0) {
addr = init;
} else if (relro && strcmp(name, "mprotect_cb") == 0) {
addr = mprotect_cb;
@ -428,6 +451,7 @@ class ElfRelHackCode_Section : public ElfSection {
ElfRelHack_Section &relhack_section;
std::vector<ElfSection *> code;
unsigned int init;
ElfSection *init_trampoline;
unsigned int mprotect_cb;
unsigned int sysconf_cb;
int entry_point;

View File

@ -20,6 +20,31 @@
# define Elf_Addr Elf32_Addr
#endif
// On ARM, PC-relative function calls have a limit in how far they can jump,
// which might not be enough for e.g. libxul.so. The easy way out would be
// to use the long_call attribute, which forces the compiler to generate code
// that can call anywhere, but clang doesn't support the attribute yet
// (https://bugs.llvm.org/show_bug.cgi?id=40623), and while the command-line
// equivalent does exist, it's currently broken
// (https://bugs.llvm.org/show_bug.cgi?id=40624). So we create a manual
// trampoline, corresponding to the code GCC generates with long_call.
#ifdef __arm__
__attribute__((section(".text._init_trampoline"), naked)) int init_trampoline(
int argc, char **argv, char **env) {
__asm__ __volatile__(
// thumb doesn't allow to use r12/ip with ldr, and thus would require an
// additional push/pop to save/restore the modified register, which would
// also change the call into a blx. It's simpler to switch to arm.
".arm\n"
" ldr ip, .LADDR\n"
".LAFTER:\n"
" add ip, pc, ip\n"
" bx ip\n"
".LADDR:\n"
" .word real_original_init-(.LAFTER+8)\n");
}
#endif
extern __attribute__((visibility("hidden"))) void original_init(int argc,
char **argv,
char **env);