| /* |
| * 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 <dlfcn.h> |
| #include <cctype> |
| #include <cmath> |
| #include <cstring> |
| |
| #include "chre/platform/shared/nanoapp_loader.h" |
| |
| #include "ash.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/target_platform/platform_cache_management.h" |
| #include "chre/util/dynamic_vector.h" |
| #include "chre/util/macros.h" |
| |
| #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; |
| |
| // The new operator is used by singleton.h which causes the delete operator to |
| // be undefined in nanoapp binaries even though it's unused. Define this in case |
| // a nanoapp actually tries to use the operator. |
| void deleteOverride(void *ptr) { |
| FATAL_ERROR("Nanoapp tried to free %p through delete operator", ptr); |
| } |
| |
| // atexit is used to register functions that must be called when a binary is |
| // removed from the system. |
| int atexitOverride(void (*function)(void)) { |
| LOGV("atexit invoked with %p", function); |
| if (gCurrentlyLoadingNanoapp == nullptr) { |
| CHRE_ASSERT_LOG(false, |
| "atexit is only supported during static initialization."); |
| return -1; |
| } |
| |
| gCurrentlyLoadingNanoapp->registerAtexitFunction(function); |
| 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 sinOverride(double rad) { |
| return sin(rad); |
| } |
| |
| double asinOverride(double val) { |
| return asin(val); |
| } |
| |
| double cosOverride(double rad) { |
| return cos(rad); |
| } |
| |
| float sqrtOverride(float val) { |
| return sqrt(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 */); |
| } |
| |
| #define ADD_EXPORTED_SYMBOL(function_name, function_string) \ |
| { reinterpret_cast<void *>(function_name), function_string } |
| #define ADD_EXPORTED_C_SYMBOL(function_name) \ |
| ADD_EXPORTED_SYMBOL(function_name, STRINGIFY(function_name)) |
| |
| // 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. |
| const ExportedData gExportedData[] = { |
| /* libmath overrrides and symbols */ |
| ADD_EXPORTED_SYMBOL(asinOverride, "asin"), |
| ADD_EXPORTED_SYMBOL(cosOverride, "cos"), |
| ADD_EXPORTED_SYMBOL(frexpOverride, "frexp"), |
| ADD_EXPORTED_SYMBOL(sinOverride, "sin"), |
| ADD_EXPORTED_SYMBOL(sqrtOverride, "sqrt"), |
| ADD_EXPORTED_C_SYMBOL(atan2f), |
| ADD_EXPORTED_C_SYMBOL(expf), |
| ADD_EXPORTED_C_SYMBOL(fmodf), |
| ADD_EXPORTED_C_SYMBOL(sqrtf), |
| ADD_EXPORTED_C_SYMBOL(tanhf), |
| /* libc overrides and symbols */ |
| ADD_EXPORTED_C_SYMBOL(__cxa_pure_virtual), |
| ADD_EXPORTED_SYMBOL(atexitOverride, "atexit"), |
| ADD_EXPORTED_SYMBOL(deleteOverride, "_ZdlPv"), |
| ADD_EXPORTED_C_SYMBOL(dlsym), |
| ADD_EXPORTED_C_SYMBOL(memcmp), |
| ADD_EXPORTED_C_SYMBOL(memcpy), |
| ADD_EXPORTED_C_SYMBOL(memmove), |
| ADD_EXPORTED_C_SYMBOL(memset), |
| ADD_EXPORTED_C_SYMBOL(strcmp), |
| ADD_EXPORTED_C_SYMBOL(strlen), |
| ADD_EXPORTED_C_SYMBOL(strncmp), |
| ADD_EXPORTED_C_SYMBOL(tolower), |
| /* ash symbols */ |
| ADD_EXPORTED_C_SYMBOL(ashLoadCalibrationParams), |
| ADD_EXPORTED_C_SYMBOL(ashSaveCalibrationParams), |
| ADD_EXPORTED_C_SYMBOL(ashSetCalibration), |
| /* CHRE symbols */ |
| ADD_EXPORTED_C_SYMBOL(chreAbort), |
| ADD_EXPORTED_C_SYMBOL(chreAudioConfigureSource), |
| ADD_EXPORTED_C_SYMBOL(chreAudioGetSource), |
| ADD_EXPORTED_C_SYMBOL(chreConfigureDebugDumpEvent), |
| ADD_EXPORTED_C_SYMBOL(chreConfigureHostSleepStateEvents), |
| ADD_EXPORTED_C_SYMBOL(chreConfigureNanoappInfoEvents), |
| ADD_EXPORTED_C_SYMBOL(chreGetApiVersion), |
| ADD_EXPORTED_C_SYMBOL(chreGetAppId), |
| ADD_EXPORTED_C_SYMBOL(chreGetInstanceId), |
| ADD_EXPORTED_C_SYMBOL(chreGetEstimatedHostTimeOffset), |
| ADD_EXPORTED_C_SYMBOL(chreGetNanoappInfoByAppId), |
| ADD_EXPORTED_C_SYMBOL(chreGetPlatformId), |
| ADD_EXPORTED_C_SYMBOL(chreGetSensorInfo), |
| ADD_EXPORTED_C_SYMBOL(chreGetSensorSamplingStatus), |
| ADD_EXPORTED_C_SYMBOL(chreGetTime), |
| 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(chreSendMessageToHostEndpoint), |
| ADD_EXPORTED_C_SYMBOL(chreSensorConfigure), |
| ADD_EXPORTED_C_SYMBOL(chreSensorFindDefault), |
| ADD_EXPORTED_C_SYMBOL(chreTimerCancel), |
| ADD_EXPORTED_C_SYMBOL(chreTimerSet), |
| ADD_EXPORTED_C_SYMBOL(chreWifiConfigureScanMonitorAsync), |
| ADD_EXPORTED_C_SYMBOL(chreWifiGetCapabilities), |
| ADD_EXPORTED_C_SYMBOL(chreWifiRequestScanAsync), |
| ADD_EXPORTED_C_SYMBOL(chreWwanGetCapabilities), |
| ADD_EXPORTED_C_SYMBOL(chreWwanGetCellInfoAsync), |
| ADD_EXPORTED_C_SYMBOL(platform_chreDebugDumpVaLog), |
| }; |
| |
| } // namespace |
| |
| void *NanoappLoader::create(void *elfInput, bool mapIntoTcm) { |
| void *instance = nullptr; |
| NanoappLoader *loader = memoryAllocDram<NanoappLoader>(elfInput, mapIntoTcm); |
| if (loader != nullptr) { |
| if (loader->open()) { |
| instance = loader; |
| } else { |
| memoryFreeDram(loader); |
| } |
| } else { |
| LOG_OOM(); |
| } |
| |
| return instance; |
| } |
| |
| 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) { |
| for (size_t i = 0; i < ARRAY_SIZE(gExportedData); i++) { |
| if (strncmp(name, gExportedData[i].dataName, |
| strlen(gExportedData[i].dataName)) == 0) { |
| return gExportedData[i].data; |
| } |
| } |
| |
| LOGE("Unable to find %s", name); |
| return nullptr; |
| } |
| |
| bool NanoappLoader::open() { |
| bool success = false; |
| if (mBinary.dataPtr != nullptr) { |
| if (!copyAndVerifyHeaders()) { |
| LOGE("Failed to verify 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(); |
| if (!callInitArray()) { |
| LOGE("Failed to perform static init"); |
| } else { |
| success = true; |
| } |
| } |
| } |
| |
| if (!success) { |
| freeAllocatedData(); |
| } |
| |
| return success; |
| } |
| |
| void NanoappLoader::close() { |
| callAtexitFunctions(); |
| callTerminatorArray(); |
| freeAllocatedData(); |
| } |
| |
| void *NanoappLoader::findSymbolByName(const char *name) { |
| void *symbol = nullptr; |
| uint8_t *index = mSymbolTablePtr; |
| while (index < (mSymbolTablePtr + mSymbolTableSize)) { |
| ElfSym *currSym = reinterpret_cast<ElfSym *>(index); |
| const char *symbolName = &mStringTablePtr[currSym->st_name]; |
| |
| if (strncmp(symbolName, name, strlen(name)) == 0) { |
| symbol = reinterpret_cast<void *>(mMapping.data + currSym->st_value); |
| break; |
| } |
| |
| index += sizeof(ElfSym); |
| } |
| return symbol; |
| } |
| |
| void NanoappLoader::registerAtexitFunction(void (*function)(void)) { |
| if (!mAtexitFunctions.push_back(function)) { |
| 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: %zu file size: %zu", 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 %zu from page %p", deltaMem, endOfFile); |
| memset(reinterpret_cast<void *>(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 = reinterpret_cast<uintptr_t>( |
| mLoadBias + mSectionHeadersPtr[i].sh_addr); |
| uintptr_t offset = 0; |
| while (offset < mSectionHeadersPtr[i].sh_size) { |
| ElfAddr *funcPtr = reinterpret_cast<ElfAddr *>(initArray + offset); |
| uintptr_t initFunction = reinterpret_cast<uintptr_t>(*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) { |
| return virtualAddr & -kBinaryAlignment; |
| } |
| |
| void NanoappLoader::freeAllocatedData() { |
| if (mIsTcmBinary) { |
| memoryFree(mMapping.dataPtr); |
| } else { |
| memoryFreeDram(mMapping.dataPtr); |
| } |
| memoryFreeDram(mSectionHeadersPtr); |
| memoryFreeDram(mSectionNamesPtr); |
| memoryFreeDram(mSymbolTablePtr); |
| memoryFreeDram(mStringTablePtr); |
| } |
| |
| bool NanoappLoader::verifyElfHeader() { |
| bool success = false; |
| 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)) { |
| success = true; |
| } |
| return success; |
| } |
| |
| bool NanoappLoader::verifyProgramHeaders() { |
| // This is a minimal check for now - |
| // there should be at least one load segment. |
| bool success = false; |
| for (size_t i = 0; i < getProgramHeaderArraySize(); ++i) { |
| if (getProgramHeaderArray()[i].p_type == PT_LOAD) { |
| success = true; |
| break; |
| } |
| } |
| return success; |
| } |
| |
| 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; |
| } |
| |
| ElfHeader *NanoappLoader::getElfHeader() { |
| return reinterpret_cast<ElfHeader *>(mBinary.data); |
| } |
| |
| ProgramHeader *NanoappLoader::getProgramHeaderArray() { |
| ElfHeader *elfHeader = getElfHeader(); |
| ProgramHeader *programHeader = nullptr; |
| if (elfHeader != nullptr) { |
| programHeader = |
| reinterpret_cast<ProgramHeader *>(mBinary.data + elfHeader->e_phoff); |
| } |
| |
| return programHeader; |
| } |
| |
| size_t NanoappLoader::getProgramHeaderArraySize() { |
| ElfHeader *elfHeader = getElfHeader(); |
| size_t arraySize = 0; |
| if (elfHeader != nullptr) { |
| arraySize = elfHeader->e_phnum; |
| } |
| |
| return arraySize; |
| } |
| |
| char *NanoappLoader::getDynamicStringTable() { |
| char *table = nullptr; |
| |
| SectionHeader *dynamicStringTablePtr = getSectionHeader(".dynstr"); |
| CHRE_ASSERT(dynamicStringTablePtr != nullptr); |
| if (dynamicStringTablePtr != nullptr && mBinary.dataPtr != nullptr) { |
| table = reinterpret_cast<char *>(mBinary.data + |
| dynamicStringTablePtr->sh_offset); |
| } |
| |
| return table; |
| } |
| |
| uint8_t *NanoappLoader::getDynamicSymbolTable() { |
| uint8_t *table = nullptr; |
| |
| SectionHeader *dynamicSymbolTablePtr = getSectionHeader(".dynsym"); |
| CHRE_ASSERT(dynamicSymbolTablePtr != nullptr); |
| if (dynamicSymbolTablePtr != nullptr && mBinary.dataPtr != nullptr) { |
| table = reinterpret_cast<uint8_t *>(mBinary.data + |
| dynamicSymbolTablePtr->sh_offset); |
| } |
| |
| return table; |
| } |
| |
| size_t NanoappLoader::getDynamicSymbolTableSize() { |
| size_t tableSize = 0; |
| |
| SectionHeader *dynamicSymbolTablePtr = getSectionHeader(".dynsym"); |
| CHRE_ASSERT(dynamicSymbolTablePtr != nullptr); |
| if (dynamicSymbolTablePtr != nullptr) { |
| tableSize = dynamicSymbolTablePtr->sh_size; |
| } |
| |
| return tableSize; |
| } |
| |
| bool NanoappLoader::verifySectionHeaders() { |
| bool foundSymbolTableHeader = false; |
| bool foundStringTableHeader = false; |
| |
| for (size_t i = 0; i < mNumSectionHeaders; ++i) { |
| const char *name = getSectionHeaderName(mSectionHeadersPtr[i].sh_name); |
| |
| if (strncmp(name, kSymTableName, strlen(kSymTableName)) == 0) { |
| foundSymbolTableHeader = true; |
| } else if (strncmp(name, kStrTableName, strlen(kStrTableName)) == 0) { |
| foundStringTableHeader = true; |
| } |
| } |
| |
| return foundSymbolTableHeader && foundStringTableHeader; |
| } |
| |
| bool NanoappLoader::copyAndVerifyHeaders() { |
| size_t offset = 0; |
| bool success = false; |
| uint8_t *pDataBytes = static_cast<uint8_t *>(mBinary.dataPtr); |
| |
| // Verify the ELF Header |
| ElfHeader *elfHeader = getElfHeader(); |
| success = verifyElfHeader(); |
| |
| LOGV("Verified ELF header %d", success); |
| |
| // Verify Program Headers |
| if (success) { |
| success = verifyProgramHeaders(); |
| } |
| |
| LOGV("Verified Program headers %d", success); |
| |
| // Load Section Headers |
| if (success) { |
| offset = elfHeader->e_shoff; |
| size_t sectionHeaderSizeBytes = sizeof(SectionHeader) * elfHeader->e_shnum; |
| mSectionHeadersPtr = |
| static_cast<SectionHeader *>(memoryAllocDram(sectionHeaderSizeBytes)); |
| if (mSectionHeadersPtr == nullptr) { |
| success = false; |
| LOG_OOM(); |
| } else { |
| memcpy(mSectionHeadersPtr, (pDataBytes + offset), sectionHeaderSizeBytes); |
| mNumSectionHeaders = elfHeader->e_shnum; |
| } |
| } |
| |
| LOGV("Loaded section headers %d", success); |
| |
| // Load section header names |
| if (success) { |
| SectionHeader &stringSection = mSectionHeadersPtr[elfHeader->e_shstrndx]; |
| size_t sectionSize = stringSection.sh_size; |
| mSectionNamesPtr = static_cast<char *>(memoryAllocDram(sectionSize)); |
| if (mSectionNamesPtr == nullptr) { |
| LOG_OOM(); |
| success = false; |
| } else { |
| memcpy(mSectionNamesPtr, |
| reinterpret_cast<void *>(mBinary.data + stringSection.sh_offset), |
| sectionSize); |
| } |
| } |
| |
| LOGV("Loaded section header names %d", success); |
| |
| success = verifySectionHeaders(); |
| LOGV("Verified Section headers %d", success); |
| |
| // Load symbol table |
| if (success) { |
| SectionHeader *symbolTableHeader = getSectionHeader(kSymTableName); |
| mSymbolTableSize = symbolTableHeader->sh_size; |
| if (mSymbolTableSize == 0) { |
| LOGE("No symbols to resolve"); |
| success = false; |
| } else { |
| mSymbolTablePtr = |
| static_cast<uint8_t *>(memoryAllocDram(mSymbolTableSize)); |
| if (mSymbolTablePtr == nullptr) { |
| LOG_OOM(); |
| success = false; |
| } else { |
| memcpy(mSymbolTablePtr, |
| reinterpret_cast<void *>(mBinary.data + |
| symbolTableHeader->sh_offset), |
| mSymbolTableSize); |
| } |
| } |
| } |
| |
| LOGV("Loaded symbol table %d", success); |
| |
| // Load string table |
| if (success) { |
| SectionHeader *stringTableHeader = getSectionHeader(kStrTableName); |
| size_t stringTableSize = stringTableHeader->sh_size; |
| if (mSymbolTableSize == 0) { |
| LOGE("No string table corresponding to symbols"); |
| success = false; |
| } else { |
| mStringTablePtr = static_cast<char *>(memoryAllocDram(stringTableSize)); |
| if (mStringTablePtr == nullptr) { |
| LOG_OOM(); |
| success = false; |
| } else { |
| memcpy(mStringTablePtr, |
| reinterpret_cast<void *>(mBinary.data + |
| stringTableHeader->sh_offset), |
| stringTableSize); |
| } |
| } |
| } |
| |
| LOGV("Loaded string table %d", success); |
| |
| return success; |
| } |
| |
| 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. |
| ElfHeader *elfHeader = getElfHeader(); |
| 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 < elfHeader->e_phoff) && |
| (first->p_filesz > |
| (elfHeader->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 memorySpan = last->p_vaddr + last->p_memsz - first->p_vaddr; |
| LOGV("Nanoapp image Memory Span: %u", memorySpan); |
| |
| if (mIsTcmBinary) { |
| mMapping.dataPtr = memoryAllocAligned(kBinaryAlignment, memorySpan); |
| } else { |
| mMapping.dataPtr = memoryAllocDramAligned(kBinaryAlignment, memorySpan); |
| } |
| |
| if (mMapping.dataPtr == nullptr) { |
| LOG_OOM(); |
| } else { |
| LOGV("Starting location of mappings %p", mMapping.dataPtr); |
| |
| // Calculate the load bias using the first load segment. |
| uintptr_t adjustedFirstLoadSegAddr = roundDownToAlign(first->p_vaddr); |
| mLoadBias = mMapping.data - adjustedFirstLoadSegAddr; |
| LOGV("Load bias is %" PRIu32, 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<void *>(roundDownToAlign(segStart)); |
| ElfAddr phOffsetPage = roundDownToAlign(ph->p_offset); |
| void *binaryStartPage = |
| reinterpret_cast<void *>(mBinary.data + phOffsetPage); |
| 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; |
| } |
| |
| const char *NanoappLoader::getDataName(size_t posInSymbolTable) { |
| size_t sectionSize = getDynamicSymbolTableSize(); |
| uint8_t *dynamicSymbolTable = getDynamicSymbolTable(); |
| size_t numElements = sectionSize / sizeof(ElfSym); |
| CHRE_ASSERT(posInSymbolTable < numElements); |
| char *dataName = nullptr; |
| if (posInSymbolTable < numElements) { |
| ElfSym *sym = reinterpret_cast<ElfSym *>( |
| &dynamicSymbolTable[posInSymbolTable * sizeof(ElfSym)]); |
| dataName = &getDynamicStringTable()[sym->st_name]; |
| } |
| return dataName; |
| } |
| |
| void *NanoappLoader::resolveData(size_t posInSymbolTable) { |
| const char *dataName = getDataName(posInSymbolTable); |
| |
| if (dataName != nullptr) { |
| LOGV("Resolving %s", dataName); |
| return findExportedSymbol(dataName); |
| } |
| |
| return nullptr; |
| } |
| |
| 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<DynamicHeader *>(programHeaders[i].p_vaddr + |
| mLoadBias); |
| 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() { |
| ElfAddr *addr; |
| DynamicHeader *dyn = getDynamicHeader(); |
| ProgramHeader *roSeg = getFirstRoSegHeader(); |
| |
| bool success = false; |
| if ((dyn == nullptr) || (roSeg == nullptr)) { |
| LOGE("Mandatory headers missing from shared object, aborting load"); |
| } else if (getDynEntry(dyn, DT_RELA) != 0) { |
| LOGE("Elf binaries with a DT_RELA dynamic entry are unsupported"); |
| } else { |
| ElfRel *reloc = |
| reinterpret_cast<ElfRel *>(getDynEntry(dyn, DT_REL) + mBinary.data); |
| size_t relocSize = getDynEntry(dyn, DT_RELSZ); |
| size_t nRelocs = relocSize / sizeof(ElfRel); |
| LOGV("Relocation %zu entries in DT_REL table", nRelocs); |
| |
| size_t i; |
| for (i = 0; i < nRelocs; ++i) { |
| ElfRel *curr = &reloc[i]; |
| int relocType = ELFW_R_TYPE(curr->r_info); |
| switch (relocType) { |
| case R_ARM_RELATIVE: |
| LOGV("Resolving ARM_RELATIVE at offset %" PRIx32, curr->r_offset); |
| addr = reinterpret_cast<ElfAddr *>(curr->r_offset + mMapping.data); |
| // TODO: When we move to DRAM allocations, we need to check if the |
| // above address is in a Read-Only section of memory, and give it |
| // temporary write permission if that is the case. |
| *addr += mMapping.data; |
| break; |
| |
| case R_ARM_ABS32: |
| case R_ARM_GLOB_DAT: { |
| LOGV("Resolving type %d at offset %" PRIx32, relocType, |
| curr->r_offset); |
| addr = reinterpret_cast<ElfAddr *>(curr->r_offset + mMapping.data); |
| size_t posInSymbolTable = ELFW_R_SYM(curr->r_info); |
| void *resolved = resolveData(posInSymbolTable); |
| if (resolved == nullptr) { |
| LOGV("Failed to resolve global symbol(%d) at offset 0x%x", i, |
| curr->r_offset); |
| return false; |
| } |
| // TODO: When we move to DRAM allocations, we need to check if the |
| // above address is in a Read-Only section of memory, and give it |
| // temporary write permission if that is the case. |
| *addr = reinterpret_cast<ElfAddr>(resolved); |
| break; |
| } |
| |
| case R_ARM_COPY: |
| LOGE("R_ARM_COPY is an invalid relocation for shared libraries"); |
| break; |
| default: |
| LOGE("Invalid relocation type %u", relocType); |
| break; |
| } |
| } |
| |
| if (i != nRelocs) { |
| LOGE("Unable to resolve all symbols in the binary"); |
| } else { |
| success = true; |
| } |
| } |
| |
| return success; |
| } |
| |
| bool NanoappLoader::resolveGot() { |
| ElfAddr *addr; |
| ElfRel *reloc = reinterpret_cast<ElfRel *>( |
| getDynEntry(getDynamicHeader(), DT_JMPREL) + mMapping.data); |
| size_t relocSize = getDynEntry(getDynamicHeader(), DT_PLTRELSZ); |
| size_t nRelocs = relocSize / sizeof(ElfRel); |
| LOGV("Resolving GOT with %zu relocations", nRelocs); |
| |
| for (size_t i = 0; i < nRelocs; ++i) { |
| ElfRel *curr = &reloc[i]; |
| int relocType = ELFW_R_TYPE(curr->r_info); |
| |
| switch (relocType) { |
| case R_ARM_JUMP_SLOT: { |
| LOGV("Resolving ARM_JUMP_SLOT at offset %" PRIx32, curr->r_offset); |
| addr = reinterpret_cast<ElfAddr *>(curr->r_offset + mMapping.data); |
| size_t posInSymbolTable = ELFW_R_SYM(curr->r_info); |
| void *resolved = resolveData(posInSymbolTable); |
| if (resolved == nullptr) { |
| LOGV("Failed to resolve symbol(%d) at offset 0x%x", i, |
| curr->r_offset); |
| return false; |
| } |
| *addr = reinterpret_cast<ElfAddr>(resolved); |
| break; |
| } |
| |
| default: |
| LOGE("Unsupported relocation type: %u for symbol %s", relocType, |
| getDataName(ELFW_R_SYM(curr->r_info))); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void NanoappLoader::callAtexitFunctions() { |
| while (!mAtexitFunctions.empty()) { |
| LOGV("Calling atexit at %p", mAtexitFunctions.back()); |
| mAtexitFunctions.back()(); |
| 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 = reinterpret_cast<uintptr_t>( |
| mLoadBias + mSectionHeadersPtr[i].sh_addr); |
| uintptr_t offset = 0; |
| while (offset < mSectionHeadersPtr[i].sh_size) { |
| ElfAddr *funcPtr = reinterpret_cast<ElfAddr *>(finiArray + offset); |
| uintptr_t finiFunction = reinterpret_cast<uintptr_t>(*funcPtr); |
| ((void (*)())finiFunction)(); |
| offset += sizeof(finiFunction); |
| } |
| break; |
| } |
| } |
| } |
| |
| } // namespace chre |