├── .gitignore
├── LICENSE.md
├── README.md
├── src
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── .gitignore
│ │ ├── build.gradle
│ │ ├── jni
│ │ │ ├── NativeTest.c
│ │ │ └── xmake.lua
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── androidTest
│ │ │ └── java
│ │ │ │ └── byopen
│ │ │ │ └── sample
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ └── byopen
│ │ │ │ │ └── sample
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ └── NativeTest.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
│ │ │ │ └── styles.xml
│ │ │ └── test
│ │ │ └── java
│ │ │ └── byopen
│ │ │ └── sample
│ │ │ └── ExampleUnitTest.java
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── lib
│ │ ├── .gitignore
│ │ ├── build.gradle
│ │ ├── consumer-rules.pro
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── androidTest
│ │ │ └── java
│ │ │ │ └── dyopen
│ │ │ │ └── lib
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── dyopen
│ │ │ │ └── lib
│ │ │ │ └── SystemLoader.java
│ │ │ └── test
│ │ │ └── java
│ │ │ └── dyopen
│ │ │ └── lib
│ │ │ └── ExampleUnitTest.java
│ └── settings.gradle
├── demo
│ ├── demo.c
│ └── xmake.lua
└── native
│ ├── byopen.h
│ ├── byopen_android.c
│ ├── byopen_macho.c
│ ├── prefix.h
│ └── xmake.lua
└── xmake.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | # build cache
2 | .xmake
3 | .gradle
4 | build/
5 |
6 | # MacOS cache
7 | .DS_Store
8 |
9 | # Other cache
10 | .idea
11 | local.properties
12 |
13 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright (C) 2020-present, TBOOX Open Source Group
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
204 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
dyOpen
3 |
A dlopen library that bypasses mobile system limitation
4 |
5 |
6 | ## 简介
7 |
8 | byOpen是一个绕过移动端系统限制的增强版dlfunctions库。
9 |
10 | ## 支持特性
11 |
12 | ### Android
13 |
14 | 支持App中加载和使用Android系统库接口(即使maps中还没有被加载也支持)。
15 |
16 | Android 7以上dlopen, System.load都是被限制调用的,虽然目前网上有[Nougat_dlfunctions](https://github.com/avs333/Nougat_dlfunctions)等库通过从maps中找so库来绕过加载限制。
17 |
18 | 不过对于app中还没被加载到maps的so库,这种方式就不行了。
19 |
20 | 而byOpen不仅支持fake dlopen方式从maps加载,还可以将还没加载到maps的so库绕过系统限制强行加载进来使用,实现更加通用化得dlopen。
21 |
22 | 注:目前的实现方式理论上还是比较通用的,至少我这Android 10上测试ok,但还没完整详细测试过,是否使用请自行评估。
23 |
24 | #### 相关原理
25 |
26 | 具体实现原理还是比较简单的,主要还是借鉴了[一种绕过Android P对非SDK接口限制的简单方法](http://weishu.me/2018/06/07/free-reflection-above-android-p/)的思想和实现方式。
27 |
28 | 虽然这篇文章中主要目的是为了绕过hide api,不过它里面使用的将自己假装成系统调用的方式,一样可以用到`System.loadLibrary`上去,让系统以为是系统自身在调用`System.loadLibrary`
29 |
30 | 从而绕过Android N的classloader-namespace限制,将系统/system/lib中任意so库加载到maps中,然后再通过fake dlopen的方式去dlsym。
31 |
32 | #### 增强版fake dlopen
33 |
34 | 关于fake dlopen的方式实现,网上已有很多实现,比如:
35 |
36 | * [Nougat_dlfunctions](https://github.com/avs333/Nougat_dlfunctions)
37 | * [Enhanced_dlfunctions](https://github.com/turing-technician/Enhanced_dlfunctions)
38 |
39 | byOpen参考了里面的实现,重新实现了一遍,并且做了一些小改进:
40 |
41 | * 不在/proc/self/maps中的系统库,也能绕过限制强行加载进来使用
42 | * 除了从.dynsym中检索符号,还支持从.symtab中检索符号(参考:Enhanced_dlfunctions,顺带修复了里面的一些bug)
43 | * 整个dlopen过程只有一次malloc分配(省去整个符号表的内存分配和copy)
44 |
45 | #### Android例子
46 |
47 | Android相关测试App例子在:[Android Sample](https://github.com/hack0z/byOpen/tree/master/src/android)
48 |
49 | 注:目前自带的App测试例子里面的系统库我写死了,有些系统版本上有可能不存在,请先改成用户自己的库和符号名,再编译测试
50 |
51 | ```java
52 | public class MainActivity extends AppCompatActivity {
53 | private static final String SYSTEM_LIBRARY = "curl";
54 | private static final String SYMBOL_NAME = "curl_version";
55 | ```
56 |
57 | 除了Native版本dlopen接口,byOpen额外提供了java版本的[System.loadLibrary](https://github.com/hack0z/byOpen/blob/master/src/android/lib/src/main/java/dyopen/lib/SystemLoader.java)接口在java层直接绕过系统库加载。
58 |
59 | 关键代码如下:
60 |
61 | ```java
62 | static public boolean loadLibrary(String libraryName) {
63 | Method forName = Class.class.getDeclaredMethod("forName", String.class);
64 | Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
65 | Class> systemClass = (Class>) forName.invoke(null, "java.lang.System");
66 | Method loadLibrary = (Method) getDeclaredMethod.invoke(systemClass, "loadLibrary", new Class[]{String.class});
67 | loadLibrary.invoke(systemClass, libraryName);
68 | }
69 | ```
70 |
71 | 而native版本的[dlopen_android.c](https://github.com/hack0z/byOpen/blob/master/src/native/byopen_android.c)实现中,我将这段绕过的系统加载的方式,通过jni重新实现了一遍,然后和fake dlopen无缝结合到了一起。
72 |
73 | ### iOS
74 |
75 | 虽然ios可以直接使用dlopen,但是审核上会有风险,苹果有可能会对提交AppStore的app扫描相关dlopen/dlsym等调用,来判断是否存在一些敏感的私有调用。
76 |
77 | 为了在通过调用一些私有接口的时候避免被苹果检测到,byOpen也通过自己实现dlopen/dlsym直接从已经加载进来的images列表里面直接查找对应symbol地址来调用。
78 |
79 | 当然,为了更加安全,相关调用的库符号硬编码字符串等,用户可以自行做层变换加密,不要直接编译进app。
80 |
81 | ## 接口用法
82 |
83 | 相关静态库和接口在:[dlopen.h](https://github.com/hack0z/byOpen/blob/master/src/native/byopen.h)
84 |
85 | 相关使用方式跟原生dlopen完全相同:
86 |
87 | ```c
88 | typedef by_char_t const* (*curl_version_t)();
89 | by_pointer_t handle = by_dlopen("libcurl.so", BY_RTLD_LAZY);
90 | if (handle)
91 | {
92 | by_pointer_t addr = by_dlsym(handle, "curl_version");
93 | if (addr)
94 | {
95 | curl_version_t curl_version = (curl_version_t)addr;
96 | by_print("curl_version: %s", curl_version());
97 | }
98 | by_dlclose(handle);
99 | }
100 | ```
101 |
102 | ## 编译
103 |
104 | 编译需要先安装:[xmake](https://github.com/xmake-io/xmake)
105 |
106 | ### Android
107 |
108 | #### 直接编译库
109 |
110 | ```console
111 | $ xmake f -p android --ndk=~/file/android-ndk-r20b
112 | $ xmake
113 | ```
114 |
115 | #### 通过gradle编译测试Apk
116 |
117 | ```console
118 | $ cd src/android
119 | $ ./gradlew app:assembleDebug
120 | ```
121 |
122 | #### 通过xmake直接编译apk
123 |
124 | ```console
125 | $ xmake apk_build
126 | ```
127 |
128 | #### 通过xmake直接安装测试apk
129 |
130 | ```console
131 | $ xmake apk_test
132 | ```
133 |
134 | ### iOS
135 |
136 | #### 直接编译库
137 |
138 | ```console
139 | $ xmake f -p iphoneos -a [armv7|arm64]
140 | $ xmake
141 | ```
142 |
143 | ### MacOS
144 |
145 | 我们也可以在macOS下编译测试,也是支持的:
146 |
147 | ```console
148 | $ xmake
149 | $ xmake run
150 | ```
151 |
--------------------------------------------------------------------------------
/src/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/src/android/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | libs
3 |
--------------------------------------------------------------------------------
/src/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.tboox.gradle-xmake-plugin' version '1.1.3'
3 | }
4 | apply plugin: 'com.android.application'
5 |
6 | android {
7 | compileSdkVersion 29
8 |
9 | defaultConfig {
10 | applicationId "byopen.sample"
11 | minSdkVersion 19
12 | targetSdkVersion 29
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 |
18 | ndk {
19 | abiFilters "armeabi-v7a", "arm64-v8a"
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | externalNativeBuild {
31 | xmake {
32 | logLevel "warning"
33 | path "../../../xmake.lua"
34 | //buildMode "minsizerel"
35 | buildMode "debug"
36 | }
37 | }
38 | }
39 |
40 | dependencies {
41 | implementation fileTree(dir: "libs", include: ["*.jar"])
42 | implementation 'androidx.appcompat:appcompat:1.1.0'
43 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
44 | implementation project(path: ':lib')
45 | testImplementation 'junit:junit:4.12'
46 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/android/app/jni/NativeTest.c:
--------------------------------------------------------------------------------
1 | /*!A dlopen library that bypasses mobile system limitation
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * Copyright (C) 2020-present, TBOOX Open Source Group.
16 | *
17 | * @author ruki
18 | * @file NativeTest.c
19 | *
20 | */
21 |
22 | /* //////////////////////////////////////////////////////////////////////////////////////
23 | * includes
24 | */
25 | #include "byopen.h"
26 |
27 | /* //////////////////////////////////////////////////////////////////////////////////////
28 | * declaration
29 | */
30 | by_void_t by_jni_javavm_set(JavaVM* jvm, by_int_t jversion);
31 |
32 | /* //////////////////////////////////////////////////////////////////////////////////////
33 | * interfaces
34 | */
35 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* jvm, by_pointer_t reserved)
36 | {
37 | by_jni_javavm_set(jvm, JNI_VERSION_1_4);
38 | return JNI_VERSION_1_4;
39 | }
40 |
41 | JNIEXPORT jboolean Java_byopen_sample_NativeTest_loadLibrary(JNIEnv* env, jclass jthis, jstring libraryName, jstring symbolName)
42 | {
43 | by_bool_t ok = by_false;
44 | by_char_t const* libraryName_cstr = (*env)->GetStringUTFChars(env, libraryName, by_null);
45 | by_char_t const* symbolName_cstr = (*env)->GetStringUTFChars(env, symbolName, by_null);
46 | if (libraryName_cstr && symbolName_cstr)
47 | {
48 | by_pointer_t handle = by_dlopen(libraryName_cstr, BY_RTLD_LAZY);
49 | if (handle)
50 | {
51 | by_pointer_t addr = by_dlsym(handle, symbolName_cstr);
52 | if (addr)
53 | {
54 | #if 0
55 | // test libcurl/curl_version()
56 | typedef char* (*func_t)();
57 | func_t func = (func_t)addr;
58 | by_trace("curl_version: %s", func());
59 | #endif
60 |
61 | // load ok
62 | ok = by_true;
63 | }
64 | by_dlclose(handle);
65 | }
66 | (*env)->ReleaseStringUTFChars(env, libraryName, libraryName_cstr);
67 | (*env)->ReleaseStringUTFChars(env, symbolName, symbolName_cstr);
68 | }
69 | return ok;
70 | }
71 | JNIEXPORT jboolean Java_byopen_sample_NativeTest_validFromMaps(JNIEnv* env, jclass jthis, jstring libraryName)
72 | {
73 | by_bool_t found = by_false;
74 | by_char_t const* libraryName_cstr = (*env)->GetStringUTFChars(env, libraryName, by_null);
75 | if (libraryName_cstr)
76 | {
77 | FILE* f = fopen("/proc/self/maps", "r");
78 | if (f)
79 | {
80 | char line[512];
81 | while (fgets(line, sizeof(line), f))
82 | {
83 | if (strstr(line, libraryName_cstr))
84 | {
85 | found = by_true;
86 | break;
87 | }
88 | }
89 | fclose(f);
90 | }
91 | (*env)->ReleaseStringUTFChars(env, libraryName, libraryName_cstr);
92 | }
93 | return found;
94 | }
95 |
--------------------------------------------------------------------------------
/src/android/app/jni/xmake.lua:
--------------------------------------------------------------------------------
1 | target("testjni")
2 | set_kind("shared")
3 | add_deps("byopen")
4 | add_files("*.c")
5 |
6 |
--------------------------------------------------------------------------------
/src/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/src/android/app/src/androidTest/java/byopen/sample/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package byopen.sample;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("byopen.sample", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/android/app/src/main/java/byopen/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package byopen.sample;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.os.Bundle;
6 | import android.util.Log;
7 | import android.view.View;
8 | import android.widget.Button;
9 |
10 | import dyopen.lib.SystemLoader;
11 |
12 | public class MainActivity extends AppCompatActivity {
13 |
14 | private static final String TAG = "byOpen";
15 | private static final String SYSTEM_LIBRARY = "curl";
16 | private static final String SYMBOL_NAME = "curl_version";
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_main);
22 |
23 | final Button origin_loadLibraryBtn = (Button) findViewById(R.id.origin_loadLibrary);
24 | origin_loadLibraryBtn.setOnClickListener(new View.OnClickListener() {
25 | @Override
26 | public void onClick(View v) {
27 | try {
28 | System.loadLibrary(SYSTEM_LIBRARY);
29 | if (NativeTest.validFromMaps(SYSTEM_LIBRARY)) {
30 | origin_loadLibraryBtn.setText("load ok!");
31 | } else {
32 | origin_loadLibraryBtn.setText("load failed!");
33 | }
34 | } catch (Throwable e) {
35 | origin_loadLibraryBtn.setText("load failed!");
36 | }
37 | }
38 | });
39 |
40 | final Button byopen_loadLibraryBtn = (Button) findViewById(R.id.byopen_loadLibrary);
41 | byopen_loadLibraryBtn.setOnClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View v) {
44 | if (SystemLoader.loadLibrary(SYSTEM_LIBRARY) && NativeTest.validFromMaps(SYSTEM_LIBRARY)) {
45 | byopen_loadLibraryBtn.setText("load ok!");
46 | } else {
47 | byopen_loadLibraryBtn.setText("load failed!");
48 | }
49 | }
50 | });
51 |
52 | final Button dlopenBtn = (Button) findViewById(R.id.byopen_dlopen);
53 | dlopenBtn.setOnClickListener(new View.OnClickListener() {
54 | @Override
55 | public void onClick(View v) {
56 | if (NativeTest.loadLibrary(SYSTEM_LIBRARY, SYMBOL_NAME) && NativeTest.validFromMaps(SYSTEM_LIBRARY)) {
57 | dlopenBtn.setText("load ok!");
58 | } else {
59 | dlopenBtn.setText("load failed!");
60 | }
61 | }
62 | });
63 |
64 | final Button validBtn = (Button) findViewById(R.id.valid_from_maps);
65 | validBtn.setOnClickListener(new View.OnClickListener() {
66 | @Override
67 | public void onClick(View v) {
68 | if (NativeTest.validFromMaps(SYSTEM_LIBRARY)) {
69 | validBtn.setText("valid ok!");
70 | } else {
71 | validBtn.setText("valid failed!");
72 | }
73 | }
74 | });
75 | }
76 | }
--------------------------------------------------------------------------------
/src/android/app/src/main/java/byopen/sample/NativeTest.java:
--------------------------------------------------------------------------------
1 | package byopen.sample;
2 |
3 | public class NativeTest {
4 | static {
5 | System.loadLibrary("testjni");
6 | }
7 | public static native boolean loadLibrary(String libraryName, String symbolName);
8 | public static native boolean validFromMaps(String libraryName);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/src/android/app/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 |
--------------------------------------------------------------------------------
/src/android/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
21 |
22 |
27 |
28 |
33 |
34 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
--------------------------------------------------------------------------------
/src/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ByOpenSample
3 |
--------------------------------------------------------------------------------
/src/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/src/android/app/src/test/java/byopen/sample/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package byopen.sample;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:4.0.0"
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
--------------------------------------------------------------------------------
/src/android/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
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
--------------------------------------------------------------------------------
/src/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/src/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jul 02 14:50:32 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/src/android/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 |
--------------------------------------------------------------------------------
/src/android/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 |
--------------------------------------------------------------------------------
/src/android/lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/src/android/lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 29
5 |
6 | defaultConfig {
7 | minSdkVersion 19
8 | targetSdkVersion 29
9 | versionCode 1
10 | versionName "1.0"
11 |
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | consumerProguardFiles "consumer-rules.pro"
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(dir: "libs", include: ["*.jar"])
26 | implementation 'androidx.appcompat:appcompat:1.1.0'
27 | testImplementation 'junit:junit:4.12'
28 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
29 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
30 |
31 | }
--------------------------------------------------------------------------------
/src/android/lib/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hack0z/byopen/ddd9c2d66e5383fdcc266290d5822fff1dd21e6d/src/android/lib/consumer-rules.pro
--------------------------------------------------------------------------------
/src/android/lib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/src/android/lib/src/androidTest/java/dyopen/lib/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package dyopen.lib;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("dyopen.lib.test", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/android/lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | /
5 |
--------------------------------------------------------------------------------
/src/android/lib/src/main/java/dyopen/lib/SystemLoader.java:
--------------------------------------------------------------------------------
1 | /*!A dlopen library that bypasses mobile system limitation
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * Copyright (C) 2020-present, TBOOX Open Source Group.
16 | *
17 | * @author ruki
18 | * @file SystemLoader.java
19 | *
20 | */
21 |
22 | package dyopen.lib;
23 |
24 | import android.os.Build;
25 | import android.util.Log;
26 |
27 | import java.lang.reflect.Method;
28 |
29 | import static android.os.Build.VERSION.SDK_INT;
30 |
31 | public class SystemLoader {
32 |
33 | private static final String TAG = "byOpen";
34 |
35 | static public boolean load(String libraryPath) {
36 | if (libraryPath == null) {
37 | return false;
38 | }
39 | try {
40 | System.load(libraryPath);
41 | return true;
42 | } catch (Throwable e) {
43 | if (SDK_INT >= Build.VERSION_CODES.P) {
44 | try {
45 |
46 | Method forName = Class.class.getDeclaredMethod("forName", String.class);
47 | Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
48 | Class> systemClass = (Class>) forName.invoke(null, "java.lang.System");
49 | Method load = (Method) getDeclaredMethod.invoke(systemClass, "load", new Class[]{String.class});
50 | load.invoke(systemClass, libraryPath);
51 | return true;
52 |
53 | } catch (Throwable ex) {
54 | Log.e(TAG, "load library failed:", ex);
55 | }
56 | } else {
57 | Log.e(TAG, "load library failed:", e);
58 | }
59 | }
60 | return false;
61 | }
62 |
63 | static public boolean loadLibrary(String libraryName) {
64 | if (libraryName == null) {
65 | return false;
66 | }
67 | try {
68 | System.loadLibrary(libraryName);
69 | return true;
70 | } catch (Throwable e) {
71 | if (SDK_INT >= Build.VERSION_CODES.P) {
72 | try {
73 |
74 | Method forName = Class.class.getDeclaredMethod("forName", String.class);
75 | Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
76 | Class> systemClass = (Class>) forName.invoke(null, "java.lang.System");
77 | Method loadLibrary = (Method) getDeclaredMethod.invoke(systemClass, "loadLibrary", new Class[]{String.class});
78 | loadLibrary.invoke(systemClass, libraryName);
79 | return true;
80 |
81 | } catch (Throwable ex) {
82 | Log.e(TAG, "load library failed:", ex);
83 | }
84 | } else {
85 | Log.e(TAG, "load library failed:", e);
86 | }
87 | }
88 | return false;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/android/lib/src/test/java/dyopen/lib/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package dyopen.lib;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':lib'
2 | include ':app'
3 | rootProject.name = "ByOpenSample"
--------------------------------------------------------------------------------
/src/demo/demo.c:
--------------------------------------------------------------------------------
1 | /*!A dlopen library that bypasses mobile system limitation
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * Copyright (C) 2020-present, TBOOX Open Source Group.
16 | *
17 | * @author ruki
18 | * @file demo.c
19 | *
20 | */
21 |
22 | /* //////////////////////////////////////////////////////////////////////////////////////
23 | * includes
24 | */
25 | #include "byopen.h"
26 |
27 | /* //////////////////////////////////////////////////////////////////////////////////////
28 | * implementation
29 | */
30 | by_int_t main(by_int_t argc, by_char_t** argv)
31 | {
32 | // only for macosx, iphoneos
33 | by_char_t const* libname = "/usr/lib/libz.1.dylib";
34 | by_char_t const* libfunc = "_zlibVersion";
35 | by_pointer_t handle = by_dlopen(libname, BY_RTLD_LAZY);
36 | if (handle)
37 | {
38 | by_pointer_t addr = by_dlsym(handle, libfunc);
39 | if (addr)
40 | {
41 | // found
42 | by_print("dlopen(%s): %s -> %p", libname, libfunc, addr);
43 |
44 | // call function
45 | typedef by_char_t const* (*zlibVersion_t)();
46 | zlibVersion_t zlibVersion = (zlibVersion_t)addr;
47 | by_print("zlibVersion: %s", zlibVersion());
48 | }
49 | by_dlclose(handle);
50 | }
51 | return 0;
52 | }
53 |
--------------------------------------------------------------------------------
/src/demo/xmake.lua:
--------------------------------------------------------------------------------
1 | target("demo")
2 | set_kind("binary")
3 | add_deps("byopen")
4 | add_files("*.c")
5 |
--------------------------------------------------------------------------------
/src/native/byopen.h:
--------------------------------------------------------------------------------
1 | /*!A dlopen library that bypasses mobile system limitation
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * Copyright (C) 2020-present, TBOOX Open Source Group.
16 | *
17 | * @author ruki
18 | * @file byopen.h
19 | *
20 | */
21 | #ifndef BY_BYOPEN_H
22 | #define BY_BYOPEN_H
23 |
24 | #ifdef __cplusplus
25 | extern "C" {
26 | #endif
27 |
28 | /* //////////////////////////////////////////////////////////////////////////////////////
29 | * includes
30 | */
31 | #include "prefix.h"
32 |
33 | /* //////////////////////////////////////////////////////////////////////////////////////
34 | * types
35 | */
36 |
37 | /// the dlopen flag enum
38 | typedef enum __by_dlopen_flag_e
39 | {
40 | BY_RTLD_LAZY = 1
41 | , BY_RTLD_NOW = 2
42 |
43 | }by_dlopen_flag_e;
44 |
45 | /* //////////////////////////////////////////////////////////////////////////////////////
46 | * interfaces
47 | */
48 |
49 | /*! The function dlopen() loads the dynamic library and returns an opaque "handle".
50 | *
51 | * @param filename the dynamic library file named by the null-terminated string filename
52 | * @param flag the load flag
53 | *
54 | * @return the dynamic library handle
55 | */
56 | by_pointer_t by_dlopen(by_char_t const* filename, by_int_t flag);
57 |
58 | /*! get the address where that symbol is loaded into memory
59 | *
60 | * @param handle the dynamic library handle
61 | *
62 | * @return the symbol address
63 | */
64 | by_pointer_t by_dlsym(by_pointer_t handle, by_char_t const* symbol);
65 |
66 | /*! It decrements the reference count on the dynamic library handle handle.
67 | * If the reference count drops to zero and no other loaded libraries use symbols in it, then the dynamic library is unloaded.
68 | *
69 | * @param handle the dynamic library handle
70 | *
71 | * @return 0 on success, and nonzero on error
72 | */
73 | by_int_t by_dlclose(by_pointer_t handle);
74 |
75 | #ifdef __cplusplus
76 | }
77 | #endif
78 | #endif
79 |
--------------------------------------------------------------------------------
/src/native/byopen_android.c:
--------------------------------------------------------------------------------
1 | /*!A dlopen library that bypasses mobile system limitation
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * Copyright (C) 2020-present, TBOOX Open Source Group.
16 | *
17 | * @author ruki
18 | * @file byopen_android.c
19 | *
20 | */
21 |
22 | /* //////////////////////////////////////////////////////////////////////////////////////
23 | * includes
24 | */
25 | #include "byopen.h"
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 |
39 | /* //////////////////////////////////////////////////////////////////////////////////////
40 | * macros
41 | */
42 |
43 | // the fake dlopen magic
44 | #define BY_FAKE_DLCTX_MAGIC (0xfaddfadd)
45 |
46 | /* g_dl_mutex in linker
47 | *
48 | * @see http://androidxref.com/5.0.0_r2/xref/bionic/linker/dlfcn.cpp#32
49 | */
50 | #define BY_LINKER_MUTEX "__dl__ZL10g_dl_mutex"
51 |
52 | // the linker name
53 | #ifndef __LP64__
54 | # define BY_LINKER_NAME "linker"
55 | #else
56 | # define BY_LINKER_NAME "linker64"
57 | #endif
58 |
59 | /* //////////////////////////////////////////////////////////////////////////////////////
60 | * types
61 | */
62 |
63 | // the dynamic library context type for fake dlopen
64 | typedef struct _by_fake_dlctx_t
65 | {
66 | // magic, mark handle for fake dlopen
67 | by_uint32_t magic;
68 |
69 | // the load bias address of the dynamic library
70 | by_pointer_t biasaddr;
71 |
72 | // the .dynsym and .dynstr sections
73 | by_pointer_t dynstr;
74 | by_pointer_t dynsym;
75 | by_int_t dynsym_num;
76 |
77 | // the .symtab and .strtab sections
78 | by_pointer_t strtab;
79 | by_pointer_t symtab;
80 | by_int_t symtab_num;
81 |
82 | // the file data and size
83 | by_pointer_t filedata;
84 | by_size_t filesize;
85 |
86 | }by_fake_dlctx_t, *by_fake_dlctx_ref_t;
87 |
88 | /* //////////////////////////////////////////////////////////////////////////////////////
89 | * globals
90 | */
91 |
92 | // the jni environment on tls
93 | __thread JNIEnv* g_tls_jnienv = by_null;
94 | static JavaVM* g_jvm = by_null;
95 | static by_int_t g_jversion = JNI_VERSION_1_4;
96 | static pthread_mutex_t* g_linker_mutex = by_null;
97 |
98 | /* //////////////////////////////////////////////////////////////////////////////////////
99 | * declaration
100 | */
101 | extern __attribute((weak)) by_int_t dl_iterate_phdr(by_int_t (*)(struct dl_phdr_info*, size_t, by_pointer_t), by_pointer_t);
102 |
103 | /* //////////////////////////////////////////////////////////////////////////////////////
104 | * declaration
105 | */
106 |
107 | // weak symbol import
108 | by_void_t __system_property_read_callback(
109 | prop_info const* info,
110 | by_void_t (*callback)(by_pointer_t cookie, by_char_t const* name, by_char_t const* value, uint32_t serial),
111 | by_void_t* cookie) __attribute__((weak));
112 |
113 | by_int_t __system_property_get(by_char_t const* name, by_char_t* value) __attribute__((weak));
114 |
115 | /* //////////////////////////////////////////////////////////////////////////////////////
116 | * private implementation
117 | */
118 |
119 | /* Technical note regarding reading system properties.
120 | *
121 | * Try to use the new __system_property_read_callback API that appeared in
122 | * Android O / API level 26 when available. Otherwise use the deprecated
123 | * __system_property_get function.
124 | *
125 | * For more technical details from an NDK maintainer, see:
126 | * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17
127 | */
128 |
129 | // callback used with __system_property_read_callback.
130 | static by_void_t by_rt_prop_read_int(by_pointer_t cookie, by_char_t const* name, by_char_t const* value, uint32_t serial)
131 | {
132 | *(by_int_t *)cookie = atoi(value);
133 | (by_void_t)name;
134 | (by_void_t)serial;
135 | }
136 |
137 | // read process output
138 | static by_int_t by_rt_process_read(by_char_t const* cmd, by_char_t* data, by_size_t maxn)
139 | {
140 | by_int_t n = 0;
141 | FILE* p = popen(cmd, "r");
142 | if (p)
143 | {
144 | by_char_t buf[256] = {0};
145 | by_char_t* pos = data;
146 | by_char_t* end = data + maxn;
147 | while (!feof(p))
148 | {
149 | if (fgets(buf, sizeof(buf), p))
150 | {
151 | by_int_t len = strlen(buf);
152 | if (pos + len < end)
153 | {
154 | memcpy(pos, buf, len);
155 | pos += len;
156 | n += len;
157 | }
158 | }
159 | }
160 |
161 | *pos = '\0';
162 | pclose(p);
163 | }
164 | return n;
165 | }
166 |
167 | // get system property integer
168 | static by_int_t by_rt_system_property_get_int(by_char_t const* name)
169 | {
170 | // check
171 | by_assert_and_check_return_val(name, -1);
172 |
173 | by_int_t result = 0;
174 | if (__system_property_read_callback)
175 | {
176 | struct prop_info const* info = __system_property_find(name);
177 | if (info) __system_property_read_callback(info, &by_rt_prop_read_int, &result);
178 | }
179 | else if (__system_property_get)
180 | {
181 | by_char_t value[PROP_VALUE_MAX] = {0};
182 | if (__system_property_get(name, value) >= 1)
183 | result = atoi(value);
184 | }
185 | else
186 | {
187 | by_char_t cmd[256];
188 | by_char_t value[PROP_VALUE_MAX];
189 | snprintf(cmd, sizeof(cmd), "getprop %s", name);
190 | if (by_rt_process_read(cmd, value, sizeof(value)) > 1)
191 | result = atoi(value);
192 | }
193 | return result;
194 | }
195 |
196 | static by_int_t by_rt_api_level()
197 | {
198 | static by_int_t s_api_level = -1;
199 | if (s_api_level < 0)
200 | s_api_level = by_rt_system_property_get_int("ro.build.version.sdk");
201 | return s_api_level;
202 | }
203 |
204 | // find the load bias address from the base address
205 | static by_pointer_t by_fake_find_biasaddr_from_baseaddr(by_pointer_t baseaddr)
206 | {
207 | // check
208 | by_assert_and_check_return_val(baseaddr, by_null);
209 |
210 | // find load bias from program header
211 | ElfW(Ehdr)* ehdr = (ElfW(Ehdr)*)baseaddr;
212 | ElfW(Phdr) const* dlpi_phdr = (ElfW(Phdr) const*)(baseaddr + ehdr->e_phoff);
213 | by_int_t dlpi_phnum = ehdr->e_phnum;
214 | uintptr_t min_vaddr = UINTPTR_MAX;
215 | for (by_int_t i = 0; i < dlpi_phnum; i++)
216 | {
217 | ElfW(Phdr) const* phdr = &(dlpi_phdr[i]);
218 | if (PT_LOAD == phdr->p_type)
219 | {
220 | if (min_vaddr > phdr->p_vaddr)
221 | min_vaddr = phdr->p_vaddr;
222 | }
223 | }
224 | return min_vaddr != UINTPTR_MAX? baseaddr - min_vaddr : by_null;
225 | }
226 |
227 | // find the load bias address and real path from the maps
228 | static by_pointer_t by_fake_find_biasaddr_from_maps(by_char_t const* filename, by_char_t* realpath, by_size_t realmaxn)
229 | {
230 | // check
231 | by_assert_and_check_return_val(filename && realpath && realmaxn, by_null);
232 |
233 | // trace
234 | by_trace("find biasaddr of %s from maps", filename);
235 |
236 | // find it
237 | by_char_t line[512];
238 | by_char_t page_attr[10];
239 | by_pointer_t biasaddr = by_null;
240 | FILE* fp = fopen("/proc/self/maps", "r");
241 | if (fp)
242 | {
243 | while (fgets(line, sizeof(line), fp))
244 | {
245 | if (strstr(line, filename))
246 | {
247 | int pos = 0;
248 | uintptr_t start = 0;
249 | uintptr_t offset = 0;
250 | // 7372a68000-7372bc1000 --xp 000fe000 fd:06 39690571 /system/lib64/libandroid_runtime.so
251 | if (3 == sscanf(line, "%"SCNxPTR"-%*"SCNxPTR" %4s %"SCNxPTR" %*x:%*x %*d%n", &start, page_attr, &offset, &pos))
252 | {
253 | // check permission and offset
254 | if (page_attr[0] != 'r') continue;
255 | if (page_attr[3] != 'p') continue;
256 | if (0 != offset) continue;
257 |
258 | // get load bias address
259 | biasaddr = by_fake_find_biasaddr_from_baseaddr((by_pointer_t)start);
260 |
261 | // get real path
262 | if (filename[0] == '/')
263 | strlcpy(realpath, filename, realmaxn);
264 | else if (pos < sizeof(line))
265 | {
266 | by_char_t* p = line + pos;
267 | by_char_t* e = p + strlen(p);
268 | while (p < e && isspace((by_int_t)*p)) p++;
269 | while (p < e && isspace((by_int_t)(*(e - 1)))) e--;
270 | *e = '\0';
271 | if (p < e) strlcpy(realpath, p, realmaxn);
272 | else realpath[0] = '\0';
273 | }
274 | else realpath[0] = '\0';
275 |
276 | // trace
277 | by_trace("realpath: %s, biasaddr: %p found!", realpath, biasaddr);
278 | }
279 | break;
280 | }
281 | }
282 | fclose(fp);
283 | }
284 | return biasaddr;
285 | }
286 |
287 | // the callback of dl_iterate_phdr()
288 | static by_int_t by_fake_find_biasaddr_from_linker_cb(struct dl_phdr_info* info, size_t size, by_pointer_t udata)
289 | {
290 | // check
291 | by_cpointer_t* args = (by_cpointer_t*)udata;
292 | by_check_return_val(args, 1);
293 | by_check_return_val(info && info->dlpi_addr && info->dlpi_name && info->dlpi_name[0] != '\0', 0);
294 |
295 | // get filename
296 | by_char_t const* filename = by_null;
297 | by_char_t const* filepath = (by_char_t const*)args[0];
298 | by_assert_and_check_return_val(filepath, 1);
299 | if (filepath[0] == '/')
300 | {
301 | by_char_t const* p = filepath + strlen(filepath);
302 | while (p >= filepath && *p != '/')
303 | p--;
304 | if (p >= filepath && *p == '/') filename = p + 1;
305 | }
306 |
307 | // find library, we can also get full path of dlpi_name from maps
308 | by_pointer_t* pbiasaddr = (by_pointer_t*)&args[3];
309 | by_char_t* realpath = (by_char_t*)args[1];
310 | by_size_t realmaxn = (by_size_t)args[2];
311 | if ((filepath && strstr(info->dlpi_name, filepath)) ||
312 | (filename && !strcmp(info->dlpi_name, filename))) // dlpi_name ma ybe not full path, e.g. libart.so
313 | {
314 | // save load bias address
315 | *pbiasaddr = (by_pointer_t)info->dlpi_addr;
316 |
317 | // get real path
318 | if (filepath[0] == '/')
319 | strlcpy(realpath, filepath, realmaxn);
320 | else if (info->dlpi_name[0] == '/')
321 | strlcpy(realpath, info->dlpi_name, realmaxn);
322 | else
323 | {
324 | // we only find real path
325 | if (!by_fake_find_biasaddr_from_maps(filepath, realpath, realmaxn))
326 | realpath[0] = '\0';
327 | }
328 |
329 | // trace
330 | by_trace("realpath: %s, biasaddr: %p found!", realpath, (by_pointer_t)info->dlpi_addr);
331 |
332 | // found, stop it
333 | return 1;
334 | }
335 | return 0;
336 | }
337 |
338 | // find the load bias address and real path from the maps
339 | static by_pointer_t by_fake_find_biasaddr_from_linker(by_char_t const* filepath, by_char_t* realpath, by_size_t realmaxn)
340 | {
341 | // check
342 | by_assert_and_check_return_val(dl_iterate_phdr && filepath && realpath && realmaxn, by_null);
343 |
344 | // trace
345 | by_trace("find biasaddr of %s from linker", filepath);
346 |
347 | // find biasaddr
348 | by_cpointer_t args[4];
349 | args[0] = (by_cpointer_t)filepath;
350 | args[1] = (by_cpointer_t)realpath;
351 | args[2] = (by_cpointer_t)realmaxn;
352 | args[3] = by_null;
353 | if (g_linker_mutex) pthread_mutex_lock(g_linker_mutex);
354 | dl_iterate_phdr(by_fake_find_biasaddr_from_linker_cb, args);
355 | if (g_linker_mutex) pthread_mutex_unlock(g_linker_mutex);
356 | return (by_pointer_t)args[3];
357 | }
358 |
359 | // find the load bias address and real path
360 | static by_pointer_t by_fake_find_biasaddr(by_char_t const* filename, by_char_t* realpath, by_size_t realmaxn)
361 | {
362 | by_assert_and_check_return_val(filename && realpath, by_null);
363 | by_pointer_t biasaddr = by_null;
364 | if (dl_iterate_phdr && 0 != strcmp(filename, BY_LINKER_NAME))
365 | biasaddr = by_fake_find_biasaddr_from_linker(filename, realpath, realmaxn);
366 | if (!biasaddr)
367 | biasaddr = by_fake_find_biasaddr_from_maps(filename, realpath, realmaxn);
368 | return biasaddr;
369 | }
370 |
371 | // open map file
372 | static by_pointer_t by_fake_open_file(by_char_t const* filepath, by_size_t* pfilesize)
373 | {
374 | // check
375 | by_assert_and_check_return_val(filepath && pfilesize, by_null);
376 |
377 | // open it
378 | by_int_t fd = -1;
379 | by_pointer_t filedata = by_null;
380 | do
381 | {
382 | // open file
383 | fd = open(filepath, O_RDONLY | O_CLOEXEC);
384 | if (fd < 0 && errno == EINTR)
385 | fd = open(filepath, O_RDONLY | O_CLOEXEC);
386 | by_check_break(fd > 0);
387 |
388 | // get file size
389 | struct stat st;
390 | if (0 != fstat(fd, &st) || 0 == st.st_size) break;
391 |
392 | // mmap the file data
393 | filedata = mmap(by_null, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
394 | by_assert_and_check_break(filedata && filedata != MAP_FAILED);
395 |
396 | // save the file size
397 | if (pfilesize) *pfilesize = (by_size_t)st.st_size;
398 |
399 | } while (0);
400 |
401 | // close the fd first
402 | if (fd > 0) close(fd);
403 | fd = -1;
404 |
405 | // ok?
406 | return filedata;
407 | }
408 |
409 | // get symbol address from the fake dlopen context
410 | static by_pointer_t by_fake_dlsym(by_fake_dlctx_ref_t dlctx, by_char_t const* symbol)
411 | {
412 | // check
413 | by_assert_and_check_return_val(dlctx && dlctx->filedata && dlctx->filesize && symbol, by_null);
414 |
415 | // find the symbol address from the .dynsym first
416 | by_int_t i = 0;
417 | by_pointer_t end = dlctx->filedata + dlctx->filesize;
418 | by_char_t const* dynstr = (by_char_t const*)dlctx->dynstr;
419 | ElfW(Sym)* dynsym = (ElfW(Sym)*)dlctx->dynsym;
420 | by_int_t dynsym_num = dlctx->dynsym_num;
421 | if (dynsym && dynstr)
422 | {
423 | for (i = 0; i < dynsym_num; i++, dynsym++)
424 | {
425 | by_char_t const* name = dynstr + dynsym->st_name;
426 | if ((by_pointer_t)name < end && strcmp(name, symbol) == 0)
427 | {
428 | /* NB: sym->st_value is an offset into the section for relocatables,
429 | * but a VMA for shared libs or exe files, so we have to subtract the bias
430 | */
431 | by_pointer_t symboladdr = (by_pointer_t)(dlctx->biasaddr + dynsym->st_value);
432 | by_trace("dlsym(%s): found at .dynsym/%p = %p + %x", symbol, symboladdr, dlctx->biasaddr, (by_int_t)dynsym->st_value);
433 | return symboladdr;
434 | }
435 | }
436 | }
437 |
438 | // find the symbol address from the .symtab
439 | by_char_t const* strtab = (by_char_t const*)dlctx->strtab;
440 | ElfW(Sym)* symtab = (ElfW(Sym)*)dlctx->symtab;
441 | by_int_t symtab_num = dlctx->symtab_num;
442 | if (symtab && strtab)
443 | {
444 | for (i = 0; i < symtab_num; i++, symtab++)
445 | {
446 | by_char_t const* name = strtab + symtab->st_name;
447 | if ((by_pointer_t)name < end && strcmp(name, symbol) == 0)
448 | {
449 | by_pointer_t symboladdr = (by_pointer_t)(dlctx->biasaddr + symtab->st_value);
450 | by_trace("dlsym(%s): found at .symtab/%p = %p + %x", symbol, symboladdr, dlctx->biasaddr, (by_int_t)symtab->st_value);
451 | return symboladdr;
452 | }
453 | }
454 | }
455 | return by_null;
456 | }
457 |
458 | // close the fake dlopen context
459 | static by_int_t by_fake_dlclose(by_fake_dlctx_ref_t dlctx)
460 | {
461 | // check
462 | by_assert_and_check_return_val(dlctx, -1);
463 |
464 | // clear data
465 | dlctx->biasaddr = by_null;
466 | dlctx->dynsym = by_null;
467 | dlctx->dynstr = by_null;
468 | dlctx->dynsym_num = 0;
469 | dlctx->strtab = by_null;
470 | dlctx->symtab = by_null;
471 | dlctx->symtab_num = 0;
472 |
473 | // unmap file data
474 | if (dlctx->filedata) munmap(dlctx->filedata, dlctx->filesize);
475 | dlctx->filedata = by_null;
476 | dlctx->filesize = 0;
477 |
478 | // free context
479 | free(dlctx);
480 | return 0;
481 | }
482 |
483 | /* @see https://www.sunmoonblog.com/2019/06/04/fake-dlopen/
484 | * https://github.com/avs333/Nougat_dlfunctions
485 | */
486 | static by_fake_dlctx_ref_t by_fake_dlopen_impl(by_char_t const* filename, by_int_t flag)
487 | {
488 | // check
489 | by_assert_and_check_return_val(filename, by_null);
490 |
491 | // do open
492 | by_bool_t ok = by_false;
493 | by_char_t realpath[512];
494 | by_fake_dlctx_ref_t dlctx = by_null;
495 | do
496 | {
497 | // attempt to find the load bias address and real path
498 | by_pointer_t biasaddr = by_fake_find_biasaddr(filename, realpath, sizeof(realpath));
499 | by_check_break(biasaddr);
500 |
501 | // init context
502 | dlctx = calloc(1, sizeof(by_fake_dlctx_t));
503 | by_assert_and_check_break(dlctx);
504 |
505 | dlctx->magic = BY_FAKE_DLCTX_MAGIC;
506 | dlctx->biasaddr = biasaddr;
507 |
508 | // open file
509 | dlctx->filedata = by_fake_open_file(realpath, &dlctx->filesize);
510 | by_assert_and_check_break(dlctx->filedata && dlctx->filesize);
511 |
512 | // trace
513 | by_trace("fake_dlopen: biasaddr: %p, realpath: %s, filesize: %d", biasaddr, realpath, (by_int_t)dlctx->filesize);
514 |
515 | // get elf
516 | ElfW(Ehdr)* elf = (ElfW(Ehdr)*)dlctx->filedata;
517 | by_pointer_t end = dlctx->filedata + dlctx->filesize;
518 | by_assert_and_check_break((by_pointer_t)(elf + 1) < end);
519 |
520 | // get .shstrtab section
521 | by_pointer_t shoff = dlctx->filedata + elf->e_shoff;
522 | ElfW(Shdr)* shstrtab = (ElfW(Shdr)*)(shoff + elf->e_shstrndx * elf->e_shentsize);
523 | by_assert_and_check_break((by_pointer_t)(shstrtab + 1) <= end);
524 |
525 | by_pointer_t shstr = dlctx->filedata + shstrtab->sh_offset;
526 | by_assert_and_check_break(shstr < end);
527 |
528 | // parse elf sections
529 | by_int_t i = 0;
530 | by_bool_t broken = by_false;
531 | for (i = 0; !broken && i < elf->e_shnum && shoff; i++, shoff += elf->e_shentsize)
532 | {
533 | // get section
534 | ElfW(Shdr)* sh = (ElfW(Shdr)*)shoff;
535 | by_assert_and_check_break((by_pointer_t)(sh + 1) <= end && shstr + sh->sh_name < end);
536 | by_assert_and_check_break(dlctx->filedata + sh->sh_offset < end);
537 |
538 | // trace
539 | by_trace("elf section(%d): type: %d, name: %s", i, sh->sh_type, shstr + sh->sh_name);
540 |
541 | // get .dynsym and .symtab sections
542 | switch(sh->sh_type)
543 | {
544 | case SHT_DYNSYM:
545 | // get .dynsym
546 | if (dlctx->dynsym)
547 | {
548 | by_trace("%s: duplicate .dynsym sections", realpath);
549 | broken = by_true;
550 | break;
551 | }
552 | dlctx->dynsym = dlctx->filedata + sh->sh_offset;
553 | dlctx->dynsym_num = (sh->sh_size / sizeof(ElfW(Sym)));
554 | by_trace(".dynsym: %p %d", dlctx->dynsym, dlctx->dynsym_num);
555 | break;
556 | case SHT_SYMTAB:
557 | // get .symtab
558 | if (dlctx->symtab)
559 | {
560 | by_trace("%s: duplicate .symtab sections", realpath);
561 | broken = by_true;
562 | break;
563 | }
564 | dlctx->symtab = dlctx->filedata + sh->sh_offset;
565 | dlctx->symtab_num = (sh->sh_size / sizeof(ElfW(Sym)));
566 | by_trace(".symtab: %p %d", dlctx->symtab, dlctx->symtab_num);
567 | break;
568 | case SHT_STRTAB:
569 | // get .dynstr
570 | if (!strcmp(shstr + sh->sh_name, ".dynstr"))
571 | {
572 | // .dynstr is guaranteed to be the first STRTAB
573 | if (dlctx->dynstr) break;
574 | dlctx->dynstr = dlctx->filedata + sh->sh_offset;
575 | by_trace(".dynstr: %p", dlctx->dynstr);
576 | }
577 | // get .strtab
578 | else if (!strcmp(shstr + sh->sh_name, ".strtab"))
579 | {
580 | if (dlctx->strtab) break;
581 | dlctx->strtab = dlctx->filedata + sh->sh_offset;
582 | by_trace(".strtab: %p", dlctx->strtab);
583 | }
584 | break;
585 | default:
586 | break;
587 | }
588 | }
589 | by_check_break(!broken && dlctx->dynstr && dlctx->dynsym);
590 |
591 | // ok
592 | ok = by_true;
593 |
594 | } while (0);
595 |
596 | // failed?
597 | if (!ok)
598 | {
599 | if (dlctx) by_fake_dlclose(dlctx);
600 | dlctx = by_null;
601 | }
602 | return dlctx;
603 | }
604 | static by_void_t by_linker_init()
605 | {
606 | static by_bool_t s_inited = by_false;
607 | if (!s_inited)
608 | {
609 | // we need linker mutex only for android 5.0 and 5.1
610 | by_size_t apilevel = by_rt_api_level();
611 | if (apilevel == __ANDROID_API_L__ || apilevel == __ANDROID_API_L_MR1__)
612 | {
613 | by_fake_dlctx_ref_t linker = by_fake_dlopen_impl(BY_LINKER_NAME, BY_RTLD_NOW);
614 | by_trace("init linker: %p", linker);
615 | if (linker)
616 | {
617 | g_linker_mutex = (pthread_mutex_t*)by_fake_dlsym(linker, BY_LINKER_MUTEX);
618 | by_trace("load g_dl_mutex: %p", g_linker_mutex);
619 | by_fake_dlclose(linker);
620 | }
621 | }
622 | s_inited = by_true;
623 | }
624 | }
625 | static by_fake_dlctx_ref_t by_fake_dlopen(by_char_t const* filename, by_int_t flag)
626 | {
627 | by_linker_init();
628 | return by_fake_dlopen_impl(filename, flag);
629 | }
630 | static by_void_t by_jni_clearException(JNIEnv* env, by_bool_t report)
631 | {
632 | jthrowable e = report? (*env)->ExceptionOccurred(env) : by_null;
633 | (*env)->ExceptionClear(env);
634 | if (e)
635 | {
636 | jclass clazz = (*env)->GetObjectClass(env, e);
637 | jmethodID printStackTrace_id = (*env)->GetMethodID(env, clazz, "printStackTrace", "()V");
638 | if (!(*env)->ExceptionCheck(env) && printStackTrace_id)
639 | (*env)->CallVoidMethod(env, e, printStackTrace_id);
640 | if ((*env)->ExceptionCheck(env))
641 | (*env)->ExceptionClear(env);
642 | }
643 | }
644 | static jobject by_jni_Class_getDeclaredMethod(JNIEnv* env)
645 | {
646 | // check
647 | by_assert_and_check_return_val(env, by_null);
648 |
649 | // push
650 | if ((*env)->PushLocalFrame(env, 10) < 0) return by_null;
651 |
652 | // get unreachable memory info
653 | jboolean check = by_false;
654 | jobject getDeclaredMethod_method = by_null;
655 | do
656 | {
657 | // get class
658 | jclass clazz = (*env)->FindClass(env, "java/lang/Class");
659 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && clazz);
660 |
661 | // get string class
662 | jclass string_clazz = (*env)->FindClass(env, "java/lang/String");
663 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && string_clazz);
664 |
665 | // get class/array class
666 | jclass classarray_clazz = (*env)->FindClass(env, "[Ljava/lang/Class;");
667 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && classarray_clazz);
668 |
669 | // get getDeclaredMethod id
670 | jmethodID getDeclaredMethod_id = (*env)->GetMethodID(env, clazz, "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
671 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && getDeclaredMethod_id);
672 |
673 | // get getDeclaredMethod name
674 | jstring getDeclaredMethod_name = (*env)->NewStringUTF(env, "getDeclaredMethod");
675 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && getDeclaredMethod_name);
676 |
677 | // get getDeclaredMethod args
678 | jobjectArray getDeclaredMethod_args = (*env)->NewObjectArray(env, 2, clazz, by_null);
679 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && getDeclaredMethod_args);
680 |
681 | (*env)->SetObjectArrayElement(env, getDeclaredMethod_args, 0, string_clazz);
682 | (*env)->SetObjectArrayElement(env, getDeclaredMethod_args, 1, classarray_clazz);
683 |
684 | // Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
685 | getDeclaredMethod_method = (jobject)(*env)->CallObjectMethod(env, clazz, getDeclaredMethod_id, getDeclaredMethod_name, getDeclaredMethod_args);
686 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && getDeclaredMethod_method);
687 |
688 | } while (0);
689 |
690 | // exception? clear it
691 | if (check)
692 | {
693 | getDeclaredMethod_method = by_null;
694 | by_jni_clearException(env, by_true);
695 | }
696 | return (jstring)(*env)->PopLocalFrame(env, getDeclaredMethod_method);
697 | }
698 |
699 | /* load library via system call
700 | *
701 | * @see http://weishu.me/2018/06/07/free-reflection-above-android-p/
702 | * https://github.com/tiann/FreeReflection/blob/c995ef100f39c2eb2d7c344384ca06e8c13b9a4c/library/src/main/java/me/weishu/reflection/Reflection.java#L23-L34
703 | *
704 | * System.load(libraryPath)
705 | *
706 | * @code
707 | Method forName = Class.class.getDeclaredMethod("forName", String.class);
708 | Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
709 | Class> systemClass = (Class>)forName.invoke(null, "java.lang.System");
710 | Method load = (Method)getDeclaredMethod.invoke(systemClass, "load", new Class[]{String.class});
711 | load.invoke(systemClass, libraryPath);
712 | * @endcode
713 | *
714 | * System.loadLibrary(libraryName)
715 | *
716 | * @code
717 | Method forName = Class.class.getDeclaredMethod("forName", String.class);
718 | Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
719 | Class> systemClass = (Class>)forName.invoke(null, "java.lang.System");
720 | Method loadLibrary = (Method)getDeclaredMethod.invoke(systemClass, "loadLibrary", new Class[]{String.class});
721 | loadLibrary.invoke(systemClass, libraryName);
722 | * @endcode
723 | */
724 | static by_bool_t by_jni_System_load_or_loadLibrary_from_sys(JNIEnv* env, by_char_t const* loadName, by_char_t const* libraryPath)
725 | {
726 | // check
727 | by_assert_and_check_return_val(env && loadName && libraryPath, by_false);
728 |
729 | // push
730 | if ((*env)->PushLocalFrame(env, 20) < 0) return by_false;
731 |
732 | // do load
733 | jboolean check = by_false;
734 | do
735 | {
736 | // get getDeclaredMethod method
737 | jobject getDeclaredMethod_method = by_jni_Class_getDeclaredMethod(env);
738 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && getDeclaredMethod_method);
739 |
740 | // get class
741 | jclass clazz = (*env)->FindClass(env, "java/lang/Class");
742 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && clazz);
743 |
744 | // get object class
745 | jclass object_clazz = (*env)->FindClass(env, "java/lang/Object");
746 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && object_clazz);
747 |
748 | // get string class
749 | jclass string_clazz = (*env)->FindClass(env, "java/lang/String");
750 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && string_clazz);
751 |
752 | // get system class
753 | jclass system_clazz = (*env)->FindClass(env, "java/lang/System");
754 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && system_clazz);
755 |
756 | // get method class
757 | jclass method_clazz = (*env)->FindClass(env, "java/lang/reflect/Method");
758 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && method_clazz);
759 |
760 | // get getDeclaredMethod_method.invoke id
761 | jmethodID invoke_id = (*env)->GetMethodID(env, method_clazz, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
762 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && invoke_id);
763 |
764 | // get load name
765 | jstring load_name = (*env)->NewStringUTF(env, loadName);
766 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && load_name);
767 |
768 | // get invoke args
769 | jobjectArray invoke_args = (*env)->NewObjectArray(env, 2, object_clazz, by_null);
770 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && invoke_args);
771 |
772 | // get load args
773 | jobjectArray load_args = (*env)->NewObjectArray(env, 1, clazz, string_clazz);
774 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && load_args);
775 |
776 | (*env)->SetObjectArrayElement(env, invoke_args, 0, load_name);
777 | (*env)->SetObjectArrayElement(env, invoke_args, 1, load_args);
778 |
779 | // Method load = (Method)getDeclaredMethod.invoke(systemClass, "load", new Class[]{String.class});
780 | jobject load_method = (jobject)(*env)->CallObjectMethod(env, getDeclaredMethod_method, invoke_id, system_clazz, invoke_args);
781 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && load_method);
782 |
783 | // load.invoke(systemClass, libraryPath)
784 | jstring libraryPath_jstr = (*env)->NewStringUTF(env, libraryPath);
785 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && libraryPath_jstr);
786 |
787 | invoke_args = (*env)->NewObjectArray(env, 1, object_clazz, libraryPath_jstr);
788 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && invoke_args);
789 |
790 | (*env)->CallObjectMethod(env, load_method, invoke_id, system_clazz, invoke_args);
791 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)));
792 |
793 | } while (0);
794 |
795 | // exception? clear it
796 | if (check) by_jni_clearException(env, by_true);
797 | (*env)->PopLocalFrame(env, by_null);
798 | return !check;
799 | }
800 | static by_bool_t by_jni_System_load_or_loadLibrary_from_app(JNIEnv* env, by_char_t const* loadName, by_char_t const* libraryPath)
801 | {
802 | // check
803 | by_assert_and_check_return_val(env && loadName && libraryPath, by_false);
804 |
805 | // push
806 | if ((*env)->PushLocalFrame(env, 10) < 0) return by_false;
807 |
808 | // do load
809 | jboolean check = by_false;
810 | do
811 | {
812 | // get system class
813 | jclass system_clazz = (*env)->FindClass(env, "java/lang/System");
814 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && system_clazz);
815 |
816 | // get load/loadLibrary id
817 | jmethodID load_id = (*env)->GetStaticMethodID(env, system_clazz, loadName, "(Ljava/lang/String;)V");
818 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && load_id);
819 |
820 | // get library path
821 | jstring libraryPath_jstr = (*env)->NewStringUTF(env, libraryPath);
822 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)) && libraryPath_jstr);
823 |
824 | // load library
825 | (*env)->CallStaticVoidMethod(env, system_clazz, load_id, libraryPath_jstr);
826 | by_assert_and_check_break(!(check = (*env)->ExceptionCheck(env)));
827 |
828 | } while (0);
829 |
830 | // exception? clear it
831 | if (check) by_jni_clearException(env, by_true);
832 | (*env)->PopLocalFrame(env, by_null);
833 | return !check;
834 | }
835 |
836 | // System.load(libraryPath)
837 | static by_bool_t by_jni_System_load(JNIEnv* env, by_char_t const* libraryPath)
838 | {
839 | by_trace("load: %s", libraryPath);
840 | return by_jni_System_load_or_loadLibrary_from_app(env, "load", libraryPath) ||
841 | by_jni_System_load_or_loadLibrary_from_sys(env, "load", libraryPath);
842 | }
843 |
844 | // System.loadLibrary(libraryName)
845 | static by_bool_t by_jni_System_loadLibrary(JNIEnv* env, by_char_t const* libraryName)
846 | {
847 | by_trace("loadLibrary: %s", libraryName);
848 | return by_jni_System_load_or_loadLibrary_from_app(env, "loadLibrary", libraryName) ||
849 | by_jni_System_load_or_loadLibrary_from_sys(env, "loadLibrary", libraryName);
850 | }
851 |
852 | /* get the current jni environment
853 | *
854 | * @see frameworks/base/core/jni/include/android_runtime/AndroidRuntime.h
855 | *
856 | * static AndroidRuntime* runtime = AndroidRuntime::getRuntime();
857 | * static JavaVM* getJavaVM() { return mJavaVM; }
858 | * static JNIEnv* getJNIEnv();
859 | */
860 | static JNIEnv* by_jni_getenv()
861 | {
862 | if (g_jvm)
863 | {
864 | JNIEnv* env = by_null;
865 | if (JNI_OK == (*g_jvm)->GetEnv(g_jvm, (by_pointer_t*)&env, g_jversion))
866 | return env;
867 | }
868 | if (!g_tls_jnienv)
869 | {
870 | by_fake_dlctx_ref_t dlctx = by_fake_dlopen("libandroid_runtime.so", BY_RTLD_NOW);
871 | if (dlctx)
872 | {
873 | typedef by_pointer_t (*getJNIEnv_t)();
874 | getJNIEnv_t getJNIEnv = (getJNIEnv_t)by_fake_dlsym(dlctx, "_ZN7android14AndroidRuntime9getJNIEnvEv");
875 | if (getJNIEnv)
876 | g_tls_jnienv = getJNIEnv();
877 | by_fake_dlclose(dlctx);
878 | }
879 |
880 | // trace
881 | by_trace("get jnienv: %p", g_tls_jnienv);
882 | }
883 | return g_tls_jnienv;
884 | }
885 |
886 | by_void_t by_jni_javavm_set(JavaVM* jvm, by_int_t jversion)
887 | {
888 | g_jvm = jvm;
889 | g_jversion = jversion;
890 | }
891 |
892 | /* //////////////////////////////////////////////////////////////////////////////////////
893 | * implementation
894 | */
895 | by_pointer_t by_dlopen(by_char_t const* filename, by_int_t flag)
896 | {
897 | // check
898 | by_assert_and_check_return_val(filename, by_null);
899 |
900 | // attempt to use original dlopen to load it fist
901 | // TODO we disable the original dlopen now, load /data/xxx.so may be returned an invalid address
902 | by_pointer_t handle = by_null;//dlopen(filename, flag == BY_RTLD_LAZY? RTLD_LAZY : RTLD_NOW);
903 |
904 | // uses the fake dlopen to load it from maps directly
905 | if (!handle) handle = (by_pointer_t)by_fake_dlopen(filename, flag);
906 |
907 | // uses the fake dlopen to load it from maps directly
908 | if (!handle)
909 | {
910 | // load it via system call
911 | JNIEnv* env = by_jni_getenv();
912 | if (env && (((strstr(filename, "/") || strstr(filename, ".so")) && by_jni_System_load(env, filename)) || by_jni_System_loadLibrary(env, filename)))
913 | handle = (by_pointer_t)by_fake_dlopen(filename, flag);
914 | }
915 | return handle;
916 | }
917 | by_pointer_t by_dlsym(by_pointer_t handle, by_char_t const* symbol)
918 | {
919 | // check
920 | by_fake_dlctx_ref_t dlctx = (by_fake_dlctx_ref_t)handle;
921 | by_assert_and_check_return_val(dlctx && symbol, by_null);
922 |
923 | // do dlsym
924 | return (dlctx->magic == BY_FAKE_DLCTX_MAGIC)? by_fake_dlsym(dlctx, symbol) : dlsym(handle, symbol);
925 | }
926 | by_int_t by_dlclose(by_pointer_t handle)
927 | {
928 | // check
929 | by_fake_dlctx_ref_t dlctx = (by_fake_dlctx_ref_t)handle;
930 | by_assert_and_check_return_val(dlctx, -1);
931 |
932 | // do dlclose
933 | return (dlctx->magic == BY_FAKE_DLCTX_MAGIC)? by_fake_dlclose(dlctx) : dlclose(handle);
934 | }
935 |
936 |
--------------------------------------------------------------------------------
/src/native/byopen_macho.c:
--------------------------------------------------------------------------------
1 | /*!A dlopen library that bypasses mobile system limitation
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * Copyright (C) 2020-present, TBOOX Open Source Group.
16 | *
17 | * @author ruki
18 | * @file byopen_macho.c
19 | *
20 | */
21 |
22 | /* //////////////////////////////////////////////////////////////////////////////////////
23 | * includes
24 | */
25 | #include "byopen.h"
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | /* //////////////////////////////////////////////////////////////////////////////////////
33 | * macros
34 | */
35 | #if defined(__LP64__)
36 | # define NLIST struct nlist_64
37 | #else
38 | # define NLIST struct nlist
39 | #endif
40 |
41 | /* //////////////////////////////////////////////////////////////////////////////////////
42 | * types
43 | */
44 | // the dynamic library context type for fake dlopen
45 | typedef struct _by_fake_dlctx_t
46 | {
47 | // the image index
48 | by_size_t image_index;
49 |
50 | // the image image_header
51 | struct mach_header const* image_header;
52 |
53 | }by_fake_dlctx_t, *by_fake_dlctx_ref_t;
54 |
55 | /* //////////////////////////////////////////////////////////////////////////////////////
56 | * private implementation
57 | */
58 |
59 | // get first cmd after image_header
60 | static by_pointer_t by_get_first_cmd_after_header(struct mach_header const* image_header)
61 | {
62 | switch (image_header->magic)
63 | {
64 | case MH_MAGIC:
65 | case MH_CIGAM:
66 | return (by_pointer_t)(image_header + 1);
67 | case MH_MAGIC_64:
68 | case MH_CIGAM_64:
69 | return (by_pointer_t)(((struct mach_header_64*)image_header) + 1);
70 | default:
71 | // image_header is corrupt
72 | return 0;
73 | }
74 | }
75 |
76 | /* //////////////////////////////////////////////////////////////////////////////////////
77 | * implementation
78 | */
79 | by_pointer_t by_dlopen(by_char_t const* filename, by_int_t flag)
80 | {
81 | // check
82 | by_assert_and_check_return_val(filename, by_null);
83 |
84 | /* skip the first image (master app), becasue it's symtab will be stripped after be archived.
85 | * so we need to keep our behavior consistent for debug/release and archived packages.
86 | */
87 | struct mach_header const* image_header = by_null;
88 | by_size_t const image_count = _dyld_image_count();
89 | for (by_size_t image_index = 1; image_index < image_count; image_index++)
90 | {
91 | image_header = _dyld_get_image_header((uint32_t)image_index);
92 | if (image_header)
93 | {
94 | by_char_t const* image_name = _dyld_get_image_name(image_index);
95 | if (image_name && 0 == strcmp(image_name, filename))
96 | {
97 | by_fake_dlctx_ref_t dlctx = calloc(1, sizeof(by_fake_dlctx_t));
98 | if (dlctx)
99 | {
100 | dlctx->image_index = image_index;
101 | dlctx->image_header = image_header;
102 | }
103 | by_trace("%s: found at %p/%d", image_name, image_header, (by_int_t)image_index);
104 | return (by_pointer_t)dlctx;
105 | }
106 | }
107 | }
108 | return by_null;
109 | }
110 | by_pointer_t by_dlsym(by_pointer_t handle, by_char_t const* symbol)
111 | {
112 | // check
113 | by_fake_dlctx_ref_t dlctx = (by_fake_dlctx_ref_t)handle;
114 | by_assert_and_check_return_val(dlctx && dlctx->image_header && symbol, by_null);
115 |
116 | // skip '_'
117 | if (*symbol == '_') symbol++;
118 |
119 | // get command pointer
120 | struct mach_header const* image_header = dlctx->image_header;
121 | uintptr_t cmd_ptr = by_get_first_cmd_after_header(image_header);
122 | uintptr_t image_vmaddr_slide = (uintptr_t)_dyld_get_image_vmaddr_slide(dlctx->image_index);
123 | if (cmd_ptr)
124 | {
125 | uintptr_t segment_base = 0;
126 | for (by_size_t cmd_index = 0; cmd_index < image_header->ncmds; cmd_index++)
127 | {
128 | // get segment base address
129 | struct load_command const* load_cmd = (struct load_command*)cmd_ptr;
130 | if (load_cmd->cmd == LC_SEGMENT)
131 | {
132 | struct segment_command const* segment_cmd = (struct segment_command*)cmd_ptr;
133 | if (strcmp(segment_cmd->segname, SEG_LINKEDIT) == 0)
134 | segment_base = (uintptr_t)(segment_cmd->vmaddr - segment_cmd->fileoff) + image_vmaddr_slide;
135 | }
136 | else if (load_cmd->cmd == LC_SEGMENT_64)
137 | {
138 | struct segment_command_64 const* segment_cmd = (struct segment_command_64*)cmd_ptr;
139 | if (strcmp(segment_cmd->segname, SEG_LINKEDIT) == 0)
140 | segment_base = (uintptr_t)(segment_cmd->vmaddr - segment_cmd->fileoff) + image_vmaddr_slide;
141 | }
142 | else if (load_cmd->cmd == LC_SYMTAB && segment_base > 0)
143 | {
144 | struct symtab_command const* symbol_table_cmd = (struct symtab_command*)cmd_ptr;
145 | NLIST const* symbol_table = (NLIST*)(segment_base + symbol_table_cmd->symoff);
146 | uintptr_t string_table = segment_base + symbol_table_cmd->stroff;
147 | for (uint32_t symbol_index = 0; symbol_index < symbol_table_cmd->nsyms; symbol_index++)
148 | {
149 | // if n_value is 0, the symbol refers to an external object.
150 | if (symbol_table[symbol_index].n_value != 0)
151 | {
152 | // get symbol name
153 | NLIST const* item = symbol_table + symbol_index;
154 | by_pointer_t dli_saddr = (by_pointer_t)(item->n_value + image_vmaddr_slide);
155 | by_char_t const* dli_sname = (by_char_t*)((intptr_t)string_table + (intptr_t)item->n_un.n_strx);
156 | if (*dli_sname == '_') dli_sname++;
157 |
158 | // found and skip symbols with '0x...'?
159 | if (*dli_sname != '0' && !strcmp(symbol, dli_sname))
160 | {
161 | // thumb function? fix address
162 | #if defined(BY_ARCH_ARM) && !defined(BY_ARCH_ARM64)
163 | # ifdef BY_ARCH_ARM_THUMB
164 | if (item->n_desc & N_ARM_THUMB_DEF)
165 | dli_saddr = (by_pointer_t)((by_ulong_t)dli_saddr | 1);
166 | # else
167 | if (item->n_desc & N_ARM_THUMB_DEF)
168 | dli_saddr = (by_pointer_t)((by_ulong_t)dli_saddr & ~1);
169 | # endif
170 | #endif
171 | // trace
172 | by_trace("dlsym(%s): %p", dli_sname, dli_saddr);
173 | return dli_saddr;
174 | }
175 | }
176 | }
177 |
178 | // reset the segment base address
179 | segment_base = 0;
180 | }
181 | cmd_ptr += load_cmd->cmdsize;
182 | }
183 | }
184 | return by_null;
185 | }
186 | by_int_t by_dlclose(by_pointer_t handle)
187 | {
188 | // check
189 | by_fake_dlctx_ref_t dlctx = (by_fake_dlctx_ref_t)handle;
190 | by_assert_and_check_return_val(dlctx, -1);
191 |
192 | // free it
193 | free(dlctx);
194 | return 0;
195 | }
196 |
--------------------------------------------------------------------------------
/src/native/prefix.h:
--------------------------------------------------------------------------------
1 | /*!A dlopen library that bypasses mobile system limitation
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * Copyright (C) 2020-present, TBOOX Open Source Group.
16 | *
17 | * @author ruki
18 | * @file prefix.h
19 | *
20 | */
21 | #ifndef BY_PREFIX_H
22 | #define BY_PREFIX_H
23 |
24 | #ifdef __cplusplus
25 | extern "C" {
26 | #endif
27 |
28 | /* //////////////////////////////////////////////////////////////////////////////////////
29 | * includes
30 | */
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #if defined(__ANDROID__)
38 | # include
39 | # include
40 | #elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
41 | # include
42 | #endif
43 |
44 | /* //////////////////////////////////////////////////////////////////////////////////////
45 | * macros
46 | */
47 |
48 | // bool values
49 | #ifdef __ANDROID__
50 | # define by_true ((by_bool_t)JNI_TRUE)
51 | # define by_false ((by_bool_t)JNI_FALSE)
52 | #else
53 | # define by_true ((by_bool_t)1)
54 | # define by_false ((by_bool_t)0)
55 | #endif
56 |
57 | // null
58 | #ifdef __cplusplus
59 | # define by_null (0)
60 | #else
61 | # define by_null ((by_pointer_t)0)
62 | #endif
63 |
64 | // print
65 | #define by_print(fmt, arg ...) by_printf(fmt "\n", ## arg)
66 |
67 | // trace
68 | #ifdef BY_DEBUG
69 | # define by_tracef(fmt, arg ...) by_printf(fmt, ## arg)
70 | # define by_trace(fmt, arg ...) by_printf(fmt "\n", ## arg)
71 | # define by_trace_line(fmt, arg ...) by_printf(fmt " at func: %s, line: %d, file: %s\n", ##arg, __FUNCTION__, __LINE__, __FILE__)
72 | #else
73 | # define by_tracef(...)
74 | # define by_trace(...)
75 | # define by_trace_line(...)
76 | #endif
77 |
78 | // check
79 | #define by_check_return(x) do { if (!(x)) return ; } while (0)
80 | #define by_check_return_val(x, v) do { if (!(x)) return (v); } while (0)
81 | #define by_check_goto(x, b) do { if (!(x)) goto b; } while (0)
82 | #define by_check_break(x) { if (!(x)) break ; }
83 | #define by_check_continue(x) { if (!(x)) continue ; }
84 |
85 | // assert
86 | #ifdef BY_DEBUG
87 | # define by_assert(x) do { if (!(x)) {by_trace_line("[assert]: expr: %s", #x); } } while(0)
88 | # define by_assert_return(x) do { if (!(x)) {by_trace_line("[assert]: expr: %s", #x); return ; } } while(0)
89 | # define by_assert_return_val(x, v) do { if (!(x)) {by_trace_line("[assert]: expr: %s", #x); return (v); } } while(0)
90 | # define by_assert_goto(x, b) do { if (!(x)) {by_trace_line("[assert]: expr: %s", #x); goto b; } } while(0)
91 | # define by_assert_break(x) { if (!(x)) {by_trace_line("[assert]: expr: %s", #x); break ; } }
92 | # define by_assert_continue(x) { if (!(x)) {by_trace_line("[assert]: expr: %s", #x); continue ; } }
93 | # define by_assert_and_check_return(x) by_assert_return(x)
94 | # define by_assert_and_check_return_val(x, v) by_assert_return_val(x, v)
95 | # define by_assert_and_check_goto(x, b) by_assert_goto(x, b)
96 | # define by_assert_and_check_break(x) by_assert_break(x)
97 | # define by_assert_and_check_continue(x) by_assert_continue(x)
98 | #else
99 | # define by_assert(x)
100 | # define by_assert_return(x)
101 | # define by_assert_return_val(x, v)
102 | # define by_assert_goto(x, b)
103 | # define by_assert_break(x)
104 | # define by_assert_continue(x)
105 | # define by_assert_and_check_return(x) by_check_return(x)
106 | # define by_assert_and_check_return_val(x, v) by_check_return_val(x, v)
107 | # define by_assert_and_check_goto(x, b) by_check_goto(x, b)
108 | # define by_assert_and_check_break(x) by_check_break(x)
109 | # define by_assert_and_check_continue(x) by_check_continue(x)
110 | #endif
111 |
112 | // printf
113 | #if defined(__ANDROID__)
114 | # define by_printf(fmt, arg ...) __android_log_print(ANDROID_LOG_INFO, "byOpen", fmt, ## arg)
115 | #elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
116 | # define by_printf(fmt, arg ...) asl_log(by_null, by_null, ASL_LEVEL_WARNING, "[byOpen]: " fmt, ## arg);
117 | #else
118 | # define by_printf(fmt, arg ...) printf(fmt, ## arg)
119 | #endif
120 |
121 | /* arch
122 | *
123 | * gcc builtin macros for gcc -dM -E - < /dev/null
124 | *
125 | * .e.g gcc -m64 -dM -E - < /dev/null | grep 64
126 | * .e.g gcc -m32 -dM -E - < /dev/null | grep 86
127 | * .e.g gcc -march=armv6 -dM -E - < /dev/null | grep ARM
128 | */
129 | #if defined(__i386) \
130 | || defined(__i686) \
131 | || defined(__i386__) \
132 | || defined(__i686__) \
133 | || defined(_M_IX86)
134 | # define BY_ARCH_x86
135 | #elif defined(__x86_64) \
136 | || defined(__amd64__) \
137 | || defined(__amd64) \
138 | || defined(_M_IA64) \
139 | || defined(_M_X64)
140 | # define BY_ARCH_x64
141 | #elif defined(__arm__) || defined(__arm64) || defined(__arm64__) || (defined(__aarch64__) && __aarch64__)
142 | # define BY_ARCH_ARM
143 | # if defined(__ARM64_ARCH_8__)
144 | # define BY_ARCH_ARM64
145 | # define BY_ARCH_ARM_v8
146 | # elif defined(__ARM_ARCH_7A__)
147 | # define BY_ARCH_ARM_v7A
148 | # elif defined(__ARM_ARCH_7__)
149 | # define BY_ARCH_ARM_v7
150 | # elif defined(__ARM_ARCH_6__)
151 | # define BY_ARCH_ARM_v6
152 | # elif defined(__ARM_ARCH_5TE__)
153 | # define BY_ARCH_ARM_v5te
154 | # elif defined(__ARM_ARCH_5__)
155 | # define BY_ARCH_ARM_v5
156 | # elif defined(__ARM_ARCH_4T__)
157 | # define BY_ARCH_ARM_v4t
158 | # elif defined(__ARM_ARCH)
159 | # define BY_ARCH_ARM_VERSION __ARM_ARCH
160 | # if __ARM_ARCH >= 8
161 | # define BY_ARCH_ARM_v8
162 | # if defined(__arm64) || defined(__arm64__)
163 | # define BY_ARCH_ARM64
164 | # elif (defined(__aarch64__) && __aarch64__)
165 | # define BY_ARCH_ARM64
166 | # endif
167 | # elif __ARM_ARCH >= 7
168 | # define BY_ARCH_ARM_v7
169 | # elif __ARM_ARCH >= 6
170 | # define BY_ARCH_ARM_v6
171 | # else
172 | # define BY_ARCH_ARM_v5
173 | # endif
174 | # elif defined(__aarch64__) && __aarch64__
175 | # define BY_ARCH_ARM_v8
176 | # define BY_ARCH_ARM64
177 | # else
178 | # error unknown arm arch version
179 | # endif
180 | # if !defined(BY_ARCH_ARM64) && (defined(__arm64) || defined(__arm64__) || (defined(__aarch64__) && __aarch64__))
181 | # define BY_ARCH_ARM64
182 | # endif
183 | # if defined(__thumb__)
184 | # define BY_ARCH_ARM_THUMB
185 | # endif
186 | # if defined(__ARM_NEON__)
187 | # define BY_ARCH_ARM_NEON
188 | # endif
189 | #else
190 | # error unknown arch
191 | #endif
192 |
193 | /* //////////////////////////////////////////////////////////////////////////////////////
194 | * types
195 | */
196 |
197 | // basic
198 | typedef signed int by_int_t;
199 | typedef unsigned int by_uint_t;
200 | typedef signed long by_long_t;
201 | typedef unsigned long by_ulong_t;
202 | typedef by_ulong_t by_size_t;
203 | typedef by_int_t by_bool_t;
204 | typedef signed char by_int8_t;
205 | typedef by_int8_t by_sint8_t;
206 | typedef unsigned char by_uint8_t;
207 | typedef signed short by_int16_t;
208 | typedef by_int16_t by_sint16_t;
209 | typedef unsigned short by_uint16_t;
210 | typedef by_int_t by_int32_t;
211 | typedef by_int32_t by_sint32_t;
212 | typedef by_uint_t by_uint32_t;
213 | typedef char by_char_t;
214 | typedef by_int32_t by_wchar_t;
215 | typedef by_int32_t by_uchar_t;
216 | typedef by_uint8_t by_byte_t;
217 | typedef void by_void_t;
218 | typedef by_void_t* by_pointer_t;
219 | typedef by_void_t const* by_cpointer_t;
220 | typedef by_pointer_t by_handle_t;
221 | typedef signed long long by_int64_t;
222 | typedef unsigned long long by_uint64_t;
223 | typedef by_int64_t by_sint64_t;
224 | typedef by_sint64_t by_hong_t;
225 | typedef by_uint64_t by_hize_t;
226 | typedef float by_float_t;
227 | typedef double by_double_t;
228 |
229 | #ifdef __cplusplus
230 | }
231 | #endif
232 | #endif
233 |
--------------------------------------------------------------------------------
/src/native/xmake.lua:
--------------------------------------------------------------------------------
1 | target("byopen")
2 | set_kind("static")
3 | if is_plat("iphoneos", "macosx") then
4 | add_files("byopen_macho.c")
5 | elseif is_plat("android") then
6 | add_files("byopen_android.c")
7 | end
8 | add_includedirs(".", {interface = true})
9 | add_headerfiles("*.h")
10 |
--------------------------------------------------------------------------------
/xmake.lua:
--------------------------------------------------------------------------------
1 | set_project("byopen")
2 | set_xmakever("2.3.5")
3 |
4 | add_rules("mode.debug", "mode.release", "mode.minsizerel")
5 | if is_mode("debug") then
6 | add_defines("BY_DEBUG")
7 | elseif is_mode("minsizerel") then
8 | add_cxflags("-flto")
9 | add_shflags("-flto")
10 | add_ldflags("-flto")
11 | if is_plat("android") then
12 | add_shflags("-fuse-ld=lld")
13 | add_ldflags("-fuse-ld=lld")
14 | end
15 | end
16 | if is_plat("android") then
17 | add_cxflags("-fPIC")
18 | end
19 |
20 | task("apk_build")
21 | set_menu {usage = "xmake apk_build [options]", description = "build android apk.", options = {}}
22 | on_run(function ()
23 | os.cd("src/android")
24 | os.exec("./gradlew app:assembleDebug")
25 | end)
26 |
27 | task("apk_install")
28 | set_menu {usage = "xmake apk_install [options]", description = "install android apk.", options = {}}
29 | on_run(function ()
30 | os.cd("src/android")
31 | os.exec("adb install -r ./app/build/outputs/apk/debug/app-debug.apk")
32 | end)
33 |
34 | task("apk_test")
35 | set_menu {usage = "xmake apk_test [options]", description = "install and test android apk.", options = {}}
36 | on_run(function ()
37 | os.cd("src/android")
38 | os.exec("./gradlew app:assembleDebug")
39 | os.exec("adb install -r ./app/build/outputs/apk/debug/app-debug.apk")
40 | os.exec("adb logcat -s byOpen")
41 | end)
42 |
43 | includes("src/native", "src/demo")
44 | if is_plat("android") then
45 | includes("src/android/app/jni")
46 | end
47 |
--------------------------------------------------------------------------------