diff options
author | Ryan Prichard <rprichard@google.com> | 2019-02-07 21:31:31 -0800 |
---|---|---|
committer | Ryan Prichard <rprichard@google.com> | 2019-02-07 21:48:42 -0800 |
commit | 1990ba5601f5230851d1e2112c28bebbf78c3f40 (patch) | |
tree | 89ac1bae3d31e58b42324787a2bf038ffe0b59f4 /linker | |
parent | f827d82cdde0fe7f11015fb5ead39bac861ab6bd (diff) | |
download | android_bionic-1990ba5601f5230851d1e2112c28bebbf78c3f40.tar.gz android_bionic-1990ba5601f5230851d1e2112c28bebbf78c3f40.tar.bz2 android_bionic-1990ba5601f5230851d1e2112c28bebbf78c3f40.zip |
Fix linker self-exec detection
When the linker is invoked on itself, (`linker64 /system/bin/linker64`),
the linker prints an error, because self-invocation isn't allowed. The
current method for detecting self-invocation fails because the second
linker instance can crash in a constructor function before reaching
__linker_init.
Fix the problem by moving the error check into a constructor function,
which finishes initializing libc sufficiently to call async_safe_fatal.
The only important thing missing is __libc_sysinfo on 32-bit x86. The aux
vector isn't readily accessible, so use the fallback int 0x80.
Bug: http://b/123637025
Test: bionic unit tests (32-bit x86)
Change-Id: I8be6369e8be3938906628ae1f82be13e6c510119
Diffstat (limited to 'linker')
-rw-r--r-- | linker/linker_main.cpp | 32 |
1 files changed, 20 insertions, 12 deletions
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp index 7486cd79b..9129a529f 100644 --- a/linker/linker_main.cpp +++ b/linker/linker_main.cpp @@ -553,6 +553,24 @@ static void get_elf_base_from_phdr(const ElfW(Phdr)* phdr_table, size_t phdr_cou async_safe_fatal("Could not find a PHDR: broken executable?"); } +// Detect an attempt to run the linker on itself. e.g.: +// /system/bin/linker64 /system/bin/linker64 +// Use priority-1 to run this constructor before other constructors. +__attribute__((constructor(1))) static void detect_self_exec() { + // Normally, the linker initializes the auxv global before calling its + // constructors. If the linker loads itself, though, the first loader calls + // the second loader's constructors before calling __linker_init. + if (__libc_shared_globals()->auxv != nullptr) { + return; + } +#if defined(__i386__) + // We don't have access to the auxv struct from here, so use the int 0x80 + // fallback. + __libc_sysinfo = reinterpret_cast<void*>(__libc_int0x80); +#endif + __linker_error("error: linker cannot load itself\n"); +} + static ElfW(Addr) __attribute__((noinline)) __linker_init_post_relocation(KernelArgumentBlock& args, soinfo& linker_so); @@ -575,18 +593,8 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { // another program), AT_BASE is 0. ElfW(Addr) linker_addr = getauxval(AT_BASE); if (linker_addr == 0) { - // Detect an attempt to run the linker on itself (e.g. - // `linker64 /system/bin/linker64`). If the kernel loaded this instance of - // the linker, then AT_ENTRY will refer to &_start. If it doesn't, then - // something else must have loaded this instance of the linker. It's - // simpler if we only allow one copy of the linker to be loaded at a time. - if (getauxval(AT_ENTRY) != reinterpret_cast<uintptr_t>(&_start)) { - // The first linker already relocated this one and set up TLS, so we don't - // need further libc initialization. - __linker_error("error: linker cannot load itself\n"); - } - // Otherwise, the AT_PHDR and AT_PHNUM aux values describe this linker - // instance, so use the phdr to find the linker's base address. + // The AT_PHDR and AT_PHNUM aux values describe this linker instance, so use + // the phdr to find the linker's base address. ElfW(Addr) load_bias; get_elf_base_from_phdr( reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR)), getauxval(AT_PHNUM), |