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 NanoappLoader(void * elfInput,bool mapIntoTcm)74 explicit NanoappLoader(void *elfInput, bool mapIntoTcm) { 75 mBinary = static_cast<uint8_t *>(elfInput); 76 mIsTcmBinary = mapIntoTcm; 77 } 78 79 /** 80 * Factory method to create a NanoappLoader Instance after loading 81 * the buffer containing the ELF binary. 82 * 83 * @param elfInput Buffer containing the elf file 84 * @param mapIntoTcm Indicates whether the elfBinary should be mapped into 85 * tightly coupled memory. 86 * @return Class instance on successful load and verification, 87 * nullptr otherwise. 88 */ 89 static void *create(void *elfInput, bool mapIntoTcm); 90 91 /** 92 * Closes and destroys the NanoappLoader instance. 93 * 94 * @param loader A non-null pointer to the loader that must be destroyed. 95 */ 96 static void destroy(NanoappLoader *loader); 97 98 /** 99 * Attempts to locate the exported symbol specified by the given function 100 * name. 101 * 102 * @param name A null-terminated char array that is the name of the function 103 * to be found. 104 * @return The address of the function. nullptr if not found. 105 */ 106 static void *findExportedSymbol(const char *name); 107 108 /** 109 * Method for pointer lookup by symbol name. Only function pointers 110 * are currently supported. 111 * 112 * @return function pointer on successful lookup, nullptr otherwise 113 */ 114 void *findSymbolByName(const char *name); 115 116 /** 117 * Registers a function provided through atexit during static initialization 118 * that should be called prior to unloading a nanoapp. 119 * 120 * @param callback Callback info that should be invoked prior to unloading a 121 * nanoapp. 122 */ 123 void registerAtexitFunction(struct AtExitCallback &cb); 124 125 /** 126 * Rounds the given address down to the closest alignment boundary. 127 * 128 * The alignment follows ELF's p_align semantics: 129 * 130 * [p_align] holds the value to which the segments are aligned in memory and 131 * in the file. Loadable process segments must have congruent values for 132 * p_vaddr and p_offset, modulo the page size. Values of zero and one mean no 133 * alignment is required. Otherwise, p_align should be a positive, integral 134 * power of two, and p_vaddr should equal p_offset, modulo p_align. 135 * 136 * @param virtualAddr The address to be rounded. 137 * @param alignment Alignment to which the address is rounded to. 138 * @return An address that is a multiple of the platform's alignment and is 139 * less than or equal to virtualAddr. 140 */ 141 static uintptr_t roundDownToAlign(uintptr_t virtualAddr, size_t alignment); 142 143 private: 144 /** 145 * Opens the ELF binary. This maps the binary into memory, resolves symbols, 146 * and invokes any static initializers. 147 * 148 * @return true if all required opening steps were completed. 149 */ 150 bool open(); 151 152 /** 153 * Closes the loader, freeing any state associated with the loaded ELF binary 154 * and unmapping it from memory. Prior to unmapping from memory, any static 155 * termination functions will be invoked. 156 */ 157 void close(); 158 159 using DynamicHeader = ElfW(Dyn); 160 using ElfAddr = ElfW(Addr); 161 using ElfHeader = ElfW(Ehdr); 162 using ElfRel = ElfW(Rel); // Relocation table entry, 163 // in section of type SHT_REL 164 using ElfRela = ElfW(Rela); 165 using ElfSym = ElfW(Sym); 166 using ElfWord = ElfW(Word); 167 using ProgramHeader = ElfW(Phdr); 168 using SectionHeader = ElfW(Shdr); 169 170 //! Name of various segments in the ELF that need to be looked up 171 static constexpr const char *kSymTableName = ".symtab"; 172 static constexpr const char *kStrTableName = ".strtab"; 173 static constexpr const char *kInitArrayName = ".init_array"; 174 static constexpr const char *kFiniArrayName = ".fini_array"; 175 176 //! Pointer to the table of all the section names. 177 char *mSectionNamesPtr = nullptr; 178 //! Pointer to the table of symbol names of defined symbols. 179 char *mStringTablePtr = nullptr; 180 //! Pointer to the table of symbol information for defined symbols. 181 uint8_t *mSymbolTablePtr = nullptr; 182 //! Pointer to the array of section header entries. 183 SectionHeader *mSectionHeadersPtr = nullptr; 184 //! Number of SectionHeaders pointed to by mSectionHeadersPtr. 185 size_t mNumSectionHeaders = 0; 186 //! Size of the data pointed to by mSymbolTablePtr. 187 size_t mSymbolTableSize = 0; 188 189 //! The ELF that is being mapped into the system. This pointer will be invalid 190 //! after open returns. 191 uint8_t *mBinary = nullptr; 192 //! The starting location of the memory that has been mapped into the system. 193 uint8_t *mMapping = nullptr; 194 //! The span of memory that has been mapped into the system. 195 size_t mMemorySpan = 0; 196 //! The difference between where the first load segment was mapped into 197 //! virtual memory and what the virtual load offset was of that segment. 198 ElfAddr mLoadBias = 0; 199 //! Dynamic vector containing functions that should be invoked prior to 200 //! unloading this nanoapp. Note that functions are stored in the order they 201 //! were added and should be called in reverse. 202 DynamicVector<struct AtExitCallback> mAtexitFunctions; 203 //! Whether this loader instance is managing a TCM nanoapp binary. 204 bool mIsTcmBinary = false; 205 206 /** 207 * Invokes all functions registered via atexit during static initialization. 208 */ 209 void callAtexitFunctions(); 210 211 /** 212 * Invokes all initialization functions in .init_array segment. 213 * 214 * @return true if static initialization succeeded. 215 */ 216 bool callInitArray(); 217 218 /** 219 * Invokes all termination functions in the .fini_array segment. 220 */ 221 void callTerminatorArray(); 222 223 /** 224 * Allocates memory for all load segments that need to be mapped into virtual 225 * memory and copies the load segments into the newly allocated memory. 226 * 227 * @return true if the memory for mapping was allocated and the load segments 228 * were formatted correctly. 229 */ 230 bool createMappings(); 231 232 /** 233 * Copies various sections and headers from the ELF while verifying that they 234 * match the ELF format specification. 235 * 236 * @return true if all data was copied and verified. 237 */ 238 bool copyAndVerifyHeaders(); 239 240 /** 241 * Resolves all relocated symbols located in the DT_REL table. 242 * 243 * @return true if all relocated symbols were resolved. 244 */ 245 bool fixRelocations(); 246 247 /** 248 * Resolves entries in the Global Offset Table (GOT) to facility the ELF's 249 * compiled using position independent code (PIC). 250 * 251 * @return true if all symbols were resolved. 252 */ 253 bool resolveGot(); 254 255 /** 256 * Verifies the ELF header has correct values based on the ELF spec. 257 * 258 * @return true if the header passed verification. 259 */ 260 bool verifyElfHeader(); 261 262 /** 263 * Verifies basic information about program headers. 264 * 265 * @return true if the headers passed verification. 266 */ 267 bool verifyProgramHeaders(); 268 269 /** 270 * Verifies basic information about section headers. 271 * 272 * @return true if the headers passed verification. 273 */ 274 bool verifySectionHeaders(); 275 276 /** 277 * Retrieves the symbol at the given position in the symbol table. 278 * 279 * @param posInSymbolTable The position in the symbol table where information 280 * about the symbol can be found. 281 * @return The symbol or nullptr if not found. 282 */ 283 ElfSym *getDynamicSymbol(size_t posInSymbolTable); 284 285 /** 286 * Retrieves the symbol name. 287 * 288 * @param symbol A pointer to the symbol. 289 * @return The symbol's name or nullptr if not found. 290 */ 291 const char *getDataName(const ElfSym *symbol); 292 293 /** 294 * Retrieves the target address of the symbol. 295 * 296 * @param symbol A pointer to the symbol. 297 * @return The target address or nullptr if the symbol is not defined. 298 */ 299 void *getSymbolTarget(const ElfSym *symbol); 300 301 /** 302 * Retrieves the name of the section header located at the given offset in 303 * the section name table. 304 * 305 * @param headerOffset The offset in the section names table where the 306 * header is located. 307 * @return The section's name or the empty string if the offset is 0. 308 */ 309 const char *getSectionHeaderName(size_t headerOffset); 310 311 /** 312 * Frees any data that was allocated as part of loading the ELF into memory. 313 */ 314 void freeAllocatedData(); 315 316 /** 317 * Ensures the BSS section is properly mapped into memory. If there is a 318 * difference between the size of the BSS section in the ELF binary and the 319 * size it needs to be in memory, the rest of the section is zeroed out. 320 * 321 * @param header The ProgramHeader of the BSS section that is being mapped in. 322 */ 323 void mapBss(const ProgramHeader *header); 324 325 /** 326 * Resolves the address of an undefined symbol located at the given position 327 * in the symbol table. This symbol must be defined and exposed by the given 328 * platform in order for it to be resolved successfully. 329 * 330 * @param posInSymbolTable The position of the undefined symbol in the symbol 331 * table. 332 * @return The address of the resolved symbol. nullptr if not found. 333 */ 334 void *resolveData(size_t posInSymbolTable); 335 336 /** 337 * @return The address for the dynamic segment. nullptr if not found. 338 */ 339 DynamicHeader *getDynamicHeader(); 340 341 /** 342 * @return The address of the first read-only segment. nullptr if not found. 343 */ 344 ProgramHeader *getFirstRoSegHeader(); 345 346 /** 347 * Retrieves the section header with the given name. 348 * 349 * @param headerName The name of the section header to find. 350 * @return The address of the section. nullptr if not found. 351 */ 352 SectionHeader *getSectionHeader(const char *headerName); 353 354 /** 355 * @return The ELF header for the binary being loaded. nullptr if it doesn't 356 * exist or no binary is being loaded. 357 */ 358 ElfHeader *getElfHeader(); 359 360 /** 361 * @return The array of program headers for the binary being loaded. nullptr 362 * if it doesn't exist or no binary is being loaded. 363 */ 364 ProgramHeader *getProgramHeaderArray(); 365 366 /** 367 * @return The size of the array of program headers for the binary being 368 * loaded. 0 if it doesn't exist or no binary is being loaded. 369 */ 370 size_t getProgramHeaderArraySize(); 371 372 /** 373 * @return An array of characters containing the symbol names for dynamic 374 * symbols inside the binary being loaded. nullptr if it doesn't exist or 375 * no binary is being loaded. 376 */ 377 char *getDynamicStringTable(); 378 379 /** 380 * @return An array of dynamic symbol information for the binary being loaded. 381 * nullptr if it doesn't exist or no binary is being loaded. 382 */ 383 uint8_t *getDynamicSymbolTable(); 384 385 /** 386 * @return The size of the array of dynamic symbol information for the binary 387 * being loaded. 0 if it doesn't exist or no binary is being loaded. 388 */ 389 size_t getDynamicSymbolTableSize(); 390 391 /** 392 * Returns the first entry in the dynamic header that has a tag that matches 393 * the given field. 394 * 395 * @param dyn The dynamic header for the binary. 396 * @param field The field to be searched for. 397 * @return The value found at the entry. 0 if the entry isn't found. 398 */ 399 static ElfWord getDynEntry(DynamicHeader *dyn, int field); 400 401 /** 402 * Handle reolcation for entries in the specified table. 403 * 404 * @param dyn The dynamic header for the binary. 405 * @param tableTag The dynamic tag (DT_x) of the relocation table. 406 * @return True if success or unsupported, false if failure. 407 */ 408 bool relocateTable(DynamicHeader *dyn, int tableTag); 409 }; 410 411 } // namespace chre 412 413 #endif // CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_ 414