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