unwind.c revision 236ef4607a5f85a089f5c2d2dc82e53a8ce82375
1/*
2 *
3 * honggfuzz - architecture dependent code (LINUX/UNWIND)
4 * -----------------------------------------
5 *
6 * Author: Robert Swiecki <swiecki@google.com>
7 *
8 * Copyright 2010-2015 by Google Inc. All Rights Reserved.
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License"); you may
11 * not use this file except in compliance with the License. You may obtain
12 * a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
19 * implied. See the License for the specific language governing
20 * permissions and limitations under the License.
21 *
22 */
23
24#include "common.h"
25#include "linux/unwind.h"
26
27#include <libunwind-ptrace.h>
28
29#include "log.h"
30
31#if defined(__ANDROID__)
32#include <sys/endian.h>         /* For __BYTE_ORDER */
33#endif
34
35/*
36 * WARNING: Ensure that _UPT-info structs are not shared between threads
37 * http://www.nongnu.org/libunwind/man/libunwind-ptrace(3).html
38 */
39
40/*
41 * TODO: Subtract from load map to have relative PC stored in report file.
42 * link_map seems to be the easiest road for that.
43 */
44
45// libunwind error codes used for debugging
46static const char *UNW_ER[] = {
47    "UNW_ESUCCESS",             /* no error */
48    "UNW_EUNSPEC",              /* unspecified (general) error */
49    "UNW_ENOMEM",               /* out of memory */
50    "UNW_EBADREG",              /* bad register number */
51    "UNW_EREADONLYREG",         /* attempt to write read-only register */
52    "UNW_ESTOPUNWIND",          /* stop unwinding */
53    "UNW_EINVALIDIP",           /* invalid IP */
54    "UNW_EBADFRAME",            /* bad frame */
55    "UNW_EINVAL",               /* unsupported operation or bad value */
56    "UNW_EBADVERSION",          /* unwind info has unsupported version */
57    "UNW_ENOINFO"               /* no unwind info found */
58};
59
60#ifndef __ANDROID__
61size_t arch_unwindStack(pid_t pid, funcs_t *funcs)
62{
63    size_t num_frames = 0;
64    void *ui = NULL;
65
66    unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER);
67    if (!as) {
68        LOGMSG(l_ERROR, "[pid='%d'] unw_create_addr_space failed", pid);
69        goto out;
70    }
71
72    ui = _UPT_create(pid);
73    if (ui == NULL) {
74        LOGMSG(l_ERROR, "[pid='%d'] _UPT_create failed", pid);
75        goto out;
76    }
77
78    unw_cursor_t c;
79    int ret = unw_init_remote(&c, as, ui);
80    if (ret < 0) {
81        LOGMSG(l_ERROR, "[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]);
82        goto out;
83    }
84
85    for (num_frames = 0; unw_step(&c) > 0 && num_frames < _HF_MAX_FUNCS; num_frames++) {
86        unw_word_t ip;
87        ret = unw_get_reg(&c, UNW_REG_IP, &ip);
88        if (ret < 0) {
89            LOGMSG(l_ERROR, "[pid='%d'] [%d] failed to read IP (%s)", pid, num_frames, UNW_ER[-ret]);
90            funcs[num_frames].pc = 0;
91        }
92        else
93            funcs[num_frames].pc = (void *)ip;
94    }
95
96 out:
97    ui ? _UPT_destroy(ui) : 0;
98    as ? unw_destroy_addr_space(as) : 0;
99    return num_frames;
100}
101
102#else                          /* !defined(__ANDROID__) */
103size_t arch_unwindStack(pid_t pid, funcs_t *funcs)
104{
105    size_t num_frames = 0;
106    struct UPT_info *ui = NULL;
107    unw_addr_space_t as = NULL;
108
109    as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER);
110    if (!as) {
111        LOGMSG(l_ERROR, "[pid='%d'] unw_create_addr_space failed", pid);
112        goto out;
113    }
114
115    ui = (struct UPT_info*)_UPT_create(pid);
116    if (ui == NULL) {
117        LOGMSG(l_ERROR, "[pid='%d'] _UPT_create failed", pid);
118        goto out;
119    }
120
121    unw_cursor_t cursor;
122    int ret = unw_init_remote(&cursor, as, ui);
123    if (ret < 0) {
124        LOGMSG(l_ERROR, "[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]);
125        goto out;
126    }
127
128    do {
129        unw_word_t pc = 0, offset = 0;
130        char buf[_HF_FUNC_NAME_SZ] = { 0 };
131
132        unw_proc_info_t frameInfo;
133        ret = unw_get_proc_info(&cursor, &frameInfo);
134        if (ret < 0) {
135            LOGMSG(l_DEBUG, "[pid='%d'] [%d] unw_get_proc_info (%s)",
136                    pid, num_frames, UNW_ER[-ret]);
137            // Not safe to keep reading
138            goto out;
139        }
140
141        ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
142        if (ret < 0) {
143            LOGMSG(l_ERROR, "[pid='%d'] [%d] failed to read IP (%s)",
144                    pid, num_frames, UNW_ER[-ret]);
145            // We don't want to try to extract info from an arbitrary IP
146            // TODO: Maybe abort completely (goto out))
147            goto skip_frame_info;
148        }
149
150        ret = unw_get_proc_name(&cursor, buf, sizeof(buf), &offset);
151        if (ret < 0) {
152            LOGMSG(l_DEBUG, "[pid='%d'] [%d] unw_get_proc_name() failed (%s)",
153                    pid, num_frames, UNW_ER[-ret]);
154            buf[0] = '\0';
155        }
156
157skip_frame_info:
158        // Compared to bfd, line var plays the role of offset from func_name
159        // Reports format is adjusted accordingly to reflect in saved file
160        funcs[num_frames].line = offset;
161        funcs[num_frames].pc = (void *)pc;
162        memcpy(funcs[num_frames].func, buf, sizeof(funcs[num_frames].func));
163
164        num_frames++;
165
166        ret = unw_step(&cursor);
167    } while (ret > 0 && num_frames < _HF_MAX_FUNCS);
168
169 out:
170    ui ? _UPT_destroy(ui) : NULL;
171    as ? unw_destroy_addr_space(as) : NULL;
172
173    ui = NULL;
174    as = NULL;
175
176    return num_frames;
177}
178#endif                          /* defined(__ANDROID__) */
179