Loading...
cache_builder/Optimizers.cpp /dev/null dyld-1340
--- /dev/null
+++ dyld/dyld-1340/cache_builder/Optimizers.cpp
@@ -0,0 +1,551 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+*
+* Copyright (c) 2017 Apple Inc. All rights reserved.
+*
+* @APPLE_LICENSE_HEADER_START@
+*
+* This file contains Original Code and/or Modifications of Original Code
+* as defined in and that are subject to the Apple Public Source License
+* Version 2.0 (the 'License'). You may not use this file except in
+* compliance with the License. Please obtain a copy of the License at
+* http://www.opensource.apple.com/apsl/ and read it before using this
+* file.
+*
+* The Original Code and all software distributed under the License are
+* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+* Please see the License for the specific language governing rights and
+* limitations under the License.
+*
+* @APPLE_LICENSE_HEADER_END@
+*/
+
+#include "Optimizers.h"
+
+using namespace cache_builder;
+
+
+//
+// MARK: --- StubOptimizer methods ---
+//
+
+// These are functions that are interposed by Instruments.app or ASan or libRPAC.dylib
+const char* const neverStubEliminateSymbols[] = {
+    "___bzero",
+    "___cxa_atexit",
+    "___cxa_throw",
+    "__longjmp",
+    "__objc_autoreleasePoolPop",
+    "_accept",
+    "_access",
+    "_asctime",
+    "_asctime_r",
+    "_asprintf",
+    "_atoi",
+    "_atol",
+    "_atoll",
+    "_calloc",
+    "_chmod",
+    "_chown",
+    "_close",
+    "_confstr",
+    "_cp_drawable_present",
+    "_cp_drawable_encode_present",
+    "_cp_drawable_enqueue_present",
+    "_CGImageDestinationFinalize",
+    "_CGImageSourceCreateThumbnailAtIndex",
+    "_CGImageSourceCreateImageAtIndex",
+    "_ctime",
+    "_ctime_r",
+    "_dispatch_after",
+    "_dispatch_after_f",
+    "_dispatch_async",
+    "_dispatch_async_f",
+    "_dispatch_barrier_async_f",
+    "_dispatch_group_async",
+    "_dispatch_group_async_f",
+    "_dispatch_source_set_cancel_handler",
+    "_dispatch_source_set_event_handler",
+    "_dispatch_sync_f",
+    "_dlclose",
+    "_dlopen",
+    "_dlsym",
+    "_dup",
+    "_dup2",
+    "_endgrent",
+    "_endpwent",
+    "_ether_aton",
+    "_ether_hostton",
+    "_ether_line",
+    "_ether_ntoa",
+    "_ether_ntohost",
+    "_fchmod",
+    "_fchown",
+    "_fclose",
+    "_fdopen",
+    "_fflush",
+    "_fopen",
+    "_fork",
+    "_fprintf",
+    "_free",
+    "_freopen",
+    "_frexp",
+    "_frexpf",
+    "_frexpl",
+    "_fscanf",
+    "_fstat",
+    "_fstatfs",
+    "_fstatfs64",
+    "_fsync",
+    "_ftime",
+    "_getaddrinfo",
+    "_getattrlist",
+    "_getcwd",
+    "_getgrent",
+    "_getgrgid",
+    "_getgrgid_r",
+    "_getgrnam",
+    "_getgrnam_r",
+    "_getgroups",
+    "_gethostbyaddr",
+    "_gethostbyname",
+    "_gethostbyname2",
+    "_gethostent",
+    "_getifaddrs",
+    "_getitimer",
+    "_getnameinfo",
+    "_getpass",
+    "_getpeername",
+    "_getpwent",
+    "_getpwnam",
+    "_getpwnam_r",
+    "_getpwuid",
+    "_getpwuid_r",
+    "_getsockname",
+    "_getsockopt",
+    "_gmtime",
+    "_gmtime_r",
+    "_if_indextoname",
+    "_if_nametoindex",
+    "_index",
+    "_inet_aton",
+    "_inet_ntop",
+    "_inet_pton",
+    "_initgroups",
+    "_ioctl",
+    "_lchown",
+    "_lgamma",
+    "_lgammaf",
+    "_lgammal",
+    "_link",
+    "_listxattr",
+    "_localtime",
+    "_localtime_r",
+    "_longjmp",
+    "_lseek",
+    "_lstat",
+    "_malloc",
+    "_malloc_create_zone",
+    "_malloc_default_purgeable_zone",
+    "_malloc_default_zone",
+    "_malloc_destroy_zone",
+    "_malloc_good_size",
+    "_malloc_make_nonpurgeable",
+    "_malloc_make_purgeable",
+    "_malloc_set_zone_name",
+    "_malloc_zone_from_ptr",
+    "_mbsnrtowcs",
+    "_mbsrtowcs",
+    "_mbstowcs",
+    "_memchr",
+    "_memcmp",
+    "_memcpy",
+    "_memmove",
+    "_memset",
+    "_mktime",
+    "_mlock",
+    "_mlockall",
+    "_modf",
+    "_modff",
+    "_modfl",
+    "_mprotect",
+    "_munlock",
+    "_munlockall",
+    "_objc_autoreleasePoolPop",
+    "_objc_setProperty",
+    "_objc_setProperty_atomic",
+    "_objc_setProperty_atomic_copy",
+    "_objc_setProperty_nonatomic",
+    "_objc_setProperty_nonatomic_copy",
+    "_objc_storeStrong",
+    "_open",
+    "_opendir",
+    "_poll",
+    "_posix_memalign",
+    "_pread",
+    "_printf",
+    "_pthread_attr_getdetachstate",
+    "_pthread_attr_getguardsize",
+    "_pthread_attr_getinheritsched",
+    "_pthread_attr_getschedparam",
+    "_pthread_attr_getschedpolicy",
+    "_pthread_attr_getscope",
+    "_pthread_attr_getstack",
+    "_pthread_attr_getstacksize",
+    "_pthread_cond_broadcast",
+    "_pthread_condattr_getpshared",
+    "_pthread_cond_signal",
+    "_pthread_cond_signal_thread_np",
+    "_pthread_cond_timedwait_relative_np",
+    "_pthread_cond_timedwait",
+    "_pthread_cond_wait",
+    "_pthread_create",
+    "_pthread_getschedparam",
+    "_pthread_join",
+    "_pthread_mutex_lock",
+    "_pthread_mutex_unlock",
+    "_pthread_mutexattr_getprioceiling",
+    "_pthread_mutexattr_getprotocol",
+    "_pthread_mutexattr_getpshared",
+    "_pthread_mutexattr_gettype",
+    "_pthread_rwlockattr_getpshared",
+    "_pthread_rwlock_rdlock",
+    "_pthread_rwlock_wrlock",
+    "_pthread_rwlock_unlock",
+    "_pwrite",
+    "_rand_r",
+    "_read",
+    "_readdir",
+    "_readdir_r",
+    "_readv",
+    "_readv$UNIX2003",
+    "_realloc",
+    "_realpath",
+    "_recv",
+    "_recvfrom",
+    "_recvmsg",
+    "_remquo",
+    "_remquof",
+    "_remquol",
+    "_scanf",
+    "_send",
+    "_sendmsg",
+    "_sendto",
+    "_setattrlist",
+    "_setgrent",
+    "_setitimer",
+    "_setlocale",
+    "_setpwent",
+    "_shm_open",
+    "_shm_unlink",
+    "_sigaction",
+    "_sigemptyset",
+    "_sigfillset",
+    "_siglongjmp",
+    "_signal",
+    "_sigpending",
+    "_sigprocmask",
+    "_sigwait",
+    "_snprintf",
+    "_sprintf",
+    "_sqlite3_close",
+    "_sqlite3_close_v2",
+    "_sqlite3_exec",
+    "_sqlite3_exec_b",
+    "_sqlite3_finalize",
+    "_sqlite3_open",
+    "_sqlite3_open16",
+    "_sqlite3_open_v2",
+    "_sqlite3_prepare",
+    "_sqlite3_prepare16",
+    "_sqlite3_prepare16_v2",
+    "_sqlite3_prepare16_v3",
+    "_sqlite3_prepare_v2",
+    "_sqlite3_prepare_v3",
+    "_sqlite3_step",
+    "_sqlite3_wal_autocheckpoint",
+    "_sqlite3_wal_checkpoint",
+    "_sqlite3_wal_checkpoint_v2",
+    "_sqlite3_wal_hook",
+    "_sscanf",
+    "_stat",
+    "_statfs",
+    "_statfs64",
+    "_strcasecmp",
+    "_strcat",
+    "_strchr",
+    "_strcmp",
+    "_strcpy",
+    "_strdup",
+    "_strerror",
+    "_strerror_r",
+    "_strlen",
+    "_strncasecmp",
+    "_strncat",
+    "_strncmp",
+    "_strncpy",
+    "_strptime",
+    "_strtoimax",
+    "_strtol",
+    "_strtoll",
+    "_strtoumax",
+    "_tempnam",
+    "_time",
+    "_times",
+    "_tmpnam",
+    "_tsearch",
+    "_unlink",
+    "_valloc",
+    "_vasprintf",
+    "_vfprintf",
+    "_vfscanf",
+    "_vprintf",
+    "_vscanf",
+    "_vsnprintf",
+    "_vsprintf",
+    "_vsscanf",
+    "_wait",
+    "_wait$UNIX2003",
+    "_wait3",
+    "_wait4",
+    "_waitid",
+    "_waitid$UNIX2003",
+    "_waitpid",
+    "_waitpid$UNIX2003",
+    "_wcslen",
+    "_wcsnrtombs",
+    "_wcsrtombs",
+    "_wcstombs",
+    "_wordexp",
+    "_write",
+    "_writev",
+    "_writev$UNIX2003",
+    "___pwrite_nocancel",
+    "___pwritev_nocancel",
+    "___write_nocancel",
+    "___writev_nocancel",
+    "_pwrite$NOCANCEL",
+    "_pwritev$NOCANCEL",
+    "_write$NOCANCEL",
+    "_writev$NOCANCEL",
+    "_xpc_connection_send_message_with_reply_sync",
+    "__dyld_register_func_for_add_image",
+    "__dyld_register_func_for_remove_image",
+    // <rdar://problem/22050956> always use stubs for C++ symbols that can be overridden
+    "__ZdaPv",
+    "__ZdlPv",
+    "__Znam",
+    "__Znwm",
+    "__ZnwmSt19__type_descriptor_t",
+    "__ZnamSt19__type_descriptor_t",
+
+    nullptr
+};
+
+void StubOptimizer::addDefaultSymbols()
+{
+    for (const char* const* p=neverStubEliminateSymbols; *p != nullptr; ++p)
+        neverStubEliminate.insert(*p);
+}
+
+
+uint64_t StubOptimizer::gotAddrFromArm64Stub(Diagnostics& diag, std::string_view dylibID,
+                                             const uint8_t* stubInstructions, uint64_t stubVMAddr)
+{
+    uint32_t stubInstr1 = *(uint32_t*)stubInstructions;
+    if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) {
+        diag.warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s",
+                     stubInstr1, (uint64_t)stubVMAddr, dylibID.data());
+        return 0;
+    }
+    int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29);
+    if ( stubInstr1 & 0x00800000 )
+        adrpValue |= 0xFFF00000;
+    uint32_t stubInstr2 = *(uint32_t*)(stubInstructions + 4);
+    if ( (stubInstr2 & 0xFFC003FF) != 0xF9400210 ) {
+        diag.warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s",
+                     stubInstr2, (uint64_t)stubVMAddr, dylibID.data());
+        return 0;
+    }
+    uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF);
+    return (stubVMAddr & (-4096)) + adrpValue*4096 + ldrValue*8;
+}
+
+void StubOptimizer::generateArm64StubTo(uint8_t* stubBuffer, uint64_t stubVMAddr,
+                                         uint64_t gotVMAddr, uint64_t targetVMAddr)
+{
+    int64_t adrpDelta = (targetVMAddr & -4096) - (stubVMAddr & -4096);
+    if ( std::abs(adrpDelta) > 0xFFFFE000 ) {
+        generateArm64StubToGOT(stubBuffer, stubVMAddr, gotVMAddr);
+        return;
+    }
+    uint32_t immhi   = (adrpDelta >> 9) & (0x00FFFFE0);
+    uint32_t immlo   = (adrpDelta << 17) & (0x60000000);
+    uint32_t newADRP = (0x90000010) | immlo | immhi;
+    uint32_t off12   = (targetVMAddr & 0xFFF);
+    uint32_t newADD  = (0x91000210) | (off12 << 10);
+
+    uint32_t* stubInstructions = (uint32_t*)stubBuffer;
+    stubInstructions[0] = newADRP;     //      ADRP   X16, target@page
+    stubInstructions[1] = newADD;      //      ADD    X16, X16, target@pageoff
+    stubInstructions[2] = 0xD61F0200;  //      BR     X16
+}
+
+void StubOptimizer::generateArm64StubToGOT(uint8_t* stubBuffer,
+                                           uint64_t stubVMAddr, uint64_t gotVMAddr)
+{
+    int64_t adrpDelta = (gotVMAddr & -4096) - (stubVMAddr & -4096);
+
+    uint32_t immhi   = (adrpDelta >> 9) & (0x00FFFFE0);
+    uint32_t immlo   = (adrpDelta << 17) & (0x60000000);
+    uint32_t newADRP = (0x90000010) | immlo | immhi;
+    uint32_t off12   = (gotVMAddr & 0xFFF) >> 3;
+    uint32_t newLDR  = (0xF9400210) | (off12 << 10);
+
+    uint32_t* stubInstructions = (uint32_t*)stubBuffer;
+    stubInstructions[0] = newADRP;     // ADRP  X16, lazy_pointer@page
+    stubInstructions[1] = newLDR;      // LDR   X16, [X16, lazy_pointer@pageoff]
+    stubInstructions[2] = 0xD61F0200;  // BR    X16
+}
+
+uint64_t StubOptimizer::gotAddrFromArm64_32Stub(Diagnostics& diag, std::string_view dylibID,
+                                                const uint8_t* stubInstructions, uint64_t stubVMAddr)
+{
+    uint32_t stubInstr1 = *(uint32_t*)stubInstructions;
+    if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) {
+        diag.warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s",
+                     stubInstr1, (uint64_t)stubVMAddr, dylibID.data());
+        return 0;
+    }
+    int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29);
+    if ( stubInstr1 & 0x00800000 )
+        adrpValue |= 0xFFF00000;
+    uint32_t stubInstr2 = *(uint32_t*)(stubInstructions + 4);
+    if ( (stubInstr2 & 0xFFC003FF) != 0xB9400210 ) {
+        diag.warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s",
+                     stubInstr2, (uint64_t)stubVMAddr, dylibID.data());
+        return 0;
+    }
+    uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF);
+    return (stubVMAddr & (-4096)) + adrpValue*4096 + ldrValue*4; // LDR Wn has a scale factor of 4
+
+}
+
+void StubOptimizer::generateArm64_32StubTo(uint8_t* stubBuffer,
+                                        uint64_t stubVMAddr, uint64_t targetVMAddr)
+{
+    int64_t adrpDelta = (targetVMAddr & -4096) - (stubVMAddr & -4096);
+
+    uint32_t immhi   = (adrpDelta >> 9) & (0x00FFFFE0);
+    uint32_t immlo   = (adrpDelta << 17) & (0x60000000);
+    uint32_t newADRP = (0x90000010) | immlo | immhi;
+    uint32_t off12   = (targetVMAddr & 0xFFF);
+    uint32_t newADD  = (0x91000210) | (off12 << 10);
+
+    uint32_t* stubInstructions = (uint32_t*)stubBuffer;
+    stubInstructions[0] = newADRP;     //      ADRP   X16, target@page
+    stubInstructions[1] = newADD;      //      ADD    X16, X16, target@pageoff
+    stubInstructions[2] = 0xD61F0200;  //      BR     X16
+}
+
+void StubOptimizer::generateArm64_32StubToGOT(uint8_t* stubBuffer,
+                                              uint64_t stubVMAddr, uint64_t gotVMAddr)
+{
+    int64_t adrpDelta = (gotVMAddr & -4096) - (stubVMAddr & -4096);
+
+    uint32_t immhi   = (adrpDelta >> 9) & (0x00FFFFE0);
+    uint32_t immlo   = (adrpDelta << 17) & (0x60000000);
+    uint32_t newADRP = (0x90000010) | immlo | immhi;
+    uint32_t off12   = (gotVMAddr & 0xFFF) >> 2;
+    uint32_t newLDR  = (0xB9400210) | (off12 << 10);
+
+    uint32_t* stubInstructions = (uint32_t*)stubBuffer;
+    stubInstructions[0] = newADRP;     // ADRP  X16, lazy_pointer@page
+    stubInstructions[1] = newLDR;      // LDR   W16, [X16, lazy_pointer@pageoff]
+    stubInstructions[2] = 0xD61F0200;  // BR    X16
+}
+
+uint64_t StubOptimizer::gotAddrFromArm64eStub(Diagnostics& diag, std::string_view dylibID,
+                                              const uint8_t* stubInstructions, uint64_t stubVMAddr)
+{
+    uint32_t stubInstr1 = *(uint32_t*)stubInstructions;
+    // ADRP  X17, dyld_ImageLoaderCache@page
+    if ( (stubInstr1 & 0x9F00001F) != 0x90000011 ) {
+        diag.warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s",
+                     stubInstr1, (uint64_t)stubVMAddr, dylibID.data());
+        return 0;
+    }
+    int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29);
+    if ( stubInstr1 & 0x00800000 )
+        adrpValue |= 0xFFF00000;
+
+    // ADD     X17, X17, dyld_ImageLoaderCache@pageoff
+    uint32_t stubInstr2 = *(uint32_t*)(stubInstructions + 4);
+    if ( (stubInstr2 & 0xFFC003FF) != 0x91000231 ) {
+        diag.warning("second instruction of stub (0x%08X) is not ADD for stub at addr 0x%0llX in %s",
+                     stubInstr2, (uint64_t)stubVMAddr, dylibID.data());
+        return 0;
+    }
+    uint32_t addValue = ((stubInstr2 & 0x003FFC00) >> 10);
+
+    // LDR   X16, [X17]
+    uint32_t stubInstr3 = *(uint32_t*)(stubInstructions + 8);
+    if ( stubInstr3 != 0xF9400230 ) {
+        diag.warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s",
+                     stubInstr2, (uint64_t)stubVMAddr, dylibID.data());
+        return 0;
+    }
+    return (stubVMAddr & (-4096)) + adrpValue*4096 + addValue;
+}
+
+void StubOptimizer::generateArm64eStubTo(uint8_t* stubBuffer, uint64_t stubVMAddr,
+                                         uint64_t gotVMAddr, uint64_t targetVMAddr)
+{
+    int64_t adrpDelta = (targetVMAddr & -4096) - (stubVMAddr & -4096);
+    if ( std::abs(adrpDelta) > 0xFFFFE000 ) {
+        generateArm64eStubToGOT(stubBuffer, stubVMAddr, gotVMAddr);
+        return;
+    }
+    uint32_t immhi   = (adrpDelta >> 9) & (0x00FFFFE0);
+    uint32_t immlo   = (adrpDelta << 17) & (0x60000000);
+    uint32_t newADRP = (0x90000010) | immlo | immhi;
+    uint32_t off12   = (targetVMAddr & 0xFFF);
+    uint32_t newADD  = (0x91000210) | (off12 << 10);
+
+    uint32_t* stubInstructions = (uint32_t*)stubBuffer;
+    stubInstructions[0] = newADRP;     //      ADRP   X16, target@page
+    stubInstructions[1] = newADD;      //      ADD    X16, X16, target@pageoff
+    stubInstructions[2] = 0xD61F0200;  //      BR     X16
+    stubInstructions[3] = 0xD4200020;  //      TRAP
+}
+
+void StubOptimizer::generateArm64eStubToGOT(uint8_t* stubBuffer,
+                                            uint64_t stubVMAddr, uint64_t gotVMAddr)
+{
+    int64_t adrpDelta = (gotVMAddr & -4096) - (stubVMAddr & -4096);
+
+    uint32_t immhi   = (adrpDelta >> 9) & (0x00FFFFE0);
+    uint32_t immlo   = (adrpDelta << 17) & (0x60000000);
+    uint32_t newADRP = (0x90000011) | immlo | immhi;
+    uint32_t off12   = (gotVMAddr & 0xFFF);
+    uint32_t newADD  = (0x91000231) | (off12 << 10);
+
+    uint32_t* stubInstructions = (uint32_t*)stubBuffer;
+    stubInstructions[0] = newADRP;     // ADRP  X17, lazy_pointer@page
+    stubInstructions[1] = newADD;      // ADD   X17, X17, lazy_pointer@pageoff
+    stubInstructions[2] = 0xF9400230;  // LDR   X16, [X17]
+    stubInstructions[3] = 0xD71F0A11;  // BRAA  X16, X17
+}
+
+//
+// MARK: --- UniquedGOTsOptimizer methods ---
+//
+void UniquedGOTsOptimizer::forEachFunctionVariant(void (^callback)(const CoalescedGOTSection::FunctionVariantInfo& tv, uint64_t gotVMAddr,
+                                                                   dyld3::MachOFile::PointerMetaData pmd)) const
+{
+    this->regularGOTs.forEachFunctionVariant(callback);
+    this->authGOTs.forEachFunctionVariant(callback);
+    this->authPtrs.forEachFunctionVariant(callback);
+}