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