• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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