robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 1 | /* |
robert.swiecki@gmail.com | 3b630b4 | 2015-02-16 10:53:53 +0000 | [diff] [blame] | 2 | * |
robert.swiecki@gmail.com | 772b33d | 2015-02-14 20:35:00 +0000 | [diff] [blame] | 3 | * honggfuzz - architecture dependent code (LINUX/UNWIND) |
| 4 | * ----------------------------------------- |
robert.swiecki@gmail.com | 3b630b4 | 2015-02-16 10:53:53 +0000 | [diff] [blame] | 5 | * |
robert.swiecki@gmail.com | 772b33d | 2015-02-14 20:35:00 +0000 | [diff] [blame] | 6 | * Author: Robert Swiecki <swiecki@google.com> |
robert.swiecki@gmail.com | 3b630b4 | 2015-02-16 10:53:53 +0000 | [diff] [blame] | 7 | * |
robert.swiecki@gmail.com | 772b33d | 2015-02-14 20:35:00 +0000 | [diff] [blame] | 8 | * Copyright 2010-2015 by Google Inc. All Rights Reserved. |
robert.swiecki@gmail.com | 3b630b4 | 2015-02-16 10:53:53 +0000 | [diff] [blame] | 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 |
robert.swiecki@gmail.com | 772b33d | 2015-02-14 20:35:00 +0000 | [diff] [blame] | 12 | * a copy of the License at |
robert.swiecki@gmail.com | 3b630b4 | 2015-02-16 10:53:53 +0000 | [diff] [blame] | 13 | * |
robert.swiecki@gmail.com | 772b33d | 2015-02-14 20:35:00 +0000 | [diff] [blame] | 14 | * http://www.apache.org/licenses/LICENSE-2.0 |
robert.swiecki@gmail.com | 3b630b4 | 2015-02-16 10:53:53 +0000 | [diff] [blame] | 15 | * |
robert.swiecki@gmail.com | 772b33d | 2015-02-14 20:35:00 +0000 | [diff] [blame] | 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. |
robert.swiecki@gmail.com | 3b630b4 | 2015-02-16 10:53:53 +0000 | [diff] [blame] | 21 | * |
robert.swiecki@gmail.com | 772b33d | 2015-02-14 20:35:00 +0000 | [diff] [blame] | 22 | */ |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 23 | |
| 24 | #include "common.h" |
| 25 | #include "linux/unwind.h" |
| 26 | |
| 27 | #include <libunwind-ptrace.h> |
| 28 | |
| 29 | #include "log.h" |
| 30 | |
Anestis Bechtsoudis | cfc39fb | 2015-08-06 10:31:36 +0300 | [diff] [blame^] | 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 |
| 46 | static 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__ |
| 61 | size_t arch_unwindStack(pid_t pid, funcs_t *funcs) |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 62 | { |
Anestis Bechtsoudis | cfc39fb | 2015-08-06 10:31:36 +0300 | [diff] [blame^] | 63 | size_t frames = 0; |
robert.swiecki@gmail.com | d712370 | 2015-02-16 14:50:50 +0000 | [diff] [blame] | 64 | void *ui = NULL; |
| 65 | |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 66 | unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER); |
| 67 | if (!as) { |
Anestis Bechtsoudis | cfc39fb | 2015-08-06 10:31:36 +0300 | [diff] [blame^] | 68 | LOGMSG(l_ERROR, "[pid='%d'] unw_create_addr_space failed", pid); |
robert.swiecki@gmail.com | d712370 | 2015-02-16 14:50:50 +0000 | [diff] [blame] | 69 | goto out; |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 70 | } |
| 71 | |
robert.swiecki@gmail.com | d712370 | 2015-02-16 14:50:50 +0000 | [diff] [blame] | 72 | ui = _UPT_create(pid); |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 73 | if (ui == NULL) { |
Anestis Bechtsoudis | cfc39fb | 2015-08-06 10:31:36 +0300 | [diff] [blame^] | 74 | LOGMSG(l_ERROR, "[pid='%d'] _UPT_create failed", pid); |
robert.swiecki@gmail.com | d712370 | 2015-02-16 14:50:50 +0000 | [diff] [blame] | 75 | goto out; |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | unw_cursor_t c; |
Anestis Bechtsoudis | cfc39fb | 2015-08-06 10:31:36 +0300 | [diff] [blame^] | 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]); |
robert.swiecki@gmail.com | d712370 | 2015-02-16 14:50:50 +0000 | [diff] [blame] | 82 | goto out; |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 83 | } |
| 84 | |
Anestis Bechtsoudis | cfc39fb | 2015-08-06 10:31:36 +0300 | [diff] [blame^] | 85 | for (frames = 0; unw_step(&c) > 0 && frames < _HF_MAX_FUNCS; frames++) { |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 86 | unw_word_t ip; |
Anestis Bechtsoudis | cfc39fb | 2015-08-06 10:31:36 +0300 | [diff] [blame^] | 87 | ret = unw_get_reg(&c, UNW_REG_IP, &ip); |
| 88 | if (ret < 0) { |
| 89 | LOMGSG(l_ERROR, "[pid='%d'] [%d] failed to read IP (%s)", pid, frames, UNW_ER[-ret]); |
| 90 | funcs[ret].pc = 0; |
| 91 | } |
| 92 | else |
| 93 | funcs[ret].pc = (void *)ip; |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 94 | } |
| 95 | |
robert.swiecki@gmail.com | 62e34ae | 2015-03-05 03:39:32 +0000 | [diff] [blame] | 96 | out: |
robert.swiecki@gmail.com | d712370 | 2015-02-16 14:50:50 +0000 | [diff] [blame] | 97 | ui ? _UPT_destroy(ui) : 0; |
| 98 | as ? unw_destroy_addr_space(as) : 0; |
robert.swiecki@gmail.com | a0d8714 | 2015-02-14 13:11:18 +0000 | [diff] [blame] | 99 | return ret; |
| 100 | } |
Anestis Bechtsoudis | cfc39fb | 2015-08-06 10:31:36 +0300 | [diff] [blame^] | 101 | |
| 102 | #else /* !defined(__ANDROID__) */ |
| 103 | size_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 | |
| 157 | skip_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__) */ |