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 // For now, assume all segments are 4K aligned. 123 static constexpr size_t kBinaryAlignment = 4096; 124 125 //! Pointer to the table of all the section names. 126 char *mSectionNamesPtr = nullptr; 127 //! Pointer to the table of symbol names of defined symbols. 128 char *mStringTablePtr = nullptr; 129 //! Pointer to the table of symbol information for defined symbols. 130 uint8_t *mSymbolTablePtr = nullptr; 131 //! Pointer to the array of section header entries. 132 SectionHeader *mSectionHeadersPtr = nullptr; 133 //! Number of SectionHeaders pointed to by mSectionHeadersPtr. 134 size_t mNumSectionHeaders = 0; 135 //! Size of the data pointed to by mSymbolTablePtr. 136 size_t mSymbolTableSize = 0; 137 138 //! The ELF that is being mapped into the system. This pointer will be invalid 139 //! after open returns. 140 uint8_t *mBinary = nullptr; 141 //! The starting location of the memory that has been mapped into the system. 142 uint8_t *mMapping = nullptr; 143 //! The difference between where the first load segment was mapped into 144 //! virtual memory and what the virtual load offset was of that segment. 145 ElfAddr mLoadBias = 0; 146 //! Dynamic vector containing functions that should be invoked prior to 147 //! unloading this nanoapp. Note that functions are stored in the order they 148 //! were added and should be called in reverse. 149 DynamicVector<void (*)(void)> mAtexitFunctions; 150 //! Whether this loader instance is managing a TCM nanoapp binary. 151 bool mIsTcmBinary = false; 152 153 /** 154 * Invokes all functions registered via atexit during static initialization. 155 */ 156 void callAtexitFunctions(); 157 158 /** 159 * Invokes all initialization functions in .init_array segment. 160 * 161 * @return true if static initialization succeeded. 162 */ 163 bool callInitArray(); 164 165 /** 166 * Invokes all termination functions in the .fini_array segment. 167 */ 168 void callTerminatorArray(); 169 170 /** 171 * Allocates memory for all load segments that need to be mapped into virtual 172 * memory and copies the load segments into the newly allocated memory. 173 * 174 * @return true if the memory for mapping was allocated and the load segments 175 * were formatted correctly. 176 */ 177 bool createMappings(); 178 179 /** 180 * Copies various sections and headers from the ELF while verifying that they 181 * match the ELF format specification. 182 * 183 * @return true if all data was copied and verified. 184 */ 185 bool copyAndVerifyHeaders(); 186 187 /** 188 * Resolves all relocated symbols located in the DT_REL table. 189 * 190 * @return true if all relocated symbols were resolved. 191 */ 192 bool fixRelocations(); 193 194 /** 195 * Resolves entries in the Global Offset Table (GOT) to facility the ELF's 196 * compiled using position independent code (PIC). 197 * 198 * @return true if all symbols were resolved. 199 */ 200 bool resolveGot(); 201 202 /** 203 * Verifies the ELF header has correct values based on the ELF spec. 204 * 205 * @return true if the header passed verification. 206 */ 207 bool verifyElfHeader(); 208 209 /** 210 * Verifies basic information about program headers. 211 * 212 * @return true if the headers passed verification. 213 */ 214 bool verifyProgramHeaders(); 215 216 /** 217 * Verifies basic information about section headers. 218 * 219 * @return true if the headers passed verification. 220 */ 221 bool verifySectionHeaders(); 222 223 /** 224 * Retrieves the symbol name of data located at the given position in the 225 * symbol table. 226 * 227 * @param posInSymbolTable The position in the symbol table where information 228 * about the symbol can be found. 229 * @return The symbol's name or nullptr if not found. 230 */ 231 const char *getDataName(size_t posInSymbolTable); 232 233 /** 234 * Retrieves the name of the section header located at the given offset in the 235 * section name table. 236 * 237 * @param headerOffset The offset in the section names table where the header 238 * is located. 239 * @return The section's name or the empty string if the offset is 0. 240 */ 241 const char *getSectionHeaderName(size_t headerOffset); 242 243 /** 244 * Rounds the given address down to the closest alignment boundary. 245 * 246 * @param virtualAddr The address to be rounded. 247 * @return An address that is a multiple of the platform's alignment and is 248 * less than or equal to virtualAddr. 249 */ 250 uintptr_t roundDownToAlign(uintptr_t virtualAddr); 251 252 /** 253 * Frees any data that was allocated as part of loading the ELF into memory. 254 */ 255 void freeAllocatedData(); 256 257 /** 258 * Ensures the BSS section is properly mapped into memory. If there is a 259 * difference between the size of the BSS section in the ELF binary and the 260 * size it needs to be in memory, the rest of the section is zeroed out. 261 * 262 * @param header The ProgramHeader of the BSS section that is being mapped in. 263 */ 264 void mapBss(const ProgramHeader *header); 265 266 /** 267 * Resolves the address of an undefined symbol located at the given position 268 * in the symbol table. This symbol must be defined and exposed by the given 269 * platform in order for it to be resolved successfully. 270 * 271 * @param posInSymbolTable The position of the undefined symbol in the symbol 272 * table. 273 * @return The address of the resolved symbol. nullptr if not found. 274 */ 275 void *resolveData(size_t posInSymbolTable); 276 277 /** 278 * @return The address for the dynamic segment. nullptr if not found. 279 */ 280 DynamicHeader *getDynamicHeader(); 281 282 /** 283 * @return The address of the first read-only segment. nullptr if not found. 284 */ 285 ProgramHeader *getFirstRoSegHeader(); 286 287 /** 288 * Retrieves the section header with the given name. 289 * 290 * @param headerName The name of the section header to find. 291 * @return The address of the section. nullptr if not found. 292 */ 293 SectionHeader *getSectionHeader(const char *headerName); 294 295 /** 296 * @return The ELF header for the binary being loaded. nullptr if it doesn't 297 * exist or no binary is being loaded. 298 */ 299 ElfHeader *getElfHeader(); 300 301 /** 302 * @return The array of program headers for the binary being loaded. nullptr 303 * if it doesn't exist or no binary is being loaded. 304 */ 305 ProgramHeader *getProgramHeaderArray(); 306 307 /** 308 * @return The size of the array of program headers for the binary being 309 * loaded. 0 if it doesn't exist or no binary is being loaded. 310 */ 311 size_t getProgramHeaderArraySize(); 312 313 /** 314 * @return An array of characters containing the symbol names for dynamic 315 * symbols inside the binary being loaded. nullptr if it doesn't exist or 316 * no binary is being loaded. 317 */ 318 char *getDynamicStringTable(); 319 320 /** 321 * @return An array of dynamic symbol information for the binary being loaded. 322 * nullptr if it doesn't exist or no binary is being loaded. 323 */ 324 uint8_t *getDynamicSymbolTable(); 325 326 /** 327 * @return The size of the array of dynamic symbol information for the binary 328 * being loaded. 0 if it doesn't exist or no binary is being loaded. 329 */ 330 size_t getDynamicSymbolTableSize(); 331 332 /** 333 * Returns the first entry in the dynamic header that has a tag that matches 334 * the given field. 335 * 336 * @param dyn The dynamic header for the binary. 337 * @param field The field to be searched for. 338 * @return The value found at the entry. 0 if the entry isn't found. 339 */ 340 static ElfWord getDynEntry(DynamicHeader *dyn, int field); 341 }; 342 343 } // namespace chre 344 345 #endif // CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_ 346