1 /*
2  * Copyright (c) 2020 Google Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <lib/backtrace/symbolize.h>
25 #include <trace.h>
26 
27 #include "elf_sym.h"
28 
29 #define LOCAL_TRACE 0
30 
31 #undef ELF_64BIT
32 #if !IS_64BIT || USER_32BIT
33 #define ELF_64BIT 0
34 #else
35 #define ELF_64BIT 1
36 #endif
37 
38 #if ELF_64BIT
39 #define ELF_SHDR Elf64_Shdr
40 #define ELF_EHDR Elf64_Ehdr
41 #define ELF_SYM Elf64_Sym
42 #else
43 #define ELF_SHDR Elf32_Shdr
44 #define ELF_EHDR Elf32_Ehdr
45 #define ELF_SYM Elf32_Sym
46 #endif
47 
range_within_app_img(uintptr_t start,size_t size,struct trusty_app_img * app_img)48 static inline bool range_within_app_img(uintptr_t start,
49                                         size_t size,
50                                         struct trusty_app_img* app_img) {
51     uintptr_t end;
52     if (__builtin_add_overflow(start, size, &end)) {
53         return false;
54     }
55     return app_img->img_start <= start && end <= app_img->img_end;
56 }
57 
range_within_range(uintptr_t start0,size_t size0,uintptr_t start1,size_t size1)58 static inline bool range_within_range(uintptr_t start0,
59                                       size_t size0,
60                                       uintptr_t start1,
61                                       size_t size1) {
62     uintptr_t end0;
63     if (__builtin_add_overflow(start0, size0, &end0)) {
64         return false;
65     }
66     uintptr_t end1;
67     if (__builtin_add_overflow(start1, size1, &end1)) {
68         return false;
69     }
70     return start1 <= start0 && end0 <= end1;
71 }
72 
trusty_app_symbolize(struct trusty_app * app,uintptr_t pc,struct pc_symbol_info * info)73 int trusty_app_symbolize(struct trusty_app* app,
74                          uintptr_t pc,
75                          struct pc_symbol_info* info) {
76     if (!app) {
77         goto out_no_symbol;
78     }
79     /* Adjust pc to be relative to app image */
80     if (__builtin_sub_overflow(pc, app->load_bias, &pc)) {
81         goto out_no_symbol;
82     }
83     /* pc must be within the app image */
84     struct trusty_app_img* app_img = &app->app_img;
85     if (app_img->img_end <= app_img->img_start) {
86         goto out_no_symbol;
87     }
88     if (pc > app_img->img_end - app_img->img_start) {
89         goto out_no_symbol;
90     }
91 
92     ELF_EHDR* ehdr = (ELF_EHDR*)app_img->img_start;
93     ELF_SHDR* shdr = (ELF_SHDR*)((uintptr_t)ehdr + ehdr->e_shoff);
94 
95     ELF_SHDR* symtab_shdr = NULL;
96     ELF_SHDR* strtab_shdr = NULL;
97 
98     /* Find section headers for .symtab and .strtab */
99     for (size_t i = 0; i < ehdr->e_shnum; i++) {
100         if (shdr[i].sh_type == SHT_SYMTAB) {
101             symtab_shdr = shdr + i;
102         }
103         if (shdr[i].sh_type == SHT_STRTAB) {
104             strtab_shdr = shdr + i;
105         }
106     }
107 
108     /* Handle the case when app is not built with .symtab or .strtab */
109     if (!symtab_shdr || !strtab_shdr) {
110         LTRACEF("App built without symbol table\n");
111         goto out_no_symbol;
112     }
113 
114     uintptr_t symtab_start = app_img->img_start + symtab_shdr->sh_offset;
115     size_t symtab_size = symtab_shdr->sh_size;
116     uintptr_t strtab_start = app_img->img_start + strtab_shdr->sh_offset;
117     size_t strtab_size = strtab_shdr->sh_size;
118 
119     /* Validate .symtab and .strtab locations */
120     if (!range_within_app_img(symtab_start, symtab_size, app_img)) {
121         TRACEF(".symtab section is not within the app image\n");
122         goto out_no_symbol;
123     }
124     if (!range_within_app_img(strtab_start, strtab_size, app_img)) {
125         TRACEF(".strtab section is not within the app image\n");
126         goto out_no_symbol;
127     }
128 
129     /* Find closest symbol preceding pc */
130     info->offset = ULONG_MAX;
131     for (uintptr_t curr = symtab_start;
132          curr < symtab_start + symtab_shdr->sh_size;
133          curr += symtab_shdr->sh_entsize) {
134         /* Entry must be within .symtab section */
135         if (!range_within_range(curr, symtab_shdr->sh_entsize, symtab_start,
136                                 symtab_size)) {
137             TRACEF(".symtab section is malformed\n");
138             goto out_no_symbol;
139         }
140 
141         ELF_SYM* symtab_entry = (ELF_SYM*)curr;
142         /* We are looking for a symbol of a function */
143         if (ELF_ST_TYPE(symtab_entry->st_info) != STT_FUNC) {
144             continue;
145         }
146 
147         uintptr_t func_start = symtab_entry->st_value;
148         if (func_start <= pc && info->offset > pc - func_start) {
149             /* Offset must be within .strtab section */
150             if (symtab_entry->st_name >= strtab_size) {
151                 TRACEF(".strtab section is malformed\n");
152                 goto out_no_symbol;
153             }
154 
155             info->symbol = (const char*)(strtab_start + symtab_entry->st_name);
156             info->offset = pc - func_start;
157             info->size = symtab_entry->st_size;
158         }
159     }
160 
161     if (info->offset == ULONG_MAX) {
162         goto out_no_symbol;
163     }
164     return NO_ERROR;
165 
166 out_no_symbol:
167     info->symbol = NULL;
168     info->offset = 0;
169     info->size = 0;
170     return ERR_NOT_FOUND;
171 }
172