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 27 namespace chre { 28 29 /** 30 * Provides dynamic loading support for nanoapps on FreeRTOS-based platforms. 31 * At a high level, this class is responsible for mapping the provided binary 32 * into CHRE's address space, relocating and resolving symbols, and initializing 33 * and freeing static data. 34 */ 35 class NanoappLoader { 36 public: 37 NanoappLoader() = delete; 38 NanoappLoader(void * elfInput,bool mapIntoTcm)39 explicit NanoappLoader(void *elfInput, bool mapIntoTcm) { 40 mBinary = static_cast<uint8_t *>(elfInput); 41 mIsTcmBinary = mapIntoTcm; 42 } 43 44 /** 45 * Factory method to create a NanoappLoader Instance after loading 46 * the buffer containing the ELF binary. 47 * 48 * @param elfInput Buffer containing the elf file 49 * @param mapIntoTcm Indicates whether the elfBinary should be mapped into 50 * tightly coupled memory. 51 * @return Class instance on successful load and verification, 52 * nullptr otherwise. 53 */ 54 static void *create(void *elfInput, bool mapIntoTcm); 55 56 /** 57 * Closes and destroys the NanoappLoader instance. 58 * 59 * @param loader A non-null pointer to the loader that must be destroyed. 60 */ 61 static void destroy(NanoappLoader *loader); 62 63 /** 64 * Attempts to locate the exported symbol specified by the given function 65 * name. 66 * 67 * @param name A null-terminated char array that is the name of the function 68 * to be found. 69 * @return The address of the function. nullptr if not found. 70 */ 71 static void *findExportedSymbol(const char *name); 72 73 /** 74 * Method for pointer lookup by symbol name. Only function pointers 75 * are currently supported. 76 * 77 * @return function pointer on successful lookup, nullptr otherwise 78 */ 79 void *findSymbolByName(const char *name); 80 81 /** 82 * Registers a function provided through atexit during static initialization 83 * that should be called prior to unloading a nanoapp. 84 * 85 * @param function Function that should be invoked prior to unloading a 86 * nanoapp. 87 */ 88 void registerAtexitFunction(void (*function)(void)); 89 90 private: 91 /** 92 * Opens the ELF binary. This maps the binary into memory, resolves symbols, 93 * and invokes any static initializers. 94 * 95 * @return true if all required opening steps were completed. 96 */ 97 bool open(); 98 99 /** 100 * Closes the loader, freeing any state associated with the loaded ELF binary 101 * and unmapping it from memory. Prior to unmapping from memory, any static 102 * termination functions will be invoked. 103 */ 104 void close(); 105 106 using DynamicHeader = ElfW(Dyn); 107 using ElfAddr = ElfW(Addr); 108 using ElfHeader = ElfW(Ehdr); 109 using ElfRel = ElfW(Rel); // Relocation table entry, 110 // in section of type SHT_REL 111 using ElfRela = ElfW(Rela); 112 using ElfSym = ElfW(Sym); 113 using ElfWord = ElfW(Word); 114 using ProgramHeader = ElfW(Phdr); 115 using SectionHeader = ElfW(Shdr); 116 117 //! Name of various segments in the ELF that need to be looked up 118 static constexpr const char *kSymTableName = ".symtab"; 119 static constexpr const char *kStrTableName = ".strtab"; 120 static constexpr const char *kInitArrayName = ".init_array"; 121 static constexpr const char *kFiniArrayName = ".fini_array"; 122 123 //! Pointer to the table of all the section names. 124 char *mSectionNamesPtr = nullptr; 125 //! Pointer to the table of symbol names of defined symbols. 126 char *mStringTablePtr = nullptr; 127 //! Pointer to the table of symbol information for defined symbols. 128 uint8_t *mSymbolTablePtr = nullptr; 129 //! Pointer to the array of section header entries. 130 SectionHeader *mSectionHeadersPtr = nullptr; 131 //! Number of SectionHeaders pointed to by mSectionHeadersPtr. 132 size_t mNumSectionHeaders = 0; 133 //! Size of the data pointed to by mSymbolTablePtr. 134 size_t mSymbolTableSize = 0; 135 136 //! The ELF that is being mapped into the system. This pointer will be invalid 137 //! after open returns. 138 uint8_t *mBinary = nullptr; 139 //! The starting location of the memory that has been mapped into the system. 140 uint8_t *mMapping = nullptr; 141 //! The difference between where the first load segment was mapped into 142 //! virtual memory and what the virtual load offset was of that segment. 143 ElfAddr mLoadBias = 0; 144 //! Dynamic vector containing functions that should be invoked prior to 145 //! unloading this nanoapp. Note that functions are stored in the order they 146 //! were added and should be called in reverse. 147 DynamicVector<void (*)(void)> mAtexitFunctions; 148 //! Whether this loader instance is managing a TCM nanoapp binary. 149 bool mIsTcmBinary = false; 150 151 /** 152 * Invokes all functions registered via atexit during static initialization. 153 */ 154 void callAtexitFunctions(); 155 156 /** 157 * Invokes all initialization functions in .init_array segment. 158 * 159 * @return true if static initialization succeeded. 160 */ 161 bool callInitArray(); 162 163 /** 164 * Invokes all termination functions in the .fini_array segment. 165 */ 166 void callTerminatorArray(); 167 168 /** 169 * Allocates memory for all load segments that need to be mapped into virtual 170 * memory and copies the load segments into the newly allocated memory. 171 * 172 * @return true if the memory for mapping was allocated and the load segments 173 * were formatted correctly. 174 */ 175 bool createMappings(); 176 177 /** 178 * Copies various sections and headers from the ELF while verifying that they 179 * match the ELF format specification. 180 * 181 * @return true if all data was copied and verified. 182 */ 183 bool copyAndVerifyHeaders(); 184 185 /** 186 * Resolves all relocated symbols located in the DT_REL table. 187 * 188 * @return true if all relocated symbols were resolved. 189 */ 190 bool fixRelocations(); 191 192 /** 193 * Resolves entries in the Global Offset Table (GOT) to facility the ELF's 194 * compiled using position independent code (PIC). 195 * 196 * @return true if all symbols were resolved. 197 */ 198 bool resolveGot(); 199 200 /** 201 * Verifies the ELF header has correct values based on the ELF spec. 202 * 203 * @return true if the header passed verification. 204 */ 205 bool verifyElfHeader(); 206 207 /** 208 * Verifies basic information about program headers. 209 * 210 * @return true if the headers passed verification. 211 */ 212 bool verifyProgramHeaders(); 213 214 /** 215 * Verifies basic information about section headers. 216 * 217 * @return true if the headers passed verification. 218 */ 219 bool verifySectionHeaders(); 220 221 /** 222 * Retrieves the symbol name of data located at the given position in the 223 * symbol table. 224 * 225 * @param posInSymbolTable The position in the symbol table where information 226 * about the symbol can be found. 227 * @return The symbol's name or nullptr if not found. 228 */ 229 const char *getDataName(size_t posInSymbolTable); 230 231 /** 232 * Retrieves the name of the section header located at the given offset in the 233 * section name table. 234 * 235 * @param headerOffset The offset in the section names table where the header 236 * is located. 237 * @return The section's name or the empty string if the offset is 0. 238 */ 239 const char *getSectionHeaderName(size_t headerOffset); 240 241 /** 242 * Rounds the given address down to the closest alignment boundary. 243 * 244 * @param virtualAddr The address to be rounded. 245 * @param alignment Alignment to which the address is rounded to. 246 * @return An address that is a multiple of the platform's alignment and is 247 * less than or equal to virtualAddr. 248 */ 249 uintptr_t roundDownToAlign(uintptr_t virtualAddr, size_t alignment); 250 251 /** 252 * Frees any data that was allocated as part of loading the ELF into memory. 253 */ 254 void freeAllocatedData(); 255 256 /** 257 * Ensures the BSS section is properly mapped into memory. If there is a 258 * difference between the size of the BSS section in the ELF binary and the 259 * size it needs to be in memory, the rest of the section is zeroed out. 260 * 261 * @param header The ProgramHeader of the BSS section that is being mapped in. 262 */ 263 void mapBss(const ProgramHeader *header); 264 265 /** 266 * Resolves the address of an undefined symbol located at the given position 267 * in the symbol table. This symbol must be defined and exposed by the given 268 * platform in order for it to be resolved successfully. 269 * 270 * @param posInSymbolTable The position of the undefined symbol in the symbol 271 * table. 272 * @return The address of the resolved symbol. nullptr if not found. 273 */ 274 void *resolveData(size_t posInSymbolTable); 275 276 /** 277 * @return The address for the dynamic segment. nullptr if not found. 278 */ 279 DynamicHeader *getDynamicHeader(); 280 281 /** 282 * @return The address of the first read-only segment. nullptr if not found. 283 */ 284 ProgramHeader *getFirstRoSegHeader(); 285 286 /** 287 * Retrieves the section header with the given name. 288 * 289 * @param headerName The name of the section header to find. 290 * @return The address of the section. nullptr if not found. 291 */ 292 SectionHeader *getSectionHeader(const char *headerName); 293 294 /** 295 * @return The ELF header for the binary being loaded. nullptr if it doesn't 296 * exist or no binary is being loaded. 297 */ 298 ElfHeader *getElfHeader(); 299 300 /** 301 * @return The array of program headers for the binary being loaded. nullptr 302 * if it doesn't exist or no binary is being loaded. 303 */ 304 ProgramHeader *getProgramHeaderArray(); 305 306 /** 307 * @return The size of the array of program headers for the binary being 308 * loaded. 0 if it doesn't exist or no binary is being loaded. 309 */ 310 size_t getProgramHeaderArraySize(); 311 312 /** 313 * @return An array of characters containing the symbol names for dynamic 314 * symbols inside the binary being loaded. nullptr if it doesn't exist or 315 * no binary is being loaded. 316 */ 317 char *getDynamicStringTable(); 318 319 /** 320 * @return An array of dynamic symbol information for the binary being loaded. 321 * nullptr if it doesn't exist or no binary is being loaded. 322 */ 323 uint8_t *getDynamicSymbolTable(); 324 325 /** 326 * @return The size of the array of dynamic symbol information for the binary 327 * being loaded. 0 if it doesn't exist or no binary is being loaded. 328 */ 329 size_t getDynamicSymbolTableSize(); 330 331 /** 332 * Returns the first entry in the dynamic header that has a tag that matches 333 * the given field. 334 * 335 * @param dyn The dynamic header for the binary. 336 * @param field The field to be searched for. 337 * @return The value found at the entry. 0 if the entry isn't found. 338 */ 339 static ElfWord getDynEntry(DynamicHeader *dyn, int field); 340 }; 341 342 } // namespace chre 343 344 #endif // CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_ 345