blob: 5ebf5deeb6717e0176ce101e2bd483b5ebf77703 [file] [log] [blame]
/*
* 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