blob: 6a585f4168ec28f446cf97ee1c1e12c5a58dfeaa [file] [log] [blame]
/*
*
* honggfuzz - architecture dependent code (LINUX/UNWIND)
* -----------------------------------------
*
* Author: Robert Swiecki <swiecki@google.com>
*
* Copyright 2010-2015 by Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*/
#include "common.h"
#include "linux/unwind.h"
#include <libunwind-ptrace.h>
#include "log.h"
#if defined(__ANDROID__)
#include <sys/endian.h> /* For __BYTE_ORDER */
#endif
/*
* WARNING: Ensure that _UPT-info structs are not shared between threads
* http://www.nongnu.org/libunwind/man/libunwind-ptrace(3).html
*/
/*
* TODO: Subtract from load map to have relative PC stored in report file.
* link_map seems to be the easiest road for that.
*/
// libunwind error codes used for debugging
static const char *UNW_ER[] = {
"UNW_ESUCCESS", /* no error */
"UNW_EUNSPEC", /* unspecified (general) error */
"UNW_ENOMEM", /* out of memory */
"UNW_EBADREG", /* bad register number */
"UNW_EREADONLYREG", /* attempt to write read-only register */
"UNW_ESTOPUNWIND", /* stop unwinding */
"UNW_EINVALIDIP", /* invalid IP */
"UNW_EBADFRAME", /* bad frame */
"UNW_EINVAL", /* unsupported operation or bad value */
"UNW_EBADVERSION", /* unwind info has unsupported version */
"UNW_ENOINFO" /* no unwind info found */
};
#ifndef __ANDROID__
size_t arch_unwindStack(pid_t pid, funcs_t *funcs)
{
size_t frames = 0;
void *ui = NULL;
unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER);
if (!as) {
LOGMSG(l_ERROR, "[pid='%d'] unw_create_addr_space failed", pid);
goto out;
}
ui = _UPT_create(pid);
if (ui == NULL) {
LOGMSG(l_ERROR, "[pid='%d'] _UPT_create failed", pid);
goto out;
}
unw_cursor_t c;
int ret = unw_init_remote(&c, as, ui);
if (ret < 0) {
LOGMSG(l_ERROR, "[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]);
goto out;
}
for (frames = 0; unw_step(&c) > 0 && frames < _HF_MAX_FUNCS; frames++) {
unw_word_t ip;
ret = unw_get_reg(&c, UNW_REG_IP, &ip);
if (ret < 0) {
LOMGSG(l_ERROR, "[pid='%d'] [%d] failed to read IP (%s)", pid, frames, UNW_ER[-ret]);
funcs[ret].pc = 0;
}
else
funcs[ret].pc = (void *)ip;
}
out:
ui ? _UPT_destroy(ui) : 0;
as ? unw_destroy_addr_space(as) : 0;
return ret;
}
#else /* !defined(__ANDROID__) */
size_t arch_unwindStack(pid_t pid, funcs_t *funcs)
{
size_t num_frames = 0;
struct UPT_info *ui = NULL;
unw_addr_space_t as = NULL;
as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER);
if (!as) {
LOGMSG(l_ERROR, "[pid='%d'] unw_create_addr_space failed", pid);
goto out;
}
ui = (struct UPT_info*)_UPT_create(pid);
if (ui == NULL) {
LOGMSG(l_ERROR, "[pid='%d'] _UPT_create failed", pid);
goto out;
}
unw_cursor_t cursor;
int ret = unw_init_remote(&cursor, as, ui);
if (ret < 0) {
LOGMSG(l_ERROR, "[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]);
goto out;
}
do {
unw_word_t pc = 0, offset = 0;
char buf[_HF_FUNC_NAME_SZ] = { 0 };
unw_proc_info_t frameInfo;
ret = unw_get_proc_info(&cursor, &frameInfo);
if (ret < 0) {
LOGMSG(l_DEBUG, "[pid='%d'] [%d] unw_get_proc_info (%s)",
pid, num_frames, UNW_ER[-ret]);
// Not safe to keep reading
goto out;
}
ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (ret < 0) {
LOGMSG(l_ERROR, "[pid='%d'] [%d] failed to read IP (%s)",
pid, num_frames, UNW_ER[-ret]);
// We don't want to try to extract info from an arbitrary IP
// TODO: Maybe abort completely (goto out))
goto skip_frame_info;
}
ret = unw_get_proc_name(&cursor, buf, sizeof(buf), &offset);
if (ret < 0) {
LOGMSG(l_DEBUG, "[pid='%d'] [%d] unw_get_proc_name() failed (%s)",
pid, num_frames, UNW_ER[-ret]);
buf[0] = '\0';
}
skip_frame_info:
// Compared to bfd, line var plays the role of offset from func_name
// Reports format is adjusted accordingly to reflect in saved file
funcs[num_frames].line = offset;
funcs[num_frames].pc = (void *)pc;
memcpy(funcs[num_frames].func, buf, sizeof(funcs[num_frames].func));
num_frames++;
ret = unw_step(&cursor);
} while (ret > 0 && num_frames < _HF_MAX_FUNCS);
out:
ui ? _UPT_destroy(ui) : NULL;
as ? unw_destroy_addr_space(as) : NULL;
ui = NULL;
as = NULL;
return num_frames;
}
#endif /* defined(__ANDROID__) */