1 /* libunwind - a platform-independent unwind library
2 Copyright (C) 2003-2005 Hewlett-Packard Co
3 Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5 This file is part of libunwind.
6
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
25
26 #include <limits.h>
27 #include <stdio.h>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30
31 #include "compiler.h"
32 #include "elfxx.h"
33 #include "libunwind_i.h"
34 #include "map_info.h"
35 #include "os-linux.h"
36
37 HIDDEN struct map_info *
maps_create_list(pid_t pid)38 maps_create_list(pid_t pid)
39 {
40 struct map_iterator mi;
41 unsigned long start, end, offset, flags;
42 struct map_info *map_list = NULL;
43 struct map_info *cur_map;
44 struct map_info *buf;
45 int sz;
46 int index = 0;
47 if ((sz = maps_init (&mi, pid)) < 0)
48 return NULL;
49
50 if (sz < 0 || sz > 65536) {
51 return NULL;
52 }
53
54 int buf_sz = sz + 256;
55 buf = (struct map_info*)mmap(NULL, buf_sz * sizeof(struct map_info), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
56 if (buf == NULL) {
57 return NULL;
58 }
59
60 while (maps_next (&mi, &start, &end, &offset, &flags))
61 {
62 if (index >= buf_sz) {
63 Dprintf("Lost Map:%p-%p %s\n", (void*)start, (void*)end, mi.path);
64 continue;
65 }
66
67 cur_map = &buf[index];
68 cur_map->next = map_list;
69 cur_map->start = start;
70 cur_map->end = end;
71 cur_map->offset = offset;
72 cur_map->flags = flags;
73 cur_map->path = strdup(mi.path);
74 cur_map->ei.size = 0;
75 cur_map->ei.image = NULL;
76 cur_map->ei.has_dyn_info = 0;
77 cur_map->ei.load_bias = -1;
78 cur_map->ei.strtab = NULL;
79 cur_map->ei.lib_name_offset = 0;
80 cur_map->sz = sz;
81 cur_map->buf = buf;
82 cur_map->buf_sz = buf_sz;
83 map_list = cur_map;
84 index = index + 1;
85 }
86 if (map_list != NULL) {
87 map_list->sz = index;
88 } else {
89 munmap(buf, buf_sz * sizeof(struct map_info));
90 }
91 maps_close (&mi);
92 Debug(12, "Finish create map list, sz:%d, index%d.\n", sz, index);
93 return map_list;
94 }
95
96 HIDDEN void
maps_destroy_list(struct map_info * map_info)97 maps_destroy_list(struct map_info *map_info)
98 {
99 struct map_info *map;
100 int buf_sz = map_info->buf_sz;
101 void* buf = map_info->buf;
102 while (map_info)
103 {
104 map = map_info;
105 map_info = map->next;
106 if (map->ei.image != MAP_FAILED && map->ei.image != NULL) {
107 munmap(map->ei.image, map->ei.size);
108 map->ei.image = NULL;
109 }
110
111 if (map->mdi.image != MAP_FAILED && map->mdi.image != NULL) {
112 munmap(map->mdi.image, map->mdi.size);
113 map->mdi.image = NULL;
114 }
115
116 if (map->path) {
117 free(map->path);
118 map->path = NULL;
119 }
120 map = NULL;
121 }
122 if (buf != NULL) {
123 munmap(buf, buf_sz * sizeof(struct map_info));
124 buf = NULL;
125 }
126 }
127
128 HIDDEN struct map_info *
get_map(struct map_info * map_list,unw_word_t addr)129 get_map(struct map_info *map_list, unw_word_t addr)
130 {
131 if (map_list == NULL) {
132 return NULL;
133 }
134
135 struct map_info* buf = map_list->buf;
136 if (buf == NULL) {
137 return NULL;
138 }
139
140 int begin = 0;
141 int end = map_list->sz - 1;
142 while (begin <= end) {
143 int mid = begin + ((end - begin) / 2);
144 if (addr < buf[mid].start) {
145 end = mid - 1;
146 } else if (addr <= buf[mid].end) {
147 return &buf[mid];
148 } else {
149 begin = mid + 1;
150 }
151 }
152
153 if ((addr >= buf[begin].start) && (addr <= buf[begin].end)) {
154 return &buf[begin];
155 }
156
157 Dprintf("Could not find map for addr:%p\n", (void*)addr);
158 return NULL;
159 }
160
161 HIDDEN int
maps_is_readable(struct map_info * map_list,unw_word_t addr)162 maps_is_readable(struct map_info *map_list, unw_word_t addr)
163 {
164 #ifndef UNW_LOCAL_ONLY
165 if (map_list == NULL)
166 return 1;
167 #endif
168 struct map_info *map = get_map(map_list, addr);
169 if (map != NULL)
170 return map->flags & PROT_READ;
171 return 0;
172 }
173
174 #ifdef PARSE_ELF_IN_HAP
map_elf_in_hap(struct map_info * map)175 static int map_elf_in_hap(struct map_info *map)
176 {
177 // elf header is in the first mmap area
178 // c3840000-c38a6000 r--p 00174000 /data/storage/el1/bundle/entry.hap <- program header
179 // c38a6000-c3945000 r-xp 001d9000 /data/storage/el1/bundle/entry.hap <- pc is in this region
180 // c3945000-c394b000 r--p 00277000 /data/storage/el1/bundle/entry.hap
181 // c394b000-c394c000 rw-p 0027c000 /data/storage/el1/bundle/entry.hap
182 if (map->next == NULL) {
183 Dprintf("no prev map exist.\n");
184 return -1;
185 }
186
187 struct map_info* prev = map->next;
188 struct stat stat;
189 int fd;
190 fd = UNW_TEMP_FAILURE_RETRY (open (map->path, O_RDONLY));
191 if (fd < 0) {
192 Dprintf("failed to open hap file.(%d)\n", errno);
193 return -1;
194 }
195
196 if (fstat (fd, &stat) < 0) {
197 Dprintf("failed to stat hap file sz.(%d)\n", errno);
198 close (fd);
199 return -1;
200 }
201
202 size_t size = prev->end - prev->start;
203 void* elf = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, prev->offset);
204 if (elf == MAP_FAILED) {
205 Dprintf("failed to map program header in hap.(%d)\n", errno);
206 close (fd);
207 return -1;
208 }
209
210 map->ei.size = calc_elf_file_size(elf, size);
211 // maybe we should get it from phdr.
212 map->offset = map->offset - prev->offset;
213 munmap(elf, size);
214 if (map->ei.size <= 0 || map->ei.size > (size_t)(stat.st_size - prev->offset))
215 {
216 Dprintf("invalid elf size? sz:%d, hap sz:%d", (int)map->ei.size, (int)stat.st_size);
217 close (fd);
218 return -1;
219 }
220
221 map->ei.image = mmap (NULL, map->ei.size, PROT_READ, MAP_PRIVATE, fd, prev->offset);
222 close (fd);
223 if (map->ei.image == MAP_FAILED) {
224 Dprintf("failed to map so in hap.\n");
225 return -1;
226 }
227
228 return 0;
229 }
230 #endif
231
232 HIDDEN int
maps_is_writable(struct map_info * map_list,unw_word_t addr)233 maps_is_writable(struct map_info *map_list, unw_word_t addr)
234 {
235 #ifndef UNW_LOCAL_ONLY
236 if (map_list == NULL)
237 return 1;
238 #endif
239 struct map_info *map = get_map(map_list, addr);
240 if (map != NULL)
241 return map->flags & PROT_WRITE;
242 return 0;
243 }
244
245 HIDDEN struct map_info*
tdep_get_elf_image(unw_addr_space_t as,pid_t pid,unw_word_t ip)246 tdep_get_elf_image(unw_addr_space_t as, pid_t pid, unw_word_t ip)
247 {
248 struct map_info *map;
249 struct cursor* cursor = get_cursor_from_as(as);
250 int find_cached_map = ((cursor != NULL) && (cursor->dwarf.ip == ip));
251 if (find_cached_map &&
252 (cursor->dwarf.ip == cursor->dwarf.cached_ip) && cursor->dwarf.cached_map != NULL) {
253 return cursor->dwarf.cached_map;
254 }
255
256 if (as->map_list == NULL && pid > 0) {
257 as->map_list = maps_create_list(pid);
258 if (as->map_list == NULL) {
259 Dprintf("Failed to maps_create_list for pid:%d\n", pid);
260 return NULL;
261 }
262 }
263
264 map = get_map(as->map_list, ip); // ip must located in executable map region
265 if (!map)
266 return NULL;
267
268 if (map->ei.image == NULL)
269 {
270 if (strstr(map->path, ".hap") != NULL) {
271 #ifdef PARSE_ELF_IN_HAP
272 if (map_elf_in_hap(map) < 0) {
273 Dprintf("invalid map_elf_in_hap?\n");
274 return NULL;
275 }
276 #else
277 Dprintf("Unsupport map_elf_in_hap\n");
278 return NULL;
279 #endif
280 } else {
281 if (elf_map_image(&map->ei, map->path) < 0) {
282 map->ei.image = NULL;
283 map->ei.has_try_load = 1;
284 Dprintf("Failed to elf_map_image for ip:%p\n", (void*)ip);
285 return NULL;
286 }
287 }
288 map->ei.mdi = &(map->mdi);
289 }
290
291 if (find_cached_map) {
292 cursor->dwarf.cached_map = map;
293 cursor->dwarf.cached_ip = ip;
294 }
295 return map;
296 }
297
get_previous_instr_sz(unw_cursor_t * cursor)298 unw_word_t get_previous_instr_sz(unw_cursor_t *cursor)
299 {
300 struct cursor *c = (struct cursor *) cursor;
301 unw_addr_space_t as = c->dwarf.as;
302 unw_accessors_t *a = unw_get_accessors (as);
303 unw_word_t ip = c->dwarf.ip;
304 int sz = 4;
305 #if defined(UNW_TARGET_ARM)
306 if (ip)
307 {
308 if (ip & 1)
309 {
310 void *arg;
311 unw_word_t value;
312 arg = c->dwarf.as_arg;
313 // 0xe000f000 ---> machine code of blx Instr (blx label)
314 if (ip < 5 || (*a->access_mem) (as, ip - 5, &value, 0, arg) < 0 ||
315 (value & 0xe000f000) != 0xe000f000)
316 sz = 2;
317 }
318 }
319 #elif defined(UNW_TARGET_ARM64)
320 sz = 4;
321 #elif defined(UNW_TARGET_X86)
322 sz = 1;
323 #elif defined(UNW_TARGET_x86_64)
324 sz = 1;
325 #else
326 // other arch need to be add here.
327 #endif
328 return sz;
329 }
330
331
332 #ifndef UNW_REMOTE_ONLY
333
334 void
tdep_get_exe_image_path(char * path)335 tdep_get_exe_image_path (char *path)
336 {
337 strcpy(path, "/proc/self/exe");
338 }
339
340 #endif /* !UNW_REMOTE_ONLY */
341