├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature-request.yml └── pull_request_template.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README.zh-CN.md ├── SECURITY.md ├── build.gradle ├── checkstyle.xml ├── clang-format.py ├── gradle.properties ├── gradle ├── check.gradle ├── publish.gradle ├── sanitizer.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── xunwind ├── .gitignore ├── build.gradle ├── consumer-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ ├── include │ │ └── xunwind.h │ ├── xu_cfi.c │ ├── xu_cfi.h │ ├── xu_config.h │ ├── xu_eh.c │ ├── xu_eh.h │ ├── xu_formatter.c │ ├── xu_formatter.h │ ├── xu_fp.h │ ├── xu_fp_arm64.c │ ├── xu_fp_unimplemented.c │ ├── xu_jni.c │ ├── xu_libbacktrace.c │ ├── xu_libbacktrace.h │ ├── xu_libcorkscrew.c │ ├── xu_libcorkscrew.h │ ├── xu_libcxx.c │ ├── xu_libcxx.h │ ├── xu_log.h │ ├── xu_printer.c │ ├── xu_printer.h │ ├── xu_util.c │ ├── xu_util.h │ ├── xunwind.c │ ├── xunwind.map.txt │ └── xunwind_ctor.c │ └── java │ └── io │ └── github │ └── hexhacking │ └── xunwind │ └── XUnwind.java └── xunwind_sample ├── .gitignore ├── build.gradle └── src └── main ├── AndroidManifest.xml ├── cpp ├── CMakeLists.txt └── sample.c ├── java └── io │ └── github │ └── hexhacking │ └── xunwind │ └── sample │ ├── MainActivity.java │ ├── MyCustomApplication.java │ ├── MyService.java │ └── NativeSample.java └── res ├── drawable-v24 └── ic_launcher_foreground.xml ├── drawable └── ic_launcher_background.xml ├── layout └── activity_main.xml ├── mipmap-anydpi-v26 ├── ic_launcher.xml └── ic_launcher_round.xml ├── mipmap-hdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-mdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xhdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xxhdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xxxhdpi ├── ic_launcher.png └── ic_launcher_round.png └── values ├── colors.xml ├── strings.xml └── themes.xml /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | --- 4 | 5 | Language: Cpp 6 | 7 | AlignConsecutiveMacros: true 8 | AllowShortFunctionsOnASingleLine: Empty 9 | ColumnLimit: 110 10 | IndentExternBlock: NoIndent 11 | TypenameMacros: ['ElfW'] 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve. 3 | labels: "bug" 4 | 5 | body: 6 | 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## Notice 11 | 12 | 1. Please follow the [Code of Conduct](https://github.com/hexhacking/xUnwind/blob/master/CODE_OF_CONDUCT.md) that this project adheres to. 13 | 2. Please test using [the latest release](https://github.com/hexhacking/xUnwind/releases) to make sure your issue has not already been fixed. 14 | 3. Before submitting a bug, please make sure the issue hasn't been already addressed by searching through [the existing and past issues](https://github.com/hexhacking/xUnwind/issues?q=is%3Aissue+sort%3Acreated-desc+). 15 | 16 | - type: input 17 | attributes: 18 | label: xUnwind Version 19 | placeholder: "1.0.0" 20 | validations: 21 | required: true 22 | 23 | - type: input 24 | attributes: 25 | label: Android OS Version 26 | placeholder: "10.0" 27 | validations: 28 | required: true 29 | 30 | - type: dropdown 31 | attributes: 32 | label: Android ABIs 33 | multiple: true 34 | options: 35 | - armeabi-v7a 36 | - armeabi-v7a on Houdini 37 | - arm64-v8a 38 | - arm64-v8a on Houdini 39 | - x86 40 | - x86_64 41 | - Other (specify below) 42 | validations: 43 | required: true 44 | 45 | - type: input 46 | attributes: 47 | label: Device Manufacturers and Models 48 | placeholder: "Google Pixel 6 Pro, Samsung Galaxy S22 Ultra, ..." 49 | validations: 50 | required: true 51 | 52 | - type: textarea 53 | attributes: 54 | label: Describe the Bug 55 | description: | 56 | Please provide a clear and concise description of what the bug is. 57 | 58 | If relevant, add a minimal example so that we can reproduce the error by running the code. It is very important for the code snippet to be as succinct (minimal) as possible, so please take time to trim down any irrelevant code to help us debug efficiently. We are going to copy-paste your code and we expect to get the same result as you did. 59 | 60 | If you observe an error, please paste the **full** error message you got. In case of a native crash, please paste the **full** tombstone in the `/data/tombstones` directory. For non-rooted devices, refer to [bugreport](https://developer.android.com/studio/debug/bug-report). 61 | validations: 62 | required: true 63 | 64 | - type: markdown 65 | attributes: 66 | value: > 67 | Thanks for contributing! :tada: 68 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions and Discussions 4 | url: https://github.com/hexhacking/xUnwind/discussions 5 | about: Looking for help. Discuss general issues. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Submit a proposal/request for a new feature. 3 | labels: "enhancement" 4 | 5 | body: 6 | 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## Notice 11 | 12 | 1. Please follow the [Code of Conduct](https://github.com/hexhacking/xUnwind/blob/master/CODE_OF_CONDUCT.md) that this project adheres to. 13 | 2. Please check [the latest release](https://github.com/hexhacking/xUnwind/releases) to make sure your proposal has not already exists. 14 | 3. Before submitting a feature request, please make sure the proposal hasn't been already addressed by searching through [the existing and past issues](https://github.com/hexhacking/xUnwind/issues?q=is%3Aissue+sort%3Acreated-desc+). 15 | 16 | - type: textarea 17 | attributes: 18 | label: the Feature, Motivation and Pitch 19 | description: > 20 | A clear and concise description of the feature proposal. Please outline the motivation for the proposal. Is your feature request related to a specific problem? If this is related to another GitHub issue, please link here too. 21 | validations: 22 | required: true 23 | 24 | - type: textarea 25 | attributes: 26 | label: Alternatives 27 | description: > 28 | A description of any alternative solutions or features you've considered, if any. 29 | 30 | - type: textarea 31 | attributes: 32 | label: Additional context 33 | description: > 34 | Add any other context about the feature request. 35 | 36 | - type: markdown 37 | attributes: 38 | value: > 39 | Thanks for contributing! :tada: 40 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | This PR fixes # 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | build/ 4 | .cxx/ 5 | .gradle/ 6 | .idea/ 7 | *.iml 8 | *.log 9 | *.so 10 | wrap.sh 11 | local.properties -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available 126 | at [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to xUnwind 2 | 3 | Welcome to the xUnwind project. Read on to learn more about our development process and how to propose bug fixes and improvements. 4 | 5 | ## Issues 6 | 7 | We use GitHub issues to track public bugs and feature requests. Before creating an issue, please note the following: 8 | 9 | 1. Please search existing issues before creating a new one. 10 | 2. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. The more information the better. 11 | 12 | 13 | ## Branch Management 14 | 15 | There are 2 main branches: 16 | 17 | 1. `master` branch 18 | 19 | * It's the latest (pre-)release branch. We use `master` for tags. 20 | * **Please do NOT submit any PR on `master` branch.** 21 | 22 | 2. `dev` branch 23 | 24 | * It's our stable developing branch. 25 | * Once `dev` has passed our internal tests, it will be merged to `master` branch for the next release. 26 | * **Please always submit PR on `dev` branch.** 27 | 28 | 29 | ## Pull Requests 30 | 31 | **If possible, always create an issue first where you can describe your problem or feature request. We can also communicate in this issue. Then you can link to that issue in the pull request.** 32 | 33 | Please make sure the following is done when submitting a pull request: 34 | 35 | 1. Fork the repo and create your branch from `master`. 36 | 2. Add the copyright notice to the top of any new files you've added. 37 | 3. Check your Java code lints and checkstyles. 38 | 4. Try your best to test your code. 39 | 5. Squash all of your commits into one meaningful commit. 40 | 41 | 42 | ## Code Style Guide 43 | 44 | 1. Follow the [.clang-format](.clang-format) for C code. 45 | 2. Follow the [checkstyle.xml](checkstyle.xml) for Java code. 46 | 47 | 48 | ## License 49 | 50 | By contributing to xUnwind, you agree that your contributions will be licensed under its [MIT LICENSE](LICENSE). 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2023 HexHacking Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xUnwind 2 | 3 | ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) 4 | ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) 5 | ![](https://img.shields.io/badge/release-2.0.0-red.svg?style=flat) 6 | ![](https://img.shields.io/badge/Android-4.1%20--%2013-blue.svg?style=flat) 7 | ![](https://img.shields.io/badge/arch-armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) 8 | 9 | xUnwind is a collection of Android native stack unwinding solutions. 10 | 11 | [README 中文版](README.zh-CN.md) 12 | 13 | 14 | ## Features 15 | 16 | * Support unwinding by: 17 | * CFI (Call Frame Info): Provided by Android system library. 18 | * EH (Exception handling GCC extension): Provided by compiler. 19 | * FP (Frame Pointer): ARM64 only. 20 | * Support unwinding from: 21 | * Current execution position. 22 | * A specified context (which may be obtained from a signal handler). 23 | * Support unwinding for process: 24 | * Local process. 25 | * Remote process: CFI only. 26 | * Support unwinding for thread(s): 27 | * Current thread. 28 | * Specified thread: CFI only. 29 | * All threads: CFI only. 30 | * Provide java method to get native backtrace directly in java code. 31 | * Support Android 4.1 - 13 (API level 16 - 33). 32 | * Support armeabi-v7a, arm64-v8a, x86 and x86_64. 33 | * MIT licensed. 34 | 35 | 36 | ## Usage 37 | 38 | ### 1. Add dependency in build.gradle 39 | 40 | xUnwind is published on [Maven Central](https://search.maven.org/), and uses [Prefab](https://google.github.io/prefab/) package format for [native dependencies](https://developer.android.com/studio/build/native-dependencies), which is supported by [Android Gradle Plugin 4.0+](https://developer.android.com/studio/releases/gradle-plugin?buildsystem=cmake#native-dependencies). 41 | 42 | ```Gradle 43 | android { 44 | buildFeatures { 45 | prefab true 46 | } 47 | } 48 | 49 | dependencies { 50 | implementation 'io.github.hexhacking:xunwind:2.0.0' 51 | } 52 | ``` 53 | 54 | **NOTE**: 55 | 56 | 1. Starting from version `2.0.0` of xUnwind, group ID changed from `io.hexhacking` to `io.github.hexhacking`. 57 | 58 | | version range | group ID | artifact ID | Repository URL | 59 | |:---------------|:-------------------------|:------------| :--------------| 60 | | [1.0.1, 1.1.1] | io.hexhacking | xunwind | [repo](https://repo1.maven.org/maven2/io/hexhacking/xunwind/) | 61 | | [2.0.0, ) | **io.github.hexhacking** | xunwind | [repo](https://repo1.maven.org/maven2/io/github/hexhacking/xunwind/) | 62 | 63 | 2. xUnwind uses the [prefab package schema v2](https://github.com/google/prefab/releases/tag/v2.0.0), which is configured by default since [Android Gradle Plugin 7.1.0](https://developer.android.com/studio/releases/gradle-plugin?buildsystem=cmake#7-1-0). If you are using Android Gradle Plugin earlier than 7.1.0, please add the following configuration to `gradle.properties`: 64 | 65 | ``` 66 | android.prefabVersion=2.0.0 67 | ``` 68 | 69 | ### 2. Add dependency in CMakeLists.txt or Android.mk 70 | 71 | If you only use the java interface of xUnwind, please skip this step. 72 | 73 | > CMakeLists.txt 74 | 75 | ```CMake 76 | find_package(xunwind REQUIRED CONFIG) 77 | 78 | add_library(mylib SHARED mylib.c) 79 | target_link_libraries(mylib xunwind::xunwind) 80 | ``` 81 | 82 | > Android.mk 83 | 84 | ``` 85 | include $(CLEAR_VARS) 86 | LOCAL_MODULE := mylib 87 | LOCAL_SRC_FILES := mylib.c 88 | LOCAL_SHARED_LIBRARIES += xunwind 89 | include $(BUILD_SHARED_LIBRARY) 90 | 91 | $(call import-module,prefab/xunwind) 92 | ``` 93 | 94 | ### 3. Specify one or more ABI(s) you need 95 | 96 | ```Gradle 97 | android { 98 | defaultConfig { 99 | ndk { 100 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 101 | } 102 | } 103 | } 104 | ``` 105 | 106 | ### 4. Add packaging options 107 | 108 | If you are using xUnwind in an SDK project, you may need to avoid packaging libxunwind.so into your AAR, so as not to encounter duplicate libxunwind.so file when packaging the app project. 109 | 110 | ```Gradle 111 | android { 112 | packagingOptions { 113 | exclude '**/libxunwind.so' 114 | } 115 | } 116 | ``` 117 | 118 | On the other hand, if you are using xUnwind in an APP project, you may need to add some options to deal with conflicts caused by duplicate libxunwind.so file. 119 | 120 | ```Gradle 121 | android { 122 | packagingOptions { 123 | pickFirst '**/libxunwind.so' 124 | } 125 | } 126 | ``` 127 | 128 | There is a sample app in the [xunwind-sample](xunwind_sample) folder you can refer to. 129 | 130 | 131 | ## Native API 132 | 133 | ```C 134 | #include "xunwind.h" 135 | ``` 136 | 137 | ### 1. CFI unwinding 138 | 139 | ```C 140 | #define XUNWIND_CURRENT_PROCESS (-1) 141 | #define XUNWIND_CURRENT_THREAD (-1) 142 | #define XUNWIND_ALL_THREADS (-2) 143 | 144 | void xunwind_cfi_log(pid_t pid, pid_t tid, void *context, const char *logtag, android_LogPriority priority, const char *prefix); 145 | void xunwind_cfi_dump(pid_t pid, pid_t tid, void *context, int fd, const char *prefix); 146 | char *xunwind_cfi_get(pid_t pid, pid_t tid, void *context, const char *prefix); 147 | ``` 148 | 149 | These three functions correspond to three ways of obtaining backtrace. They are: 150 | 151 | * `log` to Android logcat. 152 | * `dump` to a place associated with FD (such as file, pipe, socket, etc.). 153 | * `get` and return a string (which is allocated on the heap with `malloc()`, you need to `free()` it yourself). 154 | 155 | The `pid` parameter is used to specify the backtrace of which process needs to be obtained, which can be the current process (`XUNWIND_CURRENT_PROCESS` / `getpid()`) or another process. 156 | 157 | The `tid` parameter is used to specify the backtrace of which thread or threads need to be obtained, which can be the current thread (`XUNWIND_CURRENT_THREAD` / `gettid()`), a specified thread, or all threads (`XUNWIND_ALL_THREADS`). 158 | 159 | The optional `context` parameter is used to pass a register context information. For example, in signal handler, you may need to start unwinding from a specific context. 160 | 161 | The optional `prefix` parameter is used to specify a prefix string for each line of backtrace. 162 | 163 | ### 2. FP and EH unwinding 164 | 165 | ```C 166 | size_t xunwind_fp_unwind(uintptr_t* frames, size_t frames_cap, void *context); 167 | size_t xunwind_eh_unwind(uintptr_t* frames, size_t frames_cap, void *context); 168 | 169 | void xunwind_frames_log(uintptr_t* frames, size_t frames_sz, const char *logtag, android_LogPriority priority, const char *prefix); 170 | void xunwind_frames_dump(uintptr_t* frames, size_t frames_sz, int fd, const char *prefix); 171 | char *xunwind_frames_get(uintptr_t* frames, size_t frames_sz, const char *prefix); 172 | ``` 173 | 174 | Currently, FP unwinding is ARM64 only. 175 | 176 | `xunwind_fp_unwind` and `xunwind_eh_unwind` saves the absolute-PCs of the unwinding result in the array pointed to by `frames` (`frames_cap` is the size of the array), and returns the number of absolute-PCs actually obtained. 177 | 178 | The meaning of the optional `context` parameter is the same as that of CFI unwinding. 179 | 180 | The remaining three functions are used to convert the absolute-PCs in the `frames` array into backtrace (the size of the array is specified by `frames_sz`). Same as CFI unwinding, respectively output to Android logcat, FD, and return as a string. 181 | 182 | 183 | ## Java API 184 | 185 | ```Java 186 | import io.github.hexhacking.xunwind.XUnwind; 187 | ``` 188 | 189 | ### 1. Initialize 190 | 191 | ```Java 192 | public static void init(); 193 | ``` 194 | 195 | The only thing `init()` does is `System.loadLibrary("xunwind")`. If you only use xUnwind in native code, no initialization is required. 196 | 197 | ### 2. CFI unwinding 198 | 199 | ```Java 200 | public static void logLocalCurrentThread(String logtag, int priority, String prefix); 201 | public static void logLocalThread(int tid, String logtag, int priority, String prefix); 202 | public static void logLocalAllThread(String logtag, int priority, String prefix); 203 | public static void logRemoteThread(int pid, int tid, String logtag, int priority, String prefix); 204 | public static void logRemoteAllThread(int pid, String logtag, int priority, String prefix); 205 | 206 | public static void dumpLocalCurrentThread(int fd, String prefix); 207 | public static void dumpLocalThread(int tid, int fd, String prefix); 208 | public static void dumpLocalAllThread(int fd, String prefix); 209 | public static void dumpRemoteThread(int pid, int tid, int fd, String prefix); 210 | public static void dumpRemoteAllThread(int pid, int fd, String prefix); 211 | 212 | public static String getLocalCurrentThread(String prefix); 213 | public static String getLocalThread(int tid, String prefix); 214 | public static String getLocalAllThread(String prefix); 215 | public static String getRemoteThread(int pid, int tid, String prefix); 216 | public static String getRemoteAllThread(int pid, String prefix); 217 | ``` 218 | 219 | All native CFI unwinding capabilities have corresponding java functions. They call the native CFI unwinding functions through JNI. 220 | 221 | FP and EH unwinding do not have corresponding java functions. Because compared to CFI unwinding, their main advantage is faster execution speed (but the backtrace is not as complete as CFI unwinding), so they are always only used in native code. If you are a java programmer, just use the CFI unwinding functions here. 222 | 223 | 224 | ## Support 225 | 226 | * [GitHub Issues](https://github.com/hexhacking/xUnwind/issues) 227 | * [GitHub Discussions](https://github.com/hexhacking/xUnwind/discussions) 228 | * [Telegram Public Group](https://t.me/android_native_geeks) 229 | 230 | 231 | ## Contributing 232 | 233 | * [Code of Conduct](CODE_OF_CONDUCT.md) 234 | * [Contributing Guide](CONTRIBUTING.md) 235 | * [Reporting Security vulnerabilities](SECURITY.md) 236 | 237 | 238 | ## License 239 | 240 | xUnwind is MIT licensed, as found in the [LICENSE](LICENSE) file. 241 | 242 | 243 | ## History 244 | 245 | [xCrash 2.x](https://github.com/hexhacking/xCrash/tree/4748d183c1395c54bfb760ec6c454966d52ab73f) contains a set of methods [xcc\_unwind\_*](https://github.com/hexhacking/xCrash/tree/4748d183c1395c54bfb760ec6c454966d52ab73f/src/native/common) to get backtrace, which is used to try to get backtrace directly from the signal handler when the dumper child process fails. Now we have improved and expanded this set of functions so that they can be used in more scenarios. 246 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # xUnwind 2 | 3 | ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) 4 | ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) 5 | ![](https://img.shields.io/badge/release-2.0.0-red.svg?style=flat) 6 | ![](https://img.shields.io/badge/Android-4.1%20--%2013-blue.svg?style=flat) 7 | ![](https://img.shields.io/badge/arch-armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) 8 | 9 | xUnwind 是一个安卓 native 栈回溯方案的集合。 10 | 11 | [README English Version](README.md) 12 | 13 | 14 | ## 特征 15 | 16 | * 支持的栈回溯方案: 17 | * CFI (Call Frame Info):由安卓系统库提供。 18 | * EH (Exception handling GCC extension):由编译器提供。 19 | * FP (Frame Pointer):只支持 ARM64。 20 | * 支持的栈回溯起点: 21 | * 当前执行位置。 22 | * 指定的上下文(可能是从信号处理函数中获取)。 23 | * 支持的栈回溯的进程: 24 | * 当前进程。 25 | * 其他进程:只支持 CFI 方案。 26 | * 支持的栈回溯的线程: 27 | * 当前线程。 28 | * 指定的线程:只支持 CFI 方案。 29 | * 所有的线程:只支持 CFI 方案。 30 | * 提供 java 函数,在 java 代码中直接获取 native 调用栈。 31 | * 支持 Android 4.1 - 13(API level 16 - 33)。 32 | * 支持 armeabi-v7a, arm64-v8a, x86 和 x86_64。 33 | * 使用 MIT 许可证授权。 34 | 35 | 36 | ## 使用 37 | 38 | ### 1. 在 build.gradle 中增加依赖 39 | 40 | xUnwind 发布在 [Maven Central](https://search.maven.org/) 上。为了使用 [native 依赖项](https://developer.android.com/studio/build/native-dependencies),xUnwind 使用了从 [Android Gradle Plugin 4.0+](https://developer.android.com/studio/releases/gradle-plugin?buildsystem=cmake#native-dependencies) 开始支持的 [Prefab](https://google.github.io/prefab/) 包格式。 41 | 42 | ```Gradle 43 | android { 44 | buildFeatures { 45 | prefab true 46 | } 47 | } 48 | 49 | dependencies { 50 | implementation 'io.github.hexhacking:xunwind:2.0.0' 51 | } 52 | ``` 53 | 54 | **注意**: 55 | 56 | 1. xUnwind 从版本 `2.0.0` 开始,group ID 从 `io.hexhacking` 改为 `io.github.hexhacking`。 57 | 58 | | 版本号范围 | group ID | artifact ID | 仓库 URL | 59 | |:---------------|:-------------------------|:------------|:----------------------------------------------------------| 60 | | [1.0.1, 1.1.1] | io.hexhacking | xunwind | [repo](https://repo1.maven.org/maven2/io/hexhacking/xunwind/) | 61 | | [2.0.0, ) | **io.github.hexhacking** | xunwind | [repo](https://repo1.maven.org/maven2/io/github/hexhacking/xunwind/) | 62 | 63 | 2. xUnwind 使用 [prefab package schema v2](https://github.com/google/prefab/releases/tag/v2.0.0),它是从 [Android Gradle Plugin 7.1.0](https://developer.android.com/studio/releases/gradle-plugin?buildsystem=cmake#7-1-0) 开始作为默认配置的。如果你使用的是 Android Gradle Plugin 7.1.0 之前的版本,请在 `gradle.properties` 中加入以下配置: 64 | 65 | ``` 66 | android.prefabVersion=2.0.0 67 | ``` 68 | 69 | ### 2. 在 CMakeLists.txt 或 Android.mk 中增加依赖 70 | 71 | 如果你只使用 xUnwind 的 java 接口,请跳过这一步。 72 | 73 | > CMakeLists.txt 74 | 75 | ```CMake 76 | find_package(xunwind REQUIRED CONFIG) 77 | 78 | add_library(mylib SHARED mylib.c) 79 | target_link_libraries(mylib xunwind::xunwind) 80 | ``` 81 | 82 | > Android.mk 83 | 84 | ``` 85 | include $(CLEAR_VARS) 86 | LOCAL_MODULE := mylib 87 | LOCAL_SRC_FILES := mylib.c 88 | LOCAL_SHARED_LIBRARIES += xunwind 89 | include $(BUILD_SHARED_LIBRARY) 90 | 91 | $(call import-module,prefab/xunwind) 92 | ``` 93 | 94 | ### 3. 指定一个或多个你需要的 ABI 95 | 96 | ```Gradle 97 | android { 98 | defaultConfig { 99 | ndk { 100 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 101 | } 102 | } 103 | } 104 | ``` 105 | 106 | ### 4. 增加打包选项 107 | 108 | 如果你是在一个 SDK 工程里使用 xUnwind,你可能需要避免把 libxunwind.so 打包到你的 AAR 里,以免 app 工程打包时遇到重复的 libxunwind.so 文件。 109 | 110 | ```Gradle 111 | android { 112 | packagingOptions { 113 | exclude '**/libxunwind.so' 114 | } 115 | } 116 | ``` 117 | 118 | 另一方面, 如果你是在一个 APP 工程里使用 xUnwind,你可能需要增加一些选项,用来处理重复的 libxunwind.so 文件引起的冲突。 119 | 120 | ```Gradle 121 | android { 122 | packagingOptions { 123 | pickFirst '**/libxunwind.so' 124 | } 125 | } 126 | ``` 127 | 128 | 你可以参考 [xunwind-sample](xunwind_sample) 文件夹中的示例 app。 129 | 130 | 131 | ## Native API 132 | 133 | ```C 134 | #include "xunwind.h" 135 | ``` 136 | 137 | ### 1. CFI 栈回溯 138 | 139 | ```C 140 | #define XUNWIND_CURRENT_PROCESS (-1) 141 | #define XUNWIND_CURRENT_THREAD (-1) 142 | #define XUNWIND_ALL_THREADS (-2) 143 | 144 | void xunwind_cfi_log(pid_t pid, pid_t tid, void *context, const char *logtag, android_LogPriority priority, const char *prefix); 145 | void xunwind_cfi_dump(pid_t pid, pid_t tid, void *context, int fd, const char *prefix); 146 | char *xunwind_cfi_get(pid_t pid, pid_t tid, void *context, const char *prefix); 147 | ``` 148 | 149 | 这三个函数对应了三种获取 backtrace 的方式。它们是: 150 | 151 | * `log` 到 Android logcat。 152 | * `dump` 到一个 FD 相关的地方(比如文件,管道,套接字,等等)。 153 | * `get` 并返回一个字符串(由 `malloc()` 在堆中分配,你需要自己用 `free()` 释放它)。 154 | 155 | `pid` 参数用于指定需要回溯哪个进程,可以是当前进程(`XUNWIND_CURRENT_PROCESS` / `getpid()`),或者是其他进程。 156 | 157 | `tid` 参数用于指定需要回溯哪个或哪些线程,可以是当前线程(`XUNWIND_CURRENT_THREAD` / `gettid()`),某个指定的线程,或所有线程(`XUNWIND_ALL_THREADS`)。 158 | 159 | 可选的 `context` 参数用于指定寄存器上下文信息。比如,在信号处理函数中,你可以需要从某个特定的寄存器上下文开始执行栈回溯。 160 | 161 | 可选的 `prefix` 参数用于给所有的 backtrace 行指定一个字符串前缀。 162 | 163 | ### 2. FP 和 EH 栈回溯 164 | 165 | ```C 166 | size_t xunwind_fp_unwind(uintptr_t* frames, size_t frames_cap, void *context); 167 | size_t xunwind_eh_unwind(uintptr_t* frames, size_t frames_cap, void *context); 168 | 169 | void xunwind_frames_log(uintptr_t* frames, size_t frames_sz, const char *logtag, android_LogPriority priority, const char *prefix); 170 | void xunwind_frames_dump(uintptr_t* frames, size_t frames_sz, int fd, const char *prefix); 171 | char *xunwind_frames_get(uintptr_t* frames, size_t frames_sz, const char *prefix); 172 | ``` 173 | 174 | 目前,FP 栈回溯只支持 ARM64。 175 | 176 | `xunwind_fp_unwind` 和 `xunwind_eh_unwind` 将栈回溯的结果(一组绝对 PC 值)保存到 `frames` 数组中(`frames_cap` 指定了数组的大小),并且返回获取到的绝对 PC 值的个数。 177 | 178 | 可选的 `context` 参数的作用和 CFI 栈回溯中相同。 179 | 180 | 剩下的三个函数用于将 `frames` 数组中保存的绝对 PC 值转换成 backtrace(数组的大小由 `frames_sz` 参数指定)。和 CFI 栈回溯相同,分别输出到 Android logcat,FD,或者返回一个字符串。 181 | 182 | 183 | ## Java API 184 | 185 | ```Java 186 | import io.github.hexhacking.xunwind.XUnwind; 187 | ``` 188 | 189 | ### 1. 初始化 190 | 191 | ```Java 192 | public static void init(); 193 | ``` 194 | 195 | `init()` 只做了 `System.loadLibrary("xunwind")` 这一件事。如果你只在 native 代码中使用 xUnwind 的话,就不需要初始化了。 196 | 197 | ### 2. CFI 栈回溯 198 | 199 | ```Java 200 | public static void logLocalCurrentThread(String logtag, int priority, String prefix); 201 | public static void logLocalThread(int tid, String logtag, int priority, String prefix); 202 | public static void logLocalAllThread(String logtag, int priority, String prefix); 203 | public static void logRemoteThread(int pid, int tid, String logtag, int priority, String prefix); 204 | public static void logRemoteAllThread(int pid, String logtag, int priority, String prefix); 205 | 206 | public static void dumpLocalCurrentThread(int fd, String prefix); 207 | public static void dumpLocalThread(int tid, int fd, String prefix); 208 | public static void dumpLocalAllThread(int fd, String prefix); 209 | public static void dumpRemoteThread(int pid, int tid, int fd, String prefix); 210 | public static void dumpRemoteAllThread(int pid, int fd, String prefix); 211 | 212 | public static String getLocalCurrentThread(String prefix); 213 | public static String getLocalThread(int tid, String prefix); 214 | public static String getLocalAllThread(String prefix); 215 | public static String getRemoteThread(int pid, int tid, String prefix); 216 | public static String getRemoteAllThread(int pid, String prefix); 217 | ``` 218 | 219 | 所有的 native CFI 栈回溯能力都有对应的 java 函数,它们通过 JNI 调用 native CFI 函数。 220 | 221 | FP 和 EH 栈回溯没有对应的 java 函数。因为相对于 CFI 栈回溯,它们的主要优势是执行速度更快(但是 backtrace 并不像 CFI 栈回溯那样完整),所以它们总是只在 native 代码里使用。如果你是 java 程序员,只使用这里的 CFI 栈回溯就可以了。 222 | 223 | 224 | ## 技术支持 225 | 226 | * [GitHub Issues](https://github.com/hexhacking/xUnwind/issues) 227 | * [GitHub Discussions](https://github.com/hexhacking/xUnwind/discussions) 228 | * [Telegram Public Group](https://t.me/android_native_geeks) 229 | 230 | 231 | ## 贡献 232 | 233 | * [Code of Conduct](CODE_OF_CONDUCT.md) 234 | * [Contributing Guide](CONTRIBUTING.md) 235 | * [Reporting Security vulnerabilities](SECURITY.md) 236 | 237 | 238 | ## 许可证 239 | 240 | xUnwind 使用 [MIT 许可证](LICENSE)。 241 | 242 | 243 | ## 历史 244 | 245 | [xCrash 2.x](https://github.com/hexhacking/xCrash/tree/4748d183c1395c54bfb760ec6c454966d52ab73f) 包含一组用于获取 backtrace 的方法 [xcc\_unwind\_*](https://github.com/hexhacking/xCrash/tree/4748d183c1395c54bfb760ec6c454966d52ab73f/src/native/common) ,它们用于在 dumper 子进程执行失败时,在信号处理函数中直接获取 backtrace。现在,我们完善和扩展了这组方法,使它们能被用于更多的场景中。 246 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security vulnerabilities 2 | 3 | The xUnwind team and community take security bugs in xUnwind seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. 4 | 5 | **Please do not report security vulnerabilities through GitHub issues, discussions or pull requests. This makes the problem immediately visible to everyone, including malicious actors.** 6 | 7 | Instead, please send email to . If possible, encrypt your message with [this PGP key](https://raw.githubusercontent.com/caikelun/caikelun.github.io/master/site/pgp-public-key.txt). 8 | 9 | The xUnwind team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. 10 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' version '7.4.2' apply false 3 | id 'com.android.library' version '7.4.2' apply false 4 | } 5 | 6 | task clean(type: Delete) { 7 | delete rootProject.buildDir 8 | } 9 | 10 | ext { 11 | minSdkVersion = 16 12 | compileSdkVersion = 33 13 | targetSdkVersion = 33 14 | buildToolsVersion = '33.0.2' 15 | javaVersion = JavaVersion.VERSION_1_7 16 | ndkVersion = "23.2.8568313" 17 | cmakeVersion = "3.22.1" 18 | abiFilters = "armeabi-v7a,arm64-v8a,x86,x86_64" 19 | useASAN = false 20 | dependencyOnLocalLibrary = true 21 | xunwindVersion = "2.0.0" 22 | 23 | POM_GROUP_ID = "io.github.hexhacking" 24 | POM_ARTIFACT_ID = "xunwind" 25 | POM_VERSION_NAME = "2.0.0" 26 | 27 | POM_NAME = "xUnwind Android Lib" 28 | POM_DESCRIPTION = "xUnwind is a collection of Android native stack unwinding solutions." 29 | POM_URL = "https://github.com/hexhacking/xUnwind" 30 | POM_INCEPTION_YEAR = "2020" 31 | POM_PACKAGING = "aar" 32 | 33 | POM_SCM_CONNECTION = "https://github.com/hexhacking/xUnwind.git" 34 | 35 | POM_ISSUE_SYSTEM = "github" 36 | POM_ISSUE_URL = "https://github.com/hexhacking/xUnwind/issues" 37 | 38 | POM_LICENCE_NAME = "The MIT License" 39 | POM_LICENCE_URL = "https://opensource.org/licenses/MIT" 40 | POM_LICENCE_DIST = "repo" 41 | 42 | POM_DEVELOPER_ID = "HexHacking" 43 | POM_DEVELOPER_NAME = "HexHacking Team" 44 | } 45 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /clang-format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | import argparse 5 | import subprocess 6 | 7 | path_list = ['xunwind/src/main/cpp/include', 8 | 'xunwind/src/main/cpp', 9 | 'xunwind_sample/src/main/cpp'] 10 | suffix_tuple = ('.h', '.c') 11 | 12 | def main(): 13 | parser = argparse.ArgumentParser(description='clang-format runner') 14 | parser.add_argument('-n', '--dry-run', action='store_true', help='If set, do not actually make the formatting changes') 15 | options = parser.parse_args() 16 | dry_run = '-n ' if options.dry_run else '' 17 | 18 | for path in path_list: 19 | for filename in sorted(os.listdir(path)): 20 | f = os.path.join(path, filename) 21 | if os.path.isfile(f) and f.endswith(suffix_tuple): 22 | cmd = 'clang-format ' + dry_run + '--Werror -i -style=file ' + f 23 | ret = subprocess.run(cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 24 | retcode = ('' if ret.returncode == 0 else (' >>> return code: ' + str(ret.returncode))) 25 | print('>>> ' + cmd + retcode) 26 | print(ret.stdout, end='') 27 | 28 | if __name__ == '__main__': 29 | sys.exit(main()) 30 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | android.disableAutomaticComponentCreation=true 21 | -------------------------------------------------------------------------------- /gradle/check.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'checkstyle' 2 | 3 | checkstyle { 4 | configFile rootProject.file('checkstyle.xml') 5 | toolVersion '8.18' 6 | ignoreFailures false 7 | showViolations true 8 | } 9 | 10 | task('checkstyle', type: Checkstyle) { 11 | source 'src/main/java' 12 | include '**/*.java' 13 | classpath = files() 14 | } 15 | 16 | check.dependsOn('checkstyle') 17 | -------------------------------------------------------------------------------- /gradle/publish.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven-publish' 2 | apply plugin: 'signing' 3 | 4 | ext["signing.keyId"] = '' 5 | ext["signing.password"] = '' 6 | ext["signing.secretKeyRingFile"] = '' 7 | ext["ossrhUsername"] = '' 8 | ext["ossrhPassword"] = '' 9 | 10 | File secretPropsFile = project.rootProject.file('local.properties') 11 | if (secretPropsFile.exists()) { 12 | Properties p = new Properties() 13 | p.load(new FileInputStream(secretPropsFile)) 14 | p.each { name, value -> 15 | ext[name] = value 16 | } 17 | } else { 18 | ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') 19 | ext["signing.password"] = System.getenv('SIGNING_PASSWORD') 20 | ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE') 21 | ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') 22 | ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') 23 | } 24 | 25 | project.afterEvaluate { 26 | publishing { 27 | publications { 28 | release(MavenPublication) { 29 | from components.release 30 | 31 | groupId POM_GROUP_ID 32 | artifactId POM_ARTIFACT_ID 33 | version POM_VERSION_NAME 34 | 35 | pom { 36 | name = POM_NAME 37 | description = POM_DESCRIPTION 38 | url = POM_URL 39 | inceptionYear = POM_INCEPTION_YEAR 40 | packaging = POM_PACKAGING 41 | scm { 42 | connection = POM_SCM_CONNECTION 43 | url = POM_URL 44 | } 45 | issueManagement { 46 | system = POM_ISSUE_SYSTEM 47 | url = POM_ISSUE_URL 48 | } 49 | licenses { 50 | license { 51 | name = POM_LICENCE_NAME 52 | url = POM_LICENCE_URL 53 | distribution = POM_LICENCE_DIST 54 | } 55 | } 56 | developers { 57 | developer { 58 | id = POM_DEVELOPER_ID 59 | name = POM_DEVELOPER_NAME 60 | } 61 | } 62 | } 63 | } 64 | } 65 | repositories { 66 | maven { 67 | name = "sonatype" 68 | 69 | def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 70 | def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" 71 | url = POM_VERSION_NAME.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl 72 | 73 | credentials { 74 | username ossrhUsername 75 | password ossrhPassword 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | signing { 83 | sign publishing.publications 84 | } 85 | -------------------------------------------------------------------------------- /gradle/sanitizer.gradle: -------------------------------------------------------------------------------- 1 | project.afterEvaluate { 2 | 3 | preBuild.doFirst { 4 | def projectDir = project.projectDir.toString() 5 | 6 | if (rootProject.ext.useASAN) { 7 | def ndkLibDir = android.ndkDirectory.absolutePath + "/toolchains/llvm/prebuilt/" 8 | def ABIs = rootProject.ext.abiFilters.split(",") 9 | 10 | for (String abi : ABIs) { 11 | def arch = abi 12 | if (abi == 'armeabi-v7a') { 13 | arch = "arm" 14 | } else if (abi == "arm64-v8a") { 15 | arch = "aarch64" 16 | } else if (abi == "x86") { 17 | arch = "i686" 18 | } else if (abi == "x86_64") { 19 | arch = "x86_64" 20 | } 21 | 22 | // create ASAN wrap.sh 23 | def resDir = new File(projectDir + "/src/main/resources/lib/" + abi) 24 | resDir.mkdirs() 25 | def wrapFile = new File(resDir, "wrap.sh") 26 | wrapFile.withWriter { writer -> 27 | writer.write('#!/system/bin/sh\n') 28 | writer.write('HERE="$(cd "$(dirname "$0")" && pwd)"\n') 29 | writer.write('export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1,detect_stack_use_after_return=1,check_initialization_order=true,quarantine_size_mb=64,color=never,new_delete_type_mismatch=0,sleep_before_dying=5,use_sigaltstack=0\n') 30 | writer.write("ASAN_LIB=\$(ls \$HERE/libclang_rt.asan-${arch}-android.so)\n") 31 | writer.write('if [ -f "$HERE/libc++_shared.so" ]; then\n') 32 | writer.write(' export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"\n') 33 | writer.write('else\n') 34 | writer.write(' export LD_PRELOAD="$ASAN_LIB"\n') 35 | writer.write('fi\n') 36 | writer.write('"\$@"\n') 37 | } 38 | println "sanitizer: [${abi}] create wrap.sh: " + wrapFile.absolutePath 39 | 40 | // copy ASAN libs 41 | def libDir = new File(projectDir + "/src/main/jniLibs/" + abi) 42 | libDir.mkdirs() 43 | FileTree tree = fileTree(dir: ndkLibDir).include("**/libclang_rt.asan-${arch}-android.so") 44 | tree.each { File file -> 45 | copy { 46 | from file 47 | into libDir.absolutePath 48 | } 49 | println "sanitizer: [${abi}] copy lib: ${file.absolutePath} -> ${libDir.absolutePath}" 50 | } 51 | } 52 | } else { 53 | delete projectDir + '/src/main/resources/' 54 | delete projectDir + '/src/main/jniLibs/' 55 | } 56 | } 57 | } 58 | 59 | clean.doFirst { 60 | def projectDir = project.projectDir.toString() 61 | delete projectDir + '/src/main/resources/' 62 | delete projectDir + '/src/main/jniLibs/' 63 | } 64 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexhacking/xUnwind/f451f8c2948eae4e15b0df926eca2bdc2998b8a9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 29 19:39:58 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/" } 14 | } 15 | } 16 | rootProject.name = "xUnwind" 17 | include ':xunwind' 18 | include ':xunwind_sample' 19 | -------------------------------------------------------------------------------- /xunwind/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /xunwind/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | namespace 'io.github.hexhacking.xunwind' 7 | compileSdkVersion rootProject.ext.compileSdkVersion 8 | buildToolsVersion rootProject.ext.buildToolsVersion 9 | ndkVersion rootProject.ext.ndkVersion 10 | defaultConfig { 11 | minSdkVersion rootProject.ext.minSdkVersion 12 | targetSdkVersion rootProject.ext.targetSdkVersion 13 | consumerProguardFiles 'consumer-rules.pro' 14 | externalNativeBuild { 15 | cmake { 16 | abiFilters rootProject.ext.abiFilters.split(",") 17 | arguments "-DANDROID_STL=none" 18 | if(rootProject.ext.useASAN) { 19 | arguments "-DANDROID_ARM_MODE=arm" 20 | arguments "-DUSEASAN=ON" 21 | } 22 | } 23 | } 24 | } 25 | externalNativeBuild { 26 | cmake { 27 | path "src/main/cpp/CMakeLists.txt" 28 | version rootProject.ext.cmakeVersion 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility rootProject.ext.javaVersion 33 | targetCompatibility rootProject.ext.javaVersion 34 | } 35 | buildTypes { 36 | debug { 37 | minifyEnabled false 38 | } 39 | release { 40 | minifyEnabled false 41 | } 42 | } 43 | buildFeatures { 44 | prefab true 45 | prefabPublishing true 46 | buildConfig false 47 | } 48 | prefab { 49 | xunwind { 50 | headers "src/main/cpp/include" 51 | } 52 | } 53 | packagingOptions { 54 | exclude '**/libxdl.so' 55 | } 56 | publishing { 57 | singleVariant('release') { 58 | withSourcesJar() 59 | withJavadocJar() 60 | } 61 | } 62 | } 63 | 64 | dependencies { 65 | implementation 'io.github.hexhacking:xdl:2.0.0' 66 | } 67 | 68 | apply from: rootProject.file('gradle/check.gradle') 69 | apply from: rootProject.file('gradle/publish.gradle') 70 | -------------------------------------------------------------------------------- /xunwind/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class io.github.hexhacking.xunwind.XUnwind { 2 | native ; 3 | } 4 | -------------------------------------------------------------------------------- /xunwind/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22.1) 2 | project(xunwind) 3 | 4 | find_package(xdl REQUIRED CONFIG) 5 | 6 | file(GLOB XUNWIND_SRC *.c) 7 | add_library(xunwind SHARED ${XUNWIND_SRC}) 8 | target_compile_features(xunwind PUBLIC c_std_17) 9 | target_compile_options(xunwind PUBLIC -std=c17 -Weverything -Werror) 10 | target_include_directories(xunwind PUBLIC include .) 11 | target_link_libraries(xunwind xdl::xdl log) 12 | 13 | if(USEASAN) 14 | target_compile_options(xunwind PUBLIC -fsanitize=address -fno-omit-frame-pointer) 15 | target_link_options(xunwind PUBLIC -fsanitize=address) 16 | else() 17 | target_compile_options(xunwind PUBLIC -Oz -flto -faddrsig -ffunction-sections -fdata-sections) 18 | target_link_options(xunwind PUBLIC -Oz -flto -Wl,--icf=all -Wl,--exclude-libs,ALL -Wl,--gc-sections -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/xunwind.map.txt) 19 | endif() 20 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/include/xunwind.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | // 25 | // xUnwind version: 2.0.0 26 | // 27 | // xUnwind is a collection of Android native stack unwinding solutions. 28 | // For more information, documentation, and the latest version please check: 29 | // https://github.com/hexhacking/xUnwind 30 | // 31 | 32 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND 33 | #define IO_GITHUB_HEXHACKING_XUNWIND 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define XUNWIND_CURRENT_PROCESS (-1) 41 | #define XUNWIND_CURRENT_THREAD (-1) 42 | #define XUNWIND_ALL_THREADS (-2) 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | void xunwind_cfi_log(pid_t pid, pid_t tid, void *context, const char *logtag, android_LogPriority priority, 49 | const char *prefix); 50 | void xunwind_cfi_dump(pid_t pid, pid_t tid, void *context, int fd, const char *prefix); 51 | char *xunwind_cfi_get(pid_t pid, pid_t tid, void *context, const char *prefix); 52 | 53 | size_t xunwind_fp_unwind(uintptr_t *frames, size_t frames_cap, void *context); 54 | size_t xunwind_eh_unwind(uintptr_t *frames, size_t frames_cap, void *context); 55 | 56 | void xunwind_frames_log(uintptr_t *frames, size_t frames_sz, const char *logtag, android_LogPriority priority, 57 | const char *prefix); 58 | void xunwind_frames_dump(uintptr_t *frames, size_t frames_sz, int fd, const char *prefix); 59 | char *xunwind_frames_get(uintptr_t *frames, size_t frames_sz, const char *prefix); 60 | 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_cfi.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #include "xu_cfi.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "xdl.h" 38 | #include "xu_libbacktrace.h" 39 | #include "xu_libcorkscrew.h" 40 | #include "xu_libcxx.h" 41 | #include "xu_log.h" 42 | #include "xu_printer.h" 43 | #include "xu_util.h" 44 | #include "xunwind.h" 45 | 46 | #define XU_CFI_MAX_FRAME 256 47 | #define XU_CFI_FORMAT_PROCESS_HEADER "----- pid %d -----\nCmd line: %s" 48 | #define XU_CFI_FORMAT_THREAD_HEADER "\n\"%s\" sysTid=%d" 49 | #define XU_CFI_FORMAT_PROCESS_FOOTER "\n----- end %d -----" 50 | 51 | int xu_cfi_init(void) { 52 | static int init_status = -1; // -1: not init 0: init OK 1: init failed 53 | if (init_status >= 0) return init_status; 54 | 55 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 56 | pthread_mutex_lock(&lock); 57 | 58 | if (init_status < 0) { 59 | #ifdef XU_CONFIG_CFI_WITH_LIBCORKSCREW 60 | if (xu_util_get_api_level() < __ANDROID_API_L__) 61 | init_status = (0 == xu_libcorkscrew_init() ? 0 : 1); 62 | else 63 | #endif 64 | init_status = ((0 == xu_libcxx_init() && 0 == xu_libbacktrace_init()) ? 0 : 1); 65 | } 66 | 67 | pthread_mutex_unlock(&lock); 68 | 69 | return init_status; 70 | } 71 | 72 | #ifdef XU_CONFIG_CFI_WITH_LIBCORKSCREW 73 | static void xu_cfi_thread_libcorkscrew(pid_t pid, pid_t tid, void *context, size_t ignore_depth, 74 | const char *prefix, xu_printer_t *printer) { 75 | // create corkscrew object 76 | void *cs = xu_libcorkscrew_create(pid, tid); 77 | if (NULL == cs) return; 78 | 79 | // unwind 80 | size_t frames_sz = xu_libcorkscrew_unwind(cs, ignore_depth, context); 81 | if (0 == frames_sz) goto end; 82 | 83 | char frame_line[800]; 84 | for (size_t i = 0; i < frames_sz; i++) { 85 | // format the frame line 86 | frame_line[0] = '\0'; 87 | xu_libcorkscrew_format(cs, i, frame_line, sizeof(frame_line)); 88 | 89 | // check line length 90 | if (strlen(frame_line) < 5) break; 91 | 92 | // dump 93 | xu_printer_append_format(printer, "%s%s", NULL != prefix ? prefix : "", frame_line); 94 | } 95 | 96 | end: 97 | // destroy the corkscrew object 98 | xu_libcorkscrew_destroy(cs); 99 | } 100 | #endif 101 | 102 | static void xu_cfi_thread_libbacktrace(pid_t pid, pid_t tid, void *context, size_t ignore_depth, 103 | bool is_remote, const char *prefix, xu_printer_t *printer) { 104 | // create backtrace object 105 | void *bt = xu_libbacktrace_create(pid, tid); 106 | if (NULL == bt) return; 107 | 108 | if (is_remote) { 109 | // unwind remote process 110 | if (!xu_libbacktrace_remote_unwind(bt, ignore_depth, context)) goto end; 111 | } else { 112 | // unwind current process 113 | if (!xu_libbacktrace_local_unwind(bt, ignore_depth, context)) goto end; 114 | } 115 | 116 | for (size_t i = 0; i < XU_CFI_MAX_FRAME; i++) { 117 | // format the frame line 118 | xu_libcxx_string_t frame_line_str = xu_libbacktrace_format(bt, i); 119 | 120 | // c++ string to c string 121 | const char *frame_line_c_str = xu_libcxx_string_c_str(&frame_line_str); 122 | 123 | // check line length 124 | if (strlen(frame_line_c_str) < 5) { 125 | xu_libcxx_string_dtor_d1(&frame_line_str); 126 | break; 127 | } 128 | 129 | // dump 130 | xu_printer_append_format(printer, "%s%s", NULL != prefix ? prefix : "", frame_line_c_str); 131 | 132 | // free the c++ string object on stack 133 | xu_libcxx_string_dtor_d1(&frame_line_str); 134 | } 135 | 136 | end: 137 | // free the backtrace object on stack 138 | xu_libbacktrace_dtor_d1(bt); 139 | } 140 | 141 | static void xu_cfi_thread(pid_t pid, pid_t tid, void *context, size_t ignore_depth, bool is_remote, 142 | const char *prefix, xu_printer_t *printer) { 143 | #ifdef XU_CONFIG_CFI_WITH_LIBCORKSCREW 144 | if (xu_util_get_api_level() < __ANDROID_API_L__) 145 | xu_cfi_thread_libcorkscrew(pid, tid, context, ignore_depth, prefix, printer); 146 | else 147 | #endif 148 | xu_cfi_thread_libbacktrace(pid, tid, context, ignore_depth, is_remote, prefix, printer); 149 | } 150 | 151 | void xu_cfi_print(pid_t pid, pid_t tid, void *context, const char *prefix, xu_printer_t *printer) { 152 | pid_t cur_pid = getpid(); 153 | if (XUNWIND_CURRENT_PROCESS == pid) pid = cur_pid; 154 | 155 | bool is_remote = (pid != cur_pid); 156 | 157 | if (tid == XUNWIND_ALL_THREADS) { 158 | // get all thread IDs 159 | size_t tids_cnt = 0; 160 | pid_t *tids = xu_util_get_thread_ids(pid, &tids_cnt); 161 | if (NULL == tids) return; 162 | 163 | // get & print PID + process-name 164 | char pname[128]; 165 | xu_util_get_process_name(pid, pname, sizeof(pname)); 166 | xu_printer_append_format(printer, XU_CFI_FORMAT_PROCESS_HEADER, pid, pname); 167 | 168 | // ptrace attach all threads in remote process 169 | if (is_remote) { 170 | for (size_t i = 0; i < tids_cnt; i++) { 171 | if (0 != xu_util_ptrace_attach(tids[i])) tids[i] = -1; 172 | } 173 | } 174 | 175 | // for each threads 176 | for (size_t i = 0; i < tids_cnt; i++) { 177 | if (tids[i] < 0) continue; 178 | 179 | // get & print TID + thread-name 180 | char thread_name[128]; 181 | xu_util_get_thread_name(pid, tids[i], thread_name, sizeof(thread_name)); 182 | xu_printer_append_format(printer, XU_CFI_FORMAT_THREAD_HEADER, thread_name, tids[i]); 183 | 184 | // print thread's backtrace 185 | xu_cfi_thread(pid, tids[i], NULL, 0, is_remote, prefix, printer); 186 | } 187 | 188 | // ptrace detach all threads in remote process 189 | if (is_remote) { 190 | for (size_t i = 0; i < tids_cnt; i++) { 191 | if (tids[i] < 0) continue; 192 | xu_util_ptrace_detach(tids[i]); 193 | } 194 | } 195 | 196 | // print footer 197 | xu_printer_append_format(printer, XU_CFI_FORMAT_PROCESS_FOOTER, pid); 198 | } else { 199 | // ptrace attach target thread in remote process 200 | if (is_remote) { 201 | if (0 != xu_util_ptrace_attach(tid)) return; 202 | } 203 | 204 | // print thread's backtrace 205 | if (XUNWIND_CURRENT_THREAD == tid) tid = gettid(); 206 | xu_cfi_thread(pid, tid, context, 0, is_remote, prefix, printer); 207 | 208 | // ptrace detach target thread in remote process 209 | if (is_remote) { 210 | xu_util_ptrace_detach(tid); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_cfi.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_CFI 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_CFI 26 | 27 | #include 28 | #include 29 | 30 | #include "xu_printer.h" 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | int xu_cfi_init(void); 37 | void xu_cfi_print(pid_t pid, pid_t tid, void *context, const char *prefix, xu_printer_t *printer); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_config.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_CONFIG 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_CONFIG 26 | 27 | // Do not open the log when running online. 28 | // 29 | // (Default: close) 30 | //#define XU_CONFIG_WITH_LOG 31 | 32 | // We only use libcorkscrew.so for CFI unwinding on Android 4.x with arm32/x86. 33 | // 34 | // (Default: dynamically specified, generally do not need to modify it) 35 | #if !defined __LP64__ && __ANDROID_API__ < 21 36 | #define XU_CONFIG_CFI_WITH_LIBCORKSCREW 37 | #endif 38 | 39 | // syscall-checking is a dangerous thing. 40 | // It needs to read data from unknown memory addresses, which may cause a crash. 41 | // Unless your usage scenario really needs it, don't use it. 42 | // 43 | // (Default: close) 44 | //#define XU_CONFIG_FP_CHECK_SYSCALL 45 | 46 | // EH unwinding may not always be necessary, 47 | // and removing it can reduce the file size by nearly half. 48 | // 49 | // (Default: open) 50 | #define XU_CONFIG_EH_ENABLE 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_eh.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #include "xu_eh.h" 25 | 26 | #include 27 | 28 | #include "xu_config.h" 29 | 30 | #ifdef XU_CONFIG_EH_ENABLE 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | typedef struct { 38 | uintptr_t *frames; 39 | size_t frames_cap; 40 | size_t frames_sz; 41 | uintptr_t prev_sp; 42 | ucontext_t *uc; 43 | } xu_eh_info_t; 44 | 45 | static _Unwind_Reason_Code xu_eh_unwind_cb(struct _Unwind_Context *unw_ctx, void *arg) { 46 | uintptr_t pc = _Unwind_GetIP(unw_ctx); 47 | uintptr_t sp = _Unwind_GetCFA(unw_ctx); 48 | 49 | xu_eh_info_t *info = (xu_eh_info_t *)arg; 50 | if (NULL != info->uc) { 51 | #if defined(__arm__) 52 | uintptr_t ctx_pc = (uintptr_t)info->uc->uc_mcontext.arm_pc; 53 | uintptr_t ctx_lr = (uintptr_t)info->uc->uc_mcontext.arm_lr; 54 | #elif defined(__aarch64__) 55 | uintptr_t ctx_pc = (uintptr_t)info->uc->uc_mcontext.pc; 56 | uintptr_t ctx_lr = (uintptr_t)info->uc->uc_mcontext.regs[30]; 57 | #elif defined(__i386__) 58 | uintptr_t ctx_pc = (uintptr_t)info->uc->uc_mcontext.gregs[REG_EIP]; 59 | uintptr_t ctx_lr = 0; 60 | #elif defined(__x86_64__) 61 | uintptr_t ctx_pc = (uintptr_t)info->uc->uc_mcontext.gregs[REG_RIP]; 62 | uintptr_t ctx_lr = 0; 63 | #endif 64 | 65 | if ((ctx_pc >= sizeof(uintptr_t) && pc <= ctx_pc + sizeof(uintptr_t) && 66 | pc >= ctx_pc - sizeof(uintptr_t)) || 67 | (ctx_lr >= sizeof(uintptr_t) && pc <= ctx_lr + sizeof(uintptr_t) && pc >= ctx_lr - sizeof(uintptr_t))) 68 | info->uc = NULL; // we found the context PC 69 | else 70 | return _URC_NO_REASON; // skip & continue 71 | } 72 | 73 | if (info->frames_sz > 0 && pc == info->frames[info->frames_sz - 1] && sp == info->prev_sp) 74 | return _URC_END_OF_STACK; // stop 75 | 76 | info->frames[info->frames_sz++] = pc; 77 | 78 | if (info->frames_sz >= info->frames_cap) return _URC_END_OF_STACK; // stop 79 | 80 | info->prev_sp = sp; 81 | return _URC_NO_REASON; // continue 82 | } 83 | 84 | size_t xu_eh_unwind(uintptr_t *frames, size_t frames_cap, void *context) { 85 | if (NULL == frames || 0 == frames_cap) return 0; 86 | 87 | xu_eh_info_t info; 88 | info.frames = frames; 89 | info.frames_cap = frames_cap; 90 | info.frames_sz = 0; 91 | info.prev_sp = 0; 92 | info.uc = (ucontext_t *)context; 93 | 94 | _Unwind_Backtrace(xu_eh_unwind_cb, &info); 95 | 96 | return info.frames_sz; 97 | } 98 | 99 | #else 100 | 101 | size_t xu_eh_unwind(uintptr_t* frames, size_t frames_cap, void* context) { 102 | (void)frames, (void)frames_cap, (void)context; 103 | return 0; 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_eh.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_EH 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_EH 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | size_t xu_eh_unwind(uintptr_t* frames, size_t frames_cap, void* context); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_formatter.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #include "xu_formatter.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "xdl.h" 36 | #include "xu_log.h" 37 | 38 | #if defined(__LP64__) 39 | #define XU_FORMATTER_ADDR "%016" PRIxPTR 40 | #else 41 | #define XU_FORMATTER_ADDR "%08" PRIxPTR 42 | #endif 43 | 44 | #define XU_FORMATTER_PREFIX "%s#%02zu pc " XU_FORMATTER_ADDR " " 45 | 46 | static int xu_formatter_maps_addr(uintptr_t pc, xdl_info_t *info, char *buf, size_t buf_len) { 47 | memset(info, 0, sizeof(xdl_info_t)); 48 | 49 | FILE *fp = fopen("/proc/self/maps", "r"); 50 | if (NULL == fp) return 0; 51 | 52 | int r = 0; 53 | while (fgets(buf, (int)buf_len, fp)) { 54 | uintptr_t lo, hi; 55 | int pos; 56 | if (2 == sscanf(buf, "%" SCNxPTR "-%" SCNxPTR " %*4s %*lx %*x:%*x %*d%n", &lo, &hi, &pos) && lo <= pc && 57 | pc < hi) { 58 | while (isspace(buf[pos]) && pos < (int)(buf_len - 1)) pos++; 59 | if (pos >= (int)(buf_len - 1)) break; 60 | if (0 == strlen(buf + pos)) break; 61 | 62 | info->dli_fbase = (void *)lo; 63 | info->dli_fname = buf + pos; 64 | r = 1; 65 | break; 66 | } 67 | } 68 | 69 | fclose(fp); 70 | return r; 71 | } 72 | 73 | void xu_formatter_print(uintptr_t *frames, size_t frames_sz, const char *prefix, xu_printer_t *printer) { 74 | if (NULL == frames || 0 == frames_sz) return; 75 | 76 | if (NULL == prefix) prefix = ""; 77 | 78 | void *cache = NULL; 79 | xdl_info_t info; 80 | for (size_t i = 0; i < frames_sz; i++) { 81 | memset(&info, 0, sizeof(xdl_info_t)); 82 | int r = 0; 83 | 84 | if (0 != frames[i]) { 85 | // find info from linker 86 | r = xdl_addr((void *)(frames[i]), &info, &cache); 87 | 88 | // find info from maps 89 | char buf[512]; 90 | if (0 == r || (uintptr_t)info.dli_fbase > frames[i]) 91 | r = xu_formatter_maps_addr(frames[i], &info, buf, sizeof(buf)); 92 | } 93 | 94 | // do print 95 | if (0 == r || (uintptr_t)info.dli_fbase > frames[i]) 96 | xu_printer_append_format(printer, XU_FORMATTER_PREFIX "\n", prefix, i, frames[i]); 97 | else if (NULL == info.dli_fname || '\0' == info.dli_fname[0]) 98 | xu_printer_append_format(printer, XU_FORMATTER_PREFIX "\n", prefix, i, 99 | frames[i] - (uintptr_t)info.dli_fbase, (uintptr_t)info.dli_fbase); 100 | else if (NULL == info.dli_sname || '\0' == info.dli_sname[0]) 101 | xu_printer_append_format(printer, XU_FORMATTER_PREFIX "%s\n", prefix, i, 102 | frames[i] - (uintptr_t)info.dli_fbase, info.dli_fname); 103 | else if (0 == (uintptr_t)info.dli_saddr || (uintptr_t)info.dli_saddr > frames[i]) 104 | xu_printer_append_format(printer, XU_FORMATTER_PREFIX "%s (%s)\n", prefix, i, 105 | frames[i] - (uintptr_t)info.dli_fbase, info.dli_fname, info.dli_sname); 106 | else 107 | xu_printer_append_format(printer, XU_FORMATTER_PREFIX "%s (%s+%" PRIuPTR ")\n", prefix, i, 108 | frames[i] - (uintptr_t)info.dli_fbase, info.dli_fname, info.dli_sname, 109 | frames[i] - (uintptr_t)info.dli_saddr); 110 | } 111 | xdl_addr_clean(&cache); 112 | } 113 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_formatter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_FORMATTER 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_FORMATTER 26 | 27 | #include 28 | #include 29 | 30 | #include "xu_printer.h" 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | void xu_formatter_print(uintptr_t *frames, size_t frames_sz, const char *prefix, xu_printer_t *printer); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_fp.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_FP 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_FP 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | int xu_fp_init(void); 35 | size_t xu_fp_unwind(uintptr_t* frames, size_t frames_cap, void* context); 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_fp_arm64.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifdef __aarch64__ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "xdl.h" 39 | #include "xu_config.h" 40 | #include "xu_fp.h" 41 | #include "xu_log.h" 42 | 43 | #define XU_FP_VDSO_PATHNAME "[vdso]" 44 | #define XU_FP_VDSO_SIGRETURN "__kernel_rt_sigreturn" 45 | 46 | static uintptr_t xu_fp_sigreturn_addr = 0; 47 | static uintptr_t xu_fp_main_thread_stack_low = 0; 48 | static uintptr_t xu_fp_main_thread_stack_high = 0; 49 | 50 | typedef struct { 51 | uintptr_t fp; 52 | uintptr_t lr; 53 | } xu_fp_frame_record_t; 54 | 55 | static int xu_fp_set_sigreturn_addr(void) { 56 | void *handle = xdl_open(XU_FP_VDSO_PATHNAME, XDL_DEFAULT); 57 | if (NULL == handle) return -1; 58 | 59 | void *addr = xdl_sym(handle, XU_FP_VDSO_SIGRETURN, NULL); 60 | xdl_close(handle); 61 | if (NULL == addr) return -1; 62 | 63 | xu_fp_sigreturn_addr = (uintptr_t)addr; 64 | return 0; 65 | } 66 | 67 | static int xu_fp_set_main_thread_stack_range(void) { 68 | char line[512]; 69 | FILE *fp; 70 | 71 | // get stack size limit 72 | struct rlimit stack_limit; 73 | if (0 != getrlimit(RLIMIT_STACK, &stack_limit)) return -1; 74 | if (RLIM_INFINITY == stack_limit.rlim_cur) stack_limit.rlim_cur = 8 * 1024 * 1024; 75 | 76 | // get startstack 77 | uintptr_t start_stack = 0; 78 | if (NULL != (fp = fopen("/proc/self/stat", "re"))) { 79 | if (fgets(line, sizeof(line), fp)) { 80 | const char *end_of_comm = strrchr((const char *)line, (int)')'); 81 | if (1 != sscanf(end_of_comm + 1, 82 | " %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u %*u " 83 | "%*d %*u %*u %*u %" SCNuPTR, 84 | &start_stack)) 85 | start_stack = 0; 86 | } 87 | fclose(fp); 88 | } 89 | if (0 == start_stack) return -1; 90 | 91 | // get main thread stack high 92 | uintptr_t stack_high = 0; 93 | if (NULL != (fp = fopen("/proc/self/maps", "r"))) { 94 | while (fgets(line, sizeof(line), fp)) { 95 | uintptr_t lo, hi; 96 | if (2 == sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &lo, &hi) && lo <= start_stack && 97 | start_stack <= hi) { 98 | stack_high = hi; 99 | break; 100 | } 101 | } 102 | fclose(fp); 103 | } 104 | if (0 == stack_high) return -1; 105 | if (stack_high < stack_limit.rlim_cur) return -1; 106 | 107 | xu_fp_main_thread_stack_low = stack_high - stack_limit.rlim_cur; 108 | xu_fp_main_thread_stack_high = stack_high; 109 | return 0; 110 | } 111 | 112 | int xu_fp_init(void) { 113 | static int init_status = -1; // -1: not init 0: init OK 1: init failed 114 | if (init_status >= 0) return init_status; 115 | 116 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 117 | pthread_mutex_lock(&lock); 118 | 119 | if (init_status < 0) { 120 | init_status = 1; 121 | 122 | if (0 != xu_fp_set_sigreturn_addr()) goto end; 123 | XU_LOG("xu_fp_init: sigreturn %p", (void *)xu_fp_sigreturn_addr); 124 | 125 | if (0 != xu_fp_set_main_thread_stack_range()) goto end; 126 | XU_LOG("xu_fp_init: main thread stack range %p - %p, sz: %zx", (void *)xu_fp_main_thread_stack_low, 127 | (void *)xu_fp_main_thread_stack_high, 128 | (size_t)(xu_fp_main_thread_stack_high - xu_fp_main_thread_stack_low)); 129 | 130 | init_status = 0; // OK 131 | } 132 | 133 | end: 134 | pthread_mutex_unlock(&lock); 135 | return init_status; 136 | } 137 | 138 | static void xu_fp_get_thread_stack(uintptr_t *low, uintptr_t *high) { 139 | if (getpid() == gettid()) { 140 | *low = xu_fp_main_thread_stack_low; 141 | *high = xu_fp_main_thread_stack_high; 142 | } else { 143 | pthread_t thrd = pthread_self(); 144 | 145 | // thread attributes 146 | pthread_attr_t attr; 147 | if (0 != pthread_getattr_np(thrd, &attr)) return; 148 | 149 | // thread stack 150 | uintptr_t stack_low, stack_high; 151 | size_t stack_sz; 152 | if (0 != pthread_attr_getstack(&attr, (void **)(&stack_low), &stack_sz)) return; 153 | stack_high = (uintptr_t)stack_low + (uintptr_t)stack_sz; 154 | 155 | // thread stack guard 156 | uintptr_t guard_sz = 0; 157 | if (0 != pthread_attr_getguardsize(&attr, &guard_sz)) guard_sz = 0x1000; 158 | 159 | pthread_attr_destroy(&attr); 160 | 161 | *low = (stack_low + guard_sz); 162 | *high = stack_high; 163 | } 164 | } 165 | 166 | static inline bool xu_fp_is_sigreturn(uintptr_t addr) { 167 | return xu_fp_sigreturn_addr == addr; 168 | // if(0 != xu_fp_sigreturn_addr) 169 | // { 170 | // return xu_fp_sigreturn_addr == addr; 171 | // } 172 | // else 173 | // { 174 | // // __kernel_rt_sigreturn in vDSO: 175 | // // 0xd2801168 mov x8, #0x8b 176 | // // 0xd4000001 svc #0x0 177 | // return (0xd4000001d2801168ULL == *((uint64_t *)addr)) ? true : false; 178 | // } 179 | } 180 | 181 | #ifdef XU_CONFIG_FP_CHECK_SYSCALL 182 | 183 | typedef struct { 184 | siginfo_t info; 185 | ucontext_t uc; 186 | xu_fp_frame_record_t record; 187 | } xu_fp_sigframe_t; 188 | 189 | static inline bool xu_fp_is_syscall(uintptr_t addr) { 190 | // syscall in libc.so: 191 | // 0xd280???8 mov x8, #0x??? 192 | // 0xd4000001 svc #0x0 193 | return (0xd4000001d2800008ULL == (*((uint64_t *)addr) & 0xffffffffffff000fULL)) ? true : false; 194 | } 195 | 196 | static xu_fp_sigframe_t *xu_fp_find_sigframe_addr(uintptr_t stack_low, uintptr_t stack_high, size_t stack_sz, 197 | uintptr_t begin) { 198 | if (begin >= stack_high) return 0; 199 | for (; stack_high - begin >= sizeof(xu_fp_sigframe_t); begin += sizeof(uintptr_t)) { 200 | xu_fp_sigframe_t *sigframe = (xu_fp_sigframe_t *)begin; 201 | if (0 == sigframe->info.si_signo || sigframe->info.si_signo > 63) continue; 202 | if ((void *)stack_low != sigframe->uc.uc_stack.ss_sp) continue; 203 | if (stack_sz != sigframe->uc.uc_stack.ss_size) continue; 204 | return sigframe; 205 | } 206 | return NULL; 207 | } 208 | 209 | #endif 210 | 211 | static inline uintptr_t xu_fp_clear_pac_bits(uintptr_t addr) { 212 | register uintptr_t x30 __asm__("x30") = addr; 213 | __asm__("xpaclri" : "+r"(x30)); 214 | return x30; 215 | } 216 | 217 | static bool xu_fp_append_frame(uintptr_t *frames, size_t frames_cap, size_t *frames_sz, uintptr_t addr) { 218 | if (*frames_sz >= frames_cap) return false; 219 | 220 | uintptr_t addr_clear = xu_fp_clear_pac_bits(addr); 221 | 222 | if (addr_clear == addr) 223 | XU_LOG("xu_fp_unwind: append frame #%02zu %p", *frames_sz, (void *)addr_clear); 224 | else 225 | XU_LOG("xu_fp_unwind: append frame #%02zu %p (with PAC: %p)", *frames_sz, (void *)addr_clear, 226 | (void *)addr); 227 | 228 | frames[*frames_sz] = addr_clear; 229 | *frames_sz += 1; 230 | 231 | return *frames_sz >= frames_cap ? false : true; 232 | } 233 | 234 | size_t xu_fp_unwind(uintptr_t *frames, size_t frames_cap, void *context) { 235 | if (NULL == frames || 0 == frames_cap) return 0; 236 | 237 | size_t frames_sz = 0; 238 | 239 | // get current FP 240 | uintptr_t cur_fp; 241 | if (NULL == context) { 242 | // from current FP 243 | cur_fp = (uintptr_t)(__builtin_frame_address(0)); 244 | } else { 245 | // from FP in signal context 246 | mcontext_t *mc = &(((ucontext_t *)context)->uc_mcontext); 247 | cur_fp = mc->regs[29]; 248 | 249 | // append frame for "PC in signal context" 250 | if (!xu_fp_append_frame(frames, frames_cap, &frames_sz, mc->pc)) return frames_sz; 251 | 252 | #ifdef XU_CONFIG_FP_CHECK_SYSCALL 253 | // append frame for previous-PC if "PC in signal context" is a syscall() 254 | if (xu_fp_is_syscall(mc->pc - 8)) 255 | if (!xu_fp_append_frame(frames, frames_cap, &frames_sz, mc->regs[30] < 4 ? 0 : mc->regs[30] - 4)) 256 | return frames_sz; 257 | #endif 258 | } 259 | 260 | // current signal-stack info 261 | bool unwind_on_signal_stack = false; 262 | stack_t ss; 263 | uintptr_t signal_stack_low = 0, signal_stack_high = 0; 264 | size_t signal_stack_sz = 0; 265 | if (0 == sigaltstack(NULL, &ss) && ss.ss_flags & SS_ONSTACK) { 266 | unwind_on_signal_stack = true; 267 | signal_stack_low = (uintptr_t)ss.ss_sp; 268 | signal_stack_high = (uintptr_t)ss.ss_sp + (uintptr_t)ss.ss_size; 269 | signal_stack_sz = (size_t)ss.ss_size; 270 | XU_LOG("xu_fp_unwind: signal stack %p - %p, sz: %zx", (void *)signal_stack_low, (void *)signal_stack_high, 271 | signal_stack_sz); 272 | } 273 | 274 | // current thread-stack info 275 | uintptr_t thread_stack_low = 0, thread_stack_high = 0; 276 | xu_fp_get_thread_stack(&thread_stack_low, &thread_stack_high); 277 | XU_LOG("xu_fp_unwind: thread stack %p - %p, sz: %zx", (void *)thread_stack_low, (void *)thread_stack_high, 278 | thread_stack_high - thread_stack_low); 279 | 280 | // current stack 281 | uintptr_t cur_stack_low; 282 | uintptr_t cur_stack_high; 283 | if (unwind_on_signal_stack) { 284 | cur_stack_low = signal_stack_low; 285 | cur_stack_high = signal_stack_high; 286 | } else { 287 | cur_stack_low = thread_stack_low; 288 | cur_stack_high = thread_stack_high; 289 | } 290 | 291 | // walk the stack 292 | while (1) { 293 | uintptr_t prev_fp; 294 | xu_fp_frame_record_t *record = (xu_fp_frame_record_t *)cur_fp; 295 | 296 | if (unwind_on_signal_stack && xu_fp_is_sigreturn(record->lr)) { 297 | // handle sigreturn() 298 | XU_LOG("xu_fp_unwind: handle sigreturn at %p", (void *)(record->lr)); 299 | 300 | if (!xu_fp_append_frame(frames, frames_cap, &frames_sz, record->lr)) return frames_sz; 301 | prev_fp = record->fp; 302 | if (prev_fp & 0xfu) break; 303 | 304 | #ifdef XU_CONFIG_FP_CHECK_SYSCALL 305 | // append frame by syscall() 306 | xu_fp_sigframe_t *sigframe = 307 | xu_fp_find_sigframe_addr(signal_stack_low, signal_stack_high, signal_stack_sz, cur_fp); 308 | if (NULL != sigframe) { 309 | uintptr_t sigframe_pc = sigframe->uc.uc_mcontext.pc; 310 | if (xu_fp_is_syscall(sigframe_pc - 8)) { 311 | if (!xu_fp_append_frame(frames, frames_cap, &frames_sz, sigframe_pc < 4 ? 0 : sigframe_pc - 4)) 312 | return frames_sz; 313 | prev_fp = record->fp; 314 | if (prev_fp & 0xfu) break; 315 | } 316 | } 317 | #endif 318 | } else { 319 | // handle situations other than sigreturn() 320 | if (!xu_fp_append_frame(frames, frames_cap, &frames_sz, record->lr < 4 ? 0 : record->lr - 4)) 321 | return frames_sz; 322 | prev_fp = record->fp; 323 | if (prev_fp & 0xfu) break; 324 | } 325 | 326 | if (unwind_on_signal_stack && (prev_fp < signal_stack_low || signal_stack_high <= prev_fp)) { 327 | // change current stack from signal-stack to thread-stack, do this at most once 328 | unwind_on_signal_stack = false; 329 | cur_stack_low = thread_stack_low; 330 | cur_stack_high = thread_stack_high; 331 | XU_LOG("xu_fp_unwind: signal-stack -> thread-stack %p - %p, sz: %zx", (void *)thread_stack_low, 332 | (void *)thread_stack_high, thread_stack_high - thread_stack_low); 333 | } else { 334 | // ensure to walk FP from low address to high address 335 | if (prev_fp < cur_fp + sizeof(xu_fp_frame_record_t)) break; 336 | } 337 | 338 | // ensure FP is within the stack range 339 | if (prev_fp < cur_stack_low || cur_stack_high <= prev_fp) break; 340 | 341 | // walk to the previous FP 342 | cur_fp = prev_fp; 343 | } 344 | 345 | return frames_sz; 346 | } 347 | 348 | #else 349 | typedef int make_iso_happy; 350 | #endif 351 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_fp_unimplemented.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifndef __aarch64__ 25 | 26 | #include 27 | #include 28 | 29 | #include "xu_fp.h" 30 | 31 | int xu_fp_init(void) { 32 | return 0; 33 | } 34 | 35 | size_t xu_fp_unwind(uintptr_t* frames, size_t frames_cap, void* context) { 36 | (void)frames, (void)frames_cap, (void)context; 37 | return 0; 38 | } 39 | 40 | #else 41 | typedef int make_iso_happy; 42 | #endif 43 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_jni.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "xunwind.h" 35 | 36 | #define XU_JNI_VERSION JNI_VERSION_1_6 37 | #define XU_JNI_CLASS_NAME "io/github/hexhacking/xunwind/XUnwind" 38 | 39 | static void xu_jni_log(JNIEnv *env, jobject thiz, jint pid, jint tid, jstring logtag, jint priority, 40 | jstring prefix) { 41 | (void)env, (void)thiz; 42 | 43 | const char *c_logtag = NULL; 44 | const char *c_prefix = NULL; 45 | if (logtag && NULL == (c_logtag = (*env)->GetStringUTFChars(env, logtag, 0))) goto end; 46 | if (prefix && NULL == (c_prefix = (*env)->GetStringUTFChars(env, prefix, 0))) goto end; 47 | 48 | xunwind_cfi_log((pid_t)pid, (pid_t)tid, NULL, c_logtag, (android_LogPriority)priority, c_prefix); 49 | 50 | end: 51 | if (logtag && c_logtag) (*env)->ReleaseStringUTFChars(env, logtag, c_logtag); 52 | if (prefix && c_prefix) (*env)->ReleaseStringUTFChars(env, prefix, c_prefix); 53 | } 54 | 55 | static void xu_jni_dump(JNIEnv *env, jobject thiz, jint pid, jint tid, jint fd, jstring prefix) { 56 | (void)env, (void)thiz; 57 | 58 | const char *c_prefix = NULL; 59 | if (prefix && NULL == (c_prefix = (*env)->GetStringUTFChars(env, prefix, 0))) goto end; 60 | 61 | xunwind_cfi_dump((pid_t)pid, (pid_t)tid, NULL, (int)fd, c_prefix); 62 | 63 | end: 64 | if (prefix && c_prefix) (*env)->ReleaseStringUTFChars(env, prefix, c_prefix); 65 | } 66 | 67 | static jstring xu_jni_get(JNIEnv *env, jobject thiz, jint pid, jint tid, jstring prefix) { 68 | (void)env, (void)thiz; 69 | 70 | jstring str = NULL; 71 | 72 | const char *c_prefix = NULL; 73 | if (prefix && NULL == (c_prefix = (*env)->GetStringUTFChars(env, prefix, 0))) goto end; 74 | 75 | char *c_str = xunwind_cfi_get((pid_t)pid, (pid_t)tid, NULL, c_prefix); 76 | if (NULL != c_str) { 77 | str = (*env)->NewStringUTF(env, c_str); 78 | free(c_str); 79 | } 80 | 81 | end: 82 | if (prefix && c_prefix) (*env)->ReleaseStringUTFChars(env, prefix, c_prefix); 83 | return str; 84 | } 85 | 86 | static JNINativeMethod sample_jni_methods[] = { 87 | {"nativeLog", "(IILjava/lang/String;ILjava/lang/String;)V", (void *)xu_jni_log}, 88 | {"nativeDump", "(IIILjava/lang/String;)V", (void *)xu_jni_dump}, 89 | {"nativeGet", "(IILjava/lang/String;)Ljava/lang/String;", (void *)xu_jni_get}}; 90 | 91 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 92 | JNIEnv *env; 93 | jclass cls; 94 | 95 | (void)reserved; 96 | 97 | if (NULL == vm) return JNI_ERR; 98 | if (JNI_OK != (*vm)->GetEnv(vm, (void **)&env, XU_JNI_VERSION)) return JNI_ERR; 99 | if (NULL == env || NULL == *env) return JNI_ERR; 100 | if (NULL == (cls = (*env)->FindClass(env, XU_JNI_CLASS_NAME))) return JNI_ERR; 101 | if (0 != (*env)->RegisterNatives(env, cls, sample_jni_methods, 102 | sizeof(sample_jni_methods) / sizeof(sample_jni_methods[0]))) 103 | return JNI_ERR; 104 | 105 | return XU_JNI_VERSION; 106 | } 107 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_libbacktrace.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | // Android L 25 | // https://android.googlesource.com/platform/system/core/+/android-5.0.0_r1/include/backtrace/ 26 | // https://android.googlesource.com/platform/system/core/+/android-5.0.0_r1/libbacktrace/ 27 | 28 | // Android M 29 | // https://android.googlesource.com/platform/system/core/+/android-6.0.0_r1/include/backtrace/ 30 | // https://android.googlesource.com/platform/system/core/+/android-6.0.0_r1/libbacktrace/ 31 | 32 | // Android P 33 | // https://android.googlesource.com/platform/system/core/+/android-9.0.0_r1/libbacktrace/include/backtrace 34 | // https://android.googlesource.com/platform/system/core/+/android-9.0.0_r1/libbacktrace/ 35 | 36 | #include "xu_libbacktrace.h" 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "xdl.h" 44 | #include "xu_libcxx.h" 45 | #include "xu_util.h" 46 | 47 | #ifndef __LP64__ 48 | #define XU_LIBBACKTRACE_PATHNAME_L "/system/lib/libbacktrace_libc++.so" 49 | #define XU_LIBBACKTRACE_PATHNAME_M "/system/lib/libbacktrace.so" 50 | #define XU_LIBBACKTRACE_FORMAT "_ZN9Backtrace15FormatFrameDataEj" 51 | #define XU_LIBBACKTRACE_LOCAL_UNWIND_L "_ZN9Backtrace6UnwindEjP8ucontext" 52 | #define XU_LIBBACKTRACE_LOCAL_UNWIND_M "_ZN16BacktraceCurrent6UnwindEjP8ucontext" 53 | #define XU_LIBBACKTRACE_LOCAL_UNWIND_P "_ZN16BacktraceCurrent6UnwindEjPv" 54 | #define XU_LIBBACKTRACE_REMOTE_UNWIND_L "_ZN12UnwindPtrace6UnwindEjP8ucontext" 55 | #define XU_LIBBACKTRACE_REMOTE_UNWIND_P "_ZN17UnwindStackPtrace6UnwindEjPv" 56 | #else 57 | #define XU_LIBBACKTRACE_PATHNAME_L "/system/lib64/libbacktrace_libc++.so" 58 | #define XU_LIBBACKTRACE_PATHNAME_M "/system/lib64/libbacktrace.so" 59 | #define XU_LIBBACKTRACE_FORMAT "_ZN9Backtrace15FormatFrameDataEm" 60 | #define XU_LIBBACKTRACE_LOCAL_UNWIND_L "_ZN9Backtrace6UnwindEmP8ucontext" 61 | #define XU_LIBBACKTRACE_LOCAL_UNWIND_M "_ZN16BacktraceCurrent6UnwindEmP8ucontext" 62 | #define XU_LIBBACKTRACE_LOCAL_UNWIND_P "_ZN16BacktraceCurrent6UnwindEmPv" 63 | #define XU_LIBBACKTRACE_REMOTE_UNWIND_L "_ZN12UnwindPtrace6UnwindEmP8ucontext" 64 | #define XU_LIBBACKTRACE_REMOTE_UNWIND_P "_ZN17UnwindStackPtrace6UnwindEmPv" 65 | #endif 66 | #define XU_LIBBACKTRACE_CREATE "_ZN9Backtrace6CreateEiiP12BacktraceMap" 67 | #define XU_LIBBACKTRACE_DTOR_D1 "_ZN9BacktraceD1Ev" 68 | 69 | typedef void *(*xu_libbacktrace_create_t)(pid_t pid, pid_t tid, void *map); 70 | typedef void (*xu_libbacktrace_dtor_d1_t)(void *self); 71 | typedef xu_libcxx_string_t (*xu_libbacktrace_format_t)(void *self, size_t frame_num); 72 | typedef bool (*xu_libbacktrace_unwind_t)(void *self, size_t num_ignore_frames, void *context); 73 | 74 | static xu_libbacktrace_create_t xu_libbacktrace_create_ptr = NULL; 75 | static xu_libbacktrace_dtor_d1_t xu_libbacktrace_dtor_d1_ptr = NULL; 76 | static xu_libbacktrace_format_t xu_libbacktrace_format_ptr = NULL; 77 | static xu_libbacktrace_unwind_t xu_libbacktrace_local_unwind_ptr = NULL; 78 | static xu_libbacktrace_unwind_t xu_libbacktrace_remote_unwind_ptr = NULL; 79 | 80 | int xu_libbacktrace_init(void) { 81 | int api_level = xu_util_get_api_level(); 82 | const char *sym = NULL; 83 | int ret = -1; 84 | 85 | void *handle = 86 | xdl_open(api_level <= __ANDROID_API_L_MR1__ ? XU_LIBBACKTRACE_PATHNAME_L : XU_LIBBACKTRACE_PATHNAME_M, 87 | XDL_DEFAULT); 88 | if (NULL == handle) return -1; 89 | 90 | if (NULL == 91 | (xu_libbacktrace_create_ptr = (xu_libbacktrace_create_t)xdl_sym(handle, XU_LIBBACKTRACE_CREATE, NULL))) 92 | goto end; 93 | if (NULL == (xu_libbacktrace_dtor_d1_ptr = 94 | (xu_libbacktrace_dtor_d1_t)xdl_sym(handle, XU_LIBBACKTRACE_DTOR_D1, NULL))) 95 | goto end; 96 | if (NULL == 97 | (xu_libbacktrace_format_ptr = (xu_libbacktrace_format_t)xdl_sym(handle, XU_LIBBACKTRACE_FORMAT, NULL))) 98 | goto end; 99 | 100 | if (__ANDROID_API_L__ <= api_level && api_level <= __ANDROID_API_L_MR1__) 101 | sym = XU_LIBBACKTRACE_LOCAL_UNWIND_L; 102 | else if (__ANDROID_API_M__ <= api_level && api_level <= __ANDROID_API_O_MR1__) 103 | sym = XU_LIBBACKTRACE_LOCAL_UNWIND_M; 104 | else if (__ANDROID_API_P__ <= api_level) 105 | sym = XU_LIBBACKTRACE_LOCAL_UNWIND_P; 106 | if (NULL == (xu_libbacktrace_local_unwind_ptr = (xu_libbacktrace_unwind_t)xdl_sym(handle, sym, NULL))) 107 | goto end; 108 | 109 | if (__ANDROID_API_L__ <= api_level && api_level <= __ANDROID_API_O_MR1__) 110 | sym = XU_LIBBACKTRACE_REMOTE_UNWIND_L; 111 | else if (__ANDROID_API_P__ <= api_level) 112 | sym = XU_LIBBACKTRACE_REMOTE_UNWIND_P; 113 | if (NULL == (xu_libbacktrace_remote_unwind_ptr = (xu_libbacktrace_unwind_t)xdl_sym(handle, sym, NULL))) 114 | goto end; 115 | 116 | ret = 0; 117 | 118 | end: 119 | xdl_close(handle); 120 | return ret; 121 | } 122 | 123 | void *xu_libbacktrace_create(pid_t pid, pid_t tid) { 124 | return xu_libbacktrace_create_ptr(pid, tid, NULL); 125 | } 126 | 127 | void xu_libbacktrace_dtor_d1(void *self) { 128 | xu_libbacktrace_dtor_d1_ptr(self); 129 | } 130 | 131 | bool xu_libbacktrace_local_unwind(void *self, size_t num_ignore_frames, void *context) { 132 | return xu_libbacktrace_local_unwind_ptr(self, num_ignore_frames, context); 133 | } 134 | 135 | bool xu_libbacktrace_remote_unwind(void *self, size_t num_ignore_frames, void *context) { 136 | // libunwind.so in Android [5.0, 8.1] do NOT support unwinding from a specified context 137 | if (NULL != context) { 138 | int api_level = xu_util_get_api_level(); 139 | if (__ANDROID_API_L__ <= api_level && api_level <= __ANDROID_API_O_MR1__) context = NULL; 140 | } 141 | 142 | return xu_libbacktrace_remote_unwind_ptr(self, num_ignore_frames, context); 143 | } 144 | 145 | xu_libcxx_string_t xu_libbacktrace_format(void *self, size_t frame_num) { 146 | return xu_libbacktrace_format_ptr(self, frame_num); 147 | } 148 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_libbacktrace.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_LIBBACKTRACE 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_LIBBACKTRACE 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "xu_libcxx.h" 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #define XU_LIBBACKTRACE_CURRENT_PROCESS (-1) 38 | #define XU_LIBBACKTRACE_CURRENT_THREAD (-1) 39 | 40 | int xu_libbacktrace_init(void); 41 | 42 | void *xu_libbacktrace_create(pid_t pid, pid_t tid); 43 | void xu_libbacktrace_dtor_d1(void *self); 44 | bool xu_libbacktrace_local_unwind(void *self, size_t num_ignore_frames, void *context); 45 | bool xu_libbacktrace_remote_unwind(void *self, size_t num_ignore_frames, void *context); 46 | xu_libcxx_string_t xu_libbacktrace_format(void *self, size_t frame_num); 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_libcorkscrew.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | // Android K 25 | // https://android.googlesource.com/platform/system/core/+/android-4.4_r1/include/corkscrew/ 26 | // https://android.googlesource.com/platform/system/core/+/android-4.4_r1/libcorkscrew/ 27 | 28 | #include "xu_config.h" 29 | #ifdef XU_CONFIG_CFI_WITH_LIBCORKSCREW 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "xu_libcorkscrew.h" 40 | 41 | #define XU_LIBCORKSCREW_MAX_DEPTH 32 42 | 43 | typedef struct { 44 | uintptr_t placeholder[3]; 45 | } xu_libcorkscrew_frame_t; 46 | 47 | typedef struct { 48 | uintptr_t placeholder[5]; 49 | } xu_libcorkscrew_symbol_t; 50 | 51 | #pragma clang diagnostic push 52 | #pragma clang diagnostic ignored "-Wpadded" 53 | typedef struct { 54 | bool is_remote; 55 | pid_t pid; 56 | pid_t tid; 57 | xu_libcorkscrew_frame_t frames[XU_LIBCORKSCREW_MAX_DEPTH]; 58 | size_t frames_sz; 59 | xu_libcorkscrew_symbol_t symbols[XU_LIBCORKSCREW_MAX_DEPTH]; 60 | void *map; 61 | } xu_libcorkscrew_t; 62 | #pragma clang diagnostic pop 63 | 64 | #define XU_LIBCORKSCREW_BASENAME "libcorkscrew.so" 65 | #define XU_LIBCORKSCREW_LOCAL_LOAD_MAP "acquire_my_map_info_list" 66 | #define XU_LIBCORKSCREW_LOCAL_FREE_MAP "release_my_map_info_list" 67 | #define XU_LIBCORKSCREW_LOCAL_UNWIND "unwind_backtrace_thread" 68 | #define XU_LIBCORKSCREW_LOCAL_UNWIND_CONTEXT "unwind_backtrace_signal_arch" 69 | #define XU_LIBCORKSCREW_LOCAL_GET_SYM "get_backtrace_symbols" 70 | #define XU_LIBCORKSCREW_REMOTE_LOAD_MAP "load_ptrace_context" 71 | #define XU_LIBCORKSCREW_REMOTE_FREE_MAP "free_ptrace_context" 72 | #define XU_LIBCORKSCREW_REMOTE_UNWIND "unwind_backtrace_ptrace" 73 | #define XU_LIBCORKSCREW_REMOTE_GET_SYM "get_backtrace_symbols_ptrace" 74 | #define XU_LIBCORKSCREW_FREE_SYM "free_backtrace_symbols" 75 | #define XU_LIBCORKSCREW_FORMAT "format_backtrace_line" 76 | 77 | typedef void *(*xu_libcorkscrew_local_load_map_t)(void); 78 | typedef void (*xu_libcorkscrew_local_free_map_t)(void *map); 79 | typedef ssize_t (*xu_libcorkscrew_local_unwind_t)(pid_t tid, xu_libcorkscrew_frame_t *bt, size_t ignore_depth, 80 | size_t max_depth); 81 | typedef ssize_t (*xu_libcorkscrew_local_unwind_context_t)(void *siginfo, void *context, const void *map, 82 | xu_libcorkscrew_frame_t *bt, size_t ignore_depth, 83 | size_t max_depth); 84 | typedef void (*xu_libcorkscrew_local_get_sym_t)(const xu_libcorkscrew_frame_t *bt, size_t frames, 85 | xu_libcorkscrew_symbol_t *symbols); 86 | typedef void *(*xu_libcorkscrew_remote_load_map_t)(pid_t pid); 87 | typedef void (*xu_libcorkscrew_remote_free_map_t)(void *map); 88 | typedef ssize_t (*xu_libcorkscrew_remote_unwind_t)(pid_t tid, const void *map, xu_libcorkscrew_frame_t *bt, 89 | size_t ignore_depth, size_t max_depth); 90 | typedef void (*xu_libcorkscrew_remote_get_sym_t)(const void *map, const xu_libcorkscrew_frame_t *bt, 91 | size_t frames, xu_libcorkscrew_symbol_t *symbols); 92 | typedef void (*xu_libcorkscrew_free_sym_t)(xu_libcorkscrew_symbol_t *symbols, size_t frames); 93 | typedef void (*xu_libcorkscrew_format_t)(unsigned frameNumber, const xu_libcorkscrew_frame_t *frame, 94 | const xu_libcorkscrew_symbol_t *symbol, char *buffer, 95 | size_t bufferSize); 96 | 97 | static xu_libcorkscrew_local_load_map_t xu_libcorkscrew_local_load_map_ptr = NULL; 98 | static xu_libcorkscrew_local_free_map_t xu_libcorkscrew_local_free_map_ptr = NULL; 99 | static xu_libcorkscrew_local_unwind_t xu_libcorkscrew_local_unwind_ptr = NULL; 100 | static xu_libcorkscrew_local_unwind_context_t xu_libcorkscrew_local_unwind_context_ptr = NULL; 101 | static xu_libcorkscrew_local_get_sym_t xu_libcorkscrew_local_get_sym_ptr = NULL; 102 | static xu_libcorkscrew_remote_load_map_t xu_libcorkscrew_remote_load_map_ptr = NULL; 103 | static xu_libcorkscrew_remote_free_map_t xu_libcorkscrew_remote_free_map_ptr = NULL; 104 | static xu_libcorkscrew_remote_unwind_t xu_libcorkscrew_remote_unwind_ptr = NULL; 105 | static xu_libcorkscrew_remote_get_sym_t xu_libcorkscrew_remote_get_sym_ptr = NULL; 106 | static xu_libcorkscrew_free_sym_t xu_libcorkscrew_free_sym_ptr = NULL; 107 | static xu_libcorkscrew_format_t xu_libcorkscrew_format_ptr = NULL; 108 | 109 | int xu_libcorkscrew_init(void) { 110 | int ret = -1; 111 | 112 | void *handle = dlopen(XU_LIBCORKSCREW_BASENAME, RTLD_NOW); 113 | if (NULL == handle) return -1; 114 | 115 | if (NULL == (xu_libcorkscrew_local_load_map_ptr = 116 | (xu_libcorkscrew_local_load_map_t)dlsym(handle, XU_LIBCORKSCREW_LOCAL_LOAD_MAP))) 117 | goto end; 118 | if (NULL == (xu_libcorkscrew_local_free_map_ptr = 119 | (xu_libcorkscrew_local_free_map_t)dlsym(handle, XU_LIBCORKSCREW_LOCAL_FREE_MAP))) 120 | goto end; 121 | if (NULL == (xu_libcorkscrew_local_unwind_ptr = 122 | (xu_libcorkscrew_local_unwind_t)dlsym(handle, XU_LIBCORKSCREW_LOCAL_UNWIND))) 123 | goto end; 124 | if (NULL == (xu_libcorkscrew_local_unwind_context_ptr = (xu_libcorkscrew_local_unwind_context_t)dlsym( 125 | handle, XU_LIBCORKSCREW_LOCAL_UNWIND_CONTEXT))) 126 | goto end; 127 | if (NULL == (xu_libcorkscrew_local_get_sym_ptr = 128 | (xu_libcorkscrew_local_get_sym_t)dlsym(handle, XU_LIBCORKSCREW_LOCAL_GET_SYM))) 129 | goto end; 130 | if (NULL == (xu_libcorkscrew_remote_load_map_ptr = 131 | (xu_libcorkscrew_remote_load_map_t)dlsym(handle, XU_LIBCORKSCREW_REMOTE_LOAD_MAP))) 132 | goto end; 133 | if (NULL == (xu_libcorkscrew_remote_free_map_ptr = 134 | (xu_libcorkscrew_remote_free_map_t)dlsym(handle, XU_LIBCORKSCREW_REMOTE_FREE_MAP))) 135 | goto end; 136 | if (NULL == (xu_libcorkscrew_remote_unwind_ptr = 137 | (xu_libcorkscrew_remote_unwind_t)dlsym(handle, XU_LIBCORKSCREW_REMOTE_UNWIND))) 138 | goto end; 139 | if (NULL == (xu_libcorkscrew_remote_get_sym_ptr = 140 | (xu_libcorkscrew_remote_get_sym_t)dlsym(handle, XU_LIBCORKSCREW_REMOTE_GET_SYM))) 141 | goto end; 142 | if (NULL == 143 | (xu_libcorkscrew_free_sym_ptr = (xu_libcorkscrew_free_sym_t)dlsym(handle, XU_LIBCORKSCREW_FREE_SYM))) 144 | goto end; 145 | if (NULL == (xu_libcorkscrew_format_ptr = (xu_libcorkscrew_format_t)dlsym(handle, XU_LIBCORKSCREW_FORMAT))) 146 | goto end; 147 | ret = 0; 148 | 149 | end: 150 | // dlclose(handle); 151 | return ret; 152 | } 153 | 154 | void *xu_libcorkscrew_create(pid_t pid, pid_t tid) { 155 | xu_libcorkscrew_t *self = (xu_libcorkscrew_t *)calloc(1, sizeof(xu_libcorkscrew_t)); 156 | if (NULL == self) return NULL; 157 | 158 | self->is_remote = (pid == getpid() ? false : true); 159 | self->pid = pid; 160 | self->tid = tid; 161 | return (void *)self; 162 | } 163 | 164 | void xu_libcorkscrew_destroy(void *self) { 165 | xu_libcorkscrew_t *obj = (xu_libcorkscrew_t *)self; 166 | 167 | if (NULL != obj->map) { 168 | if (!obj->is_remote) 169 | xu_libcorkscrew_local_free_map_ptr(obj->map); 170 | else 171 | xu_libcorkscrew_remote_free_map_ptr(obj->map); 172 | } 173 | 174 | if (obj->frames_sz > 0) xu_libcorkscrew_free_sym_ptr(obj->symbols, obj->frames_sz); 175 | 176 | free(self); 177 | } 178 | 179 | size_t xu_libcorkscrew_unwind(void *self, size_t num_ignore_frames, void *context) { 180 | xu_libcorkscrew_t *obj = (xu_libcorkscrew_t *)self; 181 | 182 | if (!obj->is_remote) // local 183 | { 184 | ssize_t frames_sz; 185 | if (NULL != context && obj->tid == gettid()) // with context (in current thread) 186 | { 187 | obj->map = xu_libcorkscrew_local_load_map_ptr(); 188 | if (NULL == obj->map) return 0; 189 | 190 | frames_sz = xu_libcorkscrew_local_unwind_context_ptr(NULL, context, obj->map, obj->frames, 191 | num_ignore_frames, XU_LIBCORKSCREW_MAX_DEPTH); 192 | } else // without context 193 | { 194 | frames_sz = xu_libcorkscrew_local_unwind_ptr(obj->tid, obj->frames, num_ignore_frames, 195 | XU_LIBCORKSCREW_MAX_DEPTH); 196 | } 197 | 198 | if (frames_sz <= 0) return 0; 199 | obj->frames_sz = (size_t)frames_sz; 200 | 201 | xu_libcorkscrew_local_get_sym_ptr(obj->frames, obj->frames_sz, obj->symbols); 202 | } else // remote 203 | { 204 | obj->map = xu_libcorkscrew_remote_load_map_ptr(obj->pid); 205 | if (NULL == obj->map) return 0; 206 | 207 | ssize_t frames_sz = xu_libcorkscrew_remote_unwind_ptr(obj->tid, obj->map, obj->frames, num_ignore_frames, 208 | XU_LIBCORKSCREW_MAX_DEPTH); 209 | 210 | if (frames_sz <= 0) return 0; 211 | obj->frames_sz = (size_t)frames_sz; 212 | 213 | xu_libcorkscrew_remote_get_sym_ptr(obj->map, obj->frames, obj->frames_sz, obj->symbols); 214 | } 215 | 216 | return obj->frames_sz; 217 | } 218 | 219 | void xu_libcorkscrew_format(void *self, size_t frame_num, char *buf, size_t buf_cap) { 220 | xu_libcorkscrew_t *obj = (xu_libcorkscrew_t *)self; 221 | 222 | xu_libcorkscrew_format_ptr((unsigned)frame_num, &obj->frames[frame_num], &obj->symbols[frame_num], buf, 223 | buf_cap); 224 | } 225 | 226 | #else 227 | typedef int make_iso_happy; 228 | #endif 229 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_libcorkscrew.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_LIBCORKSCREW 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_LIBCORKSCREW 26 | 27 | #include "xu_config.h" 28 | #ifdef XU_CONFIG_CFI_WITH_LIBCORKSCREW 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | int xu_libcorkscrew_init(void); 39 | 40 | void *xu_libcorkscrew_create(pid_t pid, pid_t tid); 41 | void xu_libcorkscrew_destroy(void *self); 42 | size_t xu_libcorkscrew_unwind(void *self, size_t num_ignore_frames, void *context); 43 | void xu_libcorkscrew_format(void *self, size_t frame_num, char *buf, size_t buf_cap); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | 49 | #endif 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_libcxx.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-29. 23 | 24 | // https://android.googlesource.com/platform/external/libcxx/+/refs/heads/master/include/string 25 | // https://en.cppreference.com/w/cpp/string/basic_string 26 | 27 | #include "xu_libcxx.h" 28 | 29 | #include 30 | 31 | #include "xdl.h" 32 | 33 | #ifndef __LP64__ 34 | #define XU_LIBCXX_PATHNAME "/system/lib/libc++.so" 35 | #define XU_LIBCXX_STRING_COPY "_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcjj" 36 | #define XU_LIBCXX_STRING_AT "_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEj" 37 | #else 38 | #define XU_LIBCXX_PATHNAME "/system/lib64/libc++.so" 39 | #define XU_LIBCXX_STRING_COPY "_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm" 40 | #define XU_LIBCXX_STRING_AT "_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm" 41 | #endif 42 | 43 | #define XU_LIBCXX_STRING_DTOR_D1 "_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev" 44 | 45 | typedef size_t (*xu_libcxx_string_copy_t)(void *self, char *s, size_t len, size_t pos); 46 | typedef const char *(*xu_libcxx_string_at_t)(void *self, size_t pos); 47 | typedef void (*xu_libcxx_string_dtor_d1_t)(void *self); 48 | 49 | static xu_libcxx_string_copy_t xu_libcxx_string_copy_ptr = NULL; 50 | static xu_libcxx_string_at_t xu_libcxx_string_at_ptr = NULL; 51 | static xu_libcxx_string_dtor_d1_t xu_libcxx_string_dtor_d1_ptr = NULL; 52 | 53 | int xu_libcxx_init(void) { 54 | int ret = -1; 55 | 56 | void *handle = xdl_open(XU_LIBCXX_PATHNAME, XDL_DEFAULT); 57 | if (NULL == handle) return -1; 58 | 59 | if (NULL == 60 | (xu_libcxx_string_copy_ptr = (xu_libcxx_string_copy_t)xdl_sym(handle, XU_LIBCXX_STRING_COPY, NULL))) 61 | goto end; 62 | if (NULL == (xu_libcxx_string_at_ptr = (xu_libcxx_string_at_t)xdl_sym(handle, XU_LIBCXX_STRING_AT, NULL))) 63 | goto end; 64 | if (NULL == (xu_libcxx_string_dtor_d1_ptr = 65 | (xu_libcxx_string_dtor_d1_t)xdl_sym(handle, XU_LIBCXX_STRING_DTOR_D1, NULL))) 66 | goto end; 67 | 68 | ret = 0; 69 | 70 | end: 71 | xdl_close(handle); 72 | return ret; 73 | } 74 | 75 | const char *xu_libcxx_string_c_str(xu_libcxx_string_t *self) { 76 | char buf[2] = {'\0', '\0'}; 77 | size_t len = xu_libcxx_string_copy_ptr((void *)self, buf, 1, 0); 78 | return (len > 0) ? xu_libcxx_string_at_ptr(self, 0) : ""; 79 | } 80 | 81 | void xu_libcxx_string_dtor_d1(xu_libcxx_string_t *self) { 82 | xu_libcxx_string_dtor_d1_ptr((void *)self); 83 | } 84 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_libcxx.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-29. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_LIBCXX 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_LIBCXX 26 | 27 | #include 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | typedef struct { 34 | size_t placeholder[4]; 35 | } xu_libcxx_string_t; 36 | 37 | int xu_libcxx_init(void); 38 | 39 | const char *xu_libcxx_string_c_str(xu_libcxx_string_t *self); 40 | void xu_libcxx_string_dtor_d1(xu_libcxx_string_t *self); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_log.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-29. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_LOG 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_LOG 26 | 27 | #include 28 | 29 | #include "xu_config.h" 30 | 31 | #ifdef XU_CONFIG_WITH_LOG 32 | 33 | #include 34 | 35 | #define XU_LOG_TAG "xunwind_tag" 36 | #define XU_LOG_PRIORITY ANDROID_LOG_INFO 37 | 38 | #pragma clang diagnostic push 39 | #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 40 | #define XU_LOG(fmt, ...) __android_log_print(XU_LOG_PRIORITY, XU_LOG_TAG, fmt, ##__VA_ARGS__) 41 | #pragma clang diagnostic pop 42 | 43 | #else 44 | #define XU_LOG(fmt, ...) 45 | #endif 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_printer.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-29. 23 | 24 | #include "xu_printer.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "xu_util.h" 34 | 35 | #define XU_PRINTER_DEFAULT_LOGTAG "xunwind" 36 | 37 | void xu_printer_init_log(xu_printer_t *self, const char *logtag, android_LogPriority priority) { 38 | self->type = XU_PRINTER_TYPE_LOG; 39 | self->data.log.priority = (int)priority; 40 | self->data.log.logtag = (NULL == logtag ? XU_PRINTER_DEFAULT_LOGTAG : logtag); 41 | } 42 | 43 | void xu_printer_init_dump(xu_printer_t *self, int fd) { 44 | self->type = XU_PRINTER_TYPE_DUMP; 45 | self->data.dump.fd = fd; 46 | } 47 | 48 | void xu_printer_init_get(xu_printer_t *self) { 49 | self->type = XU_PRINTER_TYPE_GET; 50 | self->data.get.buf = NULL; 51 | self->data.get.buf_cap = 0; 52 | self->data.get.buf_sz = 0; 53 | } 54 | 55 | static int xu_printer_string_append_to_buf(xu_printer_t *self, const char *str) { 56 | char **buf = &(self->data.get.buf); 57 | size_t *buf_cap = &(self->data.get.buf_cap); 58 | size_t *buf_sz = &(self->data.get.buf_sz); 59 | 60 | if (NULL == str) return 0; 61 | 62 | size_t str_len = strlen(str); 63 | if (0 == str_len) return 0; 64 | 65 | if (*buf_cap - *buf_sz < str_len + 1) { 66 | size_t new_buf_cap = ((*buf_cap + str_len) & (~(0x400u - 1))) + 0x400u; 67 | char *new_buf = realloc(*buf, new_buf_cap); 68 | if (NULL == new_buf) return -1; 69 | *buf = new_buf; 70 | *buf_cap = new_buf_cap; 71 | } 72 | 73 | memcpy(*buf + *buf_sz, str, str_len); 74 | *(*buf + *buf_sz + str_len) = '\0'; 75 | *buf_sz += str_len; 76 | return 0; 77 | } 78 | 79 | void xu_printer_append_string(xu_printer_t *self, const char *str) { 80 | if (XU_PRINTER_TYPE_LOG == self->type) { 81 | __android_log_print(self->data.log.priority, self->data.log.logtag, "%s", str); 82 | } else if (XU_PRINTER_TYPE_DUMP == self->type) { 83 | size_t len = strlen(str); 84 | if (len > 0) { 85 | xu_util_write(self->data.dump.fd, str, len); 86 | if ('\n' != str[len - 1]) xu_util_write(self->data.dump.fd, "\n", 1); 87 | } 88 | } else if (XU_PRINTER_TYPE_GET == self->type) { 89 | size_t len = strlen(str); 90 | if (len > 0) { 91 | xu_printer_string_append_to_buf(self, str); 92 | if ('\n' != str[len - 1]) xu_printer_string_append_to_buf(self, "\n"); 93 | } 94 | } 95 | } 96 | 97 | void xu_printer_append_format(xu_printer_t *self, const char *format, ...) { 98 | va_list ap; 99 | va_start(ap, format); 100 | 101 | char tmpbuf[1024]; 102 | #pragma clang diagnostic push 103 | #pragma clang diagnostic ignored "-Wformat-nonliteral" 104 | vsnprintf(tmpbuf, sizeof(tmpbuf), format, ap); 105 | #pragma clang diagnostic pop 106 | 107 | va_end(ap); 108 | 109 | xu_printer_append_string(self, tmpbuf); 110 | } 111 | 112 | char *xu_printer_get(xu_printer_t *self) { 113 | if (XU_PRINTER_TYPE_GET != self->type) return NULL; 114 | 115 | return self->data.get.buf; 116 | } 117 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_printer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-29. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_PRINTER 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_PRINTER 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | #define XU_PRINTER_TYPE_LOG 0 36 | #define XU_PRINTER_TYPE_DUMP 1 37 | #define XU_PRINTER_TYPE_GET 2 38 | 39 | #pragma clang diagnostic push 40 | #pragma clang diagnostic ignored "-Wpadded" 41 | typedef struct { 42 | int type; 43 | 44 | union { 45 | struct { 46 | const char *logtag; 47 | int priority; 48 | } log; 49 | 50 | struct { 51 | int fd; 52 | } dump; 53 | 54 | struct { 55 | char *buf; 56 | size_t buf_cap; 57 | size_t buf_sz; 58 | } get; 59 | } data; 60 | } xu_printer_t; 61 | #pragma clang diagnostic pop 62 | 63 | void xu_printer_init_log(xu_printer_t *self, const char *logtag, android_LogPriority priority); 64 | void xu_printer_init_dump(xu_printer_t *self, int fd); 65 | void xu_printer_init_get(xu_printer_t *self); 66 | 67 | void xu_printer_append_string(xu_printer_t *self, const char *str); 68 | void xu_printer_append_format(xu_printer_t *self, const char *format, ...); 69 | 70 | char *xu_printer_get(xu_printer_t *self); 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_util.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-29. 23 | 24 | #include "xu_util.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "xdl.h" 39 | 40 | static bool xu_util_starts_with(const char *str, const char *start) { 41 | while (*str && *str == *start) { 42 | str++; 43 | start++; 44 | } 45 | 46 | return '\0' == *start; 47 | } 48 | 49 | static int xu_util_get_api_level_from_build_prop(void) { 50 | char buf[128]; 51 | int api_level = -1; 52 | 53 | FILE *fp = fopen("/system/build.prop", "r"); 54 | if (NULL == fp) goto end; 55 | 56 | while (fgets(buf, sizeof(buf), fp)) { 57 | if (xu_util_starts_with(buf, "ro.build.version.sdk=")) { 58 | api_level = atoi(buf + 21); 59 | break; 60 | } 61 | } 62 | fclose(fp); 63 | 64 | end: 65 | return (api_level > 0) ? api_level : -1; 66 | } 67 | 68 | int xu_util_get_api_level(void) { 69 | static int xu_util_api_level = -1; 70 | 71 | if (xu_util_api_level < 0) { 72 | int api_level = android_get_device_api_level(); 73 | if (api_level < 0) api_level = xu_util_get_api_level_from_build_prop(); // compatible with unusual models 74 | if (api_level < __ANDROID_API_J__) api_level = __ANDROID_API_J__; 75 | 76 | __atomic_store_n(&xu_util_api_level, api_level, __ATOMIC_SEQ_CST); 77 | } 78 | 79 | return xu_util_api_level; 80 | } 81 | 82 | static void xu_util_get_process_or_thread_name(const char *path, char *buf, size_t buf_len, pid_t id) { 83 | bool ok = false; 84 | FILE *fp = NULL; 85 | if (NULL == (fp = fopen(path, "r"))) goto end; 86 | if (NULL == fgets(buf, (int)buf_len, fp)) goto end; 87 | 88 | size_t len = strlen(buf); 89 | if (0 == len) goto end; 90 | if ('\n' == buf[len - 1]) { 91 | if (1 == len) goto end; 92 | buf[len - 1] = '\0'; 93 | } 94 | 95 | ok = true; 96 | 97 | end: 98 | if (NULL != fp) fclose(fp); 99 | if (!ok) snprintf(buf, buf_len, "[unknown-%d]", id); 100 | } 101 | 102 | void xu_util_get_process_name(pid_t pid, char *buf, size_t buf_len) { 103 | char path[64]; 104 | snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); 105 | xu_util_get_process_or_thread_name(path, buf, buf_len, pid); 106 | } 107 | 108 | void xu_util_get_thread_name(pid_t pid, pid_t tid, char *buf, size_t buf_len) { 109 | char path[64]; 110 | snprintf(path, sizeof(path), "/proc/%d/task/%d/comm", pid, tid); 111 | xu_util_get_process_or_thread_name(path, buf, buf_len, tid); 112 | } 113 | 114 | pid_t *xu_util_get_thread_ids(pid_t pid, size_t *tids_cnt) { 115 | pid_t *tids = NULL; 116 | *tids_cnt = 0; 117 | size_t tids_cap = 0; 118 | 119 | char path[64]; 120 | snprintf(path, sizeof(path), "/proc/%d/task", pid); 121 | 122 | DIR *dir = opendir(path); 123 | if (NULL == dir) return NULL; 124 | 125 | struct dirent *ep; 126 | while (NULL != (ep = readdir(dir))) { 127 | pid_t tid = -1; 128 | sscanf(ep->d_name, "%d", &tid); 129 | if (tid < 0) continue; 130 | 131 | if (tids_cap - *tids_cnt < 1) { 132 | size_t new_tids_cap = tids_cap + 512; 133 | pid_t *new_tids = realloc(tids, new_tids_cap * (sizeof(pid_t))); 134 | if (NULL == new_tids) { 135 | closedir(dir); 136 | if (NULL != tids) free(tids); 137 | *tids_cnt = 0; 138 | return NULL; 139 | } 140 | tids = new_tids; 141 | tids_cap = new_tids_cap; 142 | } 143 | 144 | tids[(*tids_cnt)++] = tid; 145 | } 146 | closedir(dir); 147 | 148 | return tids; 149 | } 150 | 151 | int xu_util_ptrace_attach(pid_t tid) { 152 | if (0 != ptrace(PTRACE_ATTACH, tid, NULL, NULL)) return -1; 153 | 154 | errno = 0; 155 | while (waitpid(tid, NULL, __WALL) < 0) { 156 | if (EINTR != errno) { 157 | ptrace(PTRACE_DETACH, tid, NULL, NULL); 158 | return -1; 159 | } 160 | } 161 | return 0; 162 | } 163 | 164 | void xu_util_ptrace_detach(pid_t tid) { 165 | ptrace(PTRACE_DETACH, tid, NULL, NULL); 166 | } 167 | 168 | int xu_util_write(int fd, const char *buf, size_t buf_len) { 169 | if (fd < 0) return -1; 170 | 171 | const char *ptr = buf; 172 | size_t nleft = buf_len; 173 | 174 | while (nleft > 0) { 175 | errno = 0; 176 | ssize_t nwritten = write(fd, ptr, nleft); 177 | if (nwritten <= 0) { 178 | if (nwritten < 0 && errno == EINTR) 179 | nwritten = 0; // call write() again 180 | else 181 | return -1; // error 182 | } 183 | nleft -= (size_t)nwritten; 184 | ptr += nwritten; 185 | } 186 | 187 | return 0; 188 | } 189 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xu_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-29. 23 | 24 | #ifndef IO_GITHUB_HEXHACKING_XUNWIND_UTIL 25 | #define IO_GITHUB_HEXHACKING_XUNWIND_UTIL 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | int xu_util_get_api_level(void); 37 | 38 | void xu_util_get_process_name(pid_t pid, char *buf, size_t buf_len); 39 | void xu_util_get_thread_name(pid_t pid, pid_t tid, char *buf, size_t buf_len); 40 | 41 | pid_t *xu_util_get_thread_ids(pid_t pid, size_t *tids_cnt); 42 | 43 | int xu_util_ptrace_attach(pid_t tid); 44 | void xu_util_ptrace_detach(pid_t tid); 45 | 46 | int xu_util_write(int fd, const char *buf, size_t buf_len); 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xunwind.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #include "xunwind.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "xdl.h" 38 | #include "xu_cfi.h" 39 | #include "xu_eh.h" 40 | #include "xu_formatter.h" 41 | #include "xu_fp.h" 42 | #include "xu_log.h" 43 | #include "xu_printer.h" 44 | #include "xu_util.h" 45 | 46 | void xunwind_cfi_log(pid_t pid, pid_t tid, void *context, const char *logtag, android_LogPriority priority, 47 | const char *prefix) { 48 | if (priority < ANDROID_LOG_VERBOSE || ANDROID_LOG_FATAL < priority) return; 49 | 50 | if (0 != xu_cfi_init()) return; 51 | 52 | xu_printer_t printer; 53 | xu_printer_init_log(&printer, logtag, priority); 54 | 55 | xu_cfi_print(pid, tid, context, prefix, &printer); 56 | } 57 | 58 | void xunwind_cfi_dump(pid_t pid, pid_t tid, void *context, int fd, const char *prefix) { 59 | if (fd < 0) return; 60 | 61 | if (0 != xu_cfi_init()) return; 62 | 63 | xu_printer_t printer; 64 | xu_printer_init_dump(&printer, fd); 65 | 66 | xu_cfi_print(pid, tid, context, prefix, &printer); 67 | } 68 | 69 | char *xunwind_cfi_get(pid_t pid, pid_t tid, void *context, const char *prefix) { 70 | if (0 != xu_cfi_init()) return NULL; 71 | 72 | xu_printer_t printer; 73 | xu_printer_init_get(&printer); 74 | 75 | xu_cfi_print(pid, tid, context, prefix, &printer); 76 | return xu_printer_get(&printer); 77 | } 78 | 79 | size_t xunwind_fp_unwind(uintptr_t *frames, size_t frames_cap, void *context) { 80 | if (0 != xu_fp_init()) return 0; 81 | 82 | return xu_fp_unwind(frames, frames_cap, context); 83 | } 84 | 85 | size_t xunwind_eh_unwind(uintptr_t *frames, size_t frames_cap, void *context) { 86 | return xu_eh_unwind(frames, frames_cap, context); 87 | } 88 | 89 | void xunwind_frames_log(uintptr_t *frames, size_t frames_sz, const char *logtag, android_LogPriority priority, 90 | const char *prefix) { 91 | if (priority < ANDROID_LOG_VERBOSE || ANDROID_LOG_FATAL < priority) return; 92 | 93 | xu_printer_t printer; 94 | xu_printer_init_log(&printer, logtag, priority); 95 | 96 | xu_formatter_print(frames, frames_sz, prefix, &printer); 97 | } 98 | 99 | void xunwind_frames_dump(uintptr_t *frames, size_t frames_sz, int fd, const char *prefix) { 100 | if (fd < 0) return; 101 | 102 | xu_printer_t printer; 103 | xu_printer_init_dump(&printer, fd); 104 | 105 | xu_formatter_print(frames, frames_sz, prefix, &printer); 106 | } 107 | 108 | char *xunwind_frames_get(uintptr_t *frames, size_t frames_sz, const char *prefix) { 109 | xu_printer_t printer; 110 | xu_printer_init_get(&printer); 111 | 112 | xu_formatter_print(frames, frames_sz, prefix, &printer); 113 | return xu_printer_get(&printer); 114 | } 115 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xunwind.map.txt: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | JNI_OnLoad; 4 | xunwind_cfi_log; 5 | xunwind_cfi_dump; 6 | xunwind_cfi_get; 7 | xunwind_fp_unwind; 8 | xunwind_eh_unwind; 9 | xunwind_frames_log; 10 | xunwind_frames_dump; 11 | xunwind_frames_get; 12 | 13 | local: 14 | *; 15 | }; 16 | -------------------------------------------------------------------------------- /xunwind/src/main/cpp/xunwind_ctor.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | #include 25 | 26 | #include "xu_cfi.h" 27 | #include "xu_fp.h" 28 | 29 | __attribute__((constructor)) static void xunwind_init(void) { 30 | #pragma clang diagnostic push 31 | #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 32 | #define LOG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "xunwind", fmt, ##__VA_ARGS__) 33 | #pragma clang diagnostic pop 34 | 35 | if (0 != xu_cfi_init()) LOG("CFI init FAILED"); 36 | if (0 != xu_fp_init()) LOG("FP init FAILED"); 37 | } 38 | -------------------------------------------------------------------------------- /xunwind/src/main/java/io/github/hexhacking/xunwind/XUnwind.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2023 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-21. 23 | 24 | package io.github.hexhacking.xunwind; 25 | 26 | public class XUnwind { 27 | 28 | private static final int currentProcess = -1; 29 | private static final int currentThread = -1; 30 | private static final int allThreads = -2; 31 | 32 | private static boolean initialized = false; 33 | 34 | public interface ILibLoader { 35 | void loadLibrary(String libName); 36 | } 37 | 38 | public static synchronized void init() throws SecurityException, UnsatisfiedLinkError { 39 | init(null); 40 | } 41 | 42 | public static synchronized void init(XUnwind.ILibLoader libLoader) throws SecurityException, UnsatisfiedLinkError { 43 | if (initialized) { 44 | return; 45 | } 46 | 47 | if (libLoader == null) { 48 | System.loadLibrary("xunwind"); 49 | } else { 50 | libLoader.loadLibrary("xunwind"); 51 | } 52 | 53 | initialized = true; 54 | } 55 | 56 | // dump to logcat 57 | private static void log(int pid, int tid, String logtag, int priority, String prefix) { 58 | if (!initialized) { 59 | return; 60 | } 61 | nativeLog(pid, tid, logtag, priority, prefix); 62 | } 63 | 64 | public static void logLocalCurrentThread(String logtag, int priority, String prefix) { 65 | log(currentProcess, currentThread, logtag, priority, prefix); 66 | } 67 | 68 | public static void logLocalCurrentThread(String logtag, int priority) { 69 | log(currentProcess, currentThread, logtag, priority, null); 70 | } 71 | 72 | public static void logLocalThread(int tid, String logtag, int priority, String prefix) { 73 | log(currentProcess, tid, logtag, priority, prefix); 74 | } 75 | 76 | public static void logLocalThread(int tid, String logtag, int priority) { 77 | log(currentProcess, tid, logtag, priority, null); 78 | } 79 | 80 | public static void logLocalAllThread(String logtag, int priority, String prefix) { 81 | log(currentProcess, allThreads, logtag, priority, prefix); 82 | } 83 | 84 | public static void logLocalAllThread(String logtag, int priority) { 85 | log(currentProcess, allThreads, logtag, priority, null); 86 | } 87 | 88 | public static void logRemoteThread(int pid, int tid, String logtag, int priority, String prefix) { 89 | log(pid, tid, logtag, priority, prefix); 90 | } 91 | 92 | public static void logRemoteThread(int pid, int tid, String logtag, int priority) { 93 | log(pid, tid, logtag, priority, null); 94 | } 95 | 96 | public static void logRemoteAllThread(int pid, String logtag, int priority, String prefix) { 97 | log(pid, allThreads, logtag, priority, prefix); 98 | } 99 | 100 | public static void logRemoteAllThread(int pid, String logtag, int priority) { 101 | log(pid, allThreads, logtag, priority, null); 102 | } 103 | 104 | 105 | // dump to FD 106 | private static void dump(int pid, int tid, int fd, String prefix) { 107 | if (!initialized) { 108 | return; 109 | } 110 | nativeDump(pid, tid, fd, prefix); 111 | } 112 | 113 | public static void dumpLocalCurrentThread(int fd, String prefix) { 114 | dump(currentProcess, currentThread, fd, prefix); 115 | } 116 | 117 | public static void dumpLocalCurrentThread(int fd) { 118 | dump(currentProcess, currentThread, fd, null); 119 | } 120 | 121 | public static void dumpLocalThread(int tid, int fd, String prefix) { 122 | dump(currentProcess, tid, fd, prefix); 123 | } 124 | 125 | public static void dumpLocalThread(int tid, int fd) { 126 | dump(currentProcess, tid, fd, null); 127 | } 128 | 129 | public static void dumpLocalAllThread(int fd, String prefix) { 130 | dump(currentProcess, allThreads, fd, prefix); 131 | } 132 | 133 | public static void dumpLocalAllThread(int fd) { 134 | dump(currentProcess, allThreads, fd, null); 135 | } 136 | 137 | public static void dumpRemoteThread(int pid, int tid, int fd, String prefix) { 138 | dump(pid, tid, fd, prefix); 139 | } 140 | 141 | public static void dumpRemoteThread(int pid, int tid, int fd) { 142 | dump(pid, tid, fd, null); 143 | } 144 | 145 | public static void dumpRemoteAllThread(int pid, int fd, String prefix) { 146 | dump(pid, allThreads, fd, prefix); 147 | } 148 | 149 | public static void dumpRemoteAllThread(int pid, int fd) { 150 | dump(pid, allThreads, fd, null); 151 | } 152 | 153 | 154 | // return as string 155 | private static String get(int pid, int tid, String prefix) { 156 | if (!initialized) { 157 | return null; 158 | } 159 | return nativeGet(pid, tid, prefix); 160 | } 161 | 162 | public static String getLocalCurrentThread(String prefix) { 163 | return get(currentProcess, currentThread, prefix); 164 | } 165 | 166 | public static String getLocalCurrentThread() { 167 | return get(currentProcess, currentThread, null); 168 | } 169 | 170 | public static String getLocalThread(int tid, String prefix) { 171 | return get(currentProcess, tid, prefix); 172 | } 173 | 174 | public static String getLocalThread(int tid) { 175 | return get(currentProcess, tid, null); 176 | } 177 | 178 | public static String getLocalAllThread(String prefix) { 179 | return get(currentProcess, allThreads, prefix); 180 | } 181 | 182 | public static String getLocalAllThread() { 183 | return get(currentProcess, allThreads, null); 184 | } 185 | 186 | public static String getRemoteThread(int pid, int tid, String prefix) { 187 | return get(pid, tid, prefix); 188 | } 189 | 190 | public static String getRemoteThread(int pid, int tid) { 191 | return get(pid, tid, null); 192 | } 193 | 194 | public static String getRemoteAllThread(int pid, String prefix) { 195 | return get(pid, allThreads, prefix); 196 | } 197 | 198 | public static String getRemoteAllThread(int pid) { 199 | return get(pid, allThreads, null); 200 | } 201 | 202 | 203 | private static native void nativeLog(int pid, int tid, String logtag, int priority, String prefix); 204 | 205 | private static native void nativeDump(int pid, int tid, int fd, String prefix); 206 | 207 | private static native String nativeGet(int pid, int tid, String prefix); 208 | } 209 | -------------------------------------------------------------------------------- /xunwind_sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /xunwind_sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | namespace 'io.github.hexhacking.xunwind.sample' 5 | compileSdkVersion rootProject.ext.compileSdkVersion 6 | buildToolsVersion rootProject.ext.buildToolsVersion 7 | ndkVersion rootProject.ext.ndkVersion 8 | defaultConfig { 9 | applicationId "io.github.hexhacking.xunwind.sample" 10 | minSdkVersion rootProject.ext.minSdkVersion 11 | targetSdkVersion rootProject.ext.targetSdkVersion 12 | versionCode 1 13 | versionName "1.0" 14 | ndk { 15 | abiFilters rootProject.ext.abiFilters.split(",") 16 | } 17 | externalNativeBuild { 18 | cmake { 19 | abiFilters rootProject.ext.abiFilters.split(",") 20 | } 21 | } 22 | } 23 | externalNativeBuild { 24 | cmake { 25 | path "src/main/cpp/CMakeLists.txt" 26 | version rootProject.ext.cmakeVersion 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | buildTypes { 34 | debug { 35 | minifyEnabled false 36 | } 37 | release { 38 | minifyEnabled false 39 | } 40 | } 41 | packagingOptions { 42 | jniLibs { 43 | pickFirsts += ['**/libxunwind.so'] 44 | } 45 | if (rootProject.ext.useASAN) { 46 | doNotStrip "**/*.so" 47 | } 48 | } 49 | buildFeatures { 50 | prefab true 51 | } 52 | } 53 | 54 | dependencies { 55 | implementation 'androidx.appcompat:appcompat:1.6.1' 56 | implementation 'com.google.android.material:material:1.8.0' 57 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 58 | 59 | if (rootProject.ext.dependencyOnLocalLibrary) { 60 | implementation project(':xunwind') 61 | } else { 62 | implementation 'io.github.hexhacking:xunwind:' + rootProject.ext.xunwindVersion 63 | } 64 | } 65 | 66 | apply from: rootProject.file('gradle/sanitizer.gradle') 67 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22.1) 2 | project(sample) 3 | 4 | find_package(xunwind REQUIRED CONFIG) 5 | 6 | add_library(sample SHARED sample.c) 7 | target_compile_features(sample PUBLIC c_std_17) 8 | target_compile_options(sample PUBLIC -std=c17 -Weverything -Werror) 9 | if(${ANDROID_ABI} STREQUAL "arm64-v8a") 10 | target_compile_options(sample PUBLIC -mbranch-protection=standard) 11 | endif() 12 | target_link_libraries(sample xunwind::xunwind log) 13 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/cpp/sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "xunwind.h" 13 | 14 | #define SAMPLE_JNI_VERSION JNI_VERSION_1_6 15 | #define SAMPLE_JNI_CLASS_NAME "io/github/hexhacking/xunwind/sample/NativeSample" 16 | #define SAMPLE_LOG_TAG "xunwind_tag" 17 | #define SAMPLE_LOG_PRIORITY ANDROID_LOG_INFO 18 | 19 | #pragma clang diagnostic push 20 | #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 21 | #define SAMPLE_LOG(fmt, ...) __android_log_print(SAMPLE_LOG_PRIORITY, SAMPLE_LOG_TAG, fmt, ##__VA_ARGS__) 22 | #pragma clang diagnostic pop 23 | 24 | #define SAMPLE_TEMP_FAILURE_RETRY(exp) \ 25 | ({ \ 26 | __typeof__(exp) _rc; \ 27 | do { \ 28 | errno = 0; \ 29 | _rc = (exp); \ 30 | } while (_rc == -1 && errno == EINTR); \ 31 | _rc; \ 32 | }) 33 | 34 | #define SAMPLE_SOLUTION_CFI 1 35 | #define SAMPLE_SOLUTION_FP 2 36 | #define SAMPLE_SOLUTION_EH 3 37 | 38 | // parameters from JNI 39 | static int g_solution = 0; 40 | static bool g_remote_unwind = false; 41 | static bool g_with_context = false; 42 | static bool g_signal_interrupted = false; 43 | 44 | // for sigsegv 45 | static struct sigaction g_sigsegv_oldact; 46 | static pid_t g_cur_tid = 0; 47 | static sigjmp_buf g_jb; 48 | 49 | static uintptr_t g_frames[128]; 50 | static size_t g_frames_sz = 0; 51 | 52 | // 53 | // ** Notice ** 54 | // It is not reliable to do CFI unwinding directly in a signal handler. 55 | // This is just an example. 56 | // 57 | 58 | #pragma clang optimize off 59 | static void sample_make_sigsegv() { 60 | *((volatile int *)0) = 0; 61 | } 62 | #pragma clang optimize on 63 | 64 | static void sample_sigabrt_handler(int signum, siginfo_t *siginfo, void *context) { 65 | (void)signum, (void)siginfo; 66 | 67 | if (g_solution == SAMPLE_SOLUTION_FP || g_solution == SAMPLE_SOLUTION_EH) { 68 | if (!g_signal_interrupted) { 69 | if (g_solution == SAMPLE_SOLUTION_FP) { 70 | // FP local unwind 71 | #ifdef __aarch64__ 72 | size_t frames_sz = xunwind_fp_unwind(g_frames, sizeof(g_frames) / sizeof(g_frames[0]), 73 | g_with_context ? context : NULL); 74 | __atomic_store_n(&g_frames_sz, frames_sz, __ATOMIC_SEQ_CST); 75 | #else 76 | SAMPLE_LOG("FP unwinding is only supported on arm64."); 77 | #endif 78 | } else if (g_solution == SAMPLE_SOLUTION_EH) { 79 | // EH local unwind 80 | size_t frames_sz = xunwind_eh_unwind(g_frames, sizeof(g_frames) / sizeof(g_frames[0]), 81 | g_with_context ? context : NULL); 82 | __atomic_store_n(&g_frames_sz, frames_sz, __ATOMIC_SEQ_CST); 83 | } 84 | } else { 85 | // trigger a segfault, we will do "FP local unwind" in the sigsegv's signal handler 86 | __atomic_store_n((pid_t *)&g_cur_tid, gettid(), __ATOMIC_SEQ_CST); 87 | if (0 == sigsetjmp(g_jb, 1)) sample_make_sigsegv(); 88 | } 89 | } else if (!g_remote_unwind) { 90 | // CFI local unwind 91 | xunwind_cfi_log(XUNWIND_CURRENT_PROCESS, XUNWIND_CURRENT_THREAD, context, SAMPLE_LOG_TAG, 92 | SAMPLE_LOG_PRIORITY, NULL); 93 | } else { 94 | // CFI remote unwind 95 | int notifier[2]; 96 | if (0 != pipe(notifier)) return; 97 | 98 | pid_t pid = getpid(); 99 | pid_t tid = gettid(); 100 | 101 | int child_pid = fork(); 102 | if (child_pid < 0) return; 103 | 104 | if (0 == child_pid) { 105 | // child 106 | write(notifier[1], "a", 1); 107 | xunwind_cfi_log(pid, tid, context, SAMPLE_LOG_TAG, SAMPLE_LOG_PRIORITY, NULL); 108 | _exit(0); 109 | } 110 | 111 | // parent 112 | close(notifier[1]); 113 | char buf; 114 | read(notifier[0], &buf, sizeof(buf)); 115 | close(notifier[0]); 116 | 117 | int status = 0; 118 | #pragma clang diagnostic push 119 | #pragma clang diagnostic ignored "-Wgnu-statement-expression" 120 | if (SAMPLE_TEMP_FAILURE_RETRY(waitpid(child_pid, &status, __WALL)) < 0) 121 | #pragma clang diagnostic pop 122 | { 123 | SAMPLE_LOG("waitpid failed, errno: %d", errno); 124 | return; 125 | } 126 | if (!(WIFEXITED(status)) || 0 != WEXITSTATUS(status)) { 127 | if (WIFEXITED(status) && 0 != WEXITSTATUS(status)) 128 | SAMPLE_LOG("child terminated normally with non-zero exit status: %d", WEXITSTATUS(status)); 129 | else if (WIFSIGNALED(status)) 130 | SAMPLE_LOG("child terminated by a signal: %d", WTERMSIG(status)); 131 | else 132 | SAMPLE_LOG("child terminated with other error status: %d", status); 133 | } 134 | } 135 | } 136 | 137 | static void sample_sigsegv_handler(int signum, siginfo_t *siginfo, void *context) { 138 | (void)signum, (void)siginfo; 139 | 140 | pid_t tid = gettid(); 141 | if (__atomic_compare_exchange_n(&g_cur_tid, &tid, 0, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 142 | if (g_solution == SAMPLE_SOLUTION_FP) { 143 | // FP local unwind 144 | #ifdef __aarch64__ 145 | size_t frames_sz = xunwind_fp_unwind(g_frames, sizeof(g_frames) / sizeof(g_frames[0]), 146 | g_with_context ? context : NULL); 147 | __atomic_store_n(&g_frames_sz, frames_sz, __ATOMIC_SEQ_CST); 148 | #else 149 | (void)context; 150 | SAMPLE_LOG("FP unwinding is only supported on arm64."); 151 | #endif 152 | } else if (g_solution == SAMPLE_SOLUTION_EH) { 153 | // EH local unwind 154 | size_t frames_sz = xunwind_eh_unwind(g_frames, sizeof(g_frames) / sizeof(g_frames[0]), 155 | g_with_context ? context : NULL); 156 | __atomic_store_n(&g_frames_sz, frames_sz, __ATOMIC_SEQ_CST); 157 | } 158 | siglongjmp(g_jb, 1); 159 | } else { 160 | sigaction(SIGSEGV, &g_sigsegv_oldact, NULL); 161 | } 162 | } 163 | 164 | static void sample_signal_register(void) { 165 | struct sigaction act; 166 | 167 | memset(&act, 0, sizeof(act)); 168 | sigfillset(&act.sa_mask); 169 | sigdelset(&act.sa_mask, SIGSEGV); 170 | act.sa_sigaction = sample_sigabrt_handler; 171 | act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; 172 | sigaction(SIGABRT, &act, NULL); 173 | 174 | memset(&act, 0, sizeof(act)); 175 | sigfillset(&act.sa_mask); 176 | act.sa_sigaction = sample_sigsegv_handler; 177 | act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; 178 | sigaction(SIGSEGV, &act, &g_sigsegv_oldact); 179 | } 180 | 181 | static void sample_test(int solution, jboolean remote_unwind, jboolean with_context, 182 | jboolean signal_interrupted) { 183 | g_solution = solution; 184 | g_remote_unwind = (JNI_TRUE == remote_unwind ? true : false); 185 | g_with_context = (JNI_TRUE == with_context ? true : false); 186 | g_signal_interrupted = (JNI_TRUE == signal_interrupted ? true : false); 187 | 188 | __atomic_store_n(&g_frames_sz, 0, __ATOMIC_SEQ_CST); 189 | 190 | tgkill(getpid(), gettid(), SIGABRT); 191 | 192 | if ((solution == SAMPLE_SOLUTION_FP || solution == SAMPLE_SOLUTION_EH) && 193 | __atomic_load_n(&g_frames_sz, __ATOMIC_SEQ_CST) > 0) 194 | xunwind_frames_log(g_frames, g_frames_sz, SAMPLE_LOG_TAG, SAMPLE_LOG_PRIORITY, NULL); 195 | } 196 | 197 | static void sample_test_cfi(JNIEnv *env, jobject thiz, jboolean remote_unwind) { 198 | (void)env, (void)thiz; 199 | 200 | sample_test(SAMPLE_SOLUTION_CFI, remote_unwind, JNI_TRUE, JNI_FALSE); 201 | } 202 | 203 | static void sample_test_fp(JNIEnv *env, jobject thiz, jboolean with_context, jboolean signal_interrupted) { 204 | (void)env, (void)thiz; 205 | 206 | sample_test(SAMPLE_SOLUTION_FP, JNI_FALSE, with_context, signal_interrupted); 207 | } 208 | 209 | static void sample_test_eh(JNIEnv *env, jobject thiz, jboolean with_context, jboolean signal_interrupted) { 210 | (void)env, (void)thiz; 211 | 212 | sample_test(SAMPLE_SOLUTION_EH, JNI_FALSE, with_context, signal_interrupted); 213 | } 214 | 215 | static JNINativeMethod sample_jni_methods[] = {{"nativeTestCfi", "(Z)V", (void *)sample_test_cfi}, 216 | {"nativeTestFp", "(ZZ)V", (void *)sample_test_fp}, 217 | {"nativeTestEh", "(ZZ)V", (void *)sample_test_eh}}; 218 | 219 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 220 | JNIEnv *env; 221 | jclass cls; 222 | 223 | (void)reserved; 224 | 225 | if (NULL == vm) return JNI_ERR; 226 | if (JNI_OK != (*vm)->GetEnv(vm, (void **)&env, SAMPLE_JNI_VERSION)) return JNI_ERR; 227 | if (NULL == env || NULL == *env) return JNI_ERR; 228 | if (NULL == (cls = (*env)->FindClass(env, SAMPLE_JNI_CLASS_NAME))) return JNI_ERR; 229 | if (0 != (*env)->RegisterNatives(env, cls, sample_jni_methods, 230 | sizeof(sample_jni_methods) / sizeof(sample_jni_methods[0]))) 231 | return JNI_ERR; 232 | 233 | sample_signal_register(); 234 | 235 | return SAMPLE_JNI_VERSION; 236 | } 237 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/java/io/github/hexhacking/xunwind/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.hexhacking.xunwind.sample; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.os.Process; 10 | 11 | import io.github.hexhacking.xunwind.XUnwind; 12 | 13 | public class MainActivity extends AppCompatActivity { 14 | 15 | private final String TAG = "xunwind_tag"; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_main); 21 | } 22 | 23 | public void onTestClick(View view) { 24 | int pid = Process.myPid(); 25 | int tid = Process.myTid(); 26 | 27 | int id = view.getId(); 28 | 29 | if (id == R.id.cifLocalThreadButton) { 30 | Log.i(TAG, String.format(">>> CIF: Local Process (pid: %d), Single Thread (tid: %d) <<<", pid, tid)); 31 | XUnwind.logLocalCurrentThread(TAG, Log.INFO); 32 | Log.i(TAG, ">>> finished <<<"); 33 | } else if (id == R.id.cifLocalThreadWithUContextButton) { 34 | Log.i(TAG, String.format(">>> CIF: Local Process (pid: %d), Single Thread with UContext (tid: %d) <<<", pid, tid)); 35 | NativeSample.testCfi(false); 36 | Log.i(TAG, ">>> finished <<<"); 37 | } else if (id == R.id.cfiLocalALLButton) { 38 | Log.i(TAG, String.format(">>> CIF: Local Process (pid: %d), All Threads <<<", pid)); 39 | XUnwind.logLocalAllThread(TAG, Log.INFO, " "); 40 | Log.i(TAG, ">>> finished <<<"); 41 | } else if (id == R.id.cifRemoteThreadButton) { 42 | Log.i(TAG, String.format(">>> CIF: Remote Process (pid: %d), Single Thread (tid: %d) <<<", pid, tid)); 43 | startService(new Intent(this, MyService.class).putExtra("pid", pid).putExtra("tid", tid)); 44 | } else if (id == R.id.cfiRemoteThreadWithUContextButton) { 45 | Log.i(TAG, String.format(">>> CIF: Remote Process (pid: %d), Single Thread with UContext (tid: %d) <<<", pid, tid)); 46 | NativeSample.testCfi(true); 47 | Log.i(TAG, ">>> finished <<<"); 48 | } else if (id == R.id.cfiRemoteALLButton) { 49 | Log.i(TAG, String.format(">>> CIF: Remote Process (pid: %d), All Threads <<<", pid)); 50 | startService(new Intent(this, MyService.class).putExtra("pid", pid)); 51 | } else if (id == R.id.fpLocalThreadButton) { 52 | Log.i(TAG, String.format(">>> FP: Local Process (pid: %d), Current Thread (tid: %d) <<<", pid, pid)); 53 | NativeSample.testFP(false, false); 54 | Log.i(TAG, ">>> finished <<<"); 55 | } else if (id == R.id.fpLocalThreadWithUContextButton) { 56 | Log.i(TAG, String.format(">>> FP: Local Process (pid: %d), Current Thread with UContext (tid: %d) <<<", pid, pid)); 57 | NativeSample.testFP(true, false); 58 | Log.i(TAG, ">>> finished <<<"); 59 | } else if (id == R.id.fpLocalThreadSignalInterruptedButton) { 60 | Log.i(TAG, String.format(">>> FP: Local Process (pid: %d), Current Thread (Signal Interrupted) (tid: %d) <<<", pid, pid)); 61 | NativeSample.testFP(false, true); 62 | Log.i(TAG, ">>> finished <<<"); 63 | } else if (id == R.id.fpLocalThreadWithUContextSignalInterruptedButton) { 64 | Log.i(TAG, String.format(">>> FP: Local Process (pid: %d), Current Thread with UContext (Signal Interrupted) (tid: %d) <<<", pid, pid)); 65 | NativeSample.testFP(true, true); 66 | Log.i(TAG, ">>> finished <<<"); 67 | } else if (id == R.id.ehLocalThreadButton) { 68 | Log.i(TAG, String.format(">>> EH: Local Process (pid: %d), Current Thread (tid: %d) <<<", pid, pid)); 69 | NativeSample.testEH(false, false); 70 | Log.i(TAG, ">>> finished <<<"); 71 | } else if (id == R.id.ehLocalThreadWithUContextButton) { 72 | Log.i(TAG, String.format(">>> EH: Local Process (pid: %d), Current Thread with UContext (tid: %d) <<<", pid, pid)); 73 | NativeSample.testEH(true, false); 74 | Log.i(TAG, ">>> finished <<<"); 75 | } else if (id == R.id.ehLocalThreadSignalInterruptedButton) { 76 | Log.i(TAG, String.format(">>> EH: Local Process (pid: %d), Current Thread (Signal Interrupted) (tid: %d) <<<", pid, pid)); 77 | NativeSample.testEH(false, true); 78 | Log.i(TAG, ">>> finished <<<"); 79 | } else if (id == R.id.ehLocalThreadWithUContextSignalInterruptedButton) { 80 | Log.i(TAG, String.format(">>> EH: Local Process (pid: %d), Current Thread with UContext (Signal Interrupted) (tid: %d) <<<", pid, pid)); 81 | NativeSample.testEH(true, true); 82 | Log.i(TAG, ">>> finished <<<"); 83 | } 84 | } 85 | 86 | @Override 87 | protected void onDestroy() { 88 | super.onDestroy(); 89 | stopService(new Intent(this, MyService.class)); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/java/io/github/hexhacking/xunwind/sample/MyCustomApplication.java: -------------------------------------------------------------------------------- 1 | package io.github.hexhacking.xunwind.sample; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import io.github.hexhacking.xunwind.XUnwind; 7 | 8 | public class MyCustomApplication extends Application { 9 | 10 | @Override 11 | protected void attachBaseContext(Context base) { 12 | super.attachBaseContext(base); 13 | 14 | XUnwind.init(); 15 | 16 | System.loadLibrary("sample"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/java/io/github/hexhacking/xunwind/sample/MyService.java: -------------------------------------------------------------------------------- 1 | package io.github.hexhacking.xunwind.sample; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | import android.util.Log; 7 | 8 | import io.github.hexhacking.xunwind.XUnwind; 9 | 10 | public class MyService extends Service { 11 | 12 | private final String TAG = "xunwind_tag"; 13 | 14 | public MyService() { 15 | } 16 | 17 | @Override 18 | public void onCreate() { 19 | } 20 | 21 | @Override 22 | public int onStartCommand(Intent intent, int flags, int startId) { 23 | int pid = intent.getIntExtra("pid", -1); 24 | int tid = intent.getIntExtra("tid", -1); 25 | 26 | if (tid >= 0) { 27 | XUnwind.logRemoteThread(pid, tid, TAG, Log.INFO); 28 | } else { 29 | XUnwind.logRemoteAllThread(pid, TAG, Log.INFO, " "); 30 | } 31 | Log.i(TAG, ">>> finished <<<"); 32 | 33 | return START_NOT_STICKY; 34 | } 35 | 36 | @Override 37 | public IBinder onBind(Intent intent) { 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/java/io/github/hexhacking/xunwind/sample/NativeSample.java: -------------------------------------------------------------------------------- 1 | package io.github.hexhacking.xunwind.sample; 2 | 3 | public class NativeSample { 4 | public static void testCfi(boolean remoteUnwind) { 5 | nativeTestCfi(remoteUnwind); 6 | } 7 | 8 | public static void testFP(boolean withContext, boolean signalInterrupted) { 9 | nativeTestFp(withContext, signalInterrupted); 10 | } 11 | 12 | public static void testEH(boolean withContext, boolean signalInterrupted) { 13 | nativeTestEh(withContext, signalInterrupted); 14 | } 15 | 16 | private static native void nativeTestCfi(boolean remoteUnwind); 17 | private static native void nativeTestFp(boolean withContext, boolean signalInterrupted); 18 | private static native void nativeTestEh(boolean withContext, boolean signalInterrupted); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /xunwind_sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 17 | 18 | 32 | 33 | 46 | 47 |