/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "chre/platform/shared/nanoapp_loader.h" #include "chre.h" #include "chre/platform/assert.h" #include "chre/platform/fatal_error.h" #include "chre/platform/shared/debug_dump.h" #include "chre/platform/shared/memory.h" #include "chre/platform/shared/nanoapp/tokenized_log.h" #include "chre/platform/shared/platform_cache_management.h" #include "chre/util/dynamic_vector.h" #include "chre/util/macros.h" #ifdef CHREX_SYMBOL_EXTENSIONS #include "chre/extensions/platform/symbol_list.h" #endif #ifndef CHRE_LOADER_ARCH #define CHRE_LOADER_ARCH EM_ARM #endif // CHRE_LOADER_ARCH namespace chre { namespace { using ElfHeader = ElfW(Ehdr); using ProgramHeader = ElfW(Phdr); struct ExportedData { void *data; const char *dataName; }; //! If non-null, a nanoapp is currently being loaded. This allows certain C //! functions to access the nanoapp if called during static init. NanoappLoader *gCurrentlyLoadingNanoapp = nullptr; //! Indicates whether a failure occurred during static initialization. bool gStaticInitFailure = false; void deleteOpOverride(void* /* ptr */, unsigned int size) { FATAL_ERROR("Nanoapp: delete(void *, unsigned int) override : sz = %u", size); } #ifdef __clang__ void deleteOp2Override(void*) { FATAL_ERROR("Nanoapp: delete(void *)"); } #endif int atexitInternal(struct AtExitCallback &cb) { if (gCurrentlyLoadingNanoapp == nullptr) { CHRE_ASSERT_LOG(false, "atexit is only supported during static initialization."); return -1; } gCurrentlyLoadingNanoapp->registerAtexitFunction(cb); return 0; } // atexit is used to register functions that must be called when a binary is // removed from the system. The call back function has an arg (void *) int cxaAtexitOverride(void (*func)(void *), void *arg, void *dso) { LOGV("__cxa_atexit invoked with %p, %p, %p", func, arg, dso); struct AtExitCallback cb(func, arg); atexitInternal(cb); return 0; } // The call back function has no arg. int atexitOverride(void (*func)(void)) { LOGV("atexit invoked with %p", func); struct AtExitCallback cb(func); atexitInternal(cb); return 0; } // The following functions from the cmath header need to be overridden, since // they're overloaded functions, and we need to specify explicit types of the // template for the compiler. double frexpOverride(double value, int *exp) { return frexp(value, exp); } double fmaxOverride(double x, double y) { return fmax(x, y); } double fminOverride(double x, double y) { return fmin(x, y); } double floorOverride(double value) { return floor(value); } double ceilOverride(double value) { return ceil(value); } double sinOverride(double rad) { return sin(rad); } double asinOverride(double val) { return asin(val); } double atan2Override(double y, double x) { return atan2(y, x); } double cosOverride(double rad) { return cos(rad); } double sqrtOverride(double val) { return sqrt(val); } double roundOverride(double val) { return round(val); } // This function is required to be exposed to nanoapps to handle errors from // invoking virtual functions. void __cxa_pure_virtual(void) { chreAbort(CHRE_ERROR /* abortCode */); } // TODO(karthikmb/stange): While this array was hand-coded for simple // "hello-world" prototyping, the list of exported symbols must be // generated to minimize runtime errors and build breaks. // clang-format off // Disable deprecation warning so that deprecated symbols in the array // can be exported for older nanoapps and tests. CHRE_DEPRECATED_PREAMBLE const ExportedData kExportedData[] = { /* libmath overrides and symbols */ ADD_EXPORTED_SYMBOL(asinOverride, "asin"), ADD_EXPORTED_SYMBOL(atan2Override, "atan2"), ADD_EXPORTED_SYMBOL(cosOverride, "cos"), ADD_EXPORTED_SYMBOL(floorOverride, "floor"), ADD_EXPORTED_SYMBOL(ceilOverride, "ceil"), ADD_EXPORTED_SYMBOL(fmaxOverride, "fmax"), ADD_EXPORTED_SYMBOL(fminOverride, "fmin"), ADD_EXPORTED_SYMBOL(frexpOverride, "frexp"), ADD_EXPORTED_SYMBOL(roundOverride, "round"), ADD_EXPORTED_SYMBOL(sinOverride, "sin"), ADD_EXPORTED_SYMBOL(sqrtOverride, "sqrt"), ADD_EXPORTED_C_SYMBOL(acosf), ADD_EXPORTED_C_SYMBOL(asinf), ADD_EXPORTED_C_SYMBOL(atan2f), ADD_EXPORTED_C_SYMBOL(ceilf), ADD_EXPORTED_C_SYMBOL(cosf), ADD_EXPORTED_C_SYMBOL(expf), ADD_EXPORTED_C_SYMBOL(fabsf), ADD_EXPORTED_C_SYMBOL(floorf), ADD_EXPORTED_C_SYMBOL(fmaxf), ADD_EXPORTED_C_SYMBOL(fminf), ADD_EXPORTED_C_SYMBOL(fmodf), ADD_EXPORTED_C_SYMBOL(ldexpf), ADD_EXPORTED_C_SYMBOL(log10f), ADD_EXPORTED_C_SYMBOL(log1pf), ADD_EXPORTED_C_SYMBOL(log2f), ADD_EXPORTED_C_SYMBOL(logf), ADD_EXPORTED_C_SYMBOL(lrintf), ADD_EXPORTED_C_SYMBOL(lroundf), ADD_EXPORTED_C_SYMBOL(powf), ADD_EXPORTED_C_SYMBOL(remainderf), ADD_EXPORTED_C_SYMBOL(roundf), ADD_EXPORTED_C_SYMBOL(sinf), ADD_EXPORTED_C_SYMBOL(sqrtf), ADD_EXPORTED_C_SYMBOL(tanf), ADD_EXPORTED_C_SYMBOL(tanhf), /* libc overrides and symbols */ ADD_EXPORTED_C_SYMBOL(__cxa_pure_virtual), ADD_EXPORTED_SYMBOL(cxaAtexitOverride, "__cxa_atexit"), ADD_EXPORTED_SYMBOL(atexitOverride, "atexit"), ADD_EXPORTED_SYMBOL(deleteOpOverride, "_ZdlPvj"), #ifdef __clang__ ADD_EXPORTED_SYMBOL(deleteOp2Override, "_ZdlPv"), #endif ADD_EXPORTED_C_SYMBOL(dlsym), ADD_EXPORTED_C_SYMBOL(isgraph), ADD_EXPORTED_C_SYMBOL(memcmp), ADD_EXPORTED_C_SYMBOL(memcpy), ADD_EXPORTED_C_SYMBOL(memmove), ADD_EXPORTED_C_SYMBOL(memset), ADD_EXPORTED_C_SYMBOL(snprintf), ADD_EXPORTED_C_SYMBOL(strcmp), ADD_EXPORTED_C_SYMBOL(strlen), ADD_EXPORTED_C_SYMBOL(strncmp), ADD_EXPORTED_C_SYMBOL(tolower), /* CHRE symbols */ ADD_EXPORTED_C_SYMBOL(chreAbort), ADD_EXPORTED_C_SYMBOL(chreAudioConfigureSource), ADD_EXPORTED_C_SYMBOL(chreAudioGetSource), ADD_EXPORTED_C_SYMBOL(chreBleGetCapabilities), ADD_EXPORTED_C_SYMBOL(chreBleGetFilterCapabilities), ADD_EXPORTED_C_SYMBOL(chreBleFlushAsync), ADD_EXPORTED_C_SYMBOL(chreBleStartScanAsync), ADD_EXPORTED_C_SYMBOL(chreBleStartScanAsyncV1_9), ADD_EXPORTED_C_SYMBOL(chreBleStopScanAsync), ADD_EXPORTED_C_SYMBOL(chreBleStopScanAsyncV1_9), ADD_EXPORTED_C_SYMBOL(chreBleReadRssiAsync), ADD_EXPORTED_C_SYMBOL(chreBleGetScanStatus), ADD_EXPORTED_C_SYMBOL(chreConfigureDebugDumpEvent), ADD_EXPORTED_C_SYMBOL(chreConfigureHostSleepStateEvents), ADD_EXPORTED_C_SYMBOL(chreConfigureNanoappInfoEvents), ADD_EXPORTED_C_SYMBOL(chreDebugDumpLog), ADD_EXPORTED_C_SYMBOL(chreGetApiVersion), ADD_EXPORTED_C_SYMBOL(chreGetCapabilities), ADD_EXPORTED_C_SYMBOL(chreGetMessageToHostMaxSize), ADD_EXPORTED_C_SYMBOL(chreGetAppId), ADD_EXPORTED_C_SYMBOL(chreGetInstanceId), ADD_EXPORTED_C_SYMBOL(chreGetEstimatedHostTimeOffset), ADD_EXPORTED_C_SYMBOL(chreGetNanoappInfoByAppId), ADD_EXPORTED_C_SYMBOL(chreGetNanoappInfoByInstanceId), ADD_EXPORTED_C_SYMBOL(chreGetPlatformId), ADD_EXPORTED_C_SYMBOL(chreGetSensorInfo), ADD_EXPORTED_C_SYMBOL(chreGetSensorSamplingStatus), ADD_EXPORTED_C_SYMBOL(chreGetTime), ADD_EXPORTED_C_SYMBOL(chreGetVersion), ADD_EXPORTED_C_SYMBOL(chreGnssConfigurePassiveLocationListener), ADD_EXPORTED_C_SYMBOL(chreGnssGetCapabilities), ADD_EXPORTED_C_SYMBOL(chreGnssLocationSessionStartAsync), ADD_EXPORTED_C_SYMBOL(chreGnssLocationSessionStopAsync), ADD_EXPORTED_C_SYMBOL(chreGnssMeasurementSessionStartAsync), ADD_EXPORTED_C_SYMBOL(chreGnssMeasurementSessionStopAsync), ADD_EXPORTED_C_SYMBOL(chreHeapAlloc), ADD_EXPORTED_C_SYMBOL(chreHeapFree), ADD_EXPORTED_C_SYMBOL(chreIsHostAwake), ADD_EXPORTED_C_SYMBOL(chreLog), ADD_EXPORTED_C_SYMBOL(chreSendEvent), ADD_EXPORTED_C_SYMBOL(chreSendMessageToHost), ADD_EXPORTED_C_SYMBOL(chreSendMessageToHostEndpoint), ADD_EXPORTED_C_SYMBOL(chreSendMessageWithPermissions), ADD_EXPORTED_C_SYMBOL(chreSendReliableMessageAsync), ADD_EXPORTED_C_SYMBOL(chreSensorConfigure), ADD_EXPORTED_C_SYMBOL(chreSensorConfigureBiasEvents), ADD_EXPORTED_C_SYMBOL(chreSensorFind), ADD_EXPORTED_C_SYMBOL(chreSensorFindDefault), ADD_EXPORTED_C_SYMBOL(chreSensorFlushAsync), ADD_EXPORTED_C_SYMBOL(chreSensorGetThreeAxisBias), ADD_EXPORTED_C_SYMBOL(chreTimerCancel), ADD_EXPORTED_C_SYMBOL(chreTimerSet), ADD_EXPORTED_C_SYMBOL(chreUserSettingConfigureEvents), ADD_EXPORTED_C_SYMBOL(chreUserSettingGetState), ADD_EXPORTED_C_SYMBOL(chreWifiConfigureScanMonitorAsync), ADD_EXPORTED_C_SYMBOL(chreWifiGetCapabilities), ADD_EXPORTED_C_SYMBOL(chreWifiRequestScanAsync), ADD_EXPORTED_C_SYMBOL(chreWifiRequestRangingAsync), ADD_EXPORTED_C_SYMBOL(chreWifiNanRequestRangingAsync), ADD_EXPORTED_C_SYMBOL(chreWifiNanSubscribe), ADD_EXPORTED_C_SYMBOL(chreWifiNanSubscribeCancel), ADD_EXPORTED_C_SYMBOL(chreWwanGetCapabilities), ADD_EXPORTED_C_SYMBOL(chreWwanGetCellInfoAsync), ADD_EXPORTED_C_SYMBOL(platform_chreDebugDumpVaLog), #ifdef CHRE_NANOAPP_TOKENIZED_LOGGING_SUPPORT_ENABLED ADD_EXPORTED_C_SYMBOL(platform_chrePwTokenizedLog), #endif // CHRE_NANOAPP_TOKENIZED_LOGGING_SUPPORT_ENABLED ADD_EXPORTED_C_SYMBOL(chreConfigureHostEndpointNotifications), ADD_EXPORTED_C_SYMBOL(chrePublishRpcServices), ADD_EXPORTED_C_SYMBOL(chreGetHostEndpointInfo), }; CHRE_DEPRECATED_EPILOGUE // clang-format on } // namespace NanoappLoader *NanoappLoader::create(void *elfInput, bool mapIntoTcm) { if (elfInput == nullptr) { LOGE("Elf header must not be null"); return nullptr; } auto *loader = static_cast(memoryAllocDram(sizeof(NanoappLoader))); if (loader == nullptr) { LOG_OOM(); return nullptr; } new (loader) NanoappLoader(elfInput, mapIntoTcm); if (loader->open()) { return loader; } // Call the destructor explicitly as memoryFreeDram() never calls it. loader->~NanoappLoader(); memoryFreeDram(loader); return nullptr; } void NanoappLoader::destroy(NanoappLoader *loader) { loader->close(); // TODO(b/151847750): Modify utilities to support free'ing from regions other // than SRAM. loader->~NanoappLoader(); memoryFreeDram(loader); } void *NanoappLoader::findExportedSymbol(const char *name) { size_t nameLen = strlen(name); for (size_t i = 0; i < ARRAY_SIZE(kExportedData); i++) { if (nameLen == strlen(kExportedData[i].dataName) && strncmp(name, kExportedData[i].dataName, nameLen) == 0) { return kExportedData[i].data; } } #ifdef CHREX_SYMBOL_EXTENSIONS for (size_t i = 0; i < ARRAY_SIZE(kVendorExportedData); i++) { if (nameLen == strlen(kVendorExportedData[i].dataName) && strncmp(name, kVendorExportedData[i].dataName, nameLen) == 0) { return kVendorExportedData[i].data; } } #endif return nullptr; } bool NanoappLoader::open() { if (!copyAndVerifyHeaders()) { LOGE("Failed to copy and verify elf headers"); } else if (!createMappings()) { LOGE("Failed to create mappings"); } else if (!fixRelocations()) { LOGE("Failed to fix relocations"); } else if (!resolveGot()) { LOGE("Failed to resolve GOT"); } else { // Wipe caches before calling init array to ensure initializers are not in // the data cache. wipeSystemCaches(reinterpret_cast(mMapping), mMemorySpan); if (!callInitArray()) { LOGE("Failed to perform static init"); } else { return true; } } freeAllocatedData(); return false; } void NanoappLoader::close() { callAtexitFunctions(); callTerminatorArray(); freeAllocatedData(); } void *NanoappLoader::findSymbolByName(const char *name) { for (size_t offset = 0; offset < mDynamicSymbolTableSize; offset += sizeof(ElfSym)) { ElfSym *currSym = reinterpret_cast(mDynamicSymbolTablePtr + offset); const char *symbolName = getDataName(currSym); if (strncmp(symbolName, name, strlen(name)) == 0) { return getSymbolTarget(currSym); } } return nullptr; } void NanoappLoader::registerAtexitFunction(struct AtExitCallback &cb) { if (!mAtexitFunctions.push_back(cb)) { LOG_OOM(); gStaticInitFailure = true; } } void NanoappLoader::mapBss(const ProgramHeader *hdr) { // if the memory size of this segment exceeds the file size zero fill the // difference. LOGV("Program Hdr mem sz: %u file size: %u", hdr->p_memsz, hdr->p_filesz); if (hdr->p_memsz > hdr->p_filesz) { ElfAddr endOfFile = hdr->p_vaddr + hdr->p_filesz + mLoadBias; ElfAddr endOfMem = hdr->p_vaddr + hdr->p_memsz + mLoadBias; if (endOfMem > endOfFile) { auto deltaMem = endOfMem - endOfFile; LOGV("Zeroing out %u from page %x", deltaMem, endOfFile); memset(reinterpret_cast(endOfFile), 0, deltaMem); } } } bool NanoappLoader::callInitArray() { bool success = true; // Sets global variable used by atexit in case it's invoked as part of // initializing static data. gCurrentlyLoadingNanoapp = this; // TODO(b/151847750): ELF can have other sections like .init, .preinit, .fini // etc. Be sure to look for those if they end up being something that should // be supported for nanoapps. for (size_t i = 0; i < mNumSectionHeaders; ++i) { const char *name = getSectionHeaderName(mSectionHeadersPtr[i].sh_name); if (strncmp(name, kInitArrayName, strlen(kInitArrayName)) == 0) { LOGV("Invoking init function"); uintptr_t initArray = static_cast(mLoadBias + mSectionHeadersPtr[i].sh_addr); uintptr_t offset = 0; while (offset < mSectionHeadersPtr[i].sh_size) { ElfAddr *funcPtr = reinterpret_cast(initArray + offset); uintptr_t initFunction = static_cast(*funcPtr); ((void (*)())initFunction)(); offset += sizeof(initFunction); if (gStaticInitFailure) { success = false; break; } } break; } } //! Reset global state so it doesn't leak into the next load. gCurrentlyLoadingNanoapp = nullptr; gStaticInitFailure = false; return success; } uintptr_t NanoappLoader::roundDownToAlign(uintptr_t virtualAddr, size_t alignment) { return alignment == 0 ? virtualAddr : virtualAddr & -alignment; } void NanoappLoader::freeAllocatedData() { if (mIsTcmBinary) { nanoappBinaryFree(mMapping); } else { nanoappBinaryDramFree(mMapping); } memoryFreeDram(mSectionHeadersPtr); memoryFreeDram(mSectionNamesPtr); mDynamicSymbolTablePtr = nullptr; mDynamicSymbolTableSize = 0; } bool NanoappLoader::verifyElfHeader() { ElfHeader *elfHeader = getElfHeader(); if (elfHeader != nullptr && (elfHeader->e_ident[EI_MAG0] == ELFMAG0) && (elfHeader->e_ident[EI_MAG1] == ELFMAG1) && (elfHeader->e_ident[EI_MAG2] == ELFMAG2) && (elfHeader->e_ident[EI_MAG3] == ELFMAG3) && (elfHeader->e_ehsize == sizeof(ElfHeader)) && (elfHeader->e_phentsize == sizeof(ProgramHeader)) && (elfHeader->e_shentsize == sizeof(SectionHeader)) && (elfHeader->e_shstrndx < elfHeader->e_shnum) && (elfHeader->e_version == EV_CURRENT) && (elfHeader->e_machine == CHRE_LOADER_ARCH) && (elfHeader->e_type == ET_DYN)) { return true; } return false; } bool NanoappLoader::verifyProgramHeaders() { // This is a minimal check for now - // there should be at least one load segment. for (size_t i = 0; i < getProgramHeaderArraySize(); ++i) { if (getProgramHeaderArray()[i].p_type == PT_LOAD) { return true; } } LOGE("No load segment found"); return false; } const char *NanoappLoader::getSectionHeaderName(size_t headerOffset) { if (headerOffset == 0) { return ""; } return &mSectionNamesPtr[headerOffset]; } NanoappLoader::SectionHeader *NanoappLoader::getSectionHeader( const char *headerName) { SectionHeader *rv = nullptr; for (size_t i = 0; i < mNumSectionHeaders; ++i) { const char *name = getSectionHeaderName(mSectionHeadersPtr[i].sh_name); if (strncmp(name, headerName, strlen(headerName)) == 0) { rv = &mSectionHeadersPtr[i]; break; } } return rv; } ProgramHeader *NanoappLoader::getProgramHeaderArray() { return reinterpret_cast(mBinary + getElfHeader()->e_phoff); } size_t NanoappLoader::getProgramHeaderArraySize() { return getElfHeader()->e_phnum; } bool NanoappLoader::verifyDynamicTables() { SectionHeader *dynamicStringTablePtr = getSectionHeader(kDynstrTableName); if (dynamicStringTablePtr == nullptr) { LOGE("Failed to find table %s", kDynstrTableName); return false; } mDynamicStringTablePtr = reinterpret_cast(mBinary + dynamicStringTablePtr->sh_offset); SectionHeader *dynamicSymbolTablePtr = getSectionHeader(kDynsymTableName); if (dynamicSymbolTablePtr == nullptr) { LOGE("Failed to find table %s", kDynsymTableName); return false; } mDynamicSymbolTablePtr = (mBinary + dynamicSymbolTablePtr->sh_offset); mDynamicSymbolTableSize = dynamicSymbolTablePtr->sh_size; return true; } bool NanoappLoader::copyAndVerifyHeaders() { // Verify the ELF Header if (!verifyElfHeader()) { LOGE("ELF header is invalid"); return false; } // Verify Program Headers if (!verifyProgramHeaders()) { LOGE("Program headers are invalid"); return false; } // Load Section Headers ElfHeader *elfHeader = getElfHeader(); size_t sectionHeaderSizeBytes = sizeof(SectionHeader) * elfHeader->e_shnum; mSectionHeadersPtr = static_cast(memoryAllocDram(sectionHeaderSizeBytes)); if (mSectionHeadersPtr == nullptr) { LOG_OOM(); return false; } memcpy(mSectionHeadersPtr, (mBinary + elfHeader->e_shoff), sectionHeaderSizeBytes); mNumSectionHeaders = elfHeader->e_shnum; // Load section header names SectionHeader &stringSection = mSectionHeadersPtr[elfHeader->e_shstrndx]; size_t sectionSize = stringSection.sh_size; mSectionNamesPtr = static_cast(memoryAllocDram(sectionSize)); if (mSectionNamesPtr == nullptr) { LOG_OOM(); return false; } memcpy(mSectionNamesPtr, mBinary + stringSection.sh_offset, sectionSize); // Verify dynamic symbol table if (!verifyDynamicTables()) { LOGE("Failed to verify dynamic tables"); return false; } return true; } bool NanoappLoader::createMappings() { // ELF needs pt_load segments to be in contiguous ascending order of // virtual addresses. So the first and last segs can be used to // calculate the entire address span of the image. ProgramHeader *programHeaderArray = getProgramHeaderArray(); size_t numProgramHeaders = getProgramHeaderArraySize(); const ProgramHeader *first = &programHeaderArray[0]; const ProgramHeader *last = &programHeaderArray[numProgramHeaders - 1]; // Find first load segment while (first->p_type != PT_LOAD && first <= last) { ++first; } bool success = false; if (first->p_type != PT_LOAD) { LOGE("Unable to find any load segments in the binary"); } else { // Verify that the first load segment has a program header // first byte of a valid load segment can't be greater than the // program header offset bool valid = (first->p_offset < getElfHeader()->e_phoff) && (first->p_filesz >= (getElfHeader()->e_phoff + (numProgramHeaders * sizeof(ProgramHeader)))); if (!valid) { LOGE("Load segment program header validation failed"); } else { // Get the last load segment while (last > first && last->p_type != PT_LOAD) --last; size_t alignment = first->p_align; size_t memorySpan = last->p_vaddr + last->p_memsz - first->p_vaddr; LOGV("Nanoapp image Memory Span: %zu", memorySpan); if (mIsTcmBinary) { mMapping = static_cast(nanoappBinaryAlloc(memorySpan, alignment)); } else { mMapping = static_cast( nanoappBinaryDramAlloc(memorySpan, alignment)); } if (mMapping == nullptr) { LOG_OOM(); } else { LOGV("Starting location of mappings %p", mMapping); mMemorySpan = memorySpan; // Calculate the load bias using the first load segment. uintptr_t adjustedFirstLoadSegAddr = roundDownToAlign(first->p_vaddr, alignment); mLoadBias = reinterpret_cast(mMapping) - adjustedFirstLoadSegAddr; LOGV("Load bias is %lu", static_cast(mLoadBias)); success = true; } } } if (success) { // Map the remaining segments for (const ProgramHeader *ph = first; ph <= last; ++ph) { if (ph->p_type == PT_LOAD) { ElfAddr segStart = ph->p_vaddr + mLoadBias; void *startPage = reinterpret_cast(segStart); void *binaryStartPage = mBinary + ph->p_offset; size_t segmentLen = ph->p_filesz; LOGV("Mapping start page %p from %p with length %zu", startPage, binaryStartPage, segmentLen); memcpy(startPage, binaryStartPage, segmentLen); mapBss(ph); } else { LOGE("Non-load segment found between load segments"); success = false; break; } } } return success; } NanoappLoader::ElfSym *NanoappLoader::getDynamicSymbol( size_t posInSymbolTable) { size_t numElements = mDynamicSymbolTableSize / sizeof(ElfSym); CHRE_ASSERT(posInSymbolTable < numElements); if (posInSymbolTable < numElements) { return reinterpret_cast( &mDynamicSymbolTablePtr[posInSymbolTable * sizeof(ElfSym)]); } LOGE("Symbol index %zu is out of bound %zu", posInSymbolTable, numElements); return nullptr; } const char *NanoappLoader::getDataName(const ElfSym *symbol) { return symbol == nullptr ? nullptr : &mDynamicStringTablePtr[symbol->st_name]; } void *NanoappLoader::getSymbolTarget(const ElfSym *symbol) { if (symbol == nullptr || symbol->st_shndx == SHN_UNDEF) { return nullptr; } return mMapping + symbol->st_value; } void *NanoappLoader::resolveData(size_t posInSymbolTable) { const ElfSym *symbol = getDynamicSymbol(posInSymbolTable); const char *dataName = getDataName(symbol); void *target = nullptr; if (dataName != nullptr) { LOGV("Resolving %s", dataName); target = findExportedSymbol(dataName); if (target == nullptr) { target = getSymbolTarget(symbol); } if (target == nullptr) { LOGE("Unable to find %s", dataName); } } return target; } NanoappLoader::DynamicHeader *NanoappLoader::getDynamicHeader() { DynamicHeader *dyn = nullptr; ProgramHeader *programHeaders = getProgramHeaderArray(); for (size_t i = 0; i < getProgramHeaderArraySize(); ++i) { if (programHeaders[i].p_type == PT_DYNAMIC) { dyn = reinterpret_cast(programHeaders[i].p_offset + mBinary); break; } } return dyn; } NanoappLoader::ProgramHeader *NanoappLoader::getFirstRoSegHeader() { // return the first read only segment found ProgramHeader *ro = nullptr; ProgramHeader *programHeaders = getProgramHeaderArray(); for (size_t i = 0; i < getProgramHeaderArraySize(); ++i) { if (!(programHeaders[i].p_flags & PF_W)) { ro = &programHeaders[i]; break; } } return ro; } NanoappLoader::ElfWord NanoappLoader::getDynEntry(DynamicHeader *dyn, int field) { ElfWord rv = 0; while (dyn->d_tag != DT_NULL) { if (dyn->d_tag == field) { rv = dyn->d_un.d_val; break; } ++dyn; } return rv; } bool NanoappLoader::fixRelocations() { DynamicHeader *dyn = getDynamicHeader(); if (dyn == nullptr) { LOGE("Dynamic headers are missing from shared object"); } if (relocateTable(dyn, DT_RELA) && relocateTable(dyn, DT_REL)) { return true; } LOGE("Unable to resolve all symbols in the binary"); return false; } void NanoappLoader::callAtexitFunctions() { while (!mAtexitFunctions.empty()) { struct AtExitCallback cb = mAtexitFunctions.back(); if (cb.arg.has_value()) { LOGV("Calling __cxa_atexit at %p, arg %p", cb.func1, cb.arg.value()); cb.func1(cb.arg.value()); } else { LOGV("Calling atexit at %p", cb.func0); cb.func0(); } mAtexitFunctions.pop_back(); } } void NanoappLoader::callTerminatorArray() { for (size_t i = 0; i < mNumSectionHeaders; ++i) { const char *name = getSectionHeaderName(mSectionHeadersPtr[i].sh_name); if (strncmp(name, kFiniArrayName, strlen(kFiniArrayName)) == 0) { uintptr_t finiArray = static_cast(mLoadBias + mSectionHeadersPtr[i].sh_addr); uintptr_t offset = 0; while (offset < mSectionHeadersPtr[i].sh_size) { ElfAddr *funcPtr = reinterpret_cast(finiArray + offset); uintptr_t finiFunction = static_cast(*funcPtr); ((void (*)())finiFunction)(); offset += sizeof(finiFunction); } break; } } } void NanoappLoader::getTokenDatabaseSectionInfo(uint32_t *offset, size_t *size) { // Find token database. SectionHeader *pwTokenTableHeader = getSectionHeader(kTokenTableName); if (pwTokenTableHeader != nullptr) { if (pwTokenTableHeader->sh_size != 0) { *size = pwTokenTableHeader->sh_size; *offset = pwTokenTableHeader->sh_offset; } else { LOGE("Found empty token database"); *size = 0; *offset = 0; } } else { *size = 0; *offset = 0; } } } // namespace chre