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
This commit is contained in:
parent
f827d82cdd
commit
1990ba5601
4 changed files with 25 additions and 15 deletions
|
@ -32,6 +32,10 @@
|
|||
// This file is compiled without stack protection, because it runs before TLS
|
||||
// has been set up.
|
||||
|
||||
__LIBC_HIDDEN__ __attribute__((__naked__)) void __libc_int0x80() {
|
||||
__asm__ volatile("int $0x80; ret");
|
||||
}
|
||||
|
||||
__LIBC_HIDDEN__ void __libc_init_sysinfo() {
|
||||
bool dummy;
|
||||
__libc_sysinfo = reinterpret_cast<void*>(__bionic_getauxval(AT_SYSINFO, dummy));
|
||||
|
|
|
@ -66,9 +66,6 @@ extern "C" {
|
|||
// Use an initializer so __libc_sysinfo will have a fallback implementation
|
||||
// while .preinit_array constructors run.
|
||||
#if defined(__i386__)
|
||||
static __attribute__((__naked__)) void __libc_int0x80() {
|
||||
__asm__ volatile("int $0x80; ret");
|
||||
}
|
||||
__LIBC_HIDDEN__ void* __libc_sysinfo = reinterpret_cast<void*>(__libc_int0x80);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ __LIBC_HIDDEN__ void __libc_init_vdso(libc_globals* globals);
|
|||
|
||||
#if defined(__i386__)
|
||||
__LIBC_HIDDEN__ extern void* __libc_sysinfo;
|
||||
__LIBC_HIDDEN__ void __libc_int0x80();
|
||||
__LIBC_HIDDEN__ void __libc_init_sysinfo();
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in a new issue