1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_ 18 #define CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_ 19 20 #include <cinttypes> 21 #include <cstdlib> 22 23 #include "chre/platform/shared/loader_util.h" 24 25 #include "chre/util/dynamic_vector.h" 26 #include "chre/util/optional.h" 27 28 namespace chre { 29 30 /** 31 * @struct: 32 * AtExitCallback 33 * 34 * @description: 35 * Store callback information for both atexit and __cxa_atexit. 36 * 37 * @fields: 38 * func0 :: 39 * Callback function for atexit (no arg). 40 * 41 * func1 :: 42 * Callback function for __cxa_atexit (one arg). 43 * 44 * arg :: 45 * Optional arg for __cxa_atexit only. 46 */ 47 struct AtExitCallback { 48 union { 49 void (*func0)(void); 50 void (*func1)(void *); 51 }; 52 Optional<void *> arg; 53 AtExitCallbackAtExitCallback54 AtExitCallback(void (*func)(void)) { 55 func0 = func; 56 } 57 AtExitCallbackAtExitCallback58 AtExitCallback(void (*func)(void *), void *a) { 59 func1 = func; 60 arg = a; 61 } 62 }; 63 64 /** 65 * Provides dynamic loading support for nanoapps on FreeRTOS-based platforms. 66 * At a high level, this class is responsible for mapping the provided binary 67 * into CHRE's address space, relocating and resolving symbols, and initializing 68 * and freeing static data. 69 */ 70 class NanoappLoader { 71 public: 72 NanoappLoader() = delete; 73 74 /** 75 * Factory method to create a NanoappLoader Instance after loading 76 * the buffer containing the ELF binary. 77 * 78 * @param elfInput Buffer containing the elf file 79 * @param mapIntoTcm Indicates whether the elfBinary should be mapped into 80 * tightly coupled memory. 81 * @return Class instance on successful load and verification, 82 * nullptr otherwise. 83 */ 84 static NanoappLoader *create(void *elfInput, bool mapIntoTcm); 85 86 /** 87 * Closes and destroys the NanoappLoader instance. 88 * 89 * @param loader A non-null pointer to the loader that must be destroyed. 90 */ 91 static void destroy(NanoappLoader *loader); 92 93 /** 94 * Attempts to locate the exported symbol specified by the given function 95 * name. 96 * 97 * @param name A null-terminated char array that is the name of the function 98 * to be found. 99 * @return The address of the function. nullptr if not found. 100 */ 101 static void *findExportedSymbol(const char *name); 102 103 /** 104 * Method for pointer lookup by symbol name. Only function pointers 105 * are currently supported. 106 * 107 * @return function pointer on successful lookup, nullptr otherwise 108 */ 109 void *findSymbolByName(const char *name); 110 111 /** 112 * Registers a function provided through atexit during static initialization 113 * that should be called prior to unloading a nanoapp. 114 * 115 * @param callback Callback info that should be invoked prior to unloading a 116 * nanoapp. 117 */ 118 void registerAtexitFunction(struct AtExitCallback &cb); 119 120 /** 121 * Rounds the given address down to the closest alignment boundary. 122 * 123 * The alignment follows ELF's p_align semantics: 124 * 125 * [p_align] holds the value to which the segments are aligned in memory and 126 * in the file. Loadable process segments must have congruent values for 127 * p_vaddr and p_offset, modulo the page size. Values of zero and one mean no 128 * alignment is required. Otherwise, p_align should be a positive, integral 129 * power of two, and p_vaddr should equal p_offset, modulo p_align. 130 * 131 * @param virtualAddr The address to be rounded. 132 * @param alignment Alignment to which the address is rounded to. 133 * @return An address that is a multiple of the platform's alignment and is 134 * less than or equal to virtualAddr. 135 */ 136 static uintptr_t roundDownToAlign(uintptr_t virtualAddr, size_t alignment); 137 138 /** 139 * Find if a token database exists in the nanoapp ELF binary and pass the 140 * database offset and database size to the caller. 141 * 142 * @param offset Pointer to the size offset of the token database from the 143 * start of the address of the ELF binary in bytes. 144 * @param size Pointer to the size of the token database section in the ELF 145 * binary in bytes. 146 */ 147 void getTokenDatabaseSectionInfo(uint32_t *offset, size_t *size); 148 149 private: NanoappLoader(void * elfInput,bool mapIntoTcm)150 explicit NanoappLoader(void *elfInput, bool mapIntoTcm) { 151 mBinary = static_cast<uint8_t *>(elfInput); 152 mIsTcmBinary = mapIntoTcm; 153 } 154 155 /** 156 * Opens the ELF binary. This maps the binary into memory, resolves symbols, 157 * and invokes any static initializers. 158 * 159 * <p>This function must be called before any symbol-finding functions. 160 * 161 * @return true if all required opening steps were completed. 162 */ 163 bool open(); 164 165 /** 166 * Closes the loader, freeing any state associated with the loaded ELF binary 167 * and unmapping it from memory. Prior to unmapping from memory, any static 168 * termination functions will be invoked. 169 */ 170 void close(); 171 172 using DynamicHeader = ElfW(Dyn); 173 using ElfAddr = ElfW(Addr); 174 using ElfHeader = ElfW(Ehdr); 175 using ElfRel = ElfW(Rel); // Relocation table entry, 176 // in section of type SHT_REL 177 using ElfRela = ElfW(Rela); 178 using ElfSym = ElfW(Sym); 179 using ElfWord = ElfW(Word); 180 using ProgramHeader = ElfW(Phdr); 181 using SectionHeader = ElfW(Shdr); 182 183 //! Name of various segments in the ELF that need to be looked up 184 static constexpr const char *kDynsymTableName = ".dynsym"; 185 static constexpr const char *kDynstrTableName = ".dynstr"; 186 static constexpr const char *kInitArrayName = ".init_array"; 187 static constexpr const char *kFiniArrayName = ".fini_array"; 188 static constexpr const char *kTokenTableName = ".pw_tokenizer.entries"; 189 190 //! Pointer to the table of all the section names. 191 char *mSectionNamesPtr = nullptr; 192 //! Pointer to the table of dynamic symbol names for defined symbols. 193 char *mDynamicStringTablePtr = nullptr; 194 //! Pointer to the table of dynamic symbol information for defined symbols. 195 uint8_t *mDynamicSymbolTablePtr = nullptr; 196 //! Pointer to the array of section header entries. 197 SectionHeader *mSectionHeadersPtr = nullptr; 198 //! Number of SectionHeaders pointed to by mSectionHeadersPtr. 199 size_t mNumSectionHeaders = 0; 200 //! Size of the data pointed to by mDynamicSymbolTablePtr. 201 size_t mDynamicSymbolTableSize = 0; 202 203 //! The ELF that is being mapped into the system. This pointer will be invalid 204 //! after open returns. 205 uint8_t *mBinary = nullptr; 206 //! The starting location of the memory that has been mapped into the system. 207 uint8_t *mMapping = nullptr; 208 //! The span of memory that has been mapped into the system. 209 size_t mMemorySpan = 0; 210 //! The difference between where the first load segment was mapped into 211 //! virtual memory and what the virtual load offset was of that segment. 212 ElfAddr mLoadBias = 0; 213 //! Dynamic vector containing functions that should be invoked prior to 214 //! unloading this nanoapp. Note that functions are stored in the order they 215 //! were added and should be called in reverse. 216 DynamicVector<struct AtExitCallback> mAtexitFunctions; 217 //! Whether this loader instance is managing a TCM nanoapp binary. 218 bool mIsTcmBinary = false; 219 220 /** 221 * Invokes all functions registered via atexit during static initialization. 222 */ 223 void callAtexitFunctions(); 224 225 /** 226 * Invokes all initialization functions in .init_array segment. 227 * 228 * @return true if static initialization succeeded. 229 */ 230 bool callInitArray(); 231 232 /** 233 * Invokes all termination functions in the .fini_array segment. 234 */ 235 void callTerminatorArray(); 236 237 /** 238 * Allocates memory for all load segments that need to be mapped into virtual 239 * memory and copies the load segments into the newly allocated memory. 240 * 241 * @return true if the memory for mapping was allocated and the load segments 242 * were formatted correctly. 243 */ 244 bool createMappings(); 245 246 /** 247 * Copies various sections and headers from the ELF while verifying that they 248 * match the ELF format specification. 249 * 250 * @return true if all data was copied and verified. 251 */ 252 bool copyAndVerifyHeaders(); 253 254 /** 255 * Resolves all relocated symbols located in the DT_REL table. 256 * 257 * @return true if all relocated symbols were resolved. 258 */ 259 bool fixRelocations(); 260 261 /** 262 * Resolves entries in the Global Offset Table (GOT) to facility the ELF's 263 * compiled using position independent code (PIC). 264 * 265 * @return true if all symbols were resolved. 266 */ 267 bool resolveGot(); 268 269 /** 270 * Verifies the ELF header has correct values based on the ELF spec. 271 * 272 * @return true if the header passed verification. 273 */ 274 bool verifyElfHeader(); 275 276 /** 277 * Verifies basic information about program headers. 278 * 279 * @return true if the headers passed verification. 280 */ 281 bool verifyProgramHeaders(); 282 283 /** 284 * Verifies basic information about section headers. 285 * 286 * @return true if the headers passed verification. 287 */ 288 bool verifySectionHeaders(); 289 290 /** 291 * Retrieves the symbol at the given position in the symbol table. 292 * 293 * @param posInSymbolTable The position in the symbol table where information 294 * about the symbol can be found. 295 * @return The symbol or nullptr if not found. 296 */ 297 ElfSym *getDynamicSymbol(size_t posInSymbolTable); 298 299 /** 300 * Retrieves the symbol name. 301 * 302 * @param symbol A pointer to the symbol. 303 * @return The symbol's name or nullptr if not found. 304 */ 305 const char *getDataName(const ElfSym *symbol); 306 307 /** 308 * Retrieves the target address of the symbol. 309 * 310 * @param symbol A pointer to the symbol. 311 * @return The target address or nullptr if the symbol is not defined. 312 */ 313 void *getSymbolTarget(const ElfSym *symbol); 314 315 /** 316 * Retrieves the name of the section header located at the given offset in 317 * the section name table. 318 * 319 * @param headerOffset The offset in the section names table where the 320 * header is located. 321 * @return The section's name or the empty string if the offset is 0. 322 */ 323 const char *getSectionHeaderName(size_t headerOffset); 324 325 /** 326 * Frees any data that was allocated as part of loading the ELF into memory. 327 */ 328 void freeAllocatedData(); 329 330 /** 331 * Ensures the BSS section is properly mapped into memory. If there is a 332 * difference between the size of the BSS section in the ELF binary and the 333 * size it needs to be in memory, the rest of the section is zeroed out. 334 * 335 * @param header The ProgramHeader of the BSS section that is being mapped in. 336 */ 337 void mapBss(const ProgramHeader *header); 338 339 /** 340 * Resolves the address of an undefined symbol located at the given position 341 * in the symbol table. This symbol must be defined and exposed by the given 342 * platform in order for it to be resolved successfully. 343 * 344 * @param posInSymbolTable The position of the undefined symbol in the symbol 345 * table. 346 * @return The address of the resolved symbol. nullptr if not found. 347 */ 348 void *resolveData(size_t posInSymbolTable); 349 350 /** 351 * @return The address for the dynamic segment. nullptr if not found. 352 */ 353 DynamicHeader *getDynamicHeader(); 354 355 /** 356 * @return The address of the first read-only segment. nullptr if not found. 357 */ 358 ProgramHeader *getFirstRoSegHeader(); 359 360 /** 361 * Retrieves the section header with the given name. 362 * 363 * @param headerName The name of the section header to find. 364 * @return The address of the section. nullptr if not found. 365 */ 366 SectionHeader *getSectionHeader(const char *headerName); 367 368 /** 369 * @return The ELF header for the binary being loaded. nullptr if it doesn't 370 * exist or no binary is being loaded. 371 */ getElfHeader()372 ElfHeader *getElfHeader() { 373 return reinterpret_cast<ElfHeader *>(mBinary); 374 } 375 376 /** 377 * @return The array of program headers for the binary being loaded. nullptr 378 * if it doesn't exist or no binary is being loaded. 379 */ 380 ProgramHeader *getProgramHeaderArray(); 381 382 /** 383 * @return The size of the array of program headers for the binary being 384 * loaded. 0 if it doesn't exist or no binary is being loaded. 385 */ 386 size_t getProgramHeaderArraySize(); 387 388 /** 389 * Verifies dynamic tables that must exist in the ELF header. 390 * 391 * @return true if all the required tables exist, otherwise false. 392 */ 393 bool verifyDynamicTables(); 394 395 /** 396 * Returns the first entry in the dynamic header that has a tag that matches 397 * the given field. 398 * 399 * @param dyn The dynamic header for the binary. 400 * @param field The field to be searched for. 401 * @return The value found at the entry. 0 if the entry isn't found. 402 */ 403 static ElfWord getDynEntry(DynamicHeader *dyn, int field); 404 405 /** 406 * Handle relocation for entries in the specified table. 407 * 408 * <p> this function must return true if the table is not required or is 409 * empty. If the entry is present when not expected, it must return false. 410 * 411 * @param dyn The dynamic header for the binary. 412 * @param tableTag The dynamic tag (DT_x) of the relocation table. 413 * @return True if success or unsupported, false if failure. 414 */ 415 bool relocateTable(DynamicHeader *dyn, int tableTag); 416 }; 417 418 } // namespace chre 419 420 #endif // CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_ 421