diff --git a/libc/arch-x86/bionic/__libc_init_sysinfo.cpp b/libc/arch-x86/bionic/__libc_init_sysinfo.cpp index 849d253c9..5c44b4ea4 100644 --- a/libc/arch-x86/bionic/__libc_init_sysinfo.cpp +++ b/libc/arch-x86/bionic/__libc_init_sysinfo.cpp @@ -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(__bionic_getauxval(AT_SYSINFO, dummy)); diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp index 714077606..4f3b4f7d1 100644 --- a/libc/bionic/libc_init_dynamic.cpp +++ b/libc/bionic/libc_init_dynamic.cpp @@ -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(__libc_int0x80); #endif diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h index 21a2a249b..4c4172a88 100644 --- a/libc/private/bionic_globals.h +++ b/libc/private/bionic_globals.h @@ -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 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(__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(&_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(getauxval(AT_PHDR)), getauxval(AT_PHNUM),