• 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 
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