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 index = 0;
46 if ((maps_init (&mi, pid)) < 0)
47 return NULL;
48 int buf_sz = 1024;
49 buf = (struct map_info*)mmap(NULL, buf_sz * sizeof(struct map_info), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
50 if (buf == NULL) {
51 return NULL;
52 }
53
54 while (maps_next (&mi, &start, &end, &offset, &flags))
55 {
56 if (index >= buf_sz) {
57 struct map_info *newBuf = (struct map_info *)mmap(NULL, 2 * buf_sz * sizeof(struct map_info), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
58 if (buf != NULL && newBuf != NULL) {
59 memcpy(newBuf, buf, buf_sz * sizeof(struct map_info));
60 munmap(buf, buf_sz * sizeof(struct map_info));
61 buf_sz *= 2;
62 buf = newBuf;
63 } else {
64 Dprintf("Lost Map:%p-%p %s\n", (void*)start, (void*)end, mi.path);
65 continue;
66 }
67 }
68 char *match = strstr(mi.path, "/dev");
69 if (match != NULL && match - mi.path == 0) {
70 continue;
71 }
72 cur_map = &buf[index];
73 cur_map->start = start;
74 cur_map->end = end;
75 cur_map->offset = offset;
76 cur_map->flags = flags;
77 cur_map->path = "";
78 if (mi.path != NULL) {
79 cur_map->path = strdup(mi.path);
80 }
81 cur_map->ei.size = 0;
82 cur_map->ei.image = NULL;
83 cur_map->ei.has_dyn_info = 0;
84 cur_map->ei.load_bias = -1;
85 cur_map->ei.strtab = NULL;
86 cur_map->ei.lib_name_offset = 0;
87 cur_map->sz = buf_sz;
88 cur_map->buf = buf;
89 cur_map->buf_sz = buf_sz;
90 index = index + 1;
91 }
92 if (&buf[0] != NULL) {
93 map_list = &buf[0];
94 map_list->sz = index;
95 map_list->buf = buf;
96 map_list->buf_sz = buf_sz;
97 } else {
98 munmap(buf, buf_sz * sizeof(struct map_info));
99 }
100 maps_close (&mi);
101 Debug(12, "Finish create map list, sz:%d, index%d.\n", sz, index);
102 return map_list;
103 }
104
105 HIDDEN void
maps_destroy_list(struct map_info * map_info)106 maps_destroy_list(struct map_info *map_info)
107 {
108 struct map_info *map;
109 int buf_sz = map_info->buf_sz;
110 void* buf = map_info->buf;
111 int sz = map_info->sz;
112 for (int i = 0; i < sz; i++)
113 {
114 map = &map_info[i];
115 if (map->ei.image != MAP_FAILED && map->ei.image != NULL) {
116 munmap(map->ei.image, map->ei.size);
117 map->ei.image = NULL;
118 }
119
120 if (map->mdi.image != MAP_FAILED && map->mdi.image != NULL) {
121 munmap(map->mdi.image, map->mdi.size);
122 map->mdi.image = NULL;
123 }
124
125 if (map->path) {
126 free(map->path);
127 map->path = NULL;
128 }
129 map = NULL;
130 }
131 if (buf != NULL) {
132 munmap(buf, buf_sz * sizeof(struct map_info));
133 buf = NULL;
134 }
135 }
136
137 HIDDEN struct map_info *
get_map(struct map_info * map_list,unw_word_t addr)138 get_map(struct map_info *map_list, unw_word_t addr)
139 {
140 if (map_list == NULL) {
141 return NULL;
142 }
143
144 struct map_info* buf = map_list->buf;
145 if (buf == NULL) {
146 return NULL;
147 }
148
149 int begin = 0;
150 int end = map_list->sz - 1;
151 while (begin <= end) {
152 int mid = begin + ((end - begin) / 2);
153 if (addr < buf[mid].start) {
154 end = mid - 1;
155 } else if (addr <= buf[mid].end) {
156 return &buf[mid];
157 } else {
158 begin = mid + 1;
159 }
160 }
161
162 if ((begin < map_list->sz) && (addr >= buf[begin].start) && (addr <= buf[begin].end)) {
163 return &buf[begin];
164 }
165
166 Dprintf("Could not find map for addr:%p\n", (void*)addr);
167 return NULL;
168 }
169
170 HIDDEN int
maps_is_readable(struct map_info * map_list,unw_word_t addr)171 maps_is_readable(struct map_info *map_list, unw_word_t addr)
172 {
173 #ifndef UNW_LOCAL_ONLY
174 if (map_list == NULL)
175 return 1;
176 #endif
177 struct map_info *map = get_map(map_list, addr);
178 if (map != NULL)
179 return map->flags & PROT_READ;
180 return 0;
181 }
182
183 #ifdef PARSE_ELF_IN_HAP
map_elf_in_hap(struct map_info * map)184 static int map_elf_in_hap(struct map_info *map)
185 {
186 // elf header is in the first mmap area
187 // c3840000-c38a6000 r--p 00174000 /data/storage/el1/bundle/entry.hap <- program header
188 // c38a6000-c3945000 r-xp 001d9000 /data/storage/el1/bundle/entry.hap <- pc is in this region
189 // c3945000-c394b000 r--p 00277000 /data/storage/el1/bundle/entry.hap
190 // c394b000-c394c000 rw-p 0027c000 /data/storage/el1/bundle/entry.hap
191 if (map - 1 == NULL) {
192 Dprintf("no prev map exist.\n");
193 return -1;
194 }
195
196 struct map_info* prev = map - 1;
197 struct stat stat;
198 int fd;
199 fd = UNW_TEMP_FAILURE_RETRY (open (map->path, O_RDONLY));
200 if (fd < 0) {
201 Dprintf("failed to open hap file.(%d)\n", errno);
202 return -1;
203 }
204
205 if (fstat (fd, &stat) < 0) {
206 Dprintf("failed to stat hap file sz.(%d)\n", errno);
207 close (fd);
208 return -1;
209 }
210
211 size_t size = prev->end - prev->start;
212 void* elf = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, prev->offset);
213 if (elf == MAP_FAILED) {
214 Dprintf("failed to map program header in hap.(%d)\n", errno);
215 close (fd);
216 return -1;
217 }
218
219 map->ei.size = calc_elf_file_size(elf, size);
220 // maybe we should get it from phdr.
221 map->offset = map->offset - prev->offset;
222 munmap(elf, size);
223 if (map->ei.size <= 0 || map->ei.size > (size_t)(stat.st_size - prev->offset))
224 {
225 Dprintf("invalid elf size? sz:%d, hap sz:%d", (int)map->ei.size, (int)stat.st_size);
226 close (fd);
227 return -1;
228 }
229
230 map->ei.image = mmap (NULL, map->ei.size, PROT_READ, MAP_PRIVATE, fd, prev->offset);
231 close (fd);
232 if (map->ei.image == MAP_FAILED) {
233 Dprintf("failed to map so in hap.\n");
234 return -1;
235 }
236
237 return 0;
238 }
239 #endif
240
241 HIDDEN int
maps_is_writable(struct map_info * map_list,unw_word_t addr)242 maps_is_writable(struct map_info *map_list, unw_word_t addr)
243 {
244 #ifndef UNW_LOCAL_ONLY
245 if (map_list == NULL)
246 return 1;
247 #endif
248 struct map_info *map = get_map(map_list, addr);
249 if (map != NULL)
250 return map->flags & PROT_WRITE;
251 return 0;
252 }
253
254 HIDDEN struct map_info*
tdep_get_elf_image(unw_addr_space_t as,pid_t pid,unw_word_t ip)255 tdep_get_elf_image(unw_addr_space_t as, pid_t pid, unw_word_t ip)
256 {
257 struct map_info *map;
258 struct cursor* cursor = get_cursor_from_as(as);
259 int find_cached_map = ((cursor != NULL) && (cursor->dwarf.ip == ip));
260 if (find_cached_map &&
261 (cursor->dwarf.ip == cursor->dwarf.cached_ip) && cursor->dwarf.cached_map != NULL) {
262 return cursor->dwarf.cached_map;
263 }
264
265 if (as->map_list == NULL && pid > 0) {
266 as->map_list = maps_create_list(pid);
267 if (as->map_list == NULL) {
268 Dprintf("Failed to maps_create_list for pid:%d\n", pid);
269 return NULL;
270 }
271 }
272
273 map = get_map(as->map_list, ip); // ip must located in executable map region
274 if (!map)
275 return NULL;
276
277 if (map->ei.image == NULL)
278 {
279 if (strstr(map->path, ".hap") != NULL) {
280 #ifdef PARSE_ELF_IN_HAP
281 if (map_elf_in_hap(map) < 0) {
282 Dprintf("invalid map_elf_in_hap?\n");
283 return NULL;
284 }
285 #else
286 Dprintf("Unsupport map_elf_in_hap\n");
287 return NULL;
288 #endif
289 } else {
290 if (elf_map_image(&map->ei, map->path) < 0) {
291 map->ei.image = NULL;
292 map->ei.has_try_load = 1;
293 Dprintf("Failed to elf_map_image for ip:%p\n", (void*)ip);
294 return NULL;
295 }
296 }
297 map->ei.mdi = &(map->mdi);
298 }
299
300 if (find_cached_map) {
301 cursor->dwarf.cached_map = map;
302 cursor->dwarf.cached_ip = ip;
303 }
304 return map;
305 }
306
get_previous_instr_sz(unw_cursor_t * cursor)307 unw_word_t get_previous_instr_sz(unw_cursor_t *cursor)
308 {
309 struct cursor *c = (struct cursor *) cursor;
310 unw_addr_space_t as = c->dwarf.as;
311 unw_accessors_t *a = unw_get_accessors (as);
312 unw_word_t ip = c->dwarf.ip;
313 int sz = 4;
314 #if defined(UNW_TARGET_ARM)
315 if (ip)
316 {
317 if (ip & 1)
318 {
319 void *arg;
320 unw_word_t value;
321 arg = c->dwarf.as_arg;
322 // 0xe000f000 ---> machine code of blx Instr (blx label)
323 if (ip < 5 || (*a->access_mem) (as, ip - 5, &value, 0, arg) < 0 ||
324 (value & 0xe000f000) != 0xe000f000)
325 sz = 2;
326 }
327 }
328 #elif defined(UNW_TARGET_ARM64)
329 sz = 4;
330 #elif defined(UNW_TARGET_X86)
331 sz = 1;
332 #elif defined(UNW_TARGET_x86_64)
333 sz = 1;
334 #else
335 // other arch need to be add here.
336 #endif
337 return sz;
338 }
339
340
341 #ifndef UNW_REMOTE_ONLY
342
343 void
tdep_get_exe_image_path(char * path)344 tdep_get_exe_image_path (char *path)
345 {
346 strcpy(path, "/proc/self/exe");
347 }
348
349 #endif /* !UNW_REMOTE_ONLY */
350