Add a new library for collecting stack traces.
Supports collecting the stack trace of the current thread, another thread in the same process, or a thread in a different process (using ptrace). Change-Id: Ica2594e4436edde4ceb7bcc3d78e6c31a7902cbf
This commit is contained in:
parent
08dedcfd5c
commit
501edd29b8
19 changed files with 2026 additions and 0 deletions
102
include/corkscrew/backtrace.h
Normal file
102
include/corkscrew/backtrace.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* A stack unwinder. */
|
||||
|
||||
#ifndef _CORKSCREW_BACKTRACE_H
|
||||
#define _CORKSCREW_BACKTRACE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <corkscrew/ptrace.h>
|
||||
#include <corkscrew/map_info.h>
|
||||
#include <corkscrew/symbol_table.h>
|
||||
|
||||
/*
|
||||
* Describes a single frame of a backtrace.
|
||||
*/
|
||||
typedef struct {
|
||||
uintptr_t absolute_pc; /* absolute PC offset */
|
||||
uintptr_t stack_top; /* top of stack for this frame */
|
||||
size_t stack_size; /* size of this stack frame */
|
||||
} backtrace_frame_t;
|
||||
|
||||
/*
|
||||
* Describes the symbols associated with a backtrace frame.
|
||||
*/
|
||||
typedef struct {
|
||||
uintptr_t relative_pc; /* relative PC offset from the start of the library,
|
||||
or the absolute PC if the library is unknown */
|
||||
const map_info_t* map_info; /* memory map of the library, or NULL if unknown */
|
||||
const char* name; /* symbol name, or NULL if unknown */
|
||||
char* demangled_name; /* demangled symbol name, or NULL if unknown */
|
||||
} backtrace_symbol_t;
|
||||
|
||||
/*
|
||||
* Unwinds the call stack for the current thread of execution.
|
||||
* Populates the backtrace array with the program counters from the call stack.
|
||||
* Returns the number of frames collected, or -1 if an error occurred.
|
||||
*/
|
||||
ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
|
||||
|
||||
/*
|
||||
* Unwinds the call stack for a thread within this process.
|
||||
* Populates the backtrace array with the program counters from the call stack.
|
||||
* Returns the number of frames collected, or -1 if an error occurred.
|
||||
*
|
||||
* The task is briefly suspended while the backtrace is being collected.
|
||||
*/
|
||||
ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace,
|
||||
size_t ignore_depth, size_t max_depth);
|
||||
|
||||
/*
|
||||
* Unwinds the call stack of a task within a remote process using ptrace().
|
||||
* Populates the backtrace array with the program counters from the call stack.
|
||||
* Returns the number of frames collected, or -1 if an error occurred.
|
||||
*/
|
||||
ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
|
||||
|
||||
/*
|
||||
* Gets the symbols for each frame of a backtrace.
|
||||
* The symbols array must be big enough to hold one symbol record per frame.
|
||||
* The symbols must later be freed using free_backtrace_symbols.
|
||||
*/
|
||||
void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames,
|
||||
backtrace_symbol_t* backtrace_symbols);
|
||||
|
||||
/*
|
||||
* Gets the symbols for each frame of a backtrace from a remote process.
|
||||
* The symbols array must be big enough to hold one symbol record per frame.
|
||||
* The symbols must later be freed using free_backtrace_symbols.
|
||||
*/
|
||||
void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
|
||||
const backtrace_frame_t* backtrace, size_t frames,
|
||||
backtrace_symbol_t* backtrace_symbols);
|
||||
|
||||
/*
|
||||
* Frees the storage associated with backtrace symbols.
|
||||
*/
|
||||
void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CORKSCREW_BACKTRACE_H
|
||||
42
include/corkscrew/demangle.h
Normal file
42
include/corkscrew/demangle.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* C++ symbol name demangling. */
|
||||
|
||||
#ifndef _CORKSCREW_DEMANGLE_H
|
||||
#define _CORKSCREW_DEMANGLE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Demangles a C++ symbol name.
|
||||
* If name is NULL or if the name cannot be demangled, returns NULL.
|
||||
* Otherwise, returns a newly allocated string that contains the demangled name.
|
||||
*
|
||||
* The caller must free the returned string using free().
|
||||
*/
|
||||
char* demangle_symbol_name(const char* name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CORKSCREW_DEMANGLE_H
|
||||
54
include/corkscrew/map_info.h
Normal file
54
include/corkscrew/map_info.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Process memory map. */
|
||||
|
||||
#ifndef _CORKSCREW_MAP_INFO_H
|
||||
#define _CORKSCREW_MAP_INFO_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct map_info {
|
||||
struct map_info* next;
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
bool is_executable;
|
||||
void* data; // arbitrary data associated with the map by the user, initially NULL
|
||||
char name[];
|
||||
} map_info_t;
|
||||
|
||||
/* Loads memory map from /proc/<tid>/maps. */
|
||||
map_info_t* load_map_info_list(pid_t tid);
|
||||
|
||||
/* Frees memory map. */
|
||||
void free_map_info_list(map_info_t* milist);
|
||||
|
||||
/* Finds the memory map that contains the specified address. */
|
||||
const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr);
|
||||
|
||||
/* Gets the memory map for this process. (result is cached) */
|
||||
const map_info_t* my_map_info_list();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CORKSCREW_MAP_INFO_H
|
||||
97
include/corkscrew/ptrace.h
Normal file
97
include/corkscrew/ptrace.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Useful ptrace() utility functions. */
|
||||
|
||||
#ifndef _CORKSCREW_PTRACE_H
|
||||
#define _CORKSCREW_PTRACE_H
|
||||
|
||||
#include <corkscrew/map_info.h>
|
||||
#include <corkscrew/symbol_table.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Stores information about a process that is used for several different
|
||||
* ptrace() based operations. */
|
||||
typedef struct {
|
||||
map_info_t* map_info_list;
|
||||
} ptrace_context_t;
|
||||
|
||||
#if __i386__
|
||||
/* ptrace() register context. */
|
||||
typedef struct pt_regs_x86 {
|
||||
uint32_t ebx;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
uint32_t ebp;
|
||||
uint32_t eax;
|
||||
uint32_t xds;
|
||||
uint32_t xes;
|
||||
uint32_t xfs;
|
||||
uint32_t xgs;
|
||||
uint32_t orig_eax;
|
||||
uint32_t eip;
|
||||
uint32_t xcs;
|
||||
uint32_t eflags;
|
||||
uint32_t esp;
|
||||
uint32_t xss;
|
||||
} pt_regs_x86_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Reads a word of memory safely.
|
||||
* Uses ptrace() if tid >= 0, local memory otherwise.
|
||||
* Returns false if the word could not be read.
|
||||
*/
|
||||
bool try_get_word(pid_t tid, uintptr_t ptr, uint32_t* out_value);
|
||||
|
||||
/*
|
||||
* Loads information needed for examining a remote process using ptrace().
|
||||
* The caller must already have successfully attached to the process
|
||||
* using ptrace().
|
||||
*
|
||||
* The context can be used for any threads belonging to that process
|
||||
* assuming ptrace() is attached to them before performing the actual
|
||||
* unwinding. The context can continue to be used to decode backtraces
|
||||
* even after ptrace() has been detached from the process.
|
||||
*/
|
||||
ptrace_context_t* load_ptrace_context(pid_t pid);
|
||||
|
||||
/*
|
||||
* Frees a ptrace context.
|
||||
*/
|
||||
void free_ptrace_context(ptrace_context_t* context);
|
||||
|
||||
/*
|
||||
* Finds a symbol using ptrace.
|
||||
* Returns the containing map and information about the symbol, or
|
||||
* NULL if one or the other is not available.
|
||||
*/
|
||||
void find_symbol_ptrace(const ptrace_context_t* context,
|
||||
uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CORKSCREW_PTRACE_H
|
||||
58
include/corkscrew/symbol_table.h
Normal file
58
include/corkscrew/symbol_table.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _CORKSCREW_SYMBOL_TABLE_H
|
||||
#define _CORKSCREW_SYMBOL_TABLE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
char* name;
|
||||
} symbol_t;
|
||||
|
||||
typedef struct {
|
||||
symbol_t* symbols;
|
||||
size_t num_symbols;
|
||||
} symbol_table_t;
|
||||
|
||||
/*
|
||||
* Loads a symbol table from a given file.
|
||||
* Returns NULL on error.
|
||||
*/
|
||||
symbol_table_t* load_symbol_table(const char* filename);
|
||||
|
||||
/*
|
||||
* Frees a symbol table.
|
||||
*/
|
||||
void free_symbol_table(symbol_table_t* table);
|
||||
|
||||
/*
|
||||
* Finds a symbol associated with an address in the symbol table.
|
||||
* Returns NULL if not found.
|
||||
*/
|
||||
const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CORKSCREW_SYMBOL_TABLE_H
|
||||
41
libcorkscrew/Android.mk
Normal file
41
libcorkscrew/Android.mk
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Copyright (C) 2011 The Android Open Source Project
|
||||
#
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
backtrace.c \
|
||||
backtrace-helper.c \
|
||||
demangle.c \
|
||||
map_info.c \
|
||||
ptrace.c \
|
||||
symbol_table.c
|
||||
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
LOCAL_SRC_FILES += \
|
||||
arch-arm/backtrace-arm.c \
|
||||
arch-arm/ptrace-arm.c
|
||||
LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -DCORKSCREW_HAVE_LIBIBERTY
|
||||
LOCAL_LDFLAGS += -liberty
|
||||
endif
|
||||
|
||||
LOCAL_SHARED_LIBRARIES += libdl libcutils
|
||||
|
||||
LOCAL_CFLAGS += -std=gnu99 -Werror
|
||||
LOCAL_MODULE := libcorkscrew
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
0
libcorkscrew/MODULE_LICENSE_APACHE2
Normal file
0
libcorkscrew/MODULE_LICENSE_APACHE2
Normal file
190
libcorkscrew/NOTICE
Normal file
190
libcorkscrew/NOTICE
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
|
||||
Copyright (c) 2011, The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
516
libcorkscrew/arch-arm/backtrace-arm.c
Normal file
516
libcorkscrew/arch-arm/backtrace-arm.c
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Backtracing functions for ARM.
|
||||
*
|
||||
* This implementation uses the exception unwinding tables provided by
|
||||
* the compiler to unwind call frames. Refer to the ARM Exception Handling ABI
|
||||
* documentation (EHABI) for more details about what's going on here.
|
||||
*
|
||||
* An ELF binary may contain an EXIDX section that provides an index to
|
||||
* the exception handling table of each function, sorted by program
|
||||
* counter address.
|
||||
*
|
||||
* When the executable is statically linked, the EXIDX section can be
|
||||
* accessed by querying the values of the __exidx_start and __exidx_end
|
||||
* symbols. That said, this library is currently only compiled as
|
||||
* a dynamic library, so we will not trouble ourselves with statically
|
||||
* linked executables any further.
|
||||
*
|
||||
* When the Bionic dynamic linker is used, it exports a function called
|
||||
* dl_unwind_find_exidx that obtains the EXIDX section for a given
|
||||
* absolute program counter address.
|
||||
*
|
||||
* This implementation also supports unwinding other processes via ptrace().
|
||||
* In that case, the EXIDX section is found by reading the ELF section table
|
||||
* structures using ptrace().
|
||||
*
|
||||
* Because the tables are used for exception handling, it can happen that
|
||||
* a given function will not have an exception handling table. In particular,
|
||||
* exceptions are assumes to only ever be thrown at call sites. Therefore,
|
||||
* by definition leaf functions will not have exception handling tables.
|
||||
* This may make unwinding impossible in some cases although we can still get
|
||||
* some idea of the call stack by examining the PC and LR registers.
|
||||
*
|
||||
* As we are only interested in backtrace information, we do not need
|
||||
* to perform all of the work of unwinding such as restoring register
|
||||
* state and running cleanup functions. Unwinding is performed virtually on
|
||||
* an abstract machine context consisting of just the ARM core registers.
|
||||
* Furthermore, we do not run generic "personality functions" because
|
||||
* we may not be in a position to execute arbitrary code, especially if
|
||||
* we are running in a signal handler or using ptrace()!
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Corkscrew"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "../backtrace-arch.h"
|
||||
#include "../backtrace-helper.h"
|
||||
#include "../ptrace-arch.h"
|
||||
#include <corkscrew/ptrace.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/exec_elf.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
/* Machine context at the time a signal was raised. */
|
||||
typedef struct ucontext {
|
||||
uint32_t uc_flags;
|
||||
struct ucontext* uc_link;
|
||||
stack_t uc_stack;
|
||||
struct sigcontext {
|
||||
uint32_t trap_no;
|
||||
uint32_t error_code;
|
||||
uint32_t oldmask;
|
||||
uint32_t gregs[16];
|
||||
uint32_t arm_cpsr;
|
||||
uint32_t fault_address;
|
||||
} uc_mcontext;
|
||||
uint32_t uc_sigmask;
|
||||
} ucontext_t;
|
||||
|
||||
/* Unwind state. */
|
||||
typedef struct {
|
||||
uint32_t gregs[16];
|
||||
} unwind_state_t;
|
||||
|
||||
static const int R_SP = 13;
|
||||
static const int R_LR = 14;
|
||||
static const int R_PC = 15;
|
||||
|
||||
/* Special EXIDX value that indicates that a frame cannot be unwound. */
|
||||
static const uint32_t EXIDX_CANTUNWIND = 1;
|
||||
|
||||
/* The function exported by the Bionic linker to find the EXIDX
|
||||
* table for a given program counter address. */
|
||||
extern uintptr_t dl_unwind_find_exidx(uintptr_t pc, size_t* out_exidx_size);
|
||||
|
||||
/* Transforms a 31-bit place-relative offset to an absolute address.
|
||||
* We assume the most significant bit is clear. */
|
||||
static uintptr_t prel_to_absolute(uintptr_t place, uint32_t prel_offset) {
|
||||
return place + (((int32_t)(prel_offset << 1)) >> 1);
|
||||
}
|
||||
|
||||
static uintptr_t get_exception_handler(
|
||||
const ptrace_context_t* context, pid_t tid, uintptr_t pc) {
|
||||
uintptr_t exidx_start;
|
||||
size_t exidx_size;
|
||||
if (tid < 0) {
|
||||
exidx_start = dl_unwind_find_exidx(pc, &exidx_size);
|
||||
} else {
|
||||
const map_info_t* mi = find_map_info(context->map_info_list, pc);
|
||||
if (mi && mi->data) {
|
||||
const map_info_data_t* data = (const map_info_data_t*)mi->data;
|
||||
exidx_start = data->exidx_start;
|
||||
exidx_size = data->exidx_size;
|
||||
} else {
|
||||
exidx_start = 0;
|
||||
exidx_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The PC points to the instruction following the branch.
|
||||
// We want to find the exception handler entry that corresponds to the branch itself,
|
||||
// so we offset the PC backwards into the previous instruction.
|
||||
// ARM instructions are 4 bytes, Thumb are 2, so we just subtract two so we either
|
||||
// end up in the middle (ARM) or at the beginning of the instruction (Thumb).
|
||||
if (pc >= 2) {
|
||||
pc -= 2;
|
||||
}
|
||||
|
||||
uint32_t handler = 0;
|
||||
if (exidx_start) {
|
||||
uint32_t low = 0;
|
||||
uint32_t high = exidx_size;
|
||||
while (low < high) {
|
||||
uint32_t index = (low + high) / 2;
|
||||
uintptr_t entry = exidx_start + index * 8;
|
||||
uint32_t entry_prel_pc;
|
||||
if (!try_get_word(tid, entry, &entry_prel_pc)) {
|
||||
break;
|
||||
}
|
||||
uintptr_t entry_pc = prel_to_absolute(entry, entry_prel_pc);
|
||||
if (pc < entry_pc) {
|
||||
high = index;
|
||||
continue;
|
||||
}
|
||||
if (index + 1 < exidx_size) {
|
||||
uintptr_t next_entry = entry + 8;
|
||||
uint32_t next_entry_prel_pc;
|
||||
if (!try_get_word(tid, next_entry, &next_entry_prel_pc)) {
|
||||
break;
|
||||
}
|
||||
uintptr_t next_entry_pc = prel_to_absolute(next_entry, next_entry_prel_pc);
|
||||
if (pc >= next_entry_pc) {
|
||||
low = index + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t entry_handler_ptr = entry + 4;
|
||||
uint32_t entry_handler;
|
||||
if (!try_get_word(tid, entry_handler_ptr, &entry_handler)) {
|
||||
break;
|
||||
}
|
||||
if (entry_handler & (1L << 31)) {
|
||||
handler = entry_handler_ptr; // in-place handler data
|
||||
} else if (entry_handler != EXIDX_CANTUNWIND) {
|
||||
handler = prel_to_absolute(entry_handler_ptr, entry_handler);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOGV("get handler: pc=0x%08x, exidx_start=0x%08x, exidx_size=%d, handler=0x%08x",
|
||||
pc, exidx_start, exidx_size, handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uintptr_t ptr;
|
||||
uint32_t word;
|
||||
} byte_stream_t;
|
||||
|
||||
static bool try_next_byte(pid_t tid, byte_stream_t* stream, uint8_t* out_value) {
|
||||
uint8_t result;
|
||||
switch (stream->ptr & 3) {
|
||||
case 0:
|
||||
if (!try_get_word(tid, stream->ptr, &stream->word)) {
|
||||
*out_value = 0;
|
||||
return false;
|
||||
}
|
||||
*out_value = stream->word >> 24;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*out_value = stream->word >> 16;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*out_value = stream->word >> 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
*out_value = stream->word;
|
||||
break;
|
||||
}
|
||||
|
||||
LOGV("next_byte: ptr=0x%08x, value=0x%02x", stream->ptr, *out_value);
|
||||
stream->ptr += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void set_reg(unwind_state_t* state, uint32_t reg, uint32_t value) {
|
||||
LOGV("set_reg: reg=%d, value=0x%08x", reg, value);
|
||||
state->gregs[reg] = value;
|
||||
}
|
||||
|
||||
static bool try_pop_registers(pid_t tid, unwind_state_t* state, uint32_t mask) {
|
||||
uint32_t sp = state->gregs[R_SP];
|
||||
bool sp_updated = false;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (mask & (1 << i)) {
|
||||
uint32_t value;
|
||||
if (!try_get_word(tid, sp, &value)) {
|
||||
return false;
|
||||
}
|
||||
if (i == R_SP) {
|
||||
sp_updated = true;
|
||||
}
|
||||
set_reg(state, i, value);
|
||||
sp += 4;
|
||||
}
|
||||
}
|
||||
if (!sp_updated) {
|
||||
set_reg(state, R_SP, sp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Executes a built-in personality routine as defined in the EHABI.
|
||||
* Returns true if unwinding should continue.
|
||||
*
|
||||
* The data for the built-in personality routines consists of a sequence
|
||||
* of unwinding instructions, followed by a sequence of scope descriptors,
|
||||
* each of which has a length and offset encoded using 16-bit or 32-bit
|
||||
* values.
|
||||
*
|
||||
* We only care about the unwinding instructions. They specify the
|
||||
* operations of an abstract machine whose purpose is to transform the
|
||||
* virtual register state (including the stack pointer) such that
|
||||
* the call frame is unwound and the PC register points to the call site.
|
||||
*/
|
||||
static bool execute_personality_routine(pid_t tid, unwind_state_t* state,
|
||||
byte_stream_t* stream, int pr_index) {
|
||||
size_t size;
|
||||
switch (pr_index) {
|
||||
case 0: // Personality routine #0, short frame, descriptors have 16-bit scope.
|
||||
size = 3;
|
||||
break;
|
||||
case 1: // Personality routine #1, long frame, descriptors have 16-bit scope.
|
||||
case 2: { // Personality routine #2, long frame, descriptors have 32-bit scope.
|
||||
uint8_t size_byte;
|
||||
if (!try_next_byte(tid, stream, &size_byte)) {
|
||||
return false;
|
||||
}
|
||||
size = (uint32_t)size_byte * sizeof(uint32_t) + 2;
|
||||
break;
|
||||
}
|
||||
default: // Unknown personality routine. Stop here.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pc_was_set = false;
|
||||
while (size--) {
|
||||
uint8_t op;
|
||||
if (!try_next_byte(tid, stream, &op)) {
|
||||
return false;
|
||||
}
|
||||
if ((op & 0xc0) == 0x00) {
|
||||
// "vsp = vsp + (xxxxxx << 2) + 4"
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + ((op & 0x3f) << 2) + 4);
|
||||
} else if ((op & 0xc0) == 0x40) {
|
||||
// "vsp = vsp - (xxxxxx << 2) - 4"
|
||||
set_reg(state, R_SP, state->gregs[R_SP] - ((op & 0x3f) << 2) - 4);
|
||||
} else if ((op & 0xf0) == 0x80) {
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t mask = (((uint32_t)op & 0x0f) << 12) | ((uint32_t)op2 << 4);
|
||||
if (mask) {
|
||||
// "Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}"
|
||||
if (!try_pop_registers(tid, state, mask)) {
|
||||
return false;
|
||||
}
|
||||
if (mask & (1 << R_PC)) {
|
||||
pc_was_set = true;
|
||||
}
|
||||
} else {
|
||||
// "Refuse to unwind"
|
||||
return false;
|
||||
}
|
||||
} else if ((op & 0xf0) == 0x90) {
|
||||
if (op != 0x9d && op != 0x9f) {
|
||||
// "Set vsp = r[nnnn]"
|
||||
set_reg(state, R_SP, state->gregs[op & 0x0f]);
|
||||
} else {
|
||||
// "Reserved as prefix for ARM register to register moves"
|
||||
// "Reserved as prefix for Intel Wireless MMX register to register moves"
|
||||
return false;
|
||||
}
|
||||
} else if ((op & 0xf8) == 0xa0) {
|
||||
// "Pop r4-r[4+nnn]"
|
||||
uint32_t mask = (0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0;
|
||||
if (!try_pop_registers(tid, state, mask)) {
|
||||
return false;
|
||||
}
|
||||
} else if ((op & 0xf8) == 0xa8) {
|
||||
// "Pop r4-r[4+nnn], r14"
|
||||
uint32_t mask = ((0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0) | 0x4000;
|
||||
if (!try_pop_registers(tid, state, mask)) {
|
||||
return false;
|
||||
}
|
||||
} else if (op == 0xb0) {
|
||||
// "Finish"
|
||||
break;
|
||||
} else if (op == 0xb1) {
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
if (op2 != 0x00 && (op2 & 0xf0) == 0x00) {
|
||||
// "Pop integer registers under mask {r3, r2, r1, r0}"
|
||||
if (!try_pop_registers(tid, state, op2)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// "Spare"
|
||||
return false;
|
||||
}
|
||||
} else if (op == 0xb2) {
|
||||
// "vsp = vsp + 0x204 + (uleb128 << 2)"
|
||||
uint32_t value = 0;
|
||||
uint32_t shift = 0;
|
||||
uint8_t op2;
|
||||
do {
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
value |= (op2 & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while (op2 & 0x80);
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (value << 2) + 0x204);
|
||||
} else if (op == 0xb3) {
|
||||
// "Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDX"
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 12);
|
||||
} else if ((op & 0xf8) == 0xb8) {
|
||||
// "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDX"
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 12);
|
||||
} else if ((op & 0xf8) == 0xc0) {
|
||||
// "Intel Wireless MMX pop wR[10]-wR[10+nnn]"
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8);
|
||||
} else if (op == 0xc6) {
|
||||
// "Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]"
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
|
||||
} else if (op == 0xc7) {
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
if (op2 != 0x00 && (op2 & 0xf0) == 0x00) {
|
||||
// "Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}"
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + __builtin_popcount(op2) * 4);
|
||||
} else {
|
||||
// "Spare"
|
||||
return false;
|
||||
}
|
||||
} else if (op == 0xc8) {
|
||||
// "Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc]
|
||||
// saved (as if) by FSTMFD"
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
|
||||
} else if (op == 0xc9) {
|
||||
// "Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDD"
|
||||
uint8_t op2;
|
||||
if (!(size--) || !try_next_byte(tid, stream, &op2)) {
|
||||
return false;
|
||||
}
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
|
||||
} else if ((op == 0xf8) == 0xd0) {
|
||||
// "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDD"
|
||||
set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8);
|
||||
} else {
|
||||
// "Spare"
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!pc_was_set) {
|
||||
set_reg(state, R_PC, state->gregs[R_LR]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t unwind_backtrace_common(pid_t tid, const ptrace_context_t* context,
|
||||
unwind_state_t* state, backtrace_frame_t* backtrace,
|
||||
size_t ignore_depth, size_t max_depth) {
|
||||
size_t ignored_frames = 0;
|
||||
size_t returned_frames = 0;
|
||||
|
||||
uintptr_t handler = get_exception_handler(context, tid, state->gregs[R_PC]);
|
||||
if (!handler) {
|
||||
// If there is no handler for the PC, the program may have branched to
|
||||
// an invalid address. Check whether we have a handler for the LR
|
||||
// where we came from and use that instead.
|
||||
backtrace_frame_t* frame = add_backtrace_entry(state->gregs[R_PC], backtrace,
|
||||
ignore_depth, max_depth, &ignored_frames, &returned_frames);
|
||||
if (frame) {
|
||||
frame->stack_top = state->gregs[R_SP];
|
||||
}
|
||||
|
||||
handler = get_exception_handler(context, tid, state->gregs[R_LR]);
|
||||
if (!handler) {
|
||||
// We don't have a handler here either. Unwinding will not be possible.
|
||||
// Return the PC and LR (if it looks sane) and call it good.
|
||||
if (state->gregs[R_LR] && state->gregs[R_LR] != state->gregs[R_PC]) {
|
||||
// Don't return the SP for this second frame because we don't
|
||||
// know how big the first one is so we don't know where this
|
||||
// one starts.
|
||||
frame = add_backtrace_entry(state->gregs[R_LR], backtrace,
|
||||
ignore_depth, max_depth, &ignored_frames, &returned_frames);
|
||||
}
|
||||
return returned_frames;
|
||||
}
|
||||
|
||||
// Ok, continue from the LR.
|
||||
set_reg(state, R_PC, state->gregs[R_LR]);
|
||||
}
|
||||
|
||||
while (handler && returned_frames < max_depth) {
|
||||
backtrace_frame_t* frame = add_backtrace_entry(state->gregs[R_PC], backtrace,
|
||||
ignore_depth, max_depth, &ignored_frames, &returned_frames);
|
||||
if (frame) {
|
||||
frame->stack_top = state->gregs[R_SP];
|
||||
}
|
||||
|
||||
byte_stream_t stream;
|
||||
stream.ptr = handler;
|
||||
uint8_t pr;
|
||||
if (!try_next_byte(tid, &stream, &pr)) {
|
||||
break;
|
||||
}
|
||||
if ((pr & 0xf0) != 0x80) {
|
||||
// The first word is a place-relative pointer to a generic personality
|
||||
// routine function. We don't support invoking such functions, so stop here.
|
||||
break;
|
||||
}
|
||||
|
||||
// The first byte indicates the personality routine to execute.
|
||||
// Following bytes provide instructions to the personality routine.
|
||||
if (!execute_personality_routine(tid, state, &stream, pr & 0x0f)) {
|
||||
break;
|
||||
}
|
||||
if (frame && state->gregs[R_SP] > frame->stack_top) {
|
||||
frame->stack_size = state->gregs[R_SP] - frame->stack_top;
|
||||
}
|
||||
|
||||
handler = get_exception_handler(context, tid, state->gregs[R_PC]);
|
||||
}
|
||||
return returned_frames;
|
||||
}
|
||||
|
||||
ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
|
||||
const ucontext_t* uc = (const ucontext_t*)sigcontext;
|
||||
|
||||
unwind_state_t state;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
state.gregs[i] = uc->uc_mcontext.gregs[i];
|
||||
}
|
||||
|
||||
return unwind_backtrace_common(-1, NULL, &state, backtrace, ignore_depth, max_depth);
|
||||
}
|
||||
|
||||
ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
|
||||
struct pt_regs regs;
|
||||
if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
unwind_state_t state;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
state.gregs[i] = regs.uregs[i];
|
||||
}
|
||||
|
||||
return unwind_backtrace_common(tid, context, &state, backtrace, ignore_depth, max_depth);
|
||||
}
|
||||
65
libcorkscrew/arch-arm/ptrace-arm.c
Normal file
65
libcorkscrew/arch-arm/ptrace-arm.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Corkscrew"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "../ptrace-arch.h"
|
||||
|
||||
#include <sys/exec_elf.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
#ifndef PT_ARM_EXIDX
|
||||
#define PT_ARM_EXIDX 0x70000001
|
||||
#endif
|
||||
|
||||
static void load_exidx_header(pid_t pid, map_info_t* mi,
|
||||
uintptr_t* out_exidx_start, size_t* out_exidx_size) {
|
||||
uint32_t elf_phoff;
|
||||
uint32_t elf_phnum;
|
||||
if (try_get_word(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
|
||||
&& try_get_word(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_phnum)) {
|
||||
for (uint32_t i = 0; i < elf_phnum; i++) {
|
||||
uintptr_t elf_phdr = mi->start + elf_phoff + i * sizeof(Elf32_Phdr);
|
||||
uint32_t elf_phdr_type;
|
||||
if (!try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
|
||||
break;
|
||||
}
|
||||
if (elf_phdr_type == PT_ARM_EXIDX) {
|
||||
uint32_t elf_phdr_offset;
|
||||
uint32_t elf_phdr_filesz;
|
||||
if (!try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
|
||||
&elf_phdr_offset)
|
||||
|| !try_get_word(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz),
|
||||
&elf_phdr_filesz)) {
|
||||
break;
|
||||
}
|
||||
*out_exidx_start = mi->start + elf_phdr_offset;
|
||||
*out_exidx_size = elf_phdr_filesz;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*out_exidx_start = 0;
|
||||
*out_exidx_size = 0;
|
||||
}
|
||||
|
||||
void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
|
||||
load_exidx_header(pid, mi, &data->exidx_start, &data->exidx_size);
|
||||
}
|
||||
|
||||
void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) {
|
||||
}
|
||||
41
libcorkscrew/backtrace-arch.h
Normal file
41
libcorkscrew/backtrace-arch.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Architecture dependent functions. */
|
||||
|
||||
#ifndef _CORKSCREW_BACKTRACE_ARCH_H
|
||||
#define _CORKSCREW_BACKTRACE_ARCH_H
|
||||
|
||||
#include "ptrace-arch.h"
|
||||
#include <corkscrew/backtrace.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
|
||||
|
||||
ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CORKSCREW_BACKTRACE_ARCH_H
|
||||
40
libcorkscrew/backtrace-helper.c
Normal file
40
libcorkscrew/backtrace-helper.c
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Corkscrew"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "backtrace-helper.h"
|
||||
|
||||
#include <cutils/log.h>
|
||||
|
||||
backtrace_frame_t* add_backtrace_entry(uintptr_t pc, backtrace_frame_t* backtrace,
|
||||
size_t ignore_depth, size_t max_depth,
|
||||
size_t* ignored_frames, size_t* returned_frames) {
|
||||
if (*ignored_frames < ignore_depth) {
|
||||
*ignored_frames += 1;
|
||||
return NULL;
|
||||
}
|
||||
if (*returned_frames >= max_depth) {
|
||||
return NULL;
|
||||
}
|
||||
backtrace_frame_t* frame = &backtrace[*returned_frames];
|
||||
frame->absolute_pc = pc;
|
||||
frame->stack_top = 0;
|
||||
frame->stack_size = 0;
|
||||
*returned_frames += 1;
|
||||
return frame;
|
||||
}
|
||||
43
libcorkscrew/backtrace-helper.h
Normal file
43
libcorkscrew/backtrace-helper.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Backtrace helper functions. */
|
||||
|
||||
#ifndef _CORKSCREW_BACKTRACE_HELPER_H
|
||||
#define _CORKSCREW_BACKTRACE_HELPER_H
|
||||
|
||||
#include <corkscrew/backtrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Add a program counter to a backtrace if it will fit.
|
||||
* Returns the newly added frame, or NULL if none.
|
||||
*/
|
||||
backtrace_frame_t* add_backtrace_entry(uintptr_t pc,
|
||||
backtrace_frame_t* backtrace,
|
||||
size_t ignore_depth, size_t max_depth,
|
||||
size_t* ignored_frames, size_t* returned_frames);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CORKSCREW_BACKTRACE_HELPER_H
|
||||
205
libcorkscrew/backtrace.c
Normal file
205
libcorkscrew/backtrace.c
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Corkscrew"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "backtrace-arch.h"
|
||||
#include "backtrace-helper.h"
|
||||
#include "ptrace-arch.h"
|
||||
#include <corkscrew/map_info.h>
|
||||
#include <corkscrew/symbol_table.h>
|
||||
#include <corkscrew/ptrace.h>
|
||||
#include <corkscrew/demangle.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unwind.h>
|
||||
#include <sys/exec_elf.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
#if HAVE_DLADDR
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
backtrace_frame_t* backtrace;
|
||||
size_t ignore_depth;
|
||||
size_t max_depth;
|
||||
size_t ignored_frames;
|
||||
size_t returned_frames;
|
||||
} backtrace_state_t;
|
||||
|
||||
static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) {
|
||||
backtrace_state_t* state = (backtrace_state_t*)arg;
|
||||
uintptr_t pc = _Unwind_GetIP(context);
|
||||
if (pc) {
|
||||
// TODO: Get information about the stack layout from the _Unwind_Context.
|
||||
// This will require a new architecture-specific function to query
|
||||
// the appropriate registers. Current callers of unwind_backtrace
|
||||
// don't need this information, so we won't bother collecting it just yet.
|
||||
add_backtrace_entry(pc, state->backtrace,
|
||||
state->ignore_depth, state->max_depth,
|
||||
&state->ignored_frames, &state->returned_frames);
|
||||
}
|
||||
return state->returned_frames < state->max_depth ? _URC_NO_REASON : _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
|
||||
backtrace_state_t state;
|
||||
state.backtrace = backtrace;
|
||||
state.ignore_depth = ignore_depth;
|
||||
state.max_depth = max_depth;
|
||||
state.ignored_frames = 0;
|
||||
state.returned_frames = 0;
|
||||
|
||||
_Unwind_Reason_Code rc =_Unwind_Backtrace(unwind_backtrace_callback, &state);
|
||||
if (state.returned_frames) {
|
||||
return state.returned_frames;
|
||||
}
|
||||
return rc == _URC_END_OF_STACK ? 0 : -1;
|
||||
}
|
||||
|
||||
#ifdef CORKSCREW_HAVE_ARCH
|
||||
static pthread_mutex_t g_unwind_signal_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static volatile struct {
|
||||
backtrace_frame_t* backtrace;
|
||||
size_t ignore_depth;
|
||||
size_t max_depth;
|
||||
size_t returned_frames;
|
||||
bool done;
|
||||
} g_unwind_signal_state;
|
||||
|
||||
static void unwind_backtrace_thread_signal_handler(int n, siginfo_t* siginfo, void* sigcontext) {
|
||||
backtrace_frame_t* backtrace = g_unwind_signal_state.backtrace;
|
||||
if (backtrace) {
|
||||
g_unwind_signal_state.backtrace = NULL;
|
||||
g_unwind_signal_state.returned_frames = unwind_backtrace_signal_arch(
|
||||
siginfo, sigcontext, backtrace,
|
||||
g_unwind_signal_state.ignore_depth,
|
||||
g_unwind_signal_state.max_depth);
|
||||
g_unwind_signal_state.done = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace,
|
||||
size_t ignore_depth, size_t max_depth) {
|
||||
#ifdef CORKSCREW_HAVE_ARCH
|
||||
struct sigaction act;
|
||||
struct sigaction oact;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = unwind_backtrace_thread_signal_handler;
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
sigemptyset(&act.sa_mask);
|
||||
|
||||
pthread_mutex_lock(&g_unwind_signal_mutex);
|
||||
|
||||
g_unwind_signal_state.backtrace = backtrace;
|
||||
g_unwind_signal_state.ignore_depth = ignore_depth;
|
||||
g_unwind_signal_state.max_depth = max_depth;
|
||||
g_unwind_signal_state.returned_frames = 0;
|
||||
g_unwind_signal_state.done = false;
|
||||
|
||||
ssize_t frames = -1;
|
||||
if (!sigaction(SIGURG, &act, &oact)) {
|
||||
if (!kill(tid, SIGURG)) {
|
||||
while (!g_unwind_signal_state.done) {
|
||||
usleep(1000);
|
||||
}
|
||||
frames = g_unwind_signal_state.returned_frames;
|
||||
}
|
||||
sigaction(SIGURG, &oact, NULL);
|
||||
}
|
||||
|
||||
g_unwind_signal_state.backtrace = NULL;
|
||||
|
||||
pthread_mutex_unlock(&g_unwind_signal_mutex);
|
||||
return frames;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context,
|
||||
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
|
||||
#ifdef CORKSCREW_HAVE_ARCH
|
||||
return unwind_backtrace_ptrace_arch(tid, context, backtrace, ignore_depth, max_depth);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void init_backtrace_symbol(backtrace_symbol_t* symbol, uintptr_t pc) {
|
||||
symbol->relative_pc = pc;
|
||||
symbol->map_info = NULL;
|
||||
symbol->name = NULL;
|
||||
symbol->demangled_name = NULL;
|
||||
}
|
||||
|
||||
void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames,
|
||||
backtrace_symbol_t* backtrace_symbols) {
|
||||
const map_info_t* milist = my_map_info_list();
|
||||
for (size_t i = 0; i < frames; i++) {
|
||||
const backtrace_frame_t* frame = &backtrace[i];
|
||||
backtrace_symbol_t* symbol = &backtrace_symbols[i];
|
||||
init_backtrace_symbol(symbol, frame->absolute_pc);
|
||||
|
||||
const map_info_t* mi = find_map_info(milist, frame->absolute_pc);
|
||||
if (mi) {
|
||||
symbol->relative_pc = frame->absolute_pc - mi->start;
|
||||
symbol->map_info = mi;
|
||||
#if HAVE_DLADDR
|
||||
Dl_info info;
|
||||
if (dladdr((const void*)frame->absolute_pc, &info) && info.dli_sname) {
|
||||
symbol->name = info.dli_sname;
|
||||
symbol->demangled_name = demangle_symbol_name(symbol->name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
|
||||
const backtrace_frame_t* backtrace, size_t frames,
|
||||
backtrace_symbol_t* backtrace_symbols) {
|
||||
for (size_t i = 0; i < frames; i++) {
|
||||
const backtrace_frame_t* frame = &backtrace[i];
|
||||
backtrace_symbol_t* symbol = &backtrace_symbols[i];
|
||||
init_backtrace_symbol(symbol, frame->absolute_pc);
|
||||
|
||||
const map_info_t* mi;
|
||||
const symbol_t* s;
|
||||
find_symbol_ptrace(context, frame->absolute_pc, &mi, &s);
|
||||
if (mi) {
|
||||
symbol->relative_pc = frame->absolute_pc - mi->start;
|
||||
symbol->map_info = mi;
|
||||
}
|
||||
if (s) {
|
||||
symbol->name = s->name;
|
||||
symbol->demangled_name = demangle_symbol_name(symbol->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames) {
|
||||
for (size_t i = 0; i < frames; i++) {
|
||||
backtrace_symbol_t* symbol = &backtrace_symbols[i];
|
||||
free(symbol->demangled_name);
|
||||
init_backtrace_symbol(symbol, 0);
|
||||
}
|
||||
}
|
||||
35
libcorkscrew/demangle.c
Normal file
35
libcorkscrew/demangle.c
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Corkscrew"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <corkscrew/demangle.h>
|
||||
|
||||
#include <cutils/log.h>
|
||||
|
||||
#ifdef CORKSCREW_HAVE_LIBIBERTY
|
||||
// Defined in libiberty.a
|
||||
extern char *cplus_demangle(const char *mangled, int options);
|
||||
#endif
|
||||
|
||||
char* demangle_symbol_name(const char* name) {
|
||||
#ifdef CORKSCREW_HAVE_LIBIBERTY
|
||||
return name ? cplus_demangle(name, 0) : NULL;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
112
libcorkscrew/map_info.c
Normal file
112
libcorkscrew/map_info.c
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Corkscrew"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <corkscrew/map_info.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
static map_info_t* parse_maps_line(const char* line)
|
||||
{
|
||||
unsigned long int start;
|
||||
unsigned long int end;
|
||||
char permissions[5];
|
||||
int name_pos;
|
||||
if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end,
|
||||
permissions, &name_pos) != 3) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (isspace(line[name_pos])) {
|
||||
name_pos += 1;
|
||||
}
|
||||
const char* name = line + name_pos;
|
||||
size_t name_len = strlen(name);
|
||||
if (name_len && name[name_len - 1] == '\n') {
|
||||
name_len -= 1;
|
||||
}
|
||||
|
||||
map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len + 1);
|
||||
if (mi) {
|
||||
mi->start = start;
|
||||
mi->end = end;
|
||||
mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
|
||||
mi->data = NULL;
|
||||
memcpy(mi->name, name, name_len);
|
||||
mi->name[name_len] = '\0';
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
||||
map_info_t* load_map_info_list(pid_t tid) {
|
||||
char path[PATH_MAX];
|
||||
char line[1024];
|
||||
FILE* fp;
|
||||
map_info_t* milist = NULL;
|
||||
|
||||
snprintf(path, PATH_MAX, "/proc/%d/maps", tid);
|
||||
fp = fopen(path, "r");
|
||||
if (fp) {
|
||||
while(fgets(line, sizeof(line), fp)) {
|
||||
map_info_t* mi = parse_maps_line(line);
|
||||
if (mi) {
|
||||
mi->next = milist;
|
||||
milist = mi;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
return milist;
|
||||
}
|
||||
|
||||
void free_map_info_list(map_info_t* milist) {
|
||||
while (milist) {
|
||||
map_info_t* next = milist->next;
|
||||
free(milist);
|
||||
milist = next;
|
||||
}
|
||||
}
|
||||
|
||||
const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) {
|
||||
const map_info_t* mi = milist;
|
||||
while (mi && !(addr >= mi->start && addr < mi->end)) {
|
||||
mi = mi->next;
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
||||
static pthread_once_t g_my_milist_once = PTHREAD_ONCE_INIT;
|
||||
static map_info_t* g_my_milist = NULL;
|
||||
|
||||
static void init_my_milist_once() {
|
||||
g_my_milist = load_map_info_list(getpid());
|
||||
}
|
||||
|
||||
const map_info_t* my_map_info_list() {
|
||||
pthread_once(&g_my_milist_once, init_my_milist_once);
|
||||
return g_my_milist;
|
||||
}
|
||||
47
libcorkscrew/ptrace-arch.h
Normal file
47
libcorkscrew/ptrace-arch.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Architecture dependent functions. */
|
||||
|
||||
#ifndef _CORKSCREW_PTRACE_ARCH_H
|
||||
#define _CORKSCREW_PTRACE_ARCH_H
|
||||
|
||||
#include <corkscrew/ptrace.h>
|
||||
#include <corkscrew/map_info.h>
|
||||
#include <corkscrew/symbol_table.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Custom extra data we stuff into map_info_t structures as part
|
||||
* of our ptrace_context_t. */
|
||||
typedef struct {
|
||||
#ifdef __arm__
|
||||
uintptr_t exidx_start;
|
||||
size_t exidx_size;
|
||||
#endif
|
||||
symbol_table_t* symbol_table;
|
||||
} map_info_data_t;
|
||||
|
||||
void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data);
|
||||
void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _CORKSCREW_PTRACE_ARCH_H
|
||||
134
libcorkscrew/ptrace.c
Normal file
134
libcorkscrew/ptrace.c
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Corkscrew"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "ptrace-arch.h"
|
||||
#include <corkscrew/ptrace.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
static const uint32_t ELF_MAGIC = 0x464C457f; // "ELF\0177"
|
||||
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifndef PAGE_MASK
|
||||
#define PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
#endif
|
||||
|
||||
bool try_get_word(pid_t tid, uintptr_t ptr, uint32_t* out_value) {
|
||||
if (ptr & 3) {
|
||||
LOGV("try_get_word: invalid pointer 0x%08x", ptr);
|
||||
*out_value = 0;
|
||||
return false;
|
||||
}
|
||||
if (tid < 0) {
|
||||
#if 0 /*unreliable, unclear whether this is safe from a signal handler context*/
|
||||
// Determine whether the pointer is likely to be valid before dereferencing it.
|
||||
unsigned char vec[1];
|
||||
while (mincore((void*)(ptr & PAGE_MASK), sizeof(uint32_t), vec)) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
LOGV("try_get_word: invalid pointer 0x%08x, mincore() errno=%d", ptr, errno);
|
||||
*out_value = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
*out_value = *(uint32_t*)ptr;
|
||||
return true;
|
||||
} else {
|
||||
// ptrace() returns -1 and sets errno when the operation fails.
|
||||
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
||||
errno = 0;
|
||||
*out_value = ptrace(PTRACE_PEEKTEXT, tid, (void*)ptr, NULL);
|
||||
if (*out_value == 0xffffffffL && errno) {
|
||||
LOGV("try_get_word: invalid pointer 0x%08x, ptrace() errno=%d", ptr, errno);
|
||||
*out_value = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_ptrace_map_info_data(pid_t pid, map_info_t* mi) {
|
||||
if (mi->is_executable) {
|
||||
uint32_t elf_magic;
|
||||
if (try_get_word(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) {
|
||||
map_info_data_t* data = (map_info_data_t*)calloc(1, sizeof(map_info_data_t));
|
||||
if (data) {
|
||||
mi->data = data;
|
||||
if (mi->name[0]) {
|
||||
data->symbol_table = load_symbol_table(mi->name);
|
||||
}
|
||||
#ifdef CORKSCREW_HAVE_ARCH
|
||||
load_ptrace_map_info_data_arch(pid, mi, data);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptrace_context_t* load_ptrace_context(pid_t pid) {
|
||||
ptrace_context_t* context =
|
||||
(ptrace_context_t*)calloc(1, sizeof(ptrace_context_t));
|
||||
if (context) {
|
||||
context->map_info_list = load_map_info_list(pid);
|
||||
for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) {
|
||||
load_ptrace_map_info_data(pid, mi);
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
static void free_ptrace_map_info_data(map_info_t* mi) {
|
||||
map_info_data_t* data = (map_info_data_t*)mi->data;
|
||||
if (data) {
|
||||
if (data->symbol_table) {
|
||||
free_symbol_table(data->symbol_table);
|
||||
}
|
||||
#ifdef CORKSCREW_HAVE_ARCH
|
||||
free_ptrace_map_info_data_arch(mi, data);
|
||||
#endif
|
||||
free(data);
|
||||
mi->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void free_ptrace_context(ptrace_context_t* context) {
|
||||
for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) {
|
||||
free_ptrace_map_info_data(mi);
|
||||
}
|
||||
free_map_info_list(context->map_info_list);
|
||||
}
|
||||
|
||||
void find_symbol_ptrace(const ptrace_context_t* context,
|
||||
uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol) {
|
||||
const map_info_t* mi = find_map_info(context->map_info_list, addr);
|
||||
const symbol_t* symbol = NULL;
|
||||
if (mi) {
|
||||
const map_info_data_t* data = (const map_info_data_t*)mi->data;
|
||||
if (data && data->symbol_table) {
|
||||
symbol = find_symbol(data->symbol_table, addr - mi->start);
|
||||
}
|
||||
}
|
||||
*out_map_info = mi;
|
||||
*out_symbol = symbol;
|
||||
}
|
||||
204
libcorkscrew/symbol_table.c
Normal file
204
libcorkscrew/symbol_table.c
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Corkscrew"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <corkscrew/symbol_table.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/exec_elf.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
// Compare function for qsort
|
||||
static int qcompar(const void *a, const void *b) {
|
||||
const symbol_t* asym = (const symbol_t*)a;
|
||||
const symbol_t* bsym = (const symbol_t*)b;
|
||||
if (asym->start > bsym->start) return 1;
|
||||
if (asym->start < bsym->start) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compare function for bsearch
|
||||
static int bcompar(const void *key, const void *element) {
|
||||
uintptr_t addr = *(const uintptr_t*)key;
|
||||
const symbol_t* symbol = (const symbol_t*)element;
|
||||
if (addr < symbol->start) return -1;
|
||||
if (addr >= symbol->end) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
symbol_table_t* load_symbol_table(const char *filename) {
|
||||
symbol_table_t* table = NULL;
|
||||
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb)) {
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
size_t length = sb.st_size;
|
||||
char* base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (base == MAP_FAILED) {
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
// Parse the file header
|
||||
Elf32_Ehdr *hdr = (Elf32_Ehdr*)base;
|
||||
if (!IS_ELF(*hdr)) {
|
||||
goto out_close;
|
||||
}
|
||||
Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff);
|
||||
|
||||
// Search for the dynamic symbols section
|
||||
int sym_idx = -1;
|
||||
int dynsym_idx = -1;
|
||||
for (Elf32_Half i = 0; i < hdr->e_shnum; i++) {
|
||||
if (shdr[i].sh_type == SHT_SYMTAB) {
|
||||
sym_idx = i;
|
||||
}
|
||||
if (shdr[i].sh_type == SHT_DYNSYM) {
|
||||
dynsym_idx = i;
|
||||
}
|
||||
}
|
||||
if (dynsym_idx == -1 && sym_idx == -1) {
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
table = malloc(sizeof(symbol_table_t));
|
||||
if(!table) {
|
||||
goto out_unmap;
|
||||
}
|
||||
table->num_symbols = 0;
|
||||
|
||||
Elf32_Sym *dynsyms = NULL;
|
||||
int dynnumsyms = 0;
|
||||
char *dynstr = NULL;
|
||||
if (dynsym_idx != -1) {
|
||||
dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset);
|
||||
dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize;
|
||||
int dynstr_idx = shdr[dynsym_idx].sh_link;
|
||||
dynstr = base + shdr[dynstr_idx].sh_offset;
|
||||
}
|
||||
|
||||
Elf32_Sym *syms = NULL;
|
||||
int numsyms = 0;
|
||||
char *str = NULL;
|
||||
if (sym_idx != -1) {
|
||||
syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset);
|
||||
numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize;
|
||||
int str_idx = shdr[sym_idx].sh_link;
|
||||
str = base + shdr[str_idx].sh_offset;
|
||||
}
|
||||
|
||||
int dynsymbol_count = 0;
|
||||
if (dynsym_idx != -1) {
|
||||
// Iterate through the dynamic symbol table, and count how many symbols
|
||||
// are actually defined
|
||||
for (int i = 0; i < dynnumsyms; i++) {
|
||||
if (dynsyms[i].st_shndx != SHN_UNDEF) {
|
||||
dynsymbol_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t symbol_count = 0;
|
||||
if (sym_idx != -1) {
|
||||
// Iterate through the symbol table, and count how many symbols
|
||||
// are actually defined
|
||||
for (int i = 0; i < numsyms; i++) {
|
||||
if (syms[i].st_shndx != SHN_UNDEF
|
||||
&& str[syms[i].st_name]
|
||||
&& syms[i].st_value
|
||||
&& syms[i].st_size) {
|
||||
symbol_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, create an entry in our symbol table structure for each symbol...
|
||||
table->num_symbols += symbol_count + dynsymbol_count;
|
||||
table->symbols = malloc(table->num_symbols * sizeof(symbol_t));
|
||||
if (!table->symbols) {
|
||||
free(table);
|
||||
table = NULL;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
size_t symbol_index = 0;
|
||||
if (dynsym_idx != -1) {
|
||||
// ...and populate them
|
||||
for (int i = 0; i < dynnumsyms; i++) {
|
||||
if (dynsyms[i].st_shndx != SHN_UNDEF) {
|
||||
table->symbols[symbol_index].name = strdup(dynstr + dynsyms[i].st_name);
|
||||
table->symbols[symbol_index].start = dynsyms[i].st_value;
|
||||
table->symbols[symbol_index].end = dynsyms[i].st_value + dynsyms[i].st_size;
|
||||
symbol_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sym_idx != -1) {
|
||||
// ...and populate them
|
||||
for (int i = 0; i < numsyms; i++) {
|
||||
if (syms[i].st_shndx != SHN_UNDEF
|
||||
&& str[syms[i].st_name]
|
||||
&& syms[i].st_value
|
||||
&& syms[i].st_size) {
|
||||
table->symbols[symbol_index].name = strdup(str + syms[i].st_name);
|
||||
table->symbols[symbol_index].start = syms[i].st_value;
|
||||
table->symbols[symbol_index].end = syms[i].st_value + syms[i].st_size;
|
||||
symbol_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the symbol table entries, so they can be bsearched later
|
||||
qsort(table->symbols, table->num_symbols, sizeof(symbol_t), qcompar);
|
||||
|
||||
out_unmap:
|
||||
munmap(base, length);
|
||||
|
||||
out_close:
|
||||
close(fd);
|
||||
|
||||
out:
|
||||
return table;
|
||||
}
|
||||
|
||||
void free_symbol_table(symbol_table_t* table) {
|
||||
if (table) {
|
||||
for (size_t i = 0; i < table->num_symbols; i++) {
|
||||
free(table->symbols[i].name);
|
||||
}
|
||||
free(table->symbols);
|
||||
free(table);
|
||||
}
|
||||
}
|
||||
|
||||
const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr) {
|
||||
if (!table) return NULL;
|
||||
return (const symbol_t*)bsearch(&addr, table->symbols, table->num_symbols,
|
||||
sizeof(symbol_t), bcompar);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue