mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 08:42:13 +00:00
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:
parent
1d2102e8c5
commit
247022a791
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user