From bb00b43b3c789e6d54959d414b2242255ea022dd Mon Sep 17 00:00:00 2001 From: murdle Date: Fri, 16 Jan 2026 23:23:28 +0200 Subject: [PATCH] first commit --- .gitignore | 11 + CMakeLists.txt | 37 + LICENSE | 26 + README.md | 42 + eglut/eglut.c | 398 ++++ eglut/eglut.h | 128 ++ eglut/eglut_wayland.c | 222 ++ eglut/eglut_x11.c | 378 ++++ eglut/eglutint.h | 107 + extract.sh | 19 + hybris/include/hybris/binding.h | 448 ++++ hybris/include/hybris/dlfcn.h | 39 + hybris/include/hybris/floating_point_abi.h | 42 + hybris/include/hybris/hook.h | 33 + hybris/include/hybris/properties.h | 58 + hybris/src/cache.c | 278 +++ hybris/src/dlfcn.c | 50 + hybris/src/hooks.c | 1869 ++++++++++++++++ hybris/src/hooks_shm.c | 252 +++ hybris/src/hooks_shm.h | 41 + hybris/src/jb/arch/arm/begin.S | 45 + hybris/src/jb/arch/x86/begin.S | 46 + hybris/src/jb/debugger.c | 240 ++ hybris/src/jb/dlfcn.c | 286 +++ hybris/src/jb/linker.c | 2337 ++++++++++++++++++++ hybris/src/jb/linker.h | 219 ++ hybris/src/jb/linker_debug.h | 149 ++ hybris/src/jb/linker_environ.c | 204 ++ hybris/src/jb/linker_environ.h | 54 + hybris/src/jb/linker_format.c | 703 ++++++ hybris/src/jb/linker_format.h | 41 + hybris/src/jb/rt.c | 36 + hybris/src/logging.c | 133 ++ hybris/src/logging.h | 165 ++ hybris/src/properties.c | 212 ++ hybris/src/properties_p.h | 26 + hybris/src/strlcpy.c | 51 + hybris/src/sysconf.c | 376 ++++ mcpe/App.h | 29 + mcpe/AppPlatform.h | 27 + mcpe/FilePickerSettings.h | 24 + mcpe/ImagePickingCallback.h | 12 + mcpe/Keyboard.h | 17 + mcpe/MinecraftGame.h | 38 + mcpe/Mouse.h | 8 + mcpe/Options.h | 16 + mcpe/gl.h | 23 + mcpe/types.cpp | 45 + mod_example/README.md | 4 + mod_example/jni/Android.mk | 10 + mod_example/jni/Application.mk | 5 + mod_example/jni/main.cpp | 21 + mod_example/jni/mcpelauncher_api.h | 12 + src/LinuxAppPlatform.cpp | 196 ++ src/LinuxAppPlatform.h | 145 ++ src/LinuxStore.h | 94 + src/amdfix.s | 23 + src/android_symbols.h | 46 + src/common.cpp | 160 ++ src/common.h | 12 + src/egl_symbols.h | 20 + src/fmod_symbols.h | 42 + src/gles_symbols.h | 540 +++++ src/hook.cpp | 117 + src/hook.h | 6 + src/main.cpp | 461 ++++ src/server.cpp | 0 67 files changed, 11954 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 eglut/eglut.c create mode 100644 eglut/eglut.h create mode 100644 eglut/eglut_wayland.c create mode 100644 eglut/eglut_x11.c create mode 100644 eglut/eglutint.h create mode 100755 extract.sh create mode 100644 hybris/include/hybris/binding.h create mode 100644 hybris/include/hybris/dlfcn.h create mode 100644 hybris/include/hybris/floating_point_abi.h create mode 100644 hybris/include/hybris/hook.h create mode 100644 hybris/include/hybris/properties.h create mode 100644 hybris/src/cache.c create mode 100644 hybris/src/dlfcn.c create mode 100644 hybris/src/hooks.c create mode 100644 hybris/src/hooks_shm.c create mode 100644 hybris/src/hooks_shm.h create mode 100644 hybris/src/jb/arch/arm/begin.S create mode 100644 hybris/src/jb/arch/x86/begin.S create mode 100644 hybris/src/jb/debugger.c create mode 100644 hybris/src/jb/dlfcn.c create mode 100644 hybris/src/jb/linker.c create mode 100644 hybris/src/jb/linker.h create mode 100644 hybris/src/jb/linker_debug.h create mode 100644 hybris/src/jb/linker_environ.c create mode 100644 hybris/src/jb/linker_environ.h create mode 100644 hybris/src/jb/linker_format.c create mode 100644 hybris/src/jb/linker_format.h create mode 100644 hybris/src/jb/rt.c create mode 100644 hybris/src/logging.c create mode 100644 hybris/src/logging.h create mode 100644 hybris/src/properties.c create mode 100644 hybris/src/properties_p.h create mode 100644 hybris/src/strlcpy.c create mode 100644 hybris/src/sysconf.c create mode 100644 mcpe/App.h create mode 100644 mcpe/AppPlatform.h create mode 100644 mcpe/FilePickerSettings.h create mode 100644 mcpe/ImagePickingCallback.h create mode 100644 mcpe/Keyboard.h create mode 100644 mcpe/MinecraftGame.h create mode 100644 mcpe/Mouse.h create mode 100644 mcpe/Options.h create mode 100644 mcpe/gl.h create mode 100644 mcpe/types.cpp create mode 100644 mod_example/README.md create mode 100644 mod_example/jni/Android.mk create mode 100644 mod_example/jni/Application.mk create mode 100644 mod_example/jni/main.cpp create mode 100644 mod_example/jni/mcpelauncher_api.h create mode 100644 src/LinuxAppPlatform.cpp create mode 100644 src/LinuxAppPlatform.h create mode 100644 src/LinuxStore.h create mode 100644 src/amdfix.s create mode 100644 src/android_symbols.h create mode 100644 src/common.cpp create mode 100644 src/common.h create mode 100644 src/egl_symbols.h create mode 100644 src/fmod_symbols.h create mode 100644 src/gles_symbols.h create mode 100644 src/hook.cpp create mode 100644 src/hook.h create mode 100644 src/main.cpp create mode 100644 src/server.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e71dd52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.idea/ +assets/ +data/ +tmp/ +libs/libminecraftpe.so +libs/libgnustl_shared.so +*.apk +mcpelauncher +build/ +libs/ +!libs/.gitkeep \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6810b45 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.5) +project(mcpelauncher) + +enable_language(C ASM) + +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -m32") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +set(CMAKE_LIBRARY_ARCHITECTURE "i386-linux-gnu") + +link_directories(/usr/lib32) +include_directories(/usr/include) +set(X11_X11_LIB /usr/lib32/libX11.so) + +#SET(CMAKE_CXX_COMPILER "g++-4.9") +#SET(CMAKE_C_COMPILER "gcc-4.9") + +find_package(Threads REQUIRED) +find_package(ZLIB REQUIRED) +find_package(X11 REQUIRED) + +include(FindEGL.cmake) + +include_directories(${PNG_INCLUDE_DIRS}) +include_directories(${EGL_INCLUDE_DIRS}) +include_directories(eglut) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=0") + +set(HYBRIS_SOURCES hybris/src/cache.c hybris/src/dlfcn.c hybris/src/hooks.c hybris/src/hooks_shm.c hybris/src/logging.c hybris/src/properties.c hybris/src/strlcpy.c hybris/src/sysconf.c hybris/src/jb/dlfcn.c hybris/src/jb/linker.c hybris/src/jb/linker_environ.c hybris/src/jb/linker_format.c hybris/src/jb/rt.c) +set(EGLUT_SOURCES eglut/eglut.c eglut/eglut_x11.c) + +set(SOURCE_FILES src/main.cpp src/common.cpp src/hook.cpp src/amdfix.s src/LinuxAppPlatform.cpp mcpe/types.cpp mcpe/ImagePickingCallback.h mcpe/FilePickerSettings.h) +add_executable(mcpelauncher ${HYBRIS_SOURCES} ${EGLUT_SOURCES} ${SOURCE_FILES}) +target_link_libraries(mcpelauncher ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} rt libEGL.so libGLESv2.so ${X11_X11_LIB} uuid) +target_compile_definitions(mcpelauncher PRIVATE ANDROID_X86_LINKER _GNU_SOURCE LINKER_TEXT_BASE=0xB0000100 LINKER_AREA_SIZE=0x01000000 LINKER_DEBUG=1) +set_target_properties(mcpelauncher PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba48dfb --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2015, MCMrARM +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ec7aca --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +MCPE Linux Launcher +=================== + +## Required packages + +``` + sudo apt-get install cmake zlib1g-dev:i386 libncurses5-dev:i386 libgles2-mesa-dev gcc-multilib g++-multilib zlib1g-dev:i386 libx11-dev:i386 linux-libc-dev:i386 uuid-dev:i386 libpng-dev:i386 libx11-dev:i386 libxext6:i386 +``` + +If g++-4.9 fails to install and you're using Ubuntu 14.04 you may need to add the repository ppa:ubuntu-toolchain-r/test. + +You'll also need to install 32-bit version of the graphic drivers (nvidia drivers ask you about that at installation, so +you may need to reinstall/reconfigure them; if you use mesa you'll need to install the libgles2-mesa-dev:i386 and +libegl1-mesa-dev:i386 packages) + +You may also need to do `sudo dpkg --add-architecture i386` if you have never installed i386 packages before. + +## Compiling +This app uses cmake so it is enough to do: + +``` + cmake . + make +``` + +## Running +1. Clone this repository +2. Compile the launcher +3. You'll need to obtain a x86 MCPE .apk. The easiest way to do so is to use the +[Google Play downloader tool](https://github.com/MCMrARM/Google-Play-API) (it is an console app; remember that +you need to type 'y' when you are asked if you want to use x86 as the architecture, and you must have purchased MCPE +on the Google account you are logging in with; the package name of MCPE is `com.mojang.minecraftpe`) +4. After you have downloaded MCPE, place it in the directory where you'll be running this app, and run ./extract.sh _filename_ +5. Run the launcher! + +If the extract script fails with an error about the .apk not being x86, it means that you have provided it a bad .apk. +You'll need to purchase MCPE on Google Play and use the downloader tool. + +## License and thanks +Most of the code in this repo is licensed under BSD. This project uses libc, libstdc++, libz and libm - libraries +extracted from the Android OS. A modified version of libhybris is also included, which is licensed under GPL. This project +also uses the EGLUT library and FMOD library (for sound). diff --git a/eglut/eglut.c b/eglut/eglut.c new file mode 100644 index 0000000..e221b81 --- /dev/null +++ b/eglut/eglut.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2010 LunarG Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Chia-I Wu + */ + +#include +#include +#include +#include +#include + +#include "EGL/egl.h" +#include "EGL/eglext.h" + +#include "eglutint.h" + +static struct eglut_state _eglut_state = { + .api_mask = EGLUT_OPENGL_ES1_BIT, + .window_width = 300, + .window_height = 300, + .window_fullscreen = EGLUT_WINDOWED, + .verbose = 0, + .num_windows = 0, +}; + +struct eglut_state *_eglut = &_eglut_state; + +void +_eglutFatal(char *format, ...) +{ + va_list args; + + va_start(args, format); + + fprintf(stderr, "EGLUT: "); + vfprintf(stderr, format, args); + va_end(args); + putc('\n', stderr); + + exit(1); +} + +/* return current time (in milliseconds) */ +int +_eglutNow(void) +{ + struct timeval tv; +#ifdef __VMS + (void) gettimeofday(&tv, NULL ); +#else + struct timezone tz; + (void) gettimeofday(&tv, &tz); +#endif + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +static void +_eglutDestroyWindow(struct eglut_window *win) +{ + if (_eglut->surface_type != EGL_PBUFFER_BIT) + eglDestroySurface(_eglut->dpy, win->surface); + + _eglutNativeFiniWindow(win); + + eglDestroyContext(_eglut->dpy, win->context); +} + +static EGLConfig +_eglutChooseConfig(void) +{ + EGLConfig config; + EGLint config_attribs[32]; + EGLint renderable_type, num_configs, i; + + i = 0; + config_attribs[i++] = EGL_RED_SIZE; + config_attribs[i++] = 1; + config_attribs[i++] = EGL_GREEN_SIZE; + config_attribs[i++] = 1; + config_attribs[i++] = EGL_BLUE_SIZE; + config_attribs[i++] = 1; + config_attribs[i++] = EGL_DEPTH_SIZE; + config_attribs[i++] = 1; + config_attribs[i++] = EGL_STENCIL_SIZE; + config_attribs[i++] = 8; + + config_attribs[i++] = EGL_SURFACE_TYPE; + config_attribs[i++] = _eglut->surface_type; + + config_attribs[i++] = EGL_RENDERABLE_TYPE; + renderable_type = 0x0; + if (_eglut->api_mask & EGLUT_OPENGL_BIT) + renderable_type |= EGL_OPENGL_BIT; + if (_eglut->api_mask & EGLUT_OPENGL_ES1_BIT) + renderable_type |= EGL_OPENGL_ES_BIT; + if (_eglut->api_mask & EGLUT_OPENGL_ES2_BIT) + renderable_type |= EGL_OPENGL_ES2_BIT; + if (_eglut->api_mask & EGLUT_OPENVG_BIT) + renderable_type |= EGL_OPENVG_BIT; + config_attribs[i++] = renderable_type; + + config_attribs[i] = EGL_NONE; + + if (!eglChooseConfig(_eglut->dpy, + config_attribs, &config, 1, &num_configs) || !num_configs) + _eglutFatal("failed to choose a config"); + + return config; +} + +static struct eglut_window * +_eglutCreateWindow(const char *title, int x, int y, int w, int h) +{ + struct eglut_window *win; + EGLint context_attribs[4]; + EGLint api, i; + + win = calloc(1, sizeof(*win)); + if (!win) + _eglutFatal("failed to allocate window"); + + win->config = _eglutChooseConfig(); + + i = 0; + context_attribs[i] = EGL_NONE; + + /* multiple APIs? */ + + api = EGL_OPENGL_ES_API; + if (_eglut->api_mask & EGLUT_OPENGL_BIT) { + api = EGL_OPENGL_API; + } + else if (_eglut->api_mask & EGLUT_OPENVG_BIT) { + api = EGL_OPENVG_API; + } + else if (_eglut->api_mask & EGLUT_OPENGL_ES2_BIT) { + context_attribs[i++] = EGL_CONTEXT_CLIENT_VERSION; + context_attribs[i++] = 2; + } + + context_attribs[i] = EGL_NONE; + + eglBindAPI(api); + win->context = eglCreateContext(_eglut->dpy, + win->config, EGL_NO_CONTEXT, context_attribs); + if (!win->context) + _eglutFatal("failed to create context"); + + _eglutNativeInitWindow(win, title, x, y, w, h); + switch (_eglut->surface_type) { + case EGL_WINDOW_BIT: + win->surface = eglCreateWindowSurface(_eglut->dpy, + win->config, win->native.u.window, NULL); + break; + case EGL_PIXMAP_BIT: + win->surface = eglCreatePixmapSurface(_eglut->dpy, + win->config, win->native.u.pixmap, NULL); + break; + case EGL_PBUFFER_BIT: + win->surface = win->native.u.surface; + break; + default: + break; + } + if (win->surface == EGL_NO_SURFACE) + _eglutFatal("failed to create surface"); + + return win; +} + +void +eglutInitAPIMask(int mask) +{ + _eglut->api_mask = mask; +} + +void +eglutInitWindowSize(int width, int height) +{ + _eglut->window_width = width; + _eglut->window_height = height; +} + +void +eglutInit(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-display") == 0) + _eglut->display_name = argv[++i]; + else if (strcmp(argv[i], "-info") == 0) { + _eglut->verbose = 1; + } + } + + _eglutNativeInitDisplay(); + _eglut->dpy = eglGetDisplay(_eglut->native_dpy); + + if (!eglInitialize(_eglut->dpy, &_eglut->major, &_eglut->minor)) + _eglutFatal("failed to initialize EGL display"); + + _eglut->init_time = _eglutNow(); + + printf("EGL_VERSION = %s\n", eglQueryString(_eglut->dpy, EGL_VERSION)); + if (_eglut->verbose) { + printf("EGL_VENDOR = %s\n", eglQueryString(_eglut->dpy, EGL_VENDOR)); + printf("EGL_EXTENSIONS = %s\n", + eglQueryString(_eglut->dpy, EGL_EXTENSIONS)); + printf("EGL_CLIENT_APIS = %s\n", + eglQueryString(_eglut->dpy, EGL_CLIENT_APIS)); + } +} + +int +eglutGet(int state) +{ + int val; + + switch (state) { + case EGLUT_ELAPSED_TIME: + val = _eglutNow() - _eglut->init_time; + break; + case EGLUT_FULLSCREEN_MODE: + val = _eglut->window_fullscreen; + break; + default: + val = -1; + break; + } + + return val; +} + +void +eglutIdleFunc(EGLUTidleCB func) +{ + _eglut->idle_cb = func; +} + +void +eglutPostRedisplay(void) +{ + _eglut->redisplay = 1; +} + +void +eglutMainLoop(void) +{ + struct eglut_window *win = _eglut->current; + + if (!win) + _eglutFatal("no window is created\n"); + + if (win->reshape_cb) + win->reshape_cb(win->native.width, win->native.height); + + _eglutNativeEventLoop(); +} + +void +eglutFini(void) +{ + eglTerminate(_eglut->dpy); + _eglutNativeFiniDisplay(); +} + +void +eglutDestroyWindow(int win) +{ + struct eglut_window *window = _eglut->current; + + if (window->index != win) + return; + + eglMakeCurrent(_eglut->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + _eglutDestroyWindow(_eglut->current); +} + +static void +_eglutDefaultKeyboard(char *key, int modifiers) +{ + if (key && *key == 27) { // 27 is ESC + if (_eglut->current) + eglutDestroyWindow(_eglut->current->index); + eglutFini(); + exit(0); + } +} + + +int +eglutCreateWindow(const char *title) +{ + struct eglut_window *win; + + win = _eglutCreateWindow(title, 0, 0, + _eglut->window_width, _eglut->window_height); + + win->index = _eglut->num_windows++; + win->reshape_cb = NULL; + win->display_cb = NULL; + win->keyboard_cb = _eglutDefaultKeyboard; + win->special_cb = NULL; + win->mouse_cb = NULL; + win->mouse_button_cb = NULL; + + if (!eglMakeCurrent(_eglut->dpy, win->surface, win->surface, win->context)) + _eglutFatal("failed to make window current"); + _eglut->current = win; + + return win->index; +} + +int +eglutGetWindowWidth(void) +{ + struct eglut_window *win = _eglut->current; + return win->native.width; +} + +int +eglutGetWindowHeight(void) +{ + struct eglut_window *win = _eglut->current; + return win->native.height; +} + +void +eglutDisplayFunc(EGLUTdisplayCB func) +{ + struct eglut_window *win = _eglut->current; + win->display_cb = func; + +} + +void +eglutReshapeFunc(EGLUTreshapeCB func) +{ + struct eglut_window *win = _eglut->current; + win->reshape_cb = func; +} + +void +eglutKeyboardFunc(EGLUTkeyboardCB func) +{ + struct eglut_window *win = _eglut->current; + win->keyboard_cb = func; +} + +void +eglutSpecialFunc(EGLUTspecialCB func) +{ + struct eglut_window *win = _eglut->current; + win->special_cb = func; +} + +void +eglutMouseFunc(EGLUTmouseCB func) +{ + struct eglut_window *win = _eglut->current; + win->mouse_cb = func; +} + +void +eglutMouseButtonFunc(EGLUTmouseButtonCB func) +{ + struct eglut_window *win = _eglut->current; + win->mouse_button_cb = func; +} + +void +eglutCloseWindowFunc(EGLUTcloseCB func) +{ + struct eglut_window *win = _eglut->current; + win->close_cb = func; +} \ No newline at end of file diff --git a/eglut/eglut.h b/eglut/eglut.h new file mode 100644 index 0000000..86312b7 --- /dev/null +++ b/eglut/eglut.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010 LunarG Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Chia-I Wu + */ + +#ifndef EGLUT_H +#define EGLUT_H + +/* used by eglutInitAPIMask */ +enum { + EGLUT_OPENGL_BIT = 0x1, + EGLUT_OPENGL_ES1_BIT = 0x2, + EGLUT_OPENGL_ES2_BIT = 0x4, + EGLUT_OPENVG_BIT = 0x8 +}; + +/* used by EGLUTspecialCB */ +enum { + /* function keys */ + EGLUT_KEY_F1, + EGLUT_KEY_F2, + EGLUT_KEY_F3, + EGLUT_KEY_F4, + EGLUT_KEY_F5, + EGLUT_KEY_F6, + EGLUT_KEY_F7, + EGLUT_KEY_F8, + EGLUT_KEY_F9, + EGLUT_KEY_F10, + EGLUT_KEY_F11, + EGLUT_KEY_F12, + + /* directional keys */ + EGLUT_KEY_LEFT, + EGLUT_KEY_UP, + EGLUT_KEY_RIGHT, + EGLUT_KEY_DOWN, +}; + +/* used by eglutGet */ +enum { + EGLUT_ELAPSED_TIME, + EGLUT_FULLSCREEN_MODE +}; + +/* used by EGLUTkeyboardCB */ +enum { + EGLUT_KEY_PRESS, + EGLUT_KEY_RELEASE, + EGLUT_KEY_REPEAT +}; + +/* used by EGLUTmouseButtonCB */ +enum { + EGLUT_MOUSE_PRESS, + EGLUT_MOUSE_RELEASE +}; + +enum { + EGLUT_POINTER_INVISIBLE = 0, + EGLUT_POINTER_VISIBLE = 1 +}; + +enum { + EGLUT_WINDOWED = 0, + EGLUT_FULLSCREEN = 1 +}; + +typedef void (*EGLUTidleCB)(void); +typedef void (*EGLUTreshapeCB)(int, int); +typedef void (*EGLUTdisplayCB)(void); +typedef void (*EGLUTkeyboardCB)(char[5], int); +typedef void (*EGLUTspecialCB)(int, int); +typedef void (*EGLUTmouseCB)(int, int); +typedef void (*EGLUTmouseButtonCB)(int, int, int, int); +typedef void (*EGLUTcloseCB)(void); + +void eglutInitAPIMask(int mask); +void eglutInitWindowSize(int width, int height); +void eglutInit(int argc, char **argv); + +int eglutGet(int state); + +void eglutIdleFunc(EGLUTidleCB func); +void eglutPostRedisplay(void); + +void eglutMainLoop(void); +void eglutFini(void); + +int eglutCreateWindow(const char *title); +void eglutDestroyWindow(int win); + +int eglutGetWindowWidth(void); +int eglutGetWindowHeight(void); +int eglutToggleFullscreen(void); + +void eglutDisplayFunc(EGLUTdisplayCB func); +void eglutReshapeFunc(EGLUTreshapeCB func); +void eglutKeyboardFunc(EGLUTkeyboardCB func); +void eglutSpecialFunc(EGLUTspecialCB func); +void eglutMouseFunc(EGLUTmouseCB func); +void eglutMouseButtonFunc(EGLUTmouseButtonCB func); +void eglutCloseWindowFunc(EGLUTcloseCB func); + +void eglutWarpMousePointer(int x, int y); +void eglutSetMousePointerVisiblity(int visible); + +#endif /* EGLUT_H */ \ No newline at end of file diff --git a/eglut/eglut_wayland.c b/eglut/eglut_wayland.c new file mode 100644 index 0000000..4be9451 --- /dev/null +++ b/eglut/eglut_wayland.c @@ -0,0 +1,222 @@ +#include +#include + +#include +#include +#include + +#include "eglutint.h" + +struct display { + struct wl_display *display; + struct wl_compositor *compositor; + struct wl_shell *shell; + uint32_t mask; +}; + +struct window { + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + struct wl_callback *callback; +}; + +static struct display display = {0, }; +static struct window window = {0, }; + +static void +registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, + const char *interface, uint32_t version) +{ + struct display *d = data; + + if (strcmp(interface, "wl_compositor") == 0) { + d->compositor = + wl_registry_bind(registry, id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "wl_shell") == 0) { + d->shell = wl_registry_bind(registry, id, &wl_shell_interface, 1); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static void +sync_callback(void *data, struct wl_callback *callback, uint32_t serial) +{ + int *done = data; + + *done = 1; + wl_callback_destroy(callback); +} + +static const struct wl_callback_listener sync_listener = { + sync_callback +}; + +static int +wayland_roundtrip(struct wl_display *display) +{ + struct wl_callback *callback; + int done = 0, ret = 0; + + callback = wl_display_sync(display); + wl_callback_add_listener(callback, &sync_listener, &done); + while (ret != -1 && !done) + ret = wl_display_dispatch(display); + + if (!done) + wl_callback_destroy(callback); + + return ret; +} + +void +_eglutNativeInitDisplay(void) +{ + struct wl_registry *registry; + + _eglut->native_dpy = display.display = wl_display_connect(NULL); + + if (!_eglut->native_dpy) + _eglutFatal("failed to initialize native display"); + + registry = wl_display_get_registry(_eglut->native_dpy); + wl_registry_add_listener(registry, ®istry_listener, &display); + wayland_roundtrip(_eglut->native_dpy); + wl_registry_destroy(registry); + + _eglut->surface_type = EGL_WINDOW_BIT; +} + +void +_eglutNativeFiniDisplay(void) +{ + wl_display_flush(_eglut->native_dpy); + wl_display_disconnect(_eglut->native_dpy); +} + +void +_eglutNativeInitWindow(struct eglut_window *win, const char *title, + int x, int y, int w, int h) +{ + struct wl_egl_window *native; + struct wl_region *region; + + window.surface = wl_compositor_create_surface(display.compositor); + + region = wl_compositor_create_region(display.compositor); + wl_region_add(region, 0, 0, w, h); + wl_surface_set_opaque_region(window.surface, region); + wl_region_destroy(region); + + window.shell_surface = wl_shell_get_shell_surface(display.shell, + window.surface); + native = wl_egl_window_create(window.surface, w, h); + + wl_shell_surface_set_toplevel(window.shell_surface); + + win->native.u.window = native; + win->native.width = w; + win->native.height = h; +} + +void +_eglutNativeFiniWindow(struct eglut_window *win) +{ + wl_egl_window_destroy(win->native.u.window); + + wl_shell_surface_destroy(window.shell_surface); + wl_surface_destroy(window.surface); + + if (window.callback) + wl_callback_destroy(window.callback); +} + +static void + draw(void *data, struct wl_callback *callback, uint32_t time); + +static const struct wl_callback_listener frame_listener = { + draw +}; + +static void +draw(void *data, struct wl_callback *callback, uint32_t time) +{ + struct window *window = (struct window *)data; + struct eglut_window *win = _eglut->current; + + if (win->display_cb) + win->display_cb(); + eglSwapBuffers(_eglut->dpy, win->surface); + + if (callback) + wl_callback_destroy(callback); + + window->callback = wl_surface_frame(window->surface); + wl_callback_add_listener(window->callback, &frame_listener, window); +} + +void +_eglutNativeEventLoop(void) +{ + struct pollfd pollfd; + int ret; + + draw(&window, NULL, 0); + + pollfd.fd = wl_display_get_fd(display.display); + pollfd.events = POLLIN; + pollfd.revents = 0; + + while (1) { + wl_display_dispatch_pending(display.display); + + if (_eglut->idle_cb) + _eglut->idle_cb(); + + ret = wl_display_flush(display.display); + if (ret < 0 && errno == EAGAIN) + pollfd.events |= POLLOUT; + else if (ret < 0) + break; + + if (poll(&pollfd, 1, _eglut->redisplay ? 0 : -1) == -1) + break; + + if (pollfd.revents & (POLLERR | POLLHUP)) + break; + + if (pollfd.revents & POLLIN) { + ret = wl_display_dispatch(display.display); + if (ret == -1) + break; + } + + if (pollfd.revents & POLLOUT) { + ret = wl_display_flush(display.display); + if (ret == 0) + pollfd.events &= ~POLLOUT; + else if (ret == -1 && errno != EAGAIN) + break; + } + + if (_eglut->redisplay) { + struct eglut_window *win = _eglut->current; + + _eglut->redisplay = 0; + + if (win->display_cb) + win->display_cb(); + + eglSwapBuffers(_eglut->dpy, win->surface); + } + } +} \ No newline at end of file diff --git a/eglut/eglut_x11.c b/eglut/eglut_x11.c new file mode 100644 index 0000000..fb4ba6e --- /dev/null +++ b/eglut/eglut_x11.c @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2010 LunarG Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Chia-I Wu + */ + +#include +#include +#include +#include +#include + +#include "eglutint.h" + +void +_eglutNativeInitDisplay(void) +{ + _eglut->native_dpy = XOpenDisplay(_eglut->display_name); + if (!_eglut->native_dpy) + _eglutFatal("failed to initialize native display"); + + _eglut->surface_type = EGL_WINDOW_BIT; +} + +void +_eglutNativeFiniDisplay(void) +{ + XCloseDisplay(_eglut->native_dpy); + _eglut->native_dpy = NULL; +} + +XIC x11_ic; + +void +_eglutNativeInitWindow(struct eglut_window *win, const char *title, + int x, int y, int w, int h) +{ + XVisualInfo *visInfo, visTemplate; + int num_visuals; + Window root, xwin; + XSetWindowAttributes attr; + unsigned long mask; + EGLint vid; + + if (!eglGetConfigAttrib(_eglut->dpy, + win->config, EGL_NATIVE_VISUAL_ID, &vid)) + _eglutFatal("failed to get visual id"); + + /* The X window visual must match the EGL config */ + visTemplate.visualid = vid; + visInfo = XGetVisualInfo(_eglut->native_dpy, + VisualIDMask, &visTemplate, &num_visuals); + if (!visInfo) + _eglutFatal("failed to get an visual of id 0x%x", vid); + + root = RootWindow(_eglut->native_dpy, DefaultScreen(_eglut->native_dpy)); + + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap(_eglut->native_dpy, + root, visInfo->visual, AllocNone); + attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + xwin = XCreateWindow(_eglut->native_dpy, root, x, y, w, h, + 0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr); + if (!xwin) + _eglutFatal("failed to create a window"); + + XFree(visInfo); + + /* set hints and properties */ + { + XSizeHints sizehints; + sizehints.x = x; + sizehints.y = y; + sizehints.width = w; + sizehints.height = h; + sizehints.flags = USSize | USPosition; + XSetNormalHints(_eglut->native_dpy, xwin, &sizehints); + XSetStandardProperties(_eglut->native_dpy, xwin, + title, title, None, (char **) NULL, 0, &sizehints); + } + + XMapWindow(_eglut->native_dpy, xwin); + + win->native.u.window = xwin; + win->native.width = w; + win->native.height = h; + + Atom WM_DELETE_WINDOW = XInternAtom(_eglut->native_dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(_eglut->native_dpy, xwin, &WM_DELETE_WINDOW, 1); + + XIM im = XOpenIM(_eglut->native_dpy, NULL, NULL, NULL); + if (im != NULL) { + x11_ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, xwin, NULL); + if (x11_ic != NULL) { + XSetICFocus(x11_ic); + } + } +} + +void +_eglutNativeFiniWindow(struct eglut_window *win) +{ + XDestroyWindow(_eglut->native_dpy, win->native.u.window); +} + +static int +lookup_keysym(KeySym sym) +{ + int special; + + switch (sym) { + case XK_F1: + special = EGLUT_KEY_F1; + break; + case XK_F2: + special = EGLUT_KEY_F2; + break; + case XK_F3: + special = EGLUT_KEY_F3; + break; + case XK_F4: + special = EGLUT_KEY_F4; + break; + case XK_F5: + special = EGLUT_KEY_F5; + break; + case XK_F6: + special = EGLUT_KEY_F6; + break; + case XK_F7: + special = EGLUT_KEY_F7; + break; + case XK_F8: + special = EGLUT_KEY_F8; + break; + case XK_F9: + special = EGLUT_KEY_F9; + break; + case XK_F10: + special = EGLUT_KEY_F10; + break; + case XK_F11: + special = EGLUT_KEY_F11; + break; + case XK_F12: + special = EGLUT_KEY_F12; + break; + case XK_KP_Left: + case XK_Left: + special = EGLUT_KEY_LEFT; + break; + case XK_KP_Up: + case XK_Up: + special = EGLUT_KEY_UP; + break; + case XK_KP_Right: + case XK_Right: + special = EGLUT_KEY_RIGHT; + break; + case XK_KP_Down: + case XK_Down: + special = EGLUT_KEY_DOWN; + break; + default: + special = -1; + break; + } + + return special; +} + +static void +next_event(struct eglut_window *win) +{ + int redraw = 0; + XEvent event, ahead; + + if (!XPending(_eglut->native_dpy)) { + /* there is an idle callback */ + if (_eglut->idle_cb) { + _eglut->idle_cb(); + return; + } + + /* the app requests re-display */ + if (_eglut->redisplay) + return; + } + + /* block for next event */ + XNextEvent(_eglut->native_dpy, &event); + + if (XFilterEvent(&event, win->native.u.window)) { + _eglut->redisplay = redraw; + return; + } + + switch (event.type) { + case Expose: + redraw = 1; + break; + case ConfigureNotify: + win->native.width = event.xconfigure.width; + win->native.height = event.xconfigure.height; + if (win->reshape_cb) + win->reshape_cb(win->native.width, win->native.height); + break; + case KeyPress: + case KeyRelease: + { + char buffer[5]; + memset(buffer, 0, 5); + KeySym sym; + int r; + int type; + if (event.type == KeyPress) { + r = Xutf8LookupString(x11_ic, (XKeyPressedEvent*) &event, buffer, sizeof(buffer), &sym, NULL); + type = EGLUT_KEY_PRESS; + } else { + r = XLookupString(&event.xkey, buffer, sizeof(buffer), &sym, NULL); + type = EGLUT_KEY_RELEASE; + } + + if (event.type == KeyRelease) { + if (XEventsQueued(_eglut->native_dpy, QueuedAfterReading)) { + XPeekEvent(_eglut->native_dpy, &ahead); + if (ahead.type == KeyPress && + ahead.xkey.window == event.xkey.window && + ahead.xkey.keycode == event.xkey.keycode && + ahead.xkey.time == event.xkey.time) { + type = EGLUT_KEY_REPEAT; + XNextEvent(_eglut->native_dpy, &event); + } + } + } + + if (r > 0 && win->keyboard_cb) { + win->keyboard_cb(buffer, type); + } + + if (win->special_cb) { + /*r = lookup_keysym(sym); + if (r == -1)*/ + r = sym; + if (r >= 0) + win->special_cb(r, type); + } + if (type != EGLUT_KEY_REPEAT) + redraw = 1; + break; + } + case MotionNotify: + { + if (win->mouse_cb) + win->mouse_cb(event.xmotion.x, event.xmotion.y); + break; + } + case ButtonPress: + { + if (win->mouse_button_cb) + win->mouse_button_cb(event.xbutton.x, event.xbutton.y, event.xbutton.button, EGLUT_MOUSE_PRESS); + break; + } + case ButtonRelease: + { + if (win->mouse_button_cb) + win->mouse_button_cb(event.xbutton.x, event.xbutton.y, event.xbutton.button, EGLUT_MOUSE_RELEASE); + break; + } + case ClientMessage: + { + if ((ulong) event.xclient.data.l[0] == XInternAtom(_eglut->native_dpy, "WM_DELETE_WINDOW", False)) { + if (win->close_cb) { + win->close_cb(); + } else { + if (_eglut->current) + eglutDestroyWindow(_eglut->current->index); + eglutFini(); + } + } + } + default: + ; /*no-op*/ + } + + _eglut->redisplay = redraw; +} + +void +_eglutNativeEventLoop(void) +{ + while (1) { + struct eglut_window *win = _eglut->current; + + if (_eglut->native_dpy == NULL) + break; + + next_event(win); + + if (_eglut->redisplay) { + _eglut->redisplay = 0; + + if (win->display_cb) + win->display_cb(); + eglSwapBuffers(_eglut->dpy, win->surface); + } + } +} + +void eglutWarpMousePointer(int x, int y) { + XWarpPointer(_eglut->native_dpy, None, _eglut->current->native.u.window, 0, 0, 0, 0, x, y); + XFlush(_eglut->native_dpy); +} + +void eglutSetMousePointerVisiblity(int visible) { + if (visible == EGLUT_POINTER_INVISIBLE) { + char emptyData[] = {0, 0, 0, 0, 0, 0, 0, 0}; + XColor black; + black.red = 0; + black.green = 0; + black.blue = 0; + Pixmap emptyBitmap = XCreateBitmapFromData(_eglut->native_dpy, _eglut->current->native.u.window, emptyData, 8, 8); + Cursor cursor = XCreatePixmapCursor(_eglut->native_dpy, emptyBitmap, emptyBitmap, &black, &black, 0, 0); + XDefineCursor(_eglut->native_dpy, _eglut->current->native.u.window, cursor); + XFreeCursor(_eglut->native_dpy, cursor); + XFreePixmap(_eglut->native_dpy, emptyBitmap); + } else if (visible == EGLUT_POINTER_VISIBLE) { + XUndefineCursor(_eglut->native_dpy, _eglut->current->native.u.window); + } +} +int eglutToggleFullscreen() +{ + // http://stackoverflow.com/questions/10897503/opening-a-fullscreen-opengl-window + _eglut->window_fullscreen = (_eglut->window_fullscreen == EGLUT_WINDOWED ? EGLUT_FULLSCREEN : EGLUT_WINDOWED); + Atom wm_state = XInternAtom(_eglut->native_dpy, "_NET_WM_STATE", False); + Atom fullscreen = XInternAtom(_eglut->native_dpy, "_NET_WM_STATE_FULLSCREEN", False); + + XEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = _eglut->current->native.u.window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = _eglut->window_fullscreen; + xev.xclient.data.l[1] = fullscreen; + xev.xclient.data.l[2] = 0; + + XMapWindow(_eglut->native_dpy, _eglut->current->native.u.window); + + XSendEvent (_eglut->native_dpy, DefaultRootWindow(_eglut->native_dpy), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + XFlush(_eglut->native_dpy); + return -1; +} \ No newline at end of file diff --git a/eglut/eglutint.h b/eglut/eglutint.h new file mode 100644 index 0000000..8545ac7 --- /dev/null +++ b/eglut/eglutint.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 LunarG Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Chia-I Wu + */ + +#ifndef _EGLUTINT_H_ +#define _EGLUTINT_H_ + +#include "EGL/egl.h" +#include "eglut.h" + +struct eglut_window { + EGLConfig config; + EGLContext context; + + /* initialized by native display */ + struct { + union { + EGLNativeWindowType window; + EGLNativePixmapType pixmap; + EGLSurface surface; /* pbuffer or screen surface */ + } u; + int width, height; + } native; + + EGLSurface surface; + + int index; + + EGLUTreshapeCB reshape_cb; + EGLUTdisplayCB display_cb; + EGLUTkeyboardCB keyboard_cb; + EGLUTspecialCB special_cb; + EGLUTmouseCB mouse_cb; + EGLUTmouseButtonCB mouse_button_cb; + EGLUTcloseCB close_cb; +}; + +struct eglut_state { + int api_mask; + int window_width, window_height; + int window_fullscreen; + const char *display_name; + int verbose; + int init_time; + + EGLUTidleCB idle_cb; + + int num_windows; + + /* initialized by native display */ + EGLNativeDisplayType native_dpy; + EGLint surface_type; + + EGLDisplay dpy; + EGLint major, minor; + + struct eglut_window *current; + + int redisplay; +}; + +extern struct eglut_state *_eglut; + +void + _eglutFatal(char *format, ...); + +int + _eglutNow(void); + +void + _eglutNativeInitDisplay(void); + +void + _eglutNativeFiniDisplay(void); + +void + _eglutNativeInitWindow(struct eglut_window *win, const char *title, + int x, int y, int w, int h); + +void + _eglutNativeFiniWindow(struct eglut_window *win); + +void + _eglutNativeEventLoop(void); + +#endif /* _EGLUTINT_H_ */ \ No newline at end of file diff --git a/extract.sh b/extract.sh new file mode 100755 index 0000000..e37d395 --- /dev/null +++ b/extract.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +if [ -z ${1} ]; then + echo "Please specify the archive" + exit +fi +if [ ! -f ${1} ]; then + echo "The specified file doesn't exist!" + exit +fi +if ! [[ ${1} =~ \.apk$ ]]; then + echo "The file must be an .apk" + exit +fi + +rm -rf assets/ + +unzip "${1}" "assets/*" +unzip -p "${1}" lib/x86/libminecraftpe.so > libs/libminecraftpe.so diff --git a/hybris/include/hybris/binding.h b/hybris/include/hybris/binding.h new file mode 100644 index 0000000..641f652 --- /dev/null +++ b/hybris/include/hybris/binding.h @@ -0,0 +1,448 @@ + +/** + * Copyright (C) 2013 Simon Busch + * 2012 Canonical Ltd + * 2013 Jolla Ltd. + * + * Auto-generated via "generate_wrapper_macros.py" + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#ifndef HYBRIS_BINDING_H_ +#define HYBRIS_BINDING_H_ + +/* floating_point_abi.h defines FP_ATTRIB */ +#include "floating_point_abi.h" + +void *android_dlopen(const char *filename, int flag); +void *android_dlsym(void *name, const char *symbol); +int android_dlclose(void *handle); +const char *android_dlerror(void); +int android_dladdr(const void *addr, void *info); + + + +/** + * XXX AUTO-GENERATED FILE XXX + * + * Do not edit this file directly, but update the templates in + * utils/generate_wrapper_macros.py and run it again to build + * an updated version of this header file: + * + * python utils/generate_wrapper_macros.py > \ + * hybris/include/hybris/common/binding.h + * + * If you need macros with more arguments, just customize the + * MAX_ARGS variable in generate_wrapper_macros.py. + * + * XXX AUTO-GENERATED FILE XXX + **/ + + +#define HYBRIS_DLSYSM(name, fptr, sym) \ + if (!name##_handle) \ + hybris_##name##_initialize(); \ + if (*(fptr) == NULL) \ + { \ + *(fptr) = (void *) android_dlsym(name##_handle, sym); \ + } + +#define HYBRIS_LIBRARY_INITIALIZE(name, path) \ + void *name##_handle; \ + void hybris_##name##_initialize() \ + { \ + name##_handle = android_dlopen(path, RTLD_LAZY); \ + } + + + +#define HYBRIS_IMPLEMENT_FUNCTION0(name, return_type, symbol) \ + return_type symbol() \ + { \ + static return_type (*f)() FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION1(name, return_type, symbol, a1) \ + return_type symbol(a1 n1) \ + { \ + static return_type (*f)(a1) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION2(name, return_type, symbol, a1, a2) \ + return_type symbol(a1 n1, a2 n2) \ + { \ + static return_type (*f)(a1, a2) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION3(name, return_type, symbol, a1, a2, a3) \ + return_type symbol(a1 n1, a2 n2, a3 n3) \ + { \ + static return_type (*f)(a1, a2, a3) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION4(name, return_type, symbol, a1, a2, a3, a4) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4) \ + { \ + static return_type (*f)(a1, a2, a3, a4) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION5(name, return_type, symbol, a1, a2, a3, a4, a5) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION6(name, return_type, symbol, a1, a2, a3, a4, a5, a6) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION7(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION8(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION9(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION10(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION11(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION12(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION13(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION14(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION15(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION16(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15, a16 n16) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION17(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15, a16 n16, a17 n17) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION18(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15, a16 n16, a17 n17, a18 n18) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18); \ + } + + +#define HYBRIS_IMPLEMENT_FUNCTION19(name, return_type, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) \ + return_type symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15, a16 n16, a17 n17, a18 n18, a19 n19) \ + { \ + static return_type (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + return f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION0(name, symbol) \ + void symbol() \ + { \ + static void (*f)() FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION1(name, symbol, a1) \ + void symbol(a1 n1) \ + { \ + static void (*f)(a1) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION2(name, symbol, a1, a2) \ + void symbol(a1 n1, a2 n2) \ + { \ + static void (*f)(a1, a2) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION3(name, symbol, a1, a2, a3) \ + void symbol(a1 n1, a2 n2, a3 n3) \ + { \ + static void (*f)(a1, a2, a3) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION4(name, symbol, a1, a2, a3, a4) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4) \ + { \ + static void (*f)(a1, a2, a3, a4) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION5(name, symbol, a1, a2, a3, a4, a5) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5) \ + { \ + static void (*f)(a1, a2, a3, a4, a5) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION6(name, symbol, a1, a2, a3, a4, a5, a6) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION7(name, symbol, a1, a2, a3, a4, a5, a6, a7) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION8(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION9(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION10(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION11(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION12(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION13(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION14(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION15(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION16(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15, a16 n16) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION17(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15, a16 n16, a17 n17) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION18(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15, a16 n16, a17 n17, a18 n18) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18); \ + } + + +#define HYBRIS_IMPLEMENT_VOID_FUNCTION19(name, symbol, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) \ + void symbol(a1 n1, a2 n2, a3 n3, a4 n4, a5 n5, a6 n6, a7 n7, a8 n8, a9 n9, a10 n10, a11 n11, a12 n12, a13 n13, a14 n14, a15 n15, a16 n16, a17 n17, a18 n18, a19 n19) \ + { \ + static void (*f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) FP_ATTRIB = NULL; \ + HYBRIS_DLSYSM(name, &f, #symbol); \ + f(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19); \ + } + + +/** + * XXX AUTO-GENERATED FILE XXX + * + * Do not edit this file directly, but update the templates in + * utils/generate_wrapper_macros.py and run it again to build + * an updated version of this header file: + * + * python utils/generate_wrapper_macros.py > \ + * hybris/include/hybris/common/binding.h + * + * If you need macros with more arguments, just customize the + * MAX_ARGS variable in generate_wrapper_macros.py. + * + * XXX AUTO-GENERATED FILE XXX + **/ + + +#endif /* HYBRIS_BINDING_H_ */ + diff --git a/hybris/include/hybris/dlfcn.h b/hybris/include/hybris/dlfcn.h new file mode 100644 index 0000000..99dbe73 --- /dev/null +++ b/hybris/include/hybris/dlfcn.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _HYBRIS_DLFCN_H_ +#define _HYBRIS_DLFCN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *hybris_dlopen(const char *filename, int flag); +void *hybris_dlsym(void *handle, const char *symbol); +int hybris_dlclose(void *handle); +int hybris_dladdr(const void *addr, Dl_info *info); +char *hybris_dlerror(void); + +#ifdef __cplusplus +} +#endif + +#endif // _HYBRIS_DLFCN_H_ + +// vim: noai:ts=4:sw=4:ss=4:expandtab diff --git a/hybris/include/hybris/floating_point_abi.h b/hybris/include/hybris/floating_point_abi.h new file mode 100644 index 0000000..3e14c0e --- /dev/null +++ b/hybris/include/hybris/floating_point_abi.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Jolla Ltd. + * Contact: Thomas Perl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HYBRIS_FLOATING_POINT_ABI_H_ +#define HYBRIS_FLOATING_POINT_ABI_H_ + +/** + * Make sure to use FP_ATTRIB on all functions that are loaded from + * Android (bionic libc) libraries to make sure floating point arguments + * are passed the right way. + * + * See: http://wiki.debian.org/ArmHardFloatPort/VfpComparison + * http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + * + * It doesn't hurt to have it added for non-floating point arguments and + * return types, even though it does not really have any effect. + * + * If you use the convenience macros in hybris/common/binding.h, your + * wrapper functions will automatically make use of this attribute. + **/ + +#ifdef __ARM_PCS_VFP +# define FP_ATTRIB __attribute__((pcs("aapcs"))) +#else +# define FP_ATTRIB +#endif + +#endif /* HYBRIS_FLOATING_POINT_ABI_H_ */ diff --git a/hybris/include/hybris/hook.h b/hybris/include/hybris/hook.h new file mode 100644 index 0000000..d41140d --- /dev/null +++ b/hybris/include/hybris/hook.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _HYBRIS_HOOK_H_ +#define _HYBRIS_HOOK_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void hybris_hook(const char *name, void* func); + +#ifdef __cplusplus +} +#endif + +#endif // _HYBRIS_HOOK_H_ + +// vim: noai:ts=4:sw=4:ss=4:expandtab diff --git a/hybris/include/hybris/properties.h b/hybris/include/hybris/properties.h new file mode 100644 index 0000000..12d56b6 --- /dev/null +++ b/hybris/include/hybris/properties.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 Carsten Munk + * 2013 Simon Busch + * 2008 The Android Open Source Project + * 2013 Canonical Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef PROPERTIES_H_ +#define PROPERTIES_H_ + +#include +#include +#include + +/* Based on Android */ +#define PROP_SERVICE_NAME "property_service" + +#define PROP_NAME_MAX 32 +#define PROP_VALUE_MAX 92 + +/* Only SETPROP is defined by Android, for GETPROP and LISTPROP to work + * an extended Android init service needs to be in place */ +#define PROP_MSG_SETPROP 1 +#define PROP_MSG_GETPROP 2 +#define PROP_MSG_LISTPROP 3 + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct prop_msg_s { + unsigned cmd; + char name[PROP_NAME_MAX]; + char value[PROP_VALUE_MAX]; + } prop_msg_t; + + int property_set(const char *key, const char *value); + int property_get(const char *key, char *value, const char *default_value); + int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie); + +#ifdef __cplusplus +} +#endif + +#endif // PROPERTIES_H_ diff --git a/hybris/src/cache.c b/hybris/src/cache.c new file mode 100644 index 0000000..caee370 --- /dev/null +++ b/hybris/src/cache.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2012 Carsten Munk + * Copyright (c) 2008 The Android Open Source Project + * Copyright (c) 2013 Simon Busch + * Copyright (c) 2013 Canonical Ltd + * Copyright (c) 2013 Jolla Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include + +#include "../include/hybris/properties.h" +#include "properties_p.h" + +struct hybris_prop_value +{ + char *key; + char *value; +}; + +/* surely enough for anyone */ +#define MAX_PROPS 1000 + +/* the sorted prop array */ +static struct hybris_prop_value prop_array[MAX_PROPS]; + +/* the current highest index in prop_array */ +static int max_prop; + +/* helpers */ +static void cache_update(); +static int prop_qcmp(const void *a, const void *b); +static struct hybris_prop_value *cache_find_internal(const char *key); +static void cache_add_internal(const char *key, const char *value); +static void cache_repopulate_internal(FILE *f); +static void cache_empty_internal(); +static void cache_repopulate_cmdline_internal(); + +/* the inode/mtime of the prop cache, used for invalidation */ +static ino_t static_prop_inode; +static time_t static_prop_mtime; + + +/* public: + * find a prop value from the file cache. + * + * the return value is the value of the given property key, or NULL if the + * property key is not found. the returned value is owned by the caller, + * and must be freed. + */ +char *hybris_propcache_find(const char *key) +{ + char *ret = NULL; + + cache_update(); + + /* then look up the key and do a copy if we get a result */ + struct hybris_prop_value *prop = cache_find_internal(key); + if (prop) + return prop->value; + +out: + return ret; +} + +void hybris_propcache_list(hybris_propcache_list_cb cb, void *cookie) +{ + int n; + struct hybris_prop_value *current; + + if (!cb) + return; + + cache_update(); + + for (n = 0; n < max_prop; n++) { + current = &prop_array[n]; + cb(current->key, current->value, cookie); + } +} + +static void cache_update() +{ + struct stat st; + FILE *f = fopen("/system/build.prop", "r"); + char *ret = NULL; + + if (!f) + return; + + /* before searching, we must first determine whether our cache is valid. if + * it isn't, we must discard our results and re-create the cache. + * + * we use fstat here to avoid a race between stat and something else + * touching the file. + */ + if (fstat(fileno(f), &st) != 0) { + perror("cache_find can't stat build.prop"); + goto out; + } + + /* TODO: is there any better way to detect changes? */ + if (static_prop_inode != st.st_ino || + static_prop_mtime != st.st_mtime) { + static_prop_inode = st.st_ino; + static_prop_mtime = st.st_mtime; + + /* cache is stale. fill it back up with fresh data first. */ + cache_empty_internal(); + cache_repopulate_internal(f); + cache_repopulate_cmdline_internal(); + + /* sort by keys */ + qsort(prop_array, max_prop, sizeof(struct hybris_prop_value), prop_qcmp); + } + +out: + fclose(f); +} + +/* private: + * empties the prop cache, ready for repopulation + */ +static void cache_empty_internal() +{ + int i; + for (i = 0; i < max_prop; ++i) { + free(prop_array[i].key); + free(prop_array[i].value); + } + + max_prop = 0; +} + +/* private: + * compares two hybris_prop_value by key, so as to maintain a qsorted array of + * props, and search the array. + */ +static int prop_qcmp(const void *a, const void *b) +{ + struct hybris_prop_value *aa = (struct hybris_prop_value *)a; + struct hybris_prop_value *bb = (struct hybris_prop_value *)b; + + return strcmp(aa->key, bb->key); +} + +/* private: + * find a given key in the in-memory prop cache. + * + * returns the value of the given property key, or NULL if the property is not + * found. Note that this does not pass ownership of the hybris_prop_value or the + * data inside it. + */ +static struct hybris_prop_value *cache_find_internal(const char *key) +{ + struct hybris_prop_value prop_key; + prop_key.key = (char*)key; + + return bsearch(&prop_key, prop_array, max_prop, sizeof(struct hybris_prop_value), prop_qcmp); +} + +/* private: + * add a given property to the in-memory prop cache for later retrieval. + * + * both `key' and `value' are copied from the caller. + */ +static void cache_add_internal(const char *key, const char *value) +{ + /* Skip values that can be bigger than value max */ + if (strlen(value) >= PROP_VALUE_MAX -1) + return; + + /* preserve current behavior of first prop key => match */ + if (cache_find_internal(key)) + return; + + prop_array[max_prop].key = strdup(key); + prop_array[max_prop++].value = strdup(value); + + if (max_prop >= MAX_PROPS) { + fprintf(stderr, "libhybris: ran out of props, increase MAX_PROPS"); + exit(1); + } +} + +/* private: + * repopulates the prop cache from a given file `f'. + */ +static void cache_repopulate_internal(FILE *f) +{ + char buf[1024]; + char *mkey, *value; + + while (fgets(buf, 1024, f) != NULL) { + if (strchr(buf, '\r')) + *(strchr(buf, '\r')) = '\0'; + if (strchr(buf, '\n')) + *(strchr(buf, '\n')) = '\0'; + + mkey = strtok(buf, "="); + + if (!mkey) + continue; + + value = strtok(NULL, "="); + if (!value) + continue; + + cache_add_internal(mkey, value); + } +} + +/* private: + * repopulate the prop cache from /proc/cmdline + */ +static void cache_repopulate_cmdline_internal() +{ + /* Find a key value from the kernel command line, which is parsed + * by Android at init (on an Android working system) */ + char cmdline[1024]; + char *ptr; + int fd; + + fd = open("/proc/cmdline", O_RDONLY); + if (fd >= 0) { + int n = read(fd, cmdline, 1023); + if (n < 0) n = 0; + + /* get rid of trailing newline, it happens */ + if (n > 0 && cmdline[n-1] == '\n') n--; + + cmdline[n] = 0; + close(fd); + } else { + cmdline[0] = 0; + } + + ptr = cmdline; + + while (ptr && *ptr) { + char *x = strchr(ptr, ' '); + if (x != 0) *x++ = 0; + + char *name = ptr; + ptr = x; + + char *value = strchr(name, '='); + int name_len = strlen(name); + + if (value == 0) continue; + *value++ = 0; + if (name_len == 0) continue; + + if (!strncmp(name, "androidboot.", 12) && name_len > 12) { + const char *boot_prop_name = name + 12; + char prop[PROP_NAME_MAX]; + snprintf(prop, sizeof(prop) -1, "ro.%s", boot_prop_name); + + cache_add_internal(prop, value); + } + } +} diff --git a/hybris/src/dlfcn.c b/hybris/src/dlfcn.c new file mode 100644 index 0000000..e6985b1 --- /dev/null +++ b/hybris/src/dlfcn.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "../include/hybris/dlfcn.h" +#include "../include/hybris/binding.h" + +void *hybris_dlopen(const char *filename, int flag) +{ + return android_dlopen(filename,flag); +} + + +void *hybris_dlsym(void *handle, const char *symbol) +{ + return android_dlsym(handle,symbol); +} + + +int hybris_dlclose(void *handle) +{ + return android_dlclose(handle); +} + + +int hybris_dladdr(const void *addr, Dl_info *info) +{ + return android_dladdr(addr, info); +} + + +char *hybris_dlerror(void) +{ + return android_dlerror(); +} + +// vim: noai:ts=4:sw=4:ss=4:expandtab diff --git a/hybris/src/hooks.c b/hybris/src/hooks.c new file mode 100644 index 0000000..014c34c --- /dev/null +++ b/hybris/src/hooks.c @@ -0,0 +1,1869 @@ +/* + * Copyright (c) 2012 Carsten Munk + * Copyright (c) 2012 Canonical Ltd + * Copyright (c) 2013 Christophe Chapuis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "../include/hybris/binding.h" + +#include "hooks_shm.h" + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "../include/hybris/hook.h" +#include "../include/hybris/properties.h" + +static locale_t hybris_locale; +static int locale_inited = 0; +/* TODO: +* - Check if the int arguments at attr_set/get match the ones at Android +* - Check how to deal with memory leaks (specially with static initializers) +* - Check for shared rwlock +*/ + +/* Base address to check for Android specifics */ +#define ANDROID_TOP_ADDR_VALUE_MUTEX 0xFFFF +#define ANDROID_TOP_ADDR_VALUE_COND 0xFFFF +#define ANDROID_TOP_ADDR_VALUE_RWLOCK 0xFFFF + +#define ANDROID_MUTEX_SHARED_MASK 0x2000 +#define ANDROID_COND_SHARED_MASK 0x0001 +#define ANDROID_RWLOCKATTR_SHARED_MASK 0x0010 + +/* For the static initializer types */ +#define ANDROID_PTHREAD_MUTEX_INITIALIZER 0 +#define ANDROID_PTHREAD_RECURSIVE_MUTEX_INITIALIZER 0x4000 +#define ANDROID_PTHREAD_ERRORCHECK_MUTEX_INITIALIZER 0x8000 +#define ANDROID_PTHREAD_COND_INITIALIZER 0 +#define ANDROID_PTHREAD_RWLOCK_INITIALIZER 0 + +/* Debug */ +#include "logging.h" +#define LOGD(message, ...) HYBRIS_DEBUG_LOG(HOOKS, message, ##__VA_ARGS__) + +/* we have a value p: + * - if p <= ANDROID_TOP_ADDR_VALUE_MUTEX then it is an android mutex, not one we processed + * - if p > VMALLOC_END, then the pointer is not a result of malloc ==> it is an shm offset + */ + +struct _hook { + const char *name; + void *func; +}; + +/* Helpers */ +static int hybris_check_android_shared_mutex(unsigned int mutex_addr) +{ + /* If not initialized or initialized by Android, it should contain a low + * address, which is basically just the int values for Android's own + * pthread_mutex_t */ + if ((mutex_addr <= ANDROID_TOP_ADDR_VALUE_MUTEX) && + (mutex_addr & ANDROID_MUTEX_SHARED_MASK)) + return 1; + + return 0; +} + +static int hybris_check_android_shared_cond(unsigned int cond_addr) +{ + /* If not initialized or initialized by Android, it should contain a low + * address, which is basically just the int values for Android's own + * pthread_cond_t */ + if ((cond_addr <= ANDROID_TOP_ADDR_VALUE_COND) && + (cond_addr & ANDROID_COND_SHARED_MASK)) + return 1; + + return 0; +} + +static void hybris_set_mutex_attr(unsigned int android_value, pthread_mutexattr_t *attr) +{ + /* Init already sets as PTHREAD_MUTEX_NORMAL */ + pthread_mutexattr_init(attr); + + if (android_value & ANDROID_PTHREAD_RECURSIVE_MUTEX_INITIALIZER) { + pthread_mutexattr_settype(attr, PTHREAD_MUTEX_RECURSIVE); + } else if (android_value & ANDROID_PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) { + pthread_mutexattr_settype(attr, PTHREAD_MUTEX_ERRORCHECK); + } +} + +static pthread_mutex_t* hybris_alloc_init_mutex(unsigned int android_mutex) +{ + pthread_mutex_t *realmutex = malloc(sizeof(pthread_mutex_t)); + pthread_mutexattr_t attr; + hybris_set_mutex_attr(android_mutex, &attr); + pthread_mutex_init(realmutex, &attr); + return realmutex; +} + +static pthread_cond_t* hybris_alloc_init_cond(void) +{ + pthread_cond_t *realcond = malloc(sizeof(pthread_cond_t)); + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_cond_init(realcond, &attr); + return realcond; +} + +static pthread_rwlock_t* hybris_alloc_init_rwlock(void) +{ + pthread_rwlock_t *realrwlock = malloc(sizeof(pthread_rwlock_t)); + pthread_rwlockattr_t attr; + pthread_rwlockattr_init(&attr); + pthread_rwlock_init(realrwlock, &attr); + return realrwlock; +} + +/* + * utils, such as malloc, memcpy + * + * Useful to handle hacks such as the one applied for Nvidia, and to + * avoid crashes. + * + * */ + +static void *my_malloc(size_t size) +{ + return malloc(size); +} + +static void *my_memcpy(void *dst, const void *src, size_t len) +{ + if (src == NULL || dst == NULL) + return NULL; + + return memcpy(dst, src, len); +} + +static size_t my_strlen(const char *s) +{ + + if (s == NULL) + return -1; + + return strlen(s); +} + +static pid_t my_gettid( void ) +{ + return syscall( __NR_gettid ); +} + +/* + * Main pthread functions + * + * Custom implementations to workaround difference between Bionic and Glibc. + * Our own pthread_create helps avoiding direct handling of TLS. + * + * */ + +static int my_pthread_create(pthread_t *thread, const pthread_attr_t *__attr, + void *(*start_routine)(void*), void *arg) +{ + pthread_attr_t *realattr = NULL; + + if (__attr != NULL) + realattr = (pthread_attr_t *) *(unsigned int *) __attr; + + return pthread_create(thread, realattr, start_routine, arg); +} + +/* + * pthread_attr_* functions + * + * Specific implementations to workaround the differences between at the + * pthread_attr_t struct differences between Bionic and Glibc. + * + * */ + +static int my_pthread_attr_init(pthread_attr_t *__attr) +{ + pthread_attr_t *realattr; + + realattr = malloc(sizeof(pthread_attr_t)); + *((unsigned int *)__attr) = (unsigned int) realattr; + + return pthread_attr_init(realattr); +} + +static int my_pthread_attr_destroy(pthread_attr_t *__attr) +{ + int ret; + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + + ret = pthread_attr_destroy(realattr); + /* We need to release the memory allocated at my_pthread_attr_init + * Possible side effects if destroy is called without our init */ + free(realattr); + + return ret; +} + +static int my_pthread_attr_setdetachstate(pthread_attr_t *__attr, int state) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_setdetachstate(realattr, state); +} + +static int my_pthread_attr_getdetachstate(pthread_attr_t const *__attr, int *state) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_getdetachstate(realattr, state); +} + +static int my_pthread_attr_setschedpolicy(pthread_attr_t *__attr, int policy) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_setschedpolicy(realattr, policy); +} + +static int my_pthread_attr_getschedpolicy(pthread_attr_t const *__attr, int *policy) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_getschedpolicy(realattr, policy); +} + +static int my_pthread_attr_setschedparam(pthread_attr_t *__attr, struct sched_param const *param) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_setschedparam(realattr, param); +} + +static int my_pthread_attr_getschedparam(pthread_attr_t const *__attr, struct sched_param *param) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_getschedparam(realattr, param); +} + +static int my_pthread_attr_setstacksize(pthread_attr_t *__attr, size_t stack_size) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_setstacksize(realattr, stack_size); +} + +static int my_pthread_attr_getstacksize(pthread_attr_t const *__attr, size_t *stack_size) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_getstacksize(realattr, stack_size); +} + +static int my_pthread_attr_setstackaddr(pthread_attr_t *__attr, void *stack_addr) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_setstackaddr(realattr, stack_addr); +} + +static int my_pthread_attr_getstackaddr(pthread_attr_t const *__attr, void **stack_addr) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_getstackaddr(realattr, stack_addr); +} + +static int my_pthread_attr_setstack(pthread_attr_t *__attr, void *stack_base, size_t stack_size) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_setstack(realattr, stack_base, stack_size); +} + +static int my_pthread_attr_getstack(pthread_attr_t const *__attr, void **stack_base, size_t *stack_size) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_getstack(realattr, stack_base, stack_size); +} + +static int my_pthread_attr_setguardsize(pthread_attr_t *__attr, size_t guard_size) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_setguardsize(realattr, guard_size); +} + +static int my_pthread_attr_getguardsize(pthread_attr_t const *__attr, size_t *guard_size) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_getguardsize(realattr, guard_size); +} + +static int my_pthread_attr_setscope(pthread_attr_t *__attr, int scope) +{ + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + return pthread_attr_setscope(realattr, scope); +} + +static int my_pthread_attr_getscope(pthread_attr_t const *__attr) +{ + int scope; + pthread_attr_t *realattr = (pthread_attr_t *) *(unsigned int *) __attr; + + /* Android doesn't have the scope attribute because it always + * returns PTHREAD_SCOPE_SYSTEM */ + pthread_attr_getscope(realattr, &scope); + + return scope; +} + +static int my_pthread_getattr_np(pthread_t thid, pthread_attr_t *__attr) +{ + pthread_attr_t *realattr; + + realattr = malloc(sizeof(pthread_attr_t)); + *((unsigned int *)__attr) = (unsigned int) realattr; + + return pthread_getattr_np(thid, realattr); +} + +/* + * pthread_mutex* functions + * + * Specific implementations to workaround the differences between at the + * pthread_mutex_t struct differences between Bionic and Glibc. + * + * */ + +static int my_pthread_mutex_init(pthread_mutex_t *__mutex, + __const pthread_mutexattr_t *__mutexattr) +{ + pthread_mutex_t *realmutex = NULL; + + int pshared = 0; + if (__mutexattr) + pthread_mutexattr_getpshared(__mutexattr, &pshared); + + if (!pshared) { + /* non shared, standard mutex: use malloc */ + realmutex = malloc(sizeof(pthread_mutex_t)); + + *((unsigned int *)__mutex) = (unsigned int) realmutex; + } + else { + /* process-shared mutex: use the shared memory segment */ + hybris_shm_pointer_t handle = hybris_shm_alloc(sizeof(pthread_mutex_t)); + + *((hybris_shm_pointer_t *)__mutex) = handle; + + if (handle) + realmutex = (pthread_mutex_t *)hybris_get_shmpointer(handle); + } + + return pthread_mutex_init(realmutex, __mutexattr); +} + +static int my_pthread_mutex_destroy(pthread_mutex_t *__mutex) +{ + int ret; + + if (!__mutex) + return EINVAL; + + pthread_mutex_t *realmutex = (pthread_mutex_t *) *(unsigned int *) __mutex; + + if (!realmutex) + return EINVAL; + + if (!hybris_is_pointer_in_shm((void*)realmutex)) { + ret = pthread_mutex_destroy(realmutex); + free(realmutex); + } + else { + realmutex = (pthread_mutex_t *)hybris_get_shmpointer((hybris_shm_pointer_t)realmutex); + ret = pthread_mutex_destroy(realmutex); + } + + *((unsigned int *)__mutex) = 0; + + return ret; +} + +static int my_pthread_mutex_lock(pthread_mutex_t *__mutex) +{ + if (!__mutex) { + LOGD("Null mutex lock, not locking."); + return 0; + } + + unsigned int value = (*(unsigned int *) __mutex); + if (hybris_check_android_shared_mutex(value)) { + LOGD("Shared mutex with Android, not locking."); + return 0; + } + + pthread_mutex_t *realmutex = (pthread_mutex_t *) value; + if (hybris_is_pointer_in_shm((void*)value)) + realmutex = (pthread_mutex_t *)hybris_get_shmpointer((hybris_shm_pointer_t)value); + + if (value <= ANDROID_TOP_ADDR_VALUE_MUTEX) { + realmutex = hybris_alloc_init_mutex(value); + *((unsigned int *)__mutex) = (unsigned int) realmutex; + } + + return pthread_mutex_lock(realmutex); +} + +static int my_pthread_mutex_trylock(pthread_mutex_t *__mutex) +{ + unsigned int value = (*(unsigned int *) __mutex); + + if (hybris_check_android_shared_mutex(value)) { + LOGD("Shared mutex with Android, not try locking."); + return 0; + } + + pthread_mutex_t *realmutex = (pthread_mutex_t *) value; + if (hybris_is_pointer_in_shm((void*)value)) + realmutex = (pthread_mutex_t *)hybris_get_shmpointer((hybris_shm_pointer_t)value); + + if (value <= ANDROID_TOP_ADDR_VALUE_MUTEX) { + realmutex = hybris_alloc_init_mutex(value); + *((unsigned int *)__mutex) = (unsigned int) realmutex; + } + + return pthread_mutex_trylock(realmutex); +} + +static int my_pthread_mutex_unlock(pthread_mutex_t *__mutex) +{ + if (!__mutex) { + LOGD("Null mutex lock, not unlocking."); + return 0; + } + + unsigned int value = (*(unsigned int *) __mutex); + if (hybris_check_android_shared_mutex(value)) { + LOGD("Shared mutex with Android, not unlocking."); + return 0; + } + + if (value <= ANDROID_TOP_ADDR_VALUE_MUTEX) { + LOGD("Trying to unlock a lock that's not locked/initialized" + " by Hybris, not unlocking."); + return 0; + } + + pthread_mutex_t *realmutex = (pthread_mutex_t *) value; + if (hybris_is_pointer_in_shm((void*)value)) + realmutex = (pthread_mutex_t *)hybris_get_shmpointer((hybris_shm_pointer_t)value); + + return pthread_mutex_unlock(realmutex); +} + +static int my_pthread_mutex_lock_timeout_np(pthread_mutex_t *__mutex, unsigned __msecs) +{ + struct timespec tv; + pthread_mutex_t *realmutex; + unsigned int value = (*(unsigned int *) __mutex); + + if (hybris_check_android_shared_mutex(value)) { + LOGD("Shared mutex with Android, not lock timeout np."); + return 0; + } + + realmutex = (pthread_mutex_t *) value; + + if (value <= ANDROID_TOP_ADDR_VALUE_MUTEX) { + realmutex = hybris_alloc_init_mutex(value); + *((int *)__mutex) = (int) realmutex; + } + + clock_gettime(CLOCK_REALTIME, &tv); + tv.tv_sec += __msecs/1000; + tv.tv_nsec += (__msecs % 1000) * 1000000; + if (tv.tv_nsec >= 1000000000) { + tv.tv_sec++; + tv.tv_nsec -= 1000000000; + } + + return pthread_mutex_timedlock(realmutex, &tv); +} + +static int my_pthread_mutexattr_setpshared(pthread_mutexattr_t *__attr, + int pshared) +{ + return pthread_mutexattr_setpshared(__attr, pshared); +} + +/* + * pthread_cond* functions + * + * Specific implementations to workaround the differences between at the + * pthread_cond_t struct differences between Bionic and Glibc. + * + * */ + +static int my_pthread_cond_init(pthread_cond_t *cond, + const pthread_condattr_t *attr) +{ + pthread_cond_t *realcond = NULL; + + int pshared = 0; + + if (attr) + pthread_condattr_getpshared(attr, &pshared); + + if (!pshared) { + /* non shared, standard cond: use malloc */ + realcond = malloc(sizeof(pthread_cond_t)); + + *((unsigned int *) cond) = (unsigned int) realcond; + } + else { + /* process-shared condition: use the shared memory segment */ + hybris_shm_pointer_t handle = hybris_shm_alloc(sizeof(pthread_cond_t)); + + *((unsigned int *)cond) = (unsigned int) handle; + + if (handle) + realcond = (pthread_cond_t *)hybris_get_shmpointer(handle); + } + + return pthread_cond_init(realcond, attr); +} + +static int my_pthread_cond_destroy(pthread_cond_t *cond) +{ + int ret; + pthread_cond_t *realcond = (pthread_cond_t *) *(unsigned int *) cond; + + if (!realcond) { + return EINVAL; + } + + if (!hybris_is_pointer_in_shm((void*)realcond)) { + ret = pthread_cond_destroy(realcond); + free(realcond); + } + else { + realcond = (pthread_cond_t *)hybris_get_shmpointer((hybris_shm_pointer_t)realcond); + ret = pthread_cond_destroy(realcond); + } + + *((unsigned int *)cond) = 0; + + return ret; +} + +static int my_pthread_cond_broadcast(pthread_cond_t *cond) +{ + unsigned int value = (*(unsigned int *) cond); + if (hybris_check_android_shared_cond(value)) { + LOGD("shared condition with Android, not broadcasting."); + return 0; + } + + pthread_cond_t *realcond = (pthread_cond_t *) value; + if (hybris_is_pointer_in_shm((void*)value)) + realcond = (pthread_cond_t *)hybris_get_shmpointer((hybris_shm_pointer_t)value); + + if (value <= ANDROID_TOP_ADDR_VALUE_COND) { + realcond = hybris_alloc_init_cond(); + *((unsigned int *) cond) = (unsigned int) realcond; + } + + return pthread_cond_broadcast(realcond); +} + +static int my_pthread_cond_signal(pthread_cond_t *cond) +{ + unsigned int value = (*(unsigned int *) cond); + + if (hybris_check_android_shared_cond(value)) { + LOGD("Shared condition with Android, not signaling."); + return 0; + } + + pthread_cond_t *realcond = (pthread_cond_t *) value; + if (hybris_is_pointer_in_shm((void*)value)) + realcond = (pthread_cond_t *)hybris_get_shmpointer((hybris_shm_pointer_t)value); + + if (value <= ANDROID_TOP_ADDR_VALUE_COND) { + realcond = hybris_alloc_init_cond(); + *((unsigned int *) cond) = (unsigned int) realcond; + } + + return pthread_cond_signal(realcond); +} + +static int my_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + /* Both cond and mutex can be statically initialized, check for both */ + unsigned int cvalue = (*(unsigned int *) cond); + unsigned int mvalue = (*(unsigned int *) mutex); + + if (hybris_check_android_shared_cond(cvalue) || + hybris_check_android_shared_mutex(mvalue)) { + LOGD("Shared condition/mutex with Android, not waiting."); + return 0; + } + + pthread_cond_t *realcond = (pthread_cond_t *) cvalue; + if (hybris_is_pointer_in_shm((void*)cvalue)) + realcond = (pthread_cond_t *)hybris_get_shmpointer((hybris_shm_pointer_t)cvalue); + + if (cvalue <= ANDROID_TOP_ADDR_VALUE_COND) { + realcond = hybris_alloc_init_cond(); + *((unsigned int *) cond) = (unsigned int) realcond; + } + + pthread_mutex_t *realmutex = (pthread_mutex_t *) mvalue; + if (hybris_is_pointer_in_shm((void*)mvalue)) + realmutex = (pthread_mutex_t *)hybris_get_shmpointer((hybris_shm_pointer_t)mvalue); + + if (mvalue <= ANDROID_TOP_ADDR_VALUE_MUTEX) { + realmutex = hybris_alloc_init_mutex(mvalue); + *((unsigned int *) mutex) = (unsigned int) realmutex; + } + + return pthread_cond_wait(realcond, realmutex); +} + +static int my_pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, const struct timespec *abstime) +{ + /* Both cond and mutex can be statically initialized, check for both */ + unsigned int cvalue = (*(unsigned int *) cond); + unsigned int mvalue = (*(unsigned int *) mutex); + + if (hybris_check_android_shared_cond(cvalue) || + hybris_check_android_shared_mutex(mvalue)) { + LOGD("Shared condition/mutex with Android, not waiting."); + return 0; + } + + pthread_cond_t *realcond = (pthread_cond_t *) cvalue; + if (hybris_is_pointer_in_shm((void*)cvalue)) + realcond = (pthread_cond_t *)hybris_get_shmpointer((hybris_shm_pointer_t)cvalue); + + if (cvalue <= ANDROID_TOP_ADDR_VALUE_COND) { + realcond = hybris_alloc_init_cond(); + *((unsigned int *) cond) = (unsigned int) realcond; + } + + pthread_mutex_t *realmutex = (pthread_mutex_t *) mvalue; + if (hybris_is_pointer_in_shm((void*)mvalue)) + realmutex = (pthread_mutex_t *)hybris_get_shmpointer((hybris_shm_pointer_t)mvalue); + + if (mvalue <= ANDROID_TOP_ADDR_VALUE_MUTEX) { + realmutex = hybris_alloc_init_mutex(mvalue); + *((unsigned int *) mutex) = (unsigned int) realmutex; + } + + return pthread_cond_timedwait(realcond, realmutex, abstime); +} + +static int my_pthread_cond_timedwait_relative_np(pthread_cond_t *cond, + pthread_mutex_t *mutex, const struct timespec *reltime) +{ + /* Both cond and mutex can be statically initialized, check for both */ + unsigned int cvalue = (*(unsigned int *) cond); + unsigned int mvalue = (*(unsigned int *) mutex); + + if (hybris_check_android_shared_cond(cvalue) || + hybris_check_android_shared_mutex(mvalue)) { + LOGD("Shared condition/mutex with Android, not waiting."); + return 0; + } + + pthread_cond_t *realcond = (pthread_cond_t *) cvalue; + if( hybris_is_pointer_in_shm((void*)cvalue) ) + realcond = (pthread_cond_t *)hybris_get_shmpointer((hybris_shm_pointer_t)cvalue); + + if (cvalue <= ANDROID_TOP_ADDR_VALUE_COND) { + realcond = hybris_alloc_init_cond(); + *((unsigned int *) cond) = (unsigned int) realcond; + } + + pthread_mutex_t *realmutex = (pthread_mutex_t *) mvalue; + if (hybris_is_pointer_in_shm((void*)mvalue)) + realmutex = (pthread_mutex_t *)hybris_get_shmpointer((hybris_shm_pointer_t)mvalue); + + if (mvalue <= ANDROID_TOP_ADDR_VALUE_MUTEX) { + realmutex = hybris_alloc_init_mutex(mvalue); + *((unsigned int *) mutex) = (unsigned int) realmutex; + } + + struct timespec tv; + clock_gettime(CLOCK_REALTIME, &tv); + tv.tv_sec += reltime->tv_sec; + tv.tv_nsec += reltime->tv_nsec; + if (tv.tv_nsec >= 1000000000) { + tv.tv_sec++; + tv.tv_nsec -= 1000000000; + } + return pthread_cond_timedwait(realcond, realmutex, &tv); +} + +/* + * pthread_rwlockattr_* functions + * + * Specific implementations to workaround the differences between at the + * pthread_rwlockattr_t struct differences between Bionic and Glibc. + * + * */ + +static int my_pthread_rwlockattr_init(pthread_rwlockattr_t *__attr) +{ + pthread_rwlockattr_t *realattr; + + realattr = malloc(sizeof(pthread_rwlockattr_t)); + *((unsigned int *)__attr) = (unsigned int) realattr; + + return pthread_rwlockattr_init(realattr); +} + +static int my_pthread_rwlockattr_destroy(pthread_rwlockattr_t *__attr) +{ + int ret; + pthread_rwlockattr_t *realattr = (pthread_rwlockattr_t *) *(unsigned int *) __attr; + + ret = pthread_rwlockattr_destroy(realattr); + free(realattr); + + return ret; +} + +static int my_pthread_rwlockattr_setpshared(pthread_rwlockattr_t *__attr, + int pshared) +{ + pthread_rwlockattr_t *realattr = (pthread_rwlockattr_t *) *(unsigned int *) __attr; + return pthread_rwlockattr_setpshared(realattr, pshared); +} + +static int my_pthread_rwlockattr_getpshared(pthread_rwlockattr_t *__attr, + int *pshared) +{ + pthread_rwlockattr_t *realattr = (pthread_rwlockattr_t *) *(unsigned int *) __attr; + return pthread_rwlockattr_getpshared(realattr, pshared); +} + +/* + * pthread_rwlock_* functions + * + * Specific implementations to workaround the differences between at the + * pthread_rwlock_t struct differences between Bionic and Glibc. + * + * */ + +static int my_pthread_rwlock_init(pthread_rwlock_t *__rwlock, + __const pthread_rwlockattr_t *__attr) +{ + pthread_rwlock_t *realrwlock = NULL; + pthread_rwlockattr_t *realattr = NULL; + int pshared = 0; + + if (__attr != NULL) + realattr = (pthread_rwlockattr_t *) *(unsigned int *) __attr; + + if (realattr) + pthread_rwlockattr_getpshared(realattr, &pshared); + + if (!pshared) { + /* non shared, standard rwlock: use malloc */ + realrwlock = malloc(sizeof(pthread_rwlock_t)); + + *((unsigned int *) __rwlock) = (unsigned int) realrwlock; + } + else { + /* process-shared condition: use the shared memory segment */ + hybris_shm_pointer_t handle = hybris_shm_alloc(sizeof(pthread_rwlock_t)); + + *((unsigned int *)__rwlock) = (unsigned int) handle; + + if (handle) + realrwlock = (pthread_rwlock_t *)hybris_get_shmpointer(handle); + } + + return pthread_rwlock_init(realrwlock, realattr); +} + +static int my_pthread_rwlock_destroy(pthread_rwlock_t *__rwlock) +{ + int ret; + pthread_rwlock_t *realrwlock = (pthread_rwlock_t *) *(unsigned int *) __rwlock; + + if (!hybris_is_pointer_in_shm((void*)realrwlock)) { + ret = pthread_rwlock_destroy(realrwlock); + free(realrwlock); + } + else { + ret = pthread_rwlock_destroy(realrwlock); + realrwlock = (pthread_rwlock_t *)hybris_get_shmpointer((hybris_shm_pointer_t)realrwlock); + } + + return ret; +} + +static pthread_rwlock_t* hybris_set_realrwlock(pthread_rwlock_t *rwlock) +{ + unsigned int value = (*(unsigned int *) rwlock); + pthread_rwlock_t *realrwlock = (pthread_rwlock_t *) value; + if (hybris_is_pointer_in_shm((void*)value)) + realrwlock = (pthread_rwlock_t *)hybris_get_shmpointer((hybris_shm_pointer_t)value); + + if (realrwlock <= ANDROID_TOP_ADDR_VALUE_RWLOCK) { + realrwlock = hybris_alloc_init_rwlock(); + *((unsigned int *)rwlock) = (unsigned int) realrwlock; + } + return realrwlock; +} + +static int my_pthread_rwlock_rdlock(pthread_rwlock_t *__rwlock) +{ + pthread_rwlock_t *realrwlock = hybris_set_realrwlock(__rwlock); + return pthread_rwlock_rdlock(realrwlock); +} + +static int my_pthread_rwlock_tryrdlock(pthread_rwlock_t *__rwlock) +{ + pthread_rwlock_t *realrwlock = hybris_set_realrwlock(__rwlock); + return pthread_rwlock_tryrdlock(realrwlock); +} + +static int my_pthread_rwlock_timedrdlock(pthread_rwlock_t *__rwlock, + __const struct timespec *abs_timeout) +{ + pthread_rwlock_t *realrwlock = hybris_set_realrwlock(__rwlock); + return pthread_rwlock_timedrdlock(realrwlock, abs_timeout); +} + +static int my_pthread_rwlock_wrlock(pthread_rwlock_t *__rwlock) +{ + pthread_rwlock_t *realrwlock = hybris_set_realrwlock(__rwlock); + return pthread_rwlock_wrlock(realrwlock); +} + +static int my_pthread_rwlock_trywrlock(pthread_rwlock_t *__rwlock) +{ + pthread_rwlock_t *realrwlock = hybris_set_realrwlock(__rwlock); + return pthread_rwlock_trywrlock(realrwlock); +} + +static int my_pthread_rwlock_timedwrlock(pthread_rwlock_t *__rwlock, + __const struct timespec *abs_timeout) +{ + pthread_rwlock_t *realrwlock = hybris_set_realrwlock(__rwlock); + return pthread_rwlock_timedwrlock(realrwlock, abs_timeout); +} + +static int my_pthread_rwlock_unlock(pthread_rwlock_t *__rwlock) +{ + unsigned int value = (*(unsigned int *) __rwlock); + if (value <= ANDROID_TOP_ADDR_VALUE_RWLOCK) { + LOGD("Trying to unlock a rwlock that's not locked/initialized" + " by Hybris, not unlocking."); + return 0; + } + pthread_rwlock_t *realrwlock = (pthread_rwlock_t *) value; + if (hybris_is_pointer_in_shm((void*)value)) + realrwlock = (pthread_rwlock_t *)hybris_get_shmpointer((hybris_shm_pointer_t)value); + + return pthread_rwlock_unlock(realrwlock); +} + +static void my_pthread_cleanup_push(void (*routine)(void *), + void *arg) +{ + LOGD("pthread_cleanup_push: not implemented"); +} + +static void my_pthread_cleanup_pop(int execute) +{ + LOGD("pthread_cleanup_pop: not implemented"); +} + + +static int my_set_errno(int oi_errno) +{ + errno = oi_errno; + return -1; +} + +/* + * __isthreaded is used in bionic's stdio.h to choose between a fast internal implementation + * and a more classic stdio function call. + * For example: + * #define __sfeof(p) (((p)->_flags & __SEOF) != 0) + * #define feof(p) (!__isthreaded ? __sfeof(p) : (feof)(p)) + * + * We see here that if __isthreaded is false, then it will use directly the bionic's FILE structure + * instead of calling one of the hooked methods. + * Therefore we need to set __isthreaded to true, even if we are not in a multi-threaded context. + */ +static int __my_isthreaded = 1; + +struct aFILE { + unsigned char* _p; + int _r, _w; +#if defined(__LP64__) + int _flags, _file; +#else + short _flags, _file; +#endif + FILE* actual; +}; + +/* + * redirection for bionic's __sF, which is defined as: + * FILE __sF[3]; + * #define stdin &__sF[0]; + * #define stdout &__sF[1]; + * #define stderr &__sF[2]; + * So the goal here is to catch the call to file methods where the FILE* pointer + * is either stdin, stdout or stderr, and translate that pointer to a valid glibc + * pointer. + * Currently, only fputs is managed. + */ +#define BIONIC_SIZEOF_FILE 84 +static char my_sF[3*BIONIC_SIZEOF_FILE] = {0}; +static FILE *_get_actual_fp(struct aFILE *fp) +{ + char *c_fp = (char*)fp; + if (c_fp == &my_sF[0]) + return stdin; + else if (c_fp == &my_sF[BIONIC_SIZEOF_FILE]) + return stdout; + else if (c_fp == &my_sF[BIONIC_SIZEOF_FILE*2]) + return stderr; + + return fp->actual; +} + +static void my_clearerr(FILE *fp) +{ + clearerr((FILE *)_get_actual_fp((void *)fp)); +} + +static struct aFILE* my_fopen(const char *filename, const char *mode) +{ + FILE* file = fopen(filename, mode); + if (file == NULL) + return NULL; + struct aFILE* afile = (struct aFILE*) malloc(sizeof(struct aFILE)); + afile->_file = fileno(file); + afile->actual = file; + afile->_flags = 0; + return afile; +} + +static struct aFILE* my_fdopen(const char *filename, const char *mode) +{ + FILE* file = fopen(filename, mode); + if (file == NULL) + return NULL; + struct aFILE* afile = (struct aFILE*) malloc(sizeof(struct aFILE)); + afile->_file = fileno(file); + afile->actual = file; + return afile; +} + +static int my_fclose(struct aFILE* fp) +{ + return fclose(_get_actual_fp(fp)); +} + +static int my_feof(struct aFILE* fp) +{ + return feof(_get_actual_fp(fp)); +} + +static int my_ferror(struct aFILE* fp) +{ + return ferror(_get_actual_fp(fp)); +} + +static int my_fflush(struct aFILE* fp) +{ + return fflush(_get_actual_fp(fp)); +} + +static int my_fgetc(struct aFILE* fp) +{ + return fgetc(_get_actual_fp(fp)); +} + +static int my_fgetpos(struct aFILE* fp, fpos_t *pos) +{ + return fgetpos(_get_actual_fp(fp), pos); +} + +static char* my_fgets(char *s, int n, struct aFILE* fp) +{ + return fgets(s, n, _get_actual_fp(fp)); +} + +FP_ATTRIB static int my_fprintf(struct aFILE* fp, const char *fmt, ...) +{ + int ret = 0; + + va_list args; + va_start(args,fmt); + ret = vfprintf(_get_actual_fp(fp), fmt, args); + va_end(args); + + return ret; +} + +static int my_fputc(int c, struct aFILE* fp) +{ + return fputc(c, _get_actual_fp(fp)); +} + +static int my_fputs(const char *s, struct aFILE* fp) +{ + return fputs(s, _get_actual_fp(fp)); +} + +static size_t my_fread(void *ptr, size_t size, size_t nmemb, struct aFILE* fp) +{ + size_t ret = fread(ptr, size, nmemb, _get_actual_fp(fp)); + fp->_flags = (short) (feof_unlocked(_get_actual_fp(fp)) ? 0x0020 : 0); + return ret; +} + +static FILE* my_freopen(const char *filename, const char *mode, struct aFILE* fp) +{ + return freopen(filename, mode, _get_actual_fp(fp)); +} + +FP_ATTRIB static int my_fscanf(struct aFILE* fp, const char *fmt, ...) +{ + int ret = 0; + + va_list args; + va_start(args,fmt); + ret = vfscanf(_get_actual_fp(fp), fmt, args); + va_end(args); + + return ret; +} + +static int my_fseek(struct aFILE* fp, long offset, int whence) +{ + return fseek(_get_actual_fp(fp), offset, whence); +} + +static int my_fseeko(struct aFILE* fp, off_t offset, int whence) +{ + return fseeko(_get_actual_fp(fp), offset, whence); +} + +static int my_fsetpos(struct aFILE* fp, const fpos_t *pos) +{ + return fsetpos(_get_actual_fp(fp), pos); +} + +static long my_ftell(struct aFILE* fp) +{ + return ftell(_get_actual_fp(fp)); +} + +static off_t my_ftello(struct aFILE* fp) +{ + return ftello(_get_actual_fp(fp)); +} + +static size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, struct aFILE* fp) +{ + return fwrite(ptr, size, nmemb, _get_actual_fp(fp)); +} + +static int my_getc(struct aFILE* fp) +{ + return getc(_get_actual_fp(fp)); +} + +static ssize_t my_getdelim(char ** lineptr, size_t *n, int delimiter, struct aFILE* fp) +{ + return getdelim(lineptr, n, delimiter, _get_actual_fp(fp)); +} + +static ssize_t my_getline(char **lineptr, size_t *n, struct aFILE* fp) +{ + return getline(lineptr, n, _get_actual_fp(fp)); +} + + +static int my_putc(int c, struct aFILE* fp) +{ + return putc(c, _get_actual_fp(fp)); +} + +static void my_rewind(struct aFILE* fp) +{ + rewind(_get_actual_fp(fp)); +} + +static void my_setbuf(struct aFILE* fp, char *buf) +{ + setbuf(_get_actual_fp(fp), buf); +} + +static int my_setvbuf(struct aFILE* fp, char *buf, int mode, size_t size) +{ + return setvbuf(_get_actual_fp(fp), buf, mode, size); +} + +static int my_ungetc(int c, struct aFILE* fp) +{ + return ungetc(c, _get_actual_fp(fp)); +} + +static int my_vfprintf(struct aFILE* fp, const char *fmt, va_list arg) +{ + return vfprintf(_get_actual_fp(fp), fmt, arg); +} + + +static int my_vfscanf(struct aFILE* fp, const char *fmt, va_list arg) +{ + return vfscanf(_get_actual_fp(fp), fmt, arg); +} + +static int my_fileno(struct aFILE* fp) +{ + return fileno(_get_actual_fp(fp)); +} + + +static int my_pclose(struct aFILE* fp) +{ + return pclose(_get_actual_fp(fp)); +} + +static void my_flockfile(struct aFILE* fp) +{ + return flockfile(_get_actual_fp(fp)); +} + +static int my_ftrylockfile(struct aFILE* fp) +{ + return ftrylockfile(_get_actual_fp(fp)); +} + +static void my_funlockfile(struct aFILE* fp) +{ + return funlockfile(_get_actual_fp(fp)); +} + + +static int my_getc_unlocked(struct aFILE* fp) +{ + return getc_unlocked(_get_actual_fp(fp)); +} + +static int my_putc_unlocked(int c, struct aFILE* fp) +{ + return putc_unlocked(c, _get_actual_fp(fp)); +} + +/* exists only on the BSD platform +static char* my_fgetln(FILE *fp, size_t *len) +{ + return fgetln(_get_actual_fp(fp), len); +} +*/ +static int my_fpurge(struct aFILE* fp) +{ + __fpurge(_get_actual_fp(fp)); + + return 0; +} + +static int my_getw(struct aFILE* fp) +{ + return getw(_get_actual_fp(fp)); +} + +static int my_putw(int w, struct aFILE* fp) +{ + return putw(w, _get_actual_fp(fp)); +} + +static void my_setbuffer(struct aFILE* fp, char *buf, int size) +{ + setbuffer(_get_actual_fp(fp), buf, size); +} + +static int my_setlinebuf(struct aFILE* fp) +{ + setlinebuf(_get_actual_fp(fp)); + + return 0; +} + +/* "struct dirent" from bionic/libc/include/dirent.h */ +struct bionic_dirent { + uint64_t d_ino; + int64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +static struct bionic_dirent *my_readdir(DIR *dirp) +{ + /** + * readdir(3) manpage says: + * The data returned by readdir() may be overwritten by subsequent calls + * to readdir() for the same directory stream. + * + * XXX: At the moment, for us, the data will be overwritten even by + * subsequent calls to /different/ directory streams. Eventually fix that + * (e.g. by storing per-DIR * bionic_dirent structs, and removing them on + * closedir, requires hooking of all funcs returning/taking DIR *) and + * handling the additional data attachment there) + **/ + + static struct bionic_dirent result; + + struct dirent *real_result = readdir(dirp); + if (!real_result) { + return NULL; + } + + result.d_ino = real_result->d_ino; + result.d_off = real_result->d_off; + result.d_reclen = real_result->d_reclen; + result.d_type = real_result->d_type; + memcpy(result.d_name, real_result->d_name, sizeof(result.d_name)); + + // Make sure the string is zero-terminated, even if cut off (which + // shouldn't happen, as both bionic and glibc have d_name defined + // as fixed array of 256 chars) + result.d_name[sizeof(result.d_name)-1] = '\0'; + return &result; +} + +static int my_readdir_r(DIR *dir, struct bionic_dirent *entry, + struct bionic_dirent **result) +{ + struct dirent entry_r; + struct dirent *result_r; + + int res = readdir_r(dir, &entry_r, &result_r); + + if (res == 0) { + if (result_r != NULL) { + *result = entry; + + entry->d_ino = entry_r.d_ino; + entry->d_off = entry_r.d_off; + entry->d_reclen = entry_r.d_reclen; + entry->d_type = entry_r.d_type; + memcpy(entry->d_name, entry_r.d_name, sizeof(entry->d_name)); + + // Make sure the string is zero-terminated, even if cut off (which + // shouldn't happen, as both bionic and glibc have d_name defined + // as fixed array of 256 chars) + entry->d_name[sizeof(entry->d_name) - 1] = '\0'; + } else { + *result = NULL; + } + } + + return res; +} + +static int my_alphasort(struct bionic_dirent **a, + struct bionic_dirent **b) +{ + return strcoll((*a)->d_name, (*b)->d_name); +} + +static int my_versionsort(struct bionic_dirent **a, + struct bionic_dirent **b) +{ + return strverscmp((*a)->d_name, (*b)->d_name); +} + +static int my_scandirat(int fd, const char *dir, + struct bionic_dirent ***namelist, + int (*filter) (const struct bionic_dirent *), + int (*compar) (const struct bionic_dirent **, + const struct bionic_dirent **)) +{ + struct dirent **namelist_r; + struct bionic_dirent **result; + struct bionic_dirent *filter_r; + + int i = 0; + size_t nItems = 0; + + int res = scandirat(fd, dir, &namelist_r, NULL, NULL); + + if (res != 0 && namelist_r != NULL) { + + result = malloc(res * sizeof(struct bionic_dirent)); + if (!result) + return -1; + + for (i = 0; i < res; i++) { + filter_r = malloc(sizeof(struct bionic_dirent)); + if (!filter_r) { + while (i-- > 0) + free(result[i]); + free(result); + return -1; + } + filter_r->d_ino = namelist_r[i]->d_ino; + filter_r->d_off = namelist_r[i]->d_off; + filter_r->d_reclen = namelist_r[i]->d_reclen; + filter_r->d_type = namelist_r[i]->d_type; + + strcpy(filter_r->d_name, namelist_r[i]->d_name); + filter_r->d_name[sizeof(namelist_r[i]->d_name) - 1] = '\0'; + + if (filter != NULL && !(*filter)(filter_r)) {//apply filter + free(filter_r); + continue; + } + + result[nItems++] = filter_r; + } + if (nItems && compar != NULL) // sort + qsort(result, nItems, sizeof(struct bionic_dirent *), + (int (*)(const void*, const void*)) compar); + + *namelist = result; + } + + return res; +} + +static int my_scandir(const char *dir, + struct bionic_dirent ***namelist, + int (*filter) (const struct bionic_dirent *), + int (*compar) (const struct bionic_dirent **, + const struct bionic_dirent **)) +{ + return my_scandirat(AT_FDCWD, dir, namelist, filter, compar); +} + +extern long my_sysconf(int name); + +FP_ATTRIB static double my_strtod(const char *nptr, char **endptr) +{ + if (locale_inited == 0) + { + hybris_locale = newlocale(LC_ALL_MASK, "C", 0); + locale_inited = 1; + } + return strtod_l(nptr, endptr, hybris_locale); +} + +extern int __cxa_atexit(void (*)(void*), void*, void*); +extern void __cxa_finalize(void * d); + +struct open_redirect { + const char *from; + const char *to; +}; + +struct open_redirect open_redirects[] = { + { "/dev/log/main", "/dev/log_main" }, + { "/dev/log/radio", "/dev/log_radio" }, + { "/dev/log/system", "/dev/log_system" }, + { "/dev/log/events", "/dev/log_events" }, + { NULL, NULL } +}; + +int my_open(const char *pathname, int flags, ...) +{ + va_list ap; + mode_t mode = 0; + const char *target_path = pathname; + + if (pathname != NULL) { + struct open_redirect *entry = &open_redirects[0]; + while (entry->from != NULL) { + if (strcmp(pathname, entry->from) == 0) { + target_path = entry->to; + break; + } + entry++; + } + } + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + return open(target_path, flags, mode); +} + +/** + * NOTE: Normally we don't have to wrap __system_property_get (libc.so) as it is only used + * through the property_get (libcutils.so) function. However when property_get is used + * internally in libcutils.so we don't have any chance to hook our replacement in. + * Therefore we have to hook __system_property_get too and just replace it with the + * implementation of our internal property handling + */ + +int my_system_property_get(const char *name, const char *value) +{ + return property_get(name, value, NULL); +} + +static __thread void *tls_hooks[16]; + +void *__get_tls_hooks() +{ + return tls_hooks; +} + +ssize_t my_read(int fd, void* buf, size_t nbytes) { + return read(fd, buf, nbytes); +} + +ssize_t my_write(int fd, void* buf, size_t nbytes) { + return write(fd, buf, nbytes); +} + +struct android_addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + socklen_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +struct android_addrinfo* convert_addrinfo(struct addrinfo* res) { + struct android_addrinfo* ares = (struct android_addrinfo*) malloc(sizeof(struct android_addrinfo)); + ares->ai_flags = res->ai_flags; + ares->ai_family = res->ai_family; + ares->ai_socktype = res->ai_socktype; + ares->ai_protocol = res->ai_protocol; + ares->ai_addrlen = res->ai_addrlen; + ares->ai_canonname = res->ai_canonname; + ares->ai_addr = res->ai_addr; + ares->ai_next = NULL; + if (res->ai_next != NULL) { + ares->ai_next = (struct addrinfo*) convert_addrinfo(res->ai_next); + } + return ares; +} + +int my_getaddrinfo(const char *node, const char *service, + const struct android_addrinfo *ahints, + struct android_addrinfo **ares) { + struct addrinfo hints; + if (ahints != NULL) { + hints.ai_flags = ahints->ai_flags; + hints.ai_family = ahints->ai_family; + hints.ai_socktype = ahints->ai_socktype; + hints.ai_protocol = ahints->ai_protocol; + hints.ai_addrlen = ahints->ai_addrlen; + hints.ai_canonname = ahints->ai_canonname; + hints.ai_addr = ahints->ai_addr; + } + struct addrinfo* res; + int ret = getaddrinfo(node, service, (ahints == NULL ? NULL : &hints), &res); + if (ret != 0) { + return ret; + } + if (res != NULL) { + *ares = convert_addrinfo(res); + } else { + *ares = NULL; + } + return ret; +} + +const char *my_inet_ntop (int __af, const void * __cp, + char * __buf, socklen_t __len) { + return inet_ntop(__af, __cp, __buf, __len); +} + +void *get_hooked_symbol(const char *sym); +void *my_android_dlsym(void *handle, const char *symbol) +{ + void *retval = get_hooked_symbol(symbol); + if (retval != NULL) { + return retval; + } + return android_dlsym(handle, symbol); +} + +static struct _hook hooks[] = { + {"property_get", property_get }, + {"property_set", property_set }, + {"__system_property_get", my_system_property_get }, + {"getenv", getenv }, + {"setenv", setenv }, + {"printf", printf }, + {"malloc", my_malloc }, + {"free", free }, + {"calloc", calloc }, + {"cfree", free }, + {"realloc", realloc }, + {"memalign", memalign }, + {"valloc", valloc }, + {"pvalloc", pvalloc }, + //{"fread", my_fread }, + {"getxattr", getxattr}, + /* string.h */ + {"memccpy",memccpy}, + {"memchr",memchr}, + {"memrchr",memrchr}, + {"memcmp",memcmp}, + {"memcpy",my_memcpy}, + {"memmove",memmove}, + {"memset",memset}, + {"memmem",memmem}, + // {"memswap",memswap}, + {"index",index}, + {"rindex",rindex}, + {"strchr",strchr}, + {"strrchr",strrchr}, + {"strlen",my_strlen}, + {"strcmp",strcmp}, + {"strcpy",strcpy}, + {"strcat",strcat}, + {"strcasecmp",strcasecmp}, + {"strncasecmp",strncasecmp}, + {"strdup",strdup}, + {"strstr",strstr}, + {"strtok",strtok}, + {"strtok_r",strtok_r}, + {"strerror",strerror}, + {"strerror_r",strerror_r}, + {"strnlen",strnlen}, + {"strncat",strncat}, + {"strndup",strndup}, + {"strncmp",strncmp}, + {"strncpy",strncpy}, + {"strtod", my_strtod}, + //{"strlcat",strlcat}, + //{"strlcpy",strlcpy}, + {"strcspn",strcspn}, + {"strpbrk",strpbrk}, + {"strsep",strsep}, + {"strspn",strspn}, + {"strsignal",strsignal}, + {"getgrnam", getgrnam}, + {"strcoll",strcoll}, + {"strxfrm",strxfrm}, + /* strings.h */ + {"bcmp",bcmp}, + {"bcopy",bcopy}, + {"bzero",bzero}, + {"ffs",ffs}, + {"index",index}, + {"rindex",rindex}, + {"strcasecmp",strcasecmp}, + {"strncasecmp",strncasecmp}, + /* dirent.h */ + {"opendir", opendir}, + {"closedir", closedir}, + /* pthread.h */ + {"getauxval", getauxval}, + {"gettid", my_gettid}, + {"getpid", getpid}, + {"pthread_atfork", pthread_atfork}, + {"pthread_create", my_pthread_create}, + {"pthread_kill", pthread_kill}, + {"pthread_exit", pthread_exit}, + {"pthread_join", pthread_join}, + {"pthread_detach", pthread_detach}, + {"pthread_self", pthread_self}, + {"pthread_equal", pthread_equal}, + {"pthread_getschedparam", pthread_getschedparam}, + {"pthread_setschedparam", pthread_setschedparam}, + {"pthread_mutex_init", my_pthread_mutex_init}, + {"pthread_mutex_destroy", my_pthread_mutex_destroy}, + {"pthread_mutex_lock", my_pthread_mutex_lock}, + {"pthread_mutex_unlock", my_pthread_mutex_unlock}, + {"pthread_mutex_trylock", my_pthread_mutex_trylock}, + {"pthread_mutex_lock_timeout_np", my_pthread_mutex_lock_timeout_np}, + {"pthread_mutexattr_init", pthread_mutexattr_init}, + {"pthread_mutexattr_destroy", pthread_mutexattr_destroy}, + {"pthread_mutexattr_gettype", pthread_mutexattr_gettype}, + {"pthread_mutexattr_settype", pthread_mutexattr_settype}, + {"pthread_mutexattr_getpshared", pthread_mutexattr_getpshared}, + {"pthread_mutexattr_setpshared", my_pthread_mutexattr_setpshared}, + {"pthread_condattr_init", pthread_condattr_init}, + {"pthread_condattr_getpshared", pthread_condattr_getpshared}, + {"pthread_condattr_setpshared", pthread_condattr_setpshared}, + {"pthread_condattr_destroy", pthread_condattr_destroy}, + {"pthread_cond_init", my_pthread_cond_init}, + {"pthread_cond_destroy", my_pthread_cond_destroy}, + {"pthread_cond_broadcast", my_pthread_cond_broadcast}, + {"pthread_cond_signal", my_pthread_cond_signal}, + {"pthread_cond_wait", my_pthread_cond_wait}, + {"pthread_cond_timedwait", my_pthread_cond_timedwait}, + {"pthread_cond_timedwait_monotonic", my_pthread_cond_timedwait}, + {"pthread_cond_timedwait_monotonic_np", my_pthread_cond_timedwait}, + {"pthread_cond_timedwait_relative_np", my_pthread_cond_timedwait_relative_np}, + {"pthread_key_delete", pthread_key_delete}, + {"pthread_setname_np", pthread_setname_np}, + {"pthread_once", pthread_once}, + {"pthread_key_create", pthread_key_create}, + {"pthread_setspecific", pthread_setspecific}, + {"pthread_getspecific", pthread_getspecific}, + {"pthread_attr_init", my_pthread_attr_init}, + {"pthread_attr_destroy", my_pthread_attr_destroy}, + {"pthread_attr_setdetachstate", my_pthread_attr_setdetachstate}, + {"pthread_attr_getdetachstate", my_pthread_attr_getdetachstate}, + {"pthread_attr_setschedpolicy", my_pthread_attr_setschedpolicy}, + {"pthread_attr_getschedpolicy", my_pthread_attr_getschedpolicy}, + {"pthread_attr_setschedparam", my_pthread_attr_setschedparam}, + {"pthread_attr_getschedparam", my_pthread_attr_getschedparam}, + {"pthread_attr_setstacksize", my_pthread_attr_setstacksize}, + {"pthread_attr_getstacksize", my_pthread_attr_getstacksize}, + {"pthread_attr_setstackaddr", my_pthread_attr_setstackaddr}, + {"pthread_attr_getstackaddr", my_pthread_attr_getstackaddr}, + {"pthread_attr_setstack", my_pthread_attr_setstack}, + {"pthread_attr_getstack", my_pthread_attr_getstack}, + {"pthread_attr_setguardsize", my_pthread_attr_setguardsize}, + {"pthread_attr_getguardsize", my_pthread_attr_getguardsize}, + {"pthread_attr_setscope", my_pthread_attr_setscope}, + {"pthread_attr_setscope", my_pthread_attr_getscope}, + {"pthread_getattr_np", my_pthread_getattr_np}, + {"pthread_rwlockattr_init", my_pthread_rwlockattr_init}, + {"pthread_rwlockattr_destroy", my_pthread_rwlockattr_destroy}, + {"pthread_rwlockattr_setpshared", my_pthread_rwlockattr_setpshared}, + {"pthread_rwlockattr_getpshared", my_pthread_rwlockattr_getpshared}, + {"pthread_rwlock_init", my_pthread_rwlock_init}, + {"pthread_rwlock_destroy", my_pthread_rwlock_destroy}, + {"pthread_rwlock_unlock", my_pthread_rwlock_unlock}, + {"pthread_rwlock_wrlock", my_pthread_rwlock_wrlock}, + {"pthread_rwlock_rdlock", my_pthread_rwlock_rdlock}, + {"pthread_rwlock_tryrdlock", my_pthread_rwlock_tryrdlock}, + {"pthread_rwlock_trywrlock", my_pthread_rwlock_trywrlock}, + {"pthread_rwlock_timedrdlock", my_pthread_rwlock_timedrdlock}, + {"pthread_rwlock_timedwrlock", my_pthread_rwlock_timedwrlock}, + {"__pthread_cleanup_push", my_pthread_cleanup_push}, + {"__pthread_cleanup_pop", my_pthread_cleanup_pop}, + /* stdio.h */ + {"__isthreaded", &__my_isthreaded}, + {"__sF", &my_sF}, + {"fopen", my_fopen}, + {"fdopen", my_fdopen}, + {"popen", popen}, + {"puts", puts}, + {"sprintf", sprintf}, + {"asprintf", asprintf}, + {"vasprintf", vasprintf}, + {"snprintf", snprintf}, + {"vsprintf", vsprintf}, + {"vsnprintf", vsnprintf}, + {"clearerr", my_clearerr}, + {"fclose", my_fclose}, + {"feof", my_feof}, + {"ferror", my_ferror}, + {"fflush", my_fflush}, + {"fgetc", my_fgetc}, + {"fgetpos", my_fgetpos}, + {"fgets", my_fgets}, + {"fprintf", my_fprintf}, + {"fputc", my_fputc}, + {"fputs", my_fputs}, + {"fread", my_fread}, + {"freopen", my_freopen}, + {"fscanf", my_fscanf}, + {"fseek", my_fseek}, + {"fseeko", my_fseeko}, + {"fsetpos", my_fsetpos}, + {"ftell", my_ftell}, + {"ftello", my_ftello}, + {"fwrite", my_fwrite}, + {"getc", my_getc}, + {"getdelim", my_getdelim}, + {"getline", my_getline}, + {"putc", my_putc}, + {"rewind", my_rewind}, + {"setbuf", my_setbuf}, + {"setvbuf", my_setvbuf}, + {"ungetc", my_ungetc}, + {"vasprintf", vasprintf}, + {"vfprintf", my_vfprintf}, + {"vfscanf", my_vfscanf}, + {"fileno", my_fileno}, + {"pclose", my_pclose}, + {"flockfile", my_flockfile}, + {"ftrylockfile", my_ftrylockfile}, + {"funlockfile", my_funlockfile}, + {"getc_unlocked", my_getc_unlocked}, + {"putc_unlocked", my_putc_unlocked}, + //{"fgetln", my_fgetln}, + {"fpurge", my_fpurge}, + {"getw", my_getw}, + {"putw", my_putw}, + {"setbuffer", my_setbuffer}, + {"setlinebuf", my_setlinebuf}, + {"__errno", __errno_location}, + {"__set_errno", my_set_errno}, + /* net specifics, to avoid __res_get_state */ + {"getaddrinfo", my_getaddrinfo}, + {"gethostbyaddr", gethostbyaddr}, + {"gethostbyname", gethostbyname}, + {"gethostbyname2", gethostbyname2}, + {"gethostent", gethostent}, + {"strftime", strftime}, + {"sysconf", my_sysconf}, + {"dlopen", android_dlopen}, + {"dlerror", android_dlerror}, + {"dlsym", my_android_dlsym}, + {"dladdr", android_dladdr}, + {"dlclose", android_dlclose}, + /* dirent.h */ + {"opendir", opendir}, + {"fdopendir", fdopendir}, + {"closedir", closedir}, + {"readdir", my_readdir}, + {"readdir_r", my_readdir_r}, + {"rewinddir", rewinddir}, + {"seekdir", seekdir}, + {"telldir", telldir}, + {"dirfd", dirfd}, + {"scandir", my_scandir}, + {"scandirat", my_scandirat}, + {"alphasort", my_alphasort}, + {"versionsort", my_versionsort}, + /* fcntl.h */ + {"open", my_open}, + {"__get_tls_hooks", __get_tls_hooks}, + {"sscanf", sscanf}, + {"scanf", scanf}, + {"vscanf", vscanf}, + {"vsscanf", vsscanf}, + {"openlog", openlog}, + {"syslog", syslog}, + {"closelog", closelog}, + {"vsyslog", vsyslog}, + {"timer_create", timer_create}, + {"timer_settime", timer_settime}, + {"timer_gettime", timer_gettime}, + {"timer_delete", timer_delete}, + {"timer_getoverrun", timer_getoverrun}, + {"localtime", localtime}, + {"localtime_r", localtime_r}, + {"gmtime", gmtime}, + {"abort", abort}, + {"writev", writev}, + /* unistd.h */ + {"access", access}, + {"read", my_read}, + {"write", my_write}, + /* grp.h */ + {"getgrgid", getgrgid}, + {"__cxa_atexit", __cxa_atexit}, + {"__cxa_finalize", __cxa_finalize}, + /* arpa/inet.h */ + {"inet_ntop", my_inet_ntop}, + {NULL, NULL}, +}; +static struct _hook* user_hooks = NULL; +static int user_hooks_size = 0; +static int user_hooks_arr_size = 0; + +void user_hooks_resize() { + if (user_hooks_arr_size == 0) { + user_hooks_arr_size = 256; + user_hooks = (struct _hook*) malloc(user_hooks_arr_size * sizeof(struct _hook)); + } else { + user_hooks_arr_size *= 2; + struct _hook* new_array = (struct _hook*) malloc(user_hooks_arr_size * sizeof(struct _hook)); + memcpy(&new_array[0], &user_hooks[0], user_hooks_size * sizeof(struct _hook)); + free(user_hooks); + user_hooks = new_array; + } +} + +void add_user_hook(struct _hook h) { + if (user_hooks_size + 1 >= user_hooks_arr_size) + user_hooks_resize(); + + user_hooks[user_hooks_size++] = h; +} + +void hybris_hook(const char *name, void* func) { + struct _hook h; + h.name = name; + h.func = func; + add_user_hook(h); +} + +void *get_hooked_symbol(const char *sym) +{ + int i; + struct _hook *ptr = &hooks[0]; + static int counter = -1; + + for (i = 0; i < user_hooks_size; i++) { + struct _hook* h = &user_hooks[i]; + if (strcmp(sym, h->name) == 0) { + //printf("redirect %s --> %s\n", sym, h->name); + return h->func; + } + } + + while (ptr->name != NULL) + { + if (strcmp(sym, ptr->name) == 0){ + return ptr->func; + } + ptr++; + } + if (strstr(sym, "pthread") != NULL) + { + /* safe */ + if (strcmp(sym, "pthread_sigmask") == 0) + return NULL; + /* not safe */ + counter--; + LOGD("%s %i\n", sym, counter); + return (void *) counter; + } + return NULL; +} + +void android_linker_init() +{ +} diff --git a/hybris/src/hooks_shm.c b/hybris/src/hooks_shm.c new file mode 100644 index 0000000..848dfcc --- /dev/null +++ b/hybris/src/hooks_shm.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2012 Carsten Munk + * Copyright (c) 2013 Christophe Chapuis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "hooks_shm.h" + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Debug */ +#include "logging.h" +#define LOGD(message, ...) HYBRIS_DEBUG_LOG(HOOKS, message, ##__VA_ARGS__) + +#define HYBRIS_DATA_SIZE 4000 +#define HYBRIS_SHM_MASK 0xFF000000UL +#define HYBRIS_SHM_PATH "/hybris_shm_data" + +/* Structure of a shared memory region */ +typedef struct _hybris_shm_data_t { + pthread_mutex_t access_mutex; + int current_offset; + int max_offset; + unsigned char data; +} hybris_shm_data_t; + +/* A helper to switch between the size of the data and the size of the shm object */ +const int HYBRIS_SHM_DATA_HEADER_SIZE = (sizeof(hybris_shm_data_t) - sizeof(unsigned char)); + +/* pointer to the shared memory region */ +static hybris_shm_data_t *_hybris_shm_data = NULL; + +/* the SHM mem_id of the shared memory region */ +static int _hybris_shm_fd = -1; + +/* the size of the shared memory region that is currently mmap'ed to this process */ +static int _current_mapped_size = 0; + +/* forward-declare the internal static methods */ +static void _release_shm(void); +static void _sync_mmap_with_shm(void); +static void _hybris_shm_init(void); +static void _hybris_shm_extend_region(void); + +/* + * Detach the allocated memory region, and mark it for deletion + */ +static void _release_shm(void) +{ + if (_hybris_shm_data) { + munmap(_hybris_shm_data, _current_mapped_size); /* unmap from this process */ + _hybris_shm_data = NULL; /* pointer is no more valid */ + } + if (_hybris_shm_fd >= 0) { + close(_hybris_shm_fd); /* close the shm file descriptor */ + _hybris_shm_fd = -1; + } + shm_unlink(HYBRIS_SHM_PATH); /* request the deletion of the shm region */ +} + +/* + * Synchronize the size of the mmap with the size of the shm region + */ +static void _sync_mmap_with_shm() +{ + if (_hybris_shm_fd >= 0 && _hybris_shm_data) { + if (_current_mapped_size < _hybris_shm_data->max_offset + HYBRIS_SHM_DATA_HEADER_SIZE) { + /* Note that mremap may change the address pointed by _hybris_shm_data. + * But as we never point directly into _hybris_shm_data, it's fine. + * */ + _hybris_shm_data = (hybris_shm_data_t *)mremap( _hybris_shm_data, _current_mapped_size, + _hybris_shm_data->max_offset + HYBRIS_SHM_DATA_HEADER_SIZE, + MREMAP_MAYMOVE ); + + _current_mapped_size = _hybris_shm_data->max_offset + HYBRIS_SHM_DATA_HEADER_SIZE; + } + } +} + +/* + * Initialize the shared memory region for hybris, in order to store + * pshared mutex, condition and rwlock + */ +static void _hybris_shm_init() +{ + if (_hybris_shm_fd < 0) { + const size_t size_to_map = HYBRIS_SHM_DATA_HEADER_SIZE + HYBRIS_DATA_SIZE; /* 4000 bytes for the data, plus the header size */ + + /* initialize or get shared memory segment */ + _hybris_shm_fd = shm_open(HYBRIS_SHM_PATH, O_RDWR, 0660); + if (_hybris_shm_fd >= 0) { + /* Map the memory object */ + _hybris_shm_data = (hybris_shm_data_t *)mmap( NULL, size_to_map, + PROT_READ | PROT_WRITE, MAP_SHARED, + _hybris_shm_fd, 0 ); + _current_mapped_size = size_to_map; + + _sync_mmap_with_shm(); + } + else { + LOGD("Creating a new shared memory segment."); + + _hybris_shm_fd = shm_open(HYBRIS_SHM_PATH, O_RDWR | O_CREAT, 0660); + if (_hybris_shm_fd >= 0) { + ftruncate( _hybris_shm_fd, size_to_map ); + /* Map the memory object */ + _hybris_shm_data = (hybris_shm_data_t *)mmap( NULL, size_to_map, + PROT_READ | PROT_WRITE, MAP_SHARED, + _hybris_shm_fd, 0 ); + if (_hybris_shm_data == MAP_FAILED) { + HYBRIS_ERROR_LOG(HOOKS, "ERROR: mmap failed: %s\n", strerror(errno)); + _release_shm(); + } + else { + _current_mapped_size = size_to_map; + /* Initialize the memory object */ + memset((void*)_hybris_shm_data, 0, size_to_map); + _hybris_shm_data->max_offset = HYBRIS_DATA_SIZE; + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&_hybris_shm_data->access_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + atexit(_release_shm); + } + } + else { + HYBRIS_ERROR_LOG(HOOKS, "ERROR: Couldn't create shared memory segment !"); + } + } + } +} + +/* + * Extend the SHM region's size + */ +static void _hybris_shm_extend_region() +{ + ftruncate( _hybris_shm_fd, _current_mapped_size + HYBRIS_DATA_SIZE ); + _hybris_shm_data->max_offset += HYBRIS_DATA_SIZE; + + _sync_mmap_with_shm(); +} + +/************ public functions *******************/ + + /* + * Determine if the pointer that has been extracted by hybris is + * pointing to an address in the shared memory + */ +int hybris_is_pointer_in_shm(void *ptr) +{ + if ((unsigned int)ptr >= HYBRIS_SHM_MASK) + return 1; + + return 0; +} + +/* + * Convert this offset pointer to the shared memory to an + * absolute pointer that can be used in user space + */ +void *hybris_get_shmpointer(hybris_shm_pointer_t handle) +{ + void *realpointer = NULL; + if (hybris_is_pointer_in_shm((void*)handle)) { + if (_hybris_shm_fd < 0) { + /* if we are not yet attached to any shm region, then do it now */ + _hybris_shm_init(); + } + + pthread_mutex_lock(&_hybris_shm_data->access_mutex); + + _sync_mmap_with_shm(); /* make sure our mmap is sync'ed */ + + if (_hybris_shm_data != NULL) { + unsigned int offset = handle & (~HYBRIS_SHM_MASK); + realpointer = &(_hybris_shm_data->data) + offset; + + /* Be careful when activating this trace: this method is called *a lot* ! + LOGD("handle = %x, offset = %d, realpointer = %x)", handle, offset, realpointer); + */ + } + + pthread_mutex_unlock(&_hybris_shm_data->access_mutex); + } + + return realpointer; +} + +/* + * Allocate a space in the shared memory region of hybris + */ +hybris_shm_pointer_t hybris_shm_alloc(size_t size) +{ + hybris_shm_pointer_t location = 0; + + if (_hybris_shm_fd < 0) { + /* if we are not yet attached to any shm region, then do it now */ + _hybris_shm_init(); + } + + pthread_mutex_lock(&_hybris_shm_data->access_mutex); + + /* Make sure our mmap is sync'ed */ + _sync_mmap_with_shm(); + + if (_hybris_shm_data == NULL || _hybris_shm_fd < 0) + return 0; + + if (_hybris_shm_data->current_offset + size >= _hybris_shm_data->max_offset) { + /* the current buffer if full: extend it a little bit more */ + _hybris_shm_extend_region(); + _sync_mmap_with_shm(); + } + + /* there is now enough place in this pool */ + location = _hybris_shm_data->current_offset | HYBRIS_SHM_MASK; + LOGD("Allocated a shared object (size = %d, at offset %d)", size, _hybris_shm_data->current_offset); + + _hybris_shm_data->current_offset += size; + + pthread_mutex_unlock(&_hybris_shm_data->access_mutex); + + return location; +} diff --git a/hybris/src/hooks_shm.h b/hybris/src/hooks_shm.h new file mode 100644 index 0000000..aabf5cd --- /dev/null +++ b/hybris/src/hooks_shm.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012 Carsten Munk + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef HOOKS_SHM_H_ +#define HOOKS_SHM_H_ + +#include + +typedef unsigned int hybris_shm_pointer_t; + +/* + * Allocate a space in the shared memory region of hybris + */ +hybris_shm_pointer_t hybris_shm_alloc(size_t size); +/* + * Test if the pointers points to the shm region + */ +int hybris_is_pointer_in_shm(void *ptr); +/* + * Convert an offset pointer to the shared memory to an absolute pointer that can be used in user space + * This function will return a NULL pointer if the handle does not actually point to the shm region + */ +void *hybris_get_shmpointer(hybris_shm_pointer_t handle); + +#endif + +// vim:ts=4:sw=4:noexpandtab diff --git a/hybris/src/jb/arch/arm/begin.S b/hybris/src/jb/arch/arm/begin.S new file mode 100644 index 0000000..e259902 --- /dev/null +++ b/hybris/src/jb/arch/arm/begin.S @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .align 4 + .type _start,#function + .globl _start + +_start: + mov r0, sp + mov r1, #0 + bl __linker_init + + /* linker init returns the _entry address in the main image */ + mov pc, r0 + + .section .ctors, "wa" + .globl __CTOR_LIST__ +__CTOR_LIST__: + .long -1 diff --git a/hybris/src/jb/arch/x86/begin.S b/hybris/src/jb/arch/x86/begin.S new file mode 100644 index 0000000..b4427e0 --- /dev/null +++ b/hybris/src/jb/arch/x86/begin.S @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +.text +.align 4 +.type _start, @function +.globl _start + +_start: + /* save the elfdata ptr to %eax, AND push it onto the stack */ + mov %esp, %eax + pushl %esp + + pushl %eax + call __linker_init + + /* linker init returns (%eax) the _entry address in the main image */ + /* entry point expects sp to point to elfdata */ + popl %esp + jmp *%eax + diff --git a/hybris/src/jb/debugger.c b/hybris/src/jb/debugger.c new file mode 100644 index 0000000..756b5cf --- /dev/null +++ b/hybris/src/jb/debugger.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linker.h" + +#include +#include + +extern int tgkill(int tgid, int tid, int sig); + +void notify_gdb_of_libraries(); + +#define DEBUGGER_SOCKET_NAME "android:debuggerd" + +typedef enum { + // dump a crash + DEBUGGER_ACTION_CRASH, + // dump a tombstone file + DEBUGGER_ACTION_DUMP_TOMBSTONE, + // dump a backtrace only back to the socket + DEBUGGER_ACTION_DUMP_BACKTRACE, +} debugger_action_t; + +/* message sent over the socket */ +typedef struct { + debugger_action_t action; + pid_t tid; +} debugger_msg_t; + +#define RETRY_ON_EINTR(ret,cond) \ + do { \ + ret = (cond); \ + } while (ret < 0 && errno == EINTR) + +// see man(2) prctl, specifically the section about PR_GET_NAME +#define MAX_TASK_NAME_LEN (16) + +static int socket_abstract_client(const char *name, int type) +{ + struct sockaddr_un addr; + size_t namelen; + socklen_t alen; + int s, err; + + namelen = strlen(name); + + // Test with length +1 for the *initial* '\0'. + if ((namelen + 1) > sizeof(addr.sun_path)) { + errno = EINVAL; + return -1; + } + + /* This is used for abstract socket namespace, we need + * an initial '\0' at the start of the Unix socket path. + * + * Note: The path in this case is *not* supposed to be + * '\0'-terminated. ("man 7 unix" for the gory details.) + */ + memset (&addr, 0, sizeof addr); + addr.sun_family = AF_LOCAL; + addr.sun_path[0] = 0; + memcpy(addr.sun_path + 1, name, namelen); + + alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; + + s = socket(AF_LOCAL, type, 0); + if(s < 0) return -1; + + RETRY_ON_EINTR(err,connect(s, (struct sockaddr *) &addr, alen)); + if (err < 0) { + close(s); + s = -1; + } + + return s; +} + +#include "linker_format.h" +#include <../libc/private/logd.h> + +/* + * Writes a summary of the signal to the log file. + * + * We could be here as a result of native heap corruption, or while a + * mutex is being held, so we don't want to use any libc functions that + * could allocate memory or hold a lock. + */ +static void logSignalSummary(int signum, const siginfo_t* info) +{ + char buffer[128]; + char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination + + char* signame; + switch (signum) { + case SIGILL: signame = "SIGILL"; break; + case SIGABRT: signame = "SIGABRT"; break; + case SIGBUS: signame = "SIGBUS"; break; + case SIGFPE: signame = "SIGFPE"; break; + case SIGSEGV: signame = "SIGSEGV"; break; + case SIGSTKFLT: signame = "SIGSTKFLT"; break; + case SIGPIPE: signame = "SIGPIPE"; break; + default: signame = "???"; break; + } + + if (prctl(PR_GET_NAME, (unsigned long)threadname, 0, 0, 0) != 0) { + strcpy(threadname, ""); + } else { + // short names are null terminated by prctl, but the manpage + // implies that 16 byte names are not. + threadname[MAX_TASK_NAME_LEN] = 0; + } + format_buffer(buffer, sizeof(buffer), + "Fatal signal %d (%s) at 0x%08x (code=%d), thread %d (%s)", + signum, signame, info->si_addr, info->si_code, gettid(), threadname); + + __libc_android_log_write(ANDROID_LOG_FATAL, "libc", buffer); +} + +/* + * Catches fatal signals so we can ask debuggerd to ptrace us before + * we crash. + */ +void debugger_signal_handler(int n, siginfo_t* info, void* unused) +{ + char msgbuf[128]; + unsigned tid; + int s; + + logSignalSummary(n, info); + + tid = gettid(); + s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM); + + if (s >= 0) { + /* debugger knows our pid from the credentials on the + * local socket but we need to tell it our tid. It + * is paranoid and will verify that we are giving a tid + * that's actually in our process + */ + int ret; + debugger_msg_t msg; + msg.action = DEBUGGER_ACTION_CRASH; + msg.tid = tid; + RETRY_ON_EINTR(ret, write(s, &msg, sizeof(msg))); + if (ret == sizeof(msg)) { + /* if the write failed, there is no point to read on + * the file descriptor. */ + RETRY_ON_EINTR(ret, read(s, &tid, 1)); + int savedErrno = errno; + notify_gdb_of_libraries(); + errno = savedErrno; + } + + if (ret < 0) { + /* read or write failed -- broken connection? */ + format_buffer(msgbuf, sizeof(msgbuf), + "Failed while talking to debuggerd: %s", strerror(errno)); + __libc_android_log_write(ANDROID_LOG_FATAL, "libc", msgbuf); + } + + close(s); + } else { + /* socket failed; maybe process ran out of fds */ + format_buffer(msgbuf, sizeof(msgbuf), + "Unable to open connection to debuggerd: %s", strerror(errno)); + __libc_android_log_write(ANDROID_LOG_FATAL, "libc", msgbuf); + } + + /* remove our net so we fault for real when we return */ + signal(n, SIG_DFL); + + /* + * These signals are not re-thrown when we resume. This means that + * crashing due to (say) SIGPIPE doesn't work the way you'd expect it + * to. We work around this by throwing them manually. We don't want + * to do this for *all* signals because it'll screw up the address for + * faults like SIGSEGV. + */ + switch (n) { + case SIGABRT: + case SIGFPE: + case SIGPIPE: + case SIGSTKFLT: + (void) tgkill(getpid(), gettid(), n); + break; + default: // SIGILL, SIGBUS, SIGSEGV + break; + } +} + +void debugger_init() +{ + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = debugger_signal_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&act.sa_mask); + + sigaction(SIGILL, &act, NULL); + sigaction(SIGABRT, &act, NULL); + sigaction(SIGBUS, &act, NULL); + sigaction(SIGFPE, &act, NULL); + sigaction(SIGSEGV, &act, NULL); + sigaction(SIGSTKFLT, &act, NULL); + sigaction(SIGPIPE, &act, NULL); +} diff --git a/hybris/src/jb/dlfcn.c b/hybris/src/jb/dlfcn.c new file mode 100644 index 0000000..29b14a7 --- /dev/null +++ b/hybris/src/jb/dlfcn.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE +#include +#include +#include +#include "linker.h" +#include "linker_format.h" +#include + +/* This file hijacks the symbols stubbed out in libdl.so. */ + +#define DL_SUCCESS 0 +#define DL_ERR_CANNOT_LOAD_LIBRARY 1 +#define DL_ERR_INVALID_LIBRARY_HANDLE 2 +#define DL_ERR_BAD_SYMBOL_NAME 3 +#define DL_ERR_SYMBOL_NOT_FOUND 4 +#define DL_ERR_SYMBOL_NOT_GLOBAL 5 + +static char dl_err_buf[1024]; +static const char *dl_err_str; + +static const char *dl_errors[] = { + [DL_ERR_CANNOT_LOAD_LIBRARY] = "Cannot load library", + [DL_ERR_INVALID_LIBRARY_HANDLE] = "Invalid library handle", + [DL_ERR_BAD_SYMBOL_NAME] = "Invalid symbol name", + [DL_ERR_SYMBOL_NOT_FOUND] = "Symbol not found", + [DL_ERR_SYMBOL_NOT_GLOBAL] = "Symbol is not global", +}; + +#define likely(expr) __builtin_expect (expr, 1) +#define unlikely(expr) __builtin_expect (expr, 0) + +static pthread_mutex_t dl_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + +static void set_dlerror(int err) +{ + format_buffer(dl_err_buf, sizeof(dl_err_buf), "%s: %s", dl_errors[err], + linker_get_error()); + dl_err_str = (const char *)&dl_err_buf[0]; +}; + +void *android_dlopen(const char *filename, int flag) +{ + soinfo *ret; + + pthread_mutex_lock(&dl_lock); + ret = find_library(filename); + if (unlikely(ret == NULL)) { + set_dlerror(DL_ERR_CANNOT_LOAD_LIBRARY); + } else { + call_constructors_recursive(ret); + ret->refcount++; + } + pthread_mutex_unlock(&dl_lock); + return ret; +} + +const char *android_dlerror(void) +{ + const char *tmp = dl_err_str; + dl_err_str = NULL; + return (const char *)tmp; +} + +void *android_dlsym(void *handle, const char *symbol) +{ + soinfo *found; + Elf32_Sym *sym; + unsigned bind; + + pthread_mutex_lock(&dl_lock); + + if(unlikely(handle == 0)) { + set_dlerror(DL_ERR_INVALID_LIBRARY_HANDLE); + goto err; + } + if(unlikely(symbol == 0)) { + set_dlerror(DL_ERR_BAD_SYMBOL_NAME); + goto err; + } + + if(handle == RTLD_DEFAULT) { + sym = lookup(symbol, &found, NULL); + } else if(handle == RTLD_NEXT) { + void *ret_addr = __builtin_return_address(0); + soinfo *si = find_containing_library(ret_addr); + + sym = NULL; + if(si && si->next) { + sym = lookup(symbol, &found, si->next); + } + } else { + found = (soinfo*)handle; + sym = lookup_in_library(found, symbol); + } + + if(likely(sym != 0)) { + bind = ELF32_ST_BIND(sym->st_info); + + if(likely((bind == STB_GLOBAL || bind == STB_WEAK) && (sym->st_shndx != 0))) { + unsigned ret = sym->st_value + found->base; + pthread_mutex_unlock(&dl_lock); + return (void*)ret; + } + + set_dlerror(DL_ERR_SYMBOL_NOT_GLOBAL); + } + else + set_dlerror(DL_ERR_SYMBOL_NOT_FOUND); + +err: + pthread_mutex_unlock(&dl_lock); + return 0; +} + +int android_dladdr(const void *addr, Dl_info *info) +{ + int ret = 0; + + pthread_mutex_lock(&dl_lock); + + /* Determine if this address can be found in any library currently mapped */ + soinfo *si = find_containing_library(addr); + + if(si) { + memset(info, 0, sizeof(Dl_info)); + + info->dli_fname = si->name; + info->dli_fbase = (void*)si->base; + + /* Determine if any symbol in the library contains the specified address */ + Elf32_Sym *sym = find_containing_symbol(addr, si); + + if(sym != NULL) { + info->dli_sname = si->strtab + sym->st_name; + info->dli_saddr = (void*)(si->base + sym->st_value); + } + + ret = 1; + } + + pthread_mutex_unlock(&dl_lock); + + return ret; +} + +int android_dlclose(void *handle) +{ + pthread_mutex_lock(&dl_lock); + (void)unload_library((soinfo*)handle); + pthread_mutex_unlock(&dl_lock); + return 0; +} + +int android_dl_iterate_phdr(int (*cb)(struct dl_phdr_info *info, size_t size, void *data),void *data); + +#if defined(ANDROID_ARM_LINKER) +// 0000000 00011111 111112 22222222 2333333 333344444444445555555 +// 0123456 78901234 567890 12345678 9012345 678901234567890123456 +#define ANDROID_LIBDL_STRTAB \ + "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_unwind_find_exidx\0dl_iterate_phdr\0" + +_Unwind_Ptr android_dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount); + +#elif defined(ANDROID_X86_LINKER) +// 0000000 00011111 111112 22222222 2333333 3333444444444455 +// 0123456 78901234 567890 12345678 9012345 6789012345678901 +#define ANDROID_LIBDL_STRTAB \ + "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_iterate_phdr\0" +#elif defined(ANDROID_SH_LINKER) +// 0000000 00011111 111112 22222222 2333333 3333444444444455 +// 0123456 78901234 567890 12345678 9012345 6789012345678901 +#define ANDROID_LIBDL_STRTAB \ + "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_iterate_phdr\0" + +#else /* !defined(ANDROID_ARM_LINKER) && !defined(ANDROID_X86_LINKER) */ +#error Unsupported architecture. Only ARM and x86 are presently supported. +#endif + + +static Elf32_Sym libdl_symtab[] = { + // total length of libdl_info.strtab, including trailing 0 + // This is actually the the STH_UNDEF entry. Technically, it's + // supposed to have st_name == 0, but instead, it points to an index + // in the strtab with a \0 to make iterating through the symtab easier. + { st_name: sizeof(ANDROID_LIBDL_STRTAB) - 1, + }, + { st_name: 0, // starting index of the name in libdl_info.strtab + st_value: (Elf32_Addr) &android_dlopen, + st_info: STB_GLOBAL << 4, + st_shndx: 1, + }, + { st_name: 7, + st_value: (Elf32_Addr) &android_dlclose, + st_info: STB_GLOBAL << 4, + st_shndx: 1, + }, + { st_name: 15, + st_value: (Elf32_Addr) &android_dlsym, + st_info: STB_GLOBAL << 4, + st_shndx: 1, + }, + { st_name: 21, + st_value: (Elf32_Addr) &android_dlerror, + st_info: STB_GLOBAL << 4, + st_shndx: 1, + }, + { st_name: 29, + st_value: (Elf32_Addr) &android_dladdr, + st_info: STB_GLOBAL << 4, + st_shndx: 1, + }, +#ifdef ANDROID_ARM_LINKER + { st_name: 36, + st_value: (Elf32_Addr) &android_dl_unwind_find_exidx, + st_info: STB_GLOBAL << 4, + st_shndx: 1, + }, + { st_name: 57, +#else + { st_name: 36, +#endif + st_value: (Elf32_Addr) &android_dl_iterate_phdr, + st_info: STB_GLOBAL << 4, + st_shndx: 1, + }, +}; + +/* Fake out a hash table with a single bucket. + * A search of the hash table will look through + * libdl_symtab starting with index [1], then + * use libdl_chains to find the next index to + * look at. libdl_chains should be set up to + * walk through every element in libdl_symtab, + * and then end with 0 (sentinel value). + * + * I.e., libdl_chains should look like + * { 0, 2, 3, ... N, 0 } where N is the number + * of actual symbols, or nelems(libdl_symtab)-1 + * (since the first element of libdl_symtab is not + * a real symbol). + * + * (see _elf_lookup()) + * + * Note that adding any new symbols here requires + * stubbing them out in libdl. + */ +static unsigned libdl_buckets[1] = { 1 }; +#if defined(ANDROID_ARM_LINKER) +static unsigned libdl_chains[8] = { 0, 2, 3, 4, 5, 6, 7, 0 }; +#else +static unsigned libdl_chains[7] = { 0, 2, 3, 4, 5, 6, 0 }; +#endif + +soinfo libdl_info = { + name: "libdl.so", + flags: FLAG_LINKED, + + strtab: ANDROID_LIBDL_STRTAB, + symtab: libdl_symtab, + + refcount: 1, + nbucket: 1, +#if defined(ANDROID_ARM_LINKER) + nchain: 8, +#else + nchain: 7, +#endif + bucket: libdl_buckets, + chain: libdl_chains, +}; + diff --git a/hybris/src/jb/linker.c b/hybris/src/jb/linker.c new file mode 100644 index 0000000..db8ae1c --- /dev/null +++ b/hybris/src/jb/linker.c @@ -0,0 +1,2337 @@ +/* + * Copyright (C) 2008, 2009 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/* special private C library header - see Android.mk */ +//#include "bionic_tls.h" + +#include "linker.h" +#include "linker_debug.h" +#include "linker_environ.h" +#include "linker_format.h" + +#define ALLOW_SYMBOLS_FROM_MAIN 1 +#define SO_MAX 128 + +/* Assume average path length of 64 and max 8 paths */ +#define LDPATH_BUFSIZE 512 +#define LDPATH_MAX 8 + +#define LDPRELOAD_BUFSIZE 512 +#define LDPRELOAD_MAX 8 + +/* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<< + * + * Do NOT use malloc() and friends or pthread_*() code here. + * Don't use printf() either; it's caused mysterious memory + * corruption in the past. + * The linker runs before we bring up libc and it's easiest + * to make sure it does not depend on any complex libc features + * + * open issues / todo: + * + * - are we doing everything we should for ARM_COPY relocations? + * - cleaner error reporting + * - after linking, set as much stuff as possible to READONLY + * and NOEXEC + * - linker hardcodes PAGE_SIZE and PAGE_MASK because the kernel + * headers provide versions that are negative... + * - allocate space for soinfo structs dynamically instead of + * having a hard limit (64) +*/ + + +static int link_image(soinfo *si, unsigned wr_offset); + +static int socount = 0; +static soinfo sopool[SO_MAX]; +static soinfo *freelist = NULL; +static soinfo *solist = &libdl_info; +static soinfo *sonext = &libdl_info; +#if ALLOW_SYMBOLS_FROM_MAIN +static soinfo *somain; /* main process, always the one after libdl_info */ +#endif + + +static inline int validate_soinfo(soinfo *si) +{ + return (si >= sopool && si < sopool + SO_MAX) || + si == &libdl_info; +} + +static char ldpaths_buf[LDPATH_BUFSIZE]; +static const char *ldpaths[LDPATH_MAX + 1]; + +static char ldpreloads_buf[LDPRELOAD_BUFSIZE]; +static const char *ldpreload_names[LDPRELOAD_MAX + 1]; + +static soinfo *preloads[LDPRELOAD_MAX + 1]; + +#if LINKER_DEBUG +int debug_verbosity = 0; +int debug_stdout = 0; +#endif + +static int pid; + +/* This boolean is set if the program being loaded is setuid */ +static int program_is_setuid; + +#if STATS +struct _link_stats linker_stats; +#endif + +#if COUNT_PAGES +unsigned bitmask[4096]; +#endif + +#ifndef PT_ARM_EXIDX +#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ +#endif + +#if 0 +// disable abort() since this is not a linker anymore +#define HOODLUM(name, ret, ...) \ + ret name __VA_ARGS__ \ + { \ + char errstr[] = "ERROR: " #name " called from the dynamic linker!\n"; \ + write(2, errstr, sizeof(errstr)); \ + abort(); \ + } +HOODLUM(malloc, void *, (size_t size)); +HOODLUM(free, void, (void *ptr)); +HOODLUM(realloc, void *, (void *ptr, size_t size)); +HOODLUM(calloc, void *, (size_t cnt, size_t size)); +#endif + +static char tmp_err_buf[768]; +static char __linker_dl_err_buf[768]; +#define DL_ERR(fmt, x...) \ + do { \ + format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf), \ + "%s[%d]: " fmt, __func__, __LINE__, ##x); \ + ERROR(fmt "\n", ##x); \ + } while(0) + +const char *linker_get_error(void) +{ + return (const char *)&__linker_dl_err_buf[0]; +} + +/* + * This function is an empty stub where GDB locates a breakpoint to get notified + * about linker activity. + */ +extern void __attribute__((noinline)) rtld_db_dlactivity(void); + +static struct r_debug _r_debug = {1, NULL, &rtld_db_dlactivity, + RT_CONSISTENT, 0}; +static struct link_map *r_debug_tail = 0; + +static pthread_mutex_t _r_debug_lock = PTHREAD_MUTEX_INITIALIZER; + +static void insert_soinfo_into_debug_map(soinfo * info) +{ + struct link_map * map; + + /* Copy the necessary fields into the debug structure. + */ + map = &(info->linkmap); + map->l_addr = info->base; + map->l_name = (char*) info->name; + map->l_ld = (uintptr_t)info->dynamic; + + /* Stick the new library at the end of the list. + * gdb tends to care more about libc than it does + * about leaf libraries, and ordering it this way + * reduces the back-and-forth over the wire. + */ + if (r_debug_tail) { + r_debug_tail->l_next = map; + map->l_prev = r_debug_tail; + map->l_next = 0; + } else { + _r_debug.r_map = map; + map->l_prev = 0; + map->l_next = 0; + } + r_debug_tail = map; +} + +static void remove_soinfo_from_debug_map(soinfo * info) +{ + struct link_map * map = &(info->linkmap); + + if (r_debug_tail == map) + r_debug_tail = map->l_prev; + + if (map->l_prev) map->l_prev->l_next = map->l_next; + if (map->l_next) map->l_next->l_prev = map->l_prev; +} + +void notify_gdb_of_load(soinfo * info) +{ + if (info->flags & FLAG_EXE) { + // GDB already knows about the main executable + return; + } + + pthread_mutex_lock(&_r_debug_lock); + + _r_debug.r_state = RT_ADD; + rtld_db_dlactivity(); + + insert_soinfo_into_debug_map(info); + + _r_debug.r_state = RT_CONSISTENT; + rtld_db_dlactivity(); + + pthread_mutex_unlock(&_r_debug_lock); +} + +void notify_gdb_of_unload(soinfo * info) +{ + if (info->flags & FLAG_EXE) { + // GDB already knows about the main executable + return; + } + + pthread_mutex_lock(&_r_debug_lock); + + _r_debug.r_state = RT_DELETE; + rtld_db_dlactivity(); + + remove_soinfo_from_debug_map(info); + + _r_debug.r_state = RT_CONSISTENT; + rtld_db_dlactivity(); + + pthread_mutex_unlock(&_r_debug_lock); +} + +void notify_gdb_of_libraries() +{ + pthread_mutex_lock(&_r_debug_lock); + _r_debug.r_state = RT_ADD; + rtld_db_dlactivity(); + _r_debug.r_state = RT_CONSISTENT; + rtld_db_dlactivity(); + pthread_mutex_unlock(&_r_debug_lock); +} + +static soinfo *alloc_info(const char *name) +{ + soinfo *si; + + if(strlen(name) >= SOINFO_NAME_LEN) { + DL_ERR("%5d library name %s too long", pid, name); + return NULL; + } + + /* The freelist is populated when we call free_info(), which in turn is + done only by dlclose(), which is not likely to be used. + */ + if (!freelist) { + if(socount == SO_MAX) { + DL_ERR("%5d too many libraries when loading %s", pid, name); + return NULL; + } + freelist = sopool + socount++; + freelist->next = NULL; + } + + si = freelist; + freelist = freelist->next; + + /* Make sure we get a clean block of soinfo */ + memset(si, 0, sizeof(soinfo)); + strlcpy((char*) si->name, name, sizeof(si->name)); + sonext->next = si; + si->next = NULL; + si->refcount = 0; + sonext = si; + + TRACE("%5d name %s: allocated soinfo @ %p\n", pid, name, si); + return si; +} + +static void free_info(soinfo *si) +{ + soinfo *prev = NULL, *trav; + + TRACE("%5d name %s: freeing soinfo @ %p\n", pid, si->name, si); + + for(trav = solist; trav != NULL; trav = trav->next){ + if (trav == si) + break; + prev = trav; + } + if (trav == NULL) { + /* si was not ni solist */ + DL_ERR("%5d name %s is not in solist!", pid, si->name); + return; + } + + /* prev will never be NULL, because the first entry in solist is + always the static libdl_info. + */ + prev->next = si->next; + if (si == sonext) sonext = prev; + si->next = freelist; + freelist = si; +} + +const char *addr_to_name(unsigned addr) +{ + soinfo *si; + + for(si = solist; si != 0; si = si->next){ + if((addr >= si->base) && (addr < (si->base + si->size))) { + return si->name; + } + } + + return ""; +} + +/* For a given PC, find the .so that it belongs to. + * Returns the base address of the .ARM.exidx section + * for that .so, and the number of 8-byte entries + * in that section (via *pcount). + * + * Intended to be called by libc's __gnu_Unwind_Find_exidx(). + * + * This function is exposed via dlfcn.c and libdl.so. + */ +#ifdef ANDROID_ARM_LINKER +_Unwind_Ptr android_dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount) +{ + soinfo *si; + unsigned addr = (unsigned)pc; + + for (si = solist; si != 0; si = si->next){ + if ((addr >= si->base) && (addr < (si->base + si->size))) { + *pcount = si->ARM_exidx_count; + return (_Unwind_Ptr)(si->base + (unsigned long)si->ARM_exidx); + } + } + *pcount = 0; + return NULL; +} +#endif +/* Here, we only have to provide a callback to iterate across all the + * loaded libraries. gcc_eh does the rest. */ +int +android_dl_iterate_phdr(int (*cb)(struct dl_phdr_info *info, size_t size, void *data), + void *data) +{ + soinfo *si; + struct dl_phdr_info dl_info; + int rv = 0; + + for (si = solist; si != NULL; si = si->next) { + dl_info.dlpi_addr = si->linkmap.l_addr; + dl_info.dlpi_name = si->linkmap.l_name; + dl_info.dlpi_phdr = si->phdr; + dl_info.dlpi_phnum = si->phnum; + rv = cb(&dl_info, sizeof (struct dl_phdr_info), data); + if (rv != 0) + break; + } + return rv; +} + +static Elf32_Sym *_elf_lookup(soinfo *si, unsigned hash, const char *name) +{ + Elf32_Sym *s; + Elf32_Sym *symtab = si->symtab; + const char *strtab = si->strtab; + unsigned n; + + TRACE_TYPE(LOOKUP, "%5d SEARCH %s in %s@0x%08x %08x %d\n", pid, + name, si->name, si->base, hash, hash % si->nbucket); + n = hash % si->nbucket; + + for(n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]){ + s = symtab + n; + if(strcmp(strtab + s->st_name, name)) continue; + + /* only concern ourselves with global and weak symbol definitions */ + switch(ELF32_ST_BIND(s->st_info)){ + case STB_GLOBAL: + case STB_WEAK: + /* no section == undefined */ + if(s->st_shndx == 0) continue; + + TRACE_TYPE(LOOKUP, "%5d FOUND %s in %s (%08x) %d\n", pid, + name, si->name, s->st_value, s->st_size); + return s; + } + } + + return NULL; +} + +static unsigned elfhash(const char *_name) +{ + const unsigned char *name = (const unsigned char *) _name; + unsigned h = 0, g; + + while(*name) { + h = (h << 4) + *name++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + return h; +} + +static Elf32_Sym * +_do_lookup(soinfo *si, const char *name, unsigned *base) +{ + unsigned elf_hash = elfhash(name); + Elf32_Sym *s; + unsigned *d; + soinfo *lsi = si; + int i; + + /* Look for symbols in the local scope (the object who is + * searching). This happens with C++ templates on i386 for some + * reason. + * + * Notes on weak symbols: + * The ELF specs are ambigious about treatment of weak definitions in + * dynamic linking. Some systems return the first definition found + * and some the first non-weak definition. This is system dependent. + * Here we return the first definition found for simplicity. */ + + s = _elf_lookup(si, elf_hash, name); + if(s != NULL) + goto done; + + /* Next, look for it in the preloads list */ + for(i = 0; preloads[i] != NULL; i++) { + lsi = preloads[i]; + s = _elf_lookup(lsi, elf_hash, name); + if(s != NULL) + goto done; + } + + for(d = si->dynamic; *d; d += 2) { + if(d[0] == DT_NEEDED){ + lsi = (soinfo *)d[1]; + if (!validate_soinfo(lsi)) { + DL_ERR("%5d bad DT_NEEDED pointer in %s", + pid, si->name); + return NULL; + } + + DEBUG("%5d %s: looking up %s in %s\n", + pid, si->name, name, lsi->name); + s = _elf_lookup(lsi, elf_hash, name); + if ((s != NULL) && (s->st_shndx != SHN_UNDEF)) + goto done; + } + } + +#if ALLOW_SYMBOLS_FROM_MAIN + /* If we are resolving relocations while dlopen()ing a library, it's OK for + * the library to resolve a symbol that's defined in the executable itself, + * although this is rare and is generally a bad idea. + */ + if (somain) { + lsi = somain; + DEBUG("%5d %s: looking up %s in executable %s\n", + pid, si->name, name, lsi->name); + s = _elf_lookup(lsi, elf_hash, name); + } +#endif + +done: + if(s != NULL) { + TRACE_TYPE(LOOKUP, "%5d si %s sym %s s->st_value = 0x%08x, " + "found in %s, base = 0x%08x\n", + pid, si->name, name, s->st_value, lsi->name, lsi->base); + *base = lsi->base; + return s; + } + + return NULL; +} + +/* This is used by dl_sym(). It performs symbol lookup only within the + specified soinfo object and not in any of its dependencies. + */ +Elf32_Sym *lookup_in_library(soinfo *si, const char *name) +{ + return _elf_lookup(si, elfhash(name), name); +} + +/* This is used by dl_sym(). It performs a global symbol lookup. + */ +Elf32_Sym *lookup(const char *name, soinfo **found, soinfo *start) +{ + unsigned elf_hash = elfhash(name); + Elf32_Sym *s = NULL; + soinfo *si; + + if(start == NULL) { + start = solist; + } + + for(si = start; (s == NULL) && (si != NULL); si = si->next) + { + if(si->flags & FLAG_ERROR) + continue; + s = _elf_lookup(si, elf_hash, name); + if (s != NULL) { + *found = si; + break; + } + } + + if(s != NULL) { + TRACE_TYPE(LOOKUP, "%5d %s s->st_value = 0x%08x, " + "si->base = 0x%08x\n", pid, name, s->st_value, si->base); + return s; + } + + return NULL; +} + +soinfo *find_containing_library(const void *addr) +{ + soinfo *si; + + for(si = solist; si != NULL; si = si->next) + { + if((unsigned)addr >= si->base && (unsigned)addr - si->base < si->size) { + return si; + } + } + + return NULL; +} + +Elf32_Sym *find_containing_symbol(const void *addr, soinfo *si) +{ + unsigned int i; + unsigned soaddr = (unsigned)addr - si->base; + + /* Search the library's symbol table for any defined symbol which + * contains this address */ + for(i=0; inchain; i++) { + Elf32_Sym *sym = &si->symtab[i]; + + if(sym->st_shndx != SHN_UNDEF && + soaddr >= sym->st_value && + soaddr < sym->st_value + sym->st_size) { + return sym; + } + } + + return NULL; +} + +#if 0 +static void dump(soinfo *si) +{ + Elf32_Sym *s = si->symtab; + unsigned n; + + for(n = 0; n < si->nchain; n++) { + TRACE("%5d %04d> %08x: %02x %04x %08x %08x %s\n", pid, n, s, + s->st_info, s->st_shndx, s->st_value, s->st_size, + si->strtab + s->st_name); + s++; + } +} +#endif + +static const char *sopaths[] = { + "/vendor/lib", + "/system/lib", + 0 +}; + +static int _open_lib(const char *name) +{ + int fd; + struct stat filestat; + + if ((stat(name, &filestat) >= 0) && S_ISREG(filestat.st_mode)) { + if ((fd = open(name, O_RDONLY)) >= 0) + return fd; + } + + return -1; +} + +static void parse_library_path(const char *path, char *delim); + +static int open_library(const char *name) +{ + int fd; + char buf[512]; + const char **path; + int n; + + TRACE("[ %5d opening %s ]\n", pid, name); + + if(name == 0) return -1; + if(strlen(name) > 256) return -1; + + if ((name[0] == '/') && ((fd = _open_lib(name)) >= 0)) + return fd; + +#ifdef DEFAULT_HYBRIS_LD_LIBRARY_PATH + if (getenv("HYBRIS_LD_LIBRARY_PATH") == NULL && *ldpaths == 0) + { + parse_library_path(DEFAULT_HYBRIS_LD_LIBRARY_PATH, ":"); + } +#endif + if (getenv("HYBRIS_LD_LIBRARY_PATH") != NULL && *ldpaths == 0) + { + parse_library_path(getenv("HYBRIS_LD_LIBRARY_PATH"), ":"); + } + + for (path = ldpaths; *path; path++) { + n = format_buffer(buf, sizeof(buf), "%s/%s", *path, name); + if (n < 0 || n >= (int)sizeof(buf)) { + WARN("Ignoring very long library path: %s/%s\n", *path, name); + continue; + } + if ((fd = _open_lib(buf)) >= 0) + return fd; + } + for (path = sopaths; *path; path++) { + n = format_buffer(buf, sizeof(buf), "%s/%s", *path, name); + if (n < 0 || n >= (int)sizeof(buf)) { + WARN("Ignoring very long library path: %s/%s\n", *path, name); + continue; + } + if ((fd = _open_lib(buf)) >= 0) + return fd; + } + + return -1; +} + +/* temporary space for holding the first page of the shared lib + * which contains the elf header (with the pht). */ +static unsigned char __header[PAGE_SIZE]; + +typedef struct { + long mmap_addr; + char tag[4]; /* 'P', 'R', 'E', ' ' */ +} prelink_info_t; + +/* Returns the requested base address if the library is prelinked, + * and 0 otherwise. */ +static unsigned long +is_prelinked(int fd, const char *name) +{ + off_t sz; + prelink_info_t info; + + sz = lseek(fd, -sizeof(prelink_info_t), SEEK_END); + if (sz < 0) { + DL_ERR("lseek() failed!"); + return 0; + } + + if (read(fd, &info, sizeof(info)) != sizeof(info)) { + INFO("Could not read prelink_info_t structure for `%s`\n", name); + return 0; + } + + if (strncmp(info.tag, "PRE ", 4)) { + INFO("`%s` is not a prelinked library\n", name); + return 0; + } + + return (unsigned long)info.mmap_addr; +} + +/* verify_elf_object + * Verifies if the object @ base is a valid ELF object + * + * Args: + * + * Returns: + * 0 on success + * -1 if no valid ELF object is found @ base. + */ +static int +verify_elf_object(void *base, const char *name) +{ + Elf32_Ehdr *hdr = (Elf32_Ehdr *) base; + + if (hdr->e_ident[EI_MAG0] != ELFMAG0) return -1; + if (hdr->e_ident[EI_MAG1] != ELFMAG1) return -1; + if (hdr->e_ident[EI_MAG2] != ELFMAG2) return -1; + if (hdr->e_ident[EI_MAG3] != ELFMAG3) return -1; + + /* TODO: Should we verify anything else in the header? */ +#ifdef ANDROID_ARM_LINKER + if (hdr->e_machine != EM_ARM) return -1; +#elif defined(ANDROID_X86_LINKER) + if (hdr->e_machine != EM_386) return -1; +#endif + return 0; +} + + +/* get_lib_extents + * Retrieves the base (*base) address where the ELF object should be + * mapped and its overall memory size (*total_sz). + * + * Args: + * fd: Opened file descriptor for the library + * name: The name of the library + * _hdr: Pointer to the header page of the library + * total_sz: Total size of the memory that should be allocated for + * this library + * + * Returns: + * -1 if there was an error while trying to get the lib extents. + * The possible reasons are: + * - Could not determine if the library was prelinked. + * - The library provided is not a valid ELF object + * 0 if the library did not request a specific base offset (normal + * for non-prelinked libs) + * > 0 if the library requests a specific address to be mapped to. + * This indicates a pre-linked library. + */ +static unsigned +get_lib_extents(int fd, const char *name, void *__hdr, unsigned *total_sz) +{ + unsigned req_base; + unsigned min_vaddr = 0xffffffff; + unsigned max_vaddr = 0; + unsigned char *_hdr = (unsigned char *)__hdr; + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)_hdr; + Elf32_Phdr *phdr; + int cnt; + + TRACE("[ %5d Computing extents for '%s'. ]\n", pid, name); + if (verify_elf_object(_hdr, name) < 0) { + DL_ERR("%5d - %s is not a valid ELF object", pid, name); + return (unsigned)-1; + } + + req_base = (unsigned) is_prelinked(fd, name); + if (req_base == (unsigned)-1) + return -1; + else if (req_base != 0) { + TRACE("[ %5d - Prelinked library '%s' requesting base @ 0x%08x ]\n", + pid, name, req_base); + } else { + TRACE("[ %5d - Non-prelinked library '%s' found. ]\n", pid, name); + } + + phdr = (Elf32_Phdr *)(_hdr + ehdr->e_phoff); + + /* find the min/max p_vaddrs from all the PT_LOAD segments so we can + * get the range. */ + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt, ++phdr) { + if (phdr->p_type == PT_LOAD) { + if ((phdr->p_vaddr + phdr->p_memsz) > max_vaddr) + max_vaddr = phdr->p_vaddr + phdr->p_memsz; + if (phdr->p_vaddr < min_vaddr) + min_vaddr = phdr->p_vaddr; + } + } + + if ((min_vaddr == 0xffffffff) && (max_vaddr == 0)) { + DL_ERR("%5d - No loadable segments found in %s.", pid, name); + return (unsigned)-1; + } + + /* truncate min_vaddr down to page boundary */ + min_vaddr &= ~PAGE_MASK; + + /* round max_vaddr up to the next page */ + max_vaddr = (max_vaddr + PAGE_SIZE - 1) & ~PAGE_MASK; + + *total_sz = (max_vaddr - min_vaddr); + return (unsigned)req_base; +} + +/* reserve_mem_region + * + * This function reserves a chunk of memory to be used for mapping in + * a prelinked shared library. We reserve the entire memory region here, and + * then the rest of the linker will relocate the individual loadable + * segments into the correct locations within this memory range. + * + * Args: + * si->base: The requested base of the allocation. + * si->size: The size of the allocation. + * + * Returns: + * -1 on failure, and 0 on success. On success, si->base will contain + * the virtual address at which the library will be mapped. + */ + +static int reserve_mem_region(soinfo *si) +{ + void *base = mmap((void *)si->base, si->size, PROT_NONE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (base == MAP_FAILED) { + DL_ERR("%5d can NOT map (%sprelinked) library '%s' at 0x%08x " + "as requested, will try general pool: %d (%s)", + pid, (si->base ? "" : "non-"), si->name, si->base, + errno, strerror(errno)); + return -1; + } else if (base != (void *)si->base) { + DL_ERR("OOPS: %5d %sprelinked library '%s' mapped at 0x%08x, " + "not at 0x%08x", pid, (si->base ? "" : "non-"), + si->name, (unsigned)base, si->base); + munmap(base, si->size); + return -1; + } + return 0; +} + +static int alloc_mem_region(soinfo *si) +{ + if (si->base) { + /* Attempt to mmap a prelinked library. */ + return reserve_mem_region(si); + } + + /* This is not a prelinked library, so we use the kernel's default + allocator. + */ + + void *base = mmap(NULL, si->size, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (base == MAP_FAILED) { + DL_ERR("%5d mmap of library '%s' failed: %d (%s)\n", + pid, si->name, + errno, strerror(errno)); + goto err; + } + si->base = (unsigned) base; + INFO("%5d mapped library '%s' to %08x via kernel allocator.\n", + pid, si->name, si->base); + return 0; + +err: + DL_ERR("OOPS: %5d cannot map library '%s'. no vspace available.", + pid, si->name); + return -1; +} + +#define MAYBE_MAP_FLAG(x,from,to) (((x) & (from)) ? (to) : 0) +#define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ + MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ + MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) +/* load_segments + * + * This function loads all the loadable (PT_LOAD) segments into memory + * at their appropriate memory offsets off the base address. + * + * Args: + * fd: Open file descriptor to the library to load. + * header: Pointer to a header page that contains the ELF header. + * This is needed since we haven't mapped in the real file yet. + * si: ptr to soinfo struct describing the shared object. + * + * Returns: + * 0 on success, -1 on failure. + */ +static int +load_segments(int fd, void *header, soinfo *si) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)header; + Elf32_Phdr *phdr = (Elf32_Phdr *)((unsigned char *)header + ehdr->e_phoff); + Elf32_Addr base = (Elf32_Addr) si->base; + int cnt; + unsigned len; + Elf32_Addr tmp; + unsigned char *pbase; + unsigned char *extra_base; + unsigned extra_len; + unsigned total_sz = 0; + + si->wrprotect_start = 0xffffffff; + si->wrprotect_end = 0; + + TRACE("[ %5d - Begin loading segments for '%s' @ 0x%08x ]\n", + pid, si->name, (unsigned)si->base); + /* Now go through all the PT_LOAD segments and map them into memory + * at the appropriate locations. */ + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt, ++phdr) { + if (phdr->p_type == PT_LOAD) { + DEBUG_DUMP_PHDR(phdr, "PT_LOAD", pid); + /* we want to map in the segment on a page boundary */ + tmp = base + (phdr->p_vaddr & (~PAGE_MASK)); + /* add the # of bytes we masked off above to the total length. */ + len = phdr->p_filesz + (phdr->p_vaddr & PAGE_MASK); + + TRACE("[ %d - Trying to load segment from '%s' @ 0x%08x " + "(0x%08x). p_vaddr=0x%08x p_offset=0x%08x ]\n", pid, si->name, + (unsigned)tmp, len, phdr->p_vaddr, phdr->p_offset); + pbase = mmap((void *)tmp, len, PFLAGS_TO_PROT(phdr->p_flags), + MAP_PRIVATE | MAP_FIXED, fd, + phdr->p_offset & (~PAGE_MASK)); + if (pbase == MAP_FAILED) { + DL_ERR("%d failed to map segment from '%s' @ 0x%08x (0x%08x). " + "p_vaddr=0x%08x p_offset=0x%08x", pid, si->name, + (unsigned)tmp, len, phdr->p_vaddr, phdr->p_offset); + goto fail; + } + + /* If 'len' didn't end on page boundary, and it's a writable + * segment, zero-fill the rest. */ + if ((len & PAGE_MASK) && (phdr->p_flags & PF_W)) + memset((void *)(pbase + len), 0, PAGE_SIZE - (len & PAGE_MASK)); + + /* Check to see if we need to extend the map for this segment to + * cover the diff between filesz and memsz (i.e. for bss). + * + * base _+---------------------+ page boundary + * . . + * | | + * . . + * pbase _+---------------------+ page boundary + * | | + * . . + * base + p_vaddr _| | + * . \ \ . + * . | filesz | . + * pbase + len _| / | | + * <0 pad> . . . + * extra_base _+------------|--------+ page boundary + * / . . . + * | . . . + * | +------------|--------+ page boundary + * extra_len-> | | | | + * | . | memsz . + * | . | . + * \ _| / | + * . . + * | | + * _+---------------------+ page boundary + */ + tmp = (Elf32_Addr)(((unsigned)pbase + len + PAGE_SIZE - 1) & + (~PAGE_MASK)); + if (tmp < (base + phdr->p_vaddr + phdr->p_memsz)) { + extra_len = base + phdr->p_vaddr + phdr->p_memsz - tmp; + TRACE("[ %5d - Need to extend segment from '%s' @ 0x%08x " + "(0x%08x) ]\n", pid, si->name, (unsigned)tmp, extra_len); + /* map in the extra page(s) as anonymous into the range. + * This is probably not necessary as we already mapped in + * the entire region previously, but we just want to be + * sure. This will also set the right flags on the region + * (though we can probably accomplish the same thing with + * mprotect). + */ + extra_base = mmap((void *)tmp, extra_len, + PFLAGS_TO_PROT(phdr->p_flags), + MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, + -1, 0); + if (extra_base == MAP_FAILED) { + DL_ERR("[ %5d - failed to extend segment from '%s' @ 0x%08x" + " (0x%08x) ]", pid, si->name, (unsigned)tmp, + extra_len); + goto fail; + } + /* TODO: Check if we need to memset-0 this region. + * Anonymous mappings are zero-filled copy-on-writes, so we + * shouldn't need to. */ + TRACE("[ %5d - Segment from '%s' extended @ 0x%08x " + "(0x%08x)\n", pid, si->name, (unsigned)extra_base, + extra_len); + } + /* set the len here to show the full extent of the segment we + * just loaded, mostly for debugging */ + len = (((unsigned)base + phdr->p_vaddr + phdr->p_memsz + + PAGE_SIZE - 1) & (~PAGE_MASK)) - (unsigned)pbase; + TRACE("[ %5d - Successfully loaded segment from '%s' @ 0x%08x " + "(0x%08x). p_vaddr=0x%08x p_offset=0x%08x\n", pid, si->name, + (unsigned)pbase, len, phdr->p_vaddr, phdr->p_offset); + total_sz += len; + /* Make the section writable just in case we'll have to write to + * it during relocation (i.e. text segment). However, we will + * remember what range of addresses should be write protected. + * + */ + if (!(phdr->p_flags & PF_W)) { + if ((unsigned)pbase < si->wrprotect_start) + si->wrprotect_start = (unsigned)pbase; + if (((unsigned)pbase + len) > si->wrprotect_end) + si->wrprotect_end = (unsigned)pbase + len; + mprotect(pbase, len, + PFLAGS_TO_PROT(phdr->p_flags) | PROT_WRITE); + } + } else if (phdr->p_type == PT_DYNAMIC) { + DEBUG_DUMP_PHDR(phdr, "PT_DYNAMIC", pid); + /* this segment contains the dynamic linking information */ + si->dynamic = (unsigned *)(base + phdr->p_vaddr); + } else if (phdr->p_type == PT_GNU_RELRO) { + if ((phdr->p_vaddr >= si->size) + || ((phdr->p_vaddr + phdr->p_memsz) > si->size) + || ((base + phdr->p_vaddr + phdr->p_memsz) < base)) { + DL_ERR("%d invalid GNU_RELRO in '%s' " + "p_vaddr=0x%08x p_memsz=0x%08x", pid, si->name, + phdr->p_vaddr, phdr->p_memsz); + goto fail; + } + si->gnu_relro_start = (Elf32_Addr) (base + phdr->p_vaddr); + si->gnu_relro_len = (unsigned) phdr->p_memsz; + } else { +#ifdef ANDROID_ARM_LINKER + if (phdr->p_type == PT_ARM_EXIDX) { + DEBUG_DUMP_PHDR(phdr, "PT_ARM_EXIDX", pid); + /* exidx entries (used for stack unwinding) are 8 bytes each. + */ + si->ARM_exidx = (unsigned *)phdr->p_vaddr; + si->ARM_exidx_count = phdr->p_memsz / 8; + } +#endif + } + + } + + /* Sanity check */ + if (total_sz > si->size) { + DL_ERR("%5d - Total length (0x%08x) of mapped segments from '%s' is " + "greater than what was allocated (0x%08x). THIS IS BAD!", + pid, total_sz, si->name, si->size); + goto fail; + } + + TRACE("[ %5d - Finish loading segments for '%s' @ 0x%08x. " + "Total memory footprint: 0x%08x bytes ]\n", pid, si->name, + (unsigned)si->base, si->size); + return 0; + +fail: + /* We can just blindly unmap the entire region even though some things + * were mapped in originally with anonymous and others could have been + * been mapped in from the file before we failed. The kernel will unmap + * all the pages in the range, irrespective of how they got there. + */ + munmap((void *)si->base, si->size); + si->flags |= FLAG_ERROR; + return -1; +} + +/* TODO: Implement this to take care of the fact that Android ARM + * ELF objects shove everything into a single loadable segment that has the + * write bit set. wr_offset is then used to set non-(data|bss) pages to be + * non-writable. + */ +#if 0 +static unsigned +get_wr_offset(int fd, const char *name, Elf32_Ehdr *ehdr) +{ + Elf32_Shdr *shdr_start; + Elf32_Shdr *shdr; + int shdr_sz = ehdr->e_shnum * sizeof(Elf32_Shdr); + int cnt; + unsigned wr_offset = 0xffffffff; + + shdr_start = mmap(0, shdr_sz, PROT_READ, MAP_PRIVATE, fd, + ehdr->e_shoff & (~PAGE_MASK)); + if (shdr_start == MAP_FAILED) { + WARN("%5d - Could not read section header info from '%s'. Will not " + "not be able to determine write-protect offset.\n", pid, name); + return (unsigned)-1; + } + + for(cnt = 0, shdr = shdr_start; cnt < ehdr->e_shnum; ++cnt, ++shdr) { + if ((shdr->sh_type != SHT_NULL) && (shdr->sh_flags & SHF_WRITE) && + (shdr->sh_addr < wr_offset)) { + wr_offset = shdr->sh_addr; + } + } + + munmap(shdr_start, shdr_sz); + return wr_offset; +} +#endif + +static soinfo * +load_library(const char *name) +{ + int fd = open_library(name); + int cnt; + unsigned ext_sz; + unsigned req_base; + const char *bname; + soinfo *si = NULL; + Elf32_Ehdr *hdr; + + if(fd == -1) { + DL_ERR("Library '%s' not found", name); + return NULL; + } + + /* We have to read the ELF header to figure out what to do with this image + */ + if (lseek(fd, 0, SEEK_SET) < 0) { + DL_ERR("lseek() failed!"); + goto fail; + } + + if ((cnt = read(fd, &__header[0], PAGE_SIZE)) < 0) { + DL_ERR("read() failed!"); + goto fail; + } + + /* Parse the ELF header and get the size of the memory footprint for + * the library */ + req_base = get_lib_extents(fd, name, &__header[0], &ext_sz); + if (req_base == (unsigned)-1) + goto fail; + TRACE("[ %5d - '%s' (%s) wants base=0x%08x sz=0x%08x ]\n", pid, name, + (req_base ? "prelinked" : "not pre-linked"), req_base, ext_sz); + + /* Now configure the soinfo struct where we'll store all of our data + * for the ELF object. If the loading fails, we waste the entry, but + * same thing would happen if we failed during linking. Configuring the + * soinfo struct here is a lot more convenient. + */ + bname = strrchr(name, '/'); + si = alloc_info(bname ? bname + 1 : name); + if (si == NULL) + goto fail; + + /* Carve out a chunk of memory where we will map in the individual + * segments */ + si->base = req_base; + si->size = ext_sz; + si->flags = 0; + si->entry = 0; + si->dynamic = (unsigned *)-1; + if (alloc_mem_region(si) < 0) + goto fail; + + TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n", + pid, name, (void *)si->base, (unsigned) ext_sz); + + /* Now actually load the library's segments into right places in memory */ + if (load_segments(fd, &__header[0], si) < 0) { + goto fail; + } + + /* this might not be right. Technically, we don't even need this info + * once we go through 'load_segments'. */ + hdr = (Elf32_Ehdr *)si->base; + si->phdr = (Elf32_Phdr *)((unsigned char *)si->base + hdr->e_phoff); + si->phnum = hdr->e_phnum; + /**/ + + close(fd); + return si; + +fail: + if (si) free_info(si); + close(fd); + return NULL; +} + +static soinfo * +init_library(soinfo *si) +{ + unsigned wr_offset = 0xffffffff; + +#if LINKER_DEBUG + /* Has to be set via init_library as we don't get called via the + * traditional android init library path */ + const char* env; + env = getenv("HYBRIS_LINKER_DEBUG"); + if (env) + debug_verbosity = atoi(env); + if (getenv("HYBRIS_LINKER_STDOUT")) + debug_stdout = 1; + + INFO("[ HYBRIS: initializing library '%s']\n", si->name); +#endif + + /* At this point we know that whatever is loaded @ base is a valid ELF + * shared library whose segments are properly mapped in. */ + TRACE("[ %5d init_library base=0x%08x sz=0x%08x name='%s') ]\n", + pid, si->base, si->size, si->name); + + if(link_image(si, wr_offset)) { + /* We failed to link. However, we can only restore libbase + ** if no additional libraries have moved it since we updated it. + */ + munmap((void *)si->base, si->size); + return NULL; + } + + return si; +} + +soinfo *find_library(const char *name) +{ + soinfo *si; + const char *bname; + +#if ALLOW_SYMBOLS_FROM_MAIN + if (name == NULL) + return somain; +#else + if (name == NULL) + return NULL; +#endif + + bname = strrchr(name, '/'); + bname = bname ? bname + 1 : name; + + for(si = solist; si != 0; si = si->next){ + if(!strcmp(bname, si->name)) { + if(si->flags & FLAG_ERROR) { + DL_ERR("%5d '%s' failed to load previously", pid, bname); + return NULL; + } + if(si->flags & FLAG_LINKED) return si; + DL_ERR("OOPS: %5d recursive link to '%s'", pid, si->name); + return NULL; + } + } + + TRACE("[ %5d '%s' has not been loaded yet. Locating...]\n", pid, name); + si = load_library(name); + if(si == NULL) + return NULL; + return init_library(si); +} + +/* TODO: + * notify gdb of unload + * for non-prelinked libraries, find a way to decrement libbase + */ +static void call_destructors(soinfo *si); +unsigned unload_library(soinfo *si) +{ + unsigned *d; + if (si->refcount == 1) { + TRACE("%5d unloading '%s'\n", pid, si->name); + call_destructors(si); + + /* + * Make sure that we undo the PT_GNU_RELRO protections we added + * in link_image. This is needed to undo the DT_NEEDED hack below. + */ + if ((si->gnu_relro_start != 0) && (si->gnu_relro_len != 0)) { + Elf32_Addr start = (si->gnu_relro_start & ~PAGE_MASK); + unsigned len = (si->gnu_relro_start - start) + si->gnu_relro_len; + if (mprotect((void *) start, len, PROT_READ | PROT_WRITE) < 0) + DL_ERR("%5d %s: could not undo GNU_RELRO protections. " + "Expect a crash soon. errno=%d (%s)", + pid, si->name, errno, strerror(errno)); + + } + + for(d = si->dynamic; *d; d += 2) { + if(d[0] == DT_NEEDED){ + soinfo *lsi = (soinfo *)d[1]; + + // The next line will segfault if the we don't undo the + // PT_GNU_RELRO protections (see comments above and in + // link_image(). + d[1] = 0; + + if (validate_soinfo(lsi)) { + TRACE("%5d %s needs to unload %s\n", pid, + si->name, lsi->name); + unload_library(lsi); + } + else + DL_ERR("%5d %s: could not unload dependent library", + pid, si->name); + } + } + + munmap((char *)si->base, si->size); + notify_gdb_of_unload(si); + free_info(si); + si->refcount = 0; + } + else { + si->refcount--; + INFO("%5d not unloading '%s', decrementing refcount to %d\n", + pid, si->name, si->refcount); + } + return si->refcount; +} + +void *get_hooked_symbol(const char *sym); + +/* TODO: don't use unsigned for addrs below. It works, but is not + * ideal. They should probably be either uint32_t, Elf32_Addr, or unsigned + * long. + */ +static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count) +{ + Elf32_Sym *symtab = si->symtab; + const char *strtab = si->strtab; + Elf32_Sym *s; + unsigned base; + Elf32_Rel *start = rel; + unsigned idx; + + for (idx = 0; idx < count; ++idx) { + unsigned type = ELF32_R_TYPE(rel->r_info); + unsigned sym = ELF32_R_SYM(rel->r_info); + unsigned reloc = (unsigned)(rel->r_offset + si->base); + unsigned sym_addr = 0; + char *sym_name = NULL; + + DEBUG("%5d Processing '%s' relocation at index %d\n", pid, + si->name, idx); + if(sym != 0) { + sym_name = (char *)(strtab + symtab[sym].st_name); + INFO("HYBRIS: '%s' checking hooks for sym '%s'\n", si->name, sym_name); + sym_addr = (unsigned) get_hooked_symbol(sym_name); + if (sym_addr != NULL) { + INFO("HYBRIS: '%s' hooked symbol %s to %x\n", si->name, + sym_name, sym_addr); + } else { + s = _do_lookup(si, sym_name, &base); + } + if(sym_addr == NULL) + if(s == NULL) { + /* We only allow an undefined symbol if this is a weak + reference.. */ + s = &symtab[sym]; + if (ELF32_ST_BIND(s->st_info) != STB_WEAK && strcmp(si->name, "libdsyscalls.so") != 0) { + DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name); + return -1; + } + + /* IHI0044C AAELF 4.5.1.1: + + Libraries are not searched to resolve weak references. + It is not an error for a weak reference to remain + unsatisfied. + + During linking, the value of an undefined weak reference is: + - Zero if the relocation type is absolute + - The address of the place if the relocation is pc-relative + - The address of nominial base address if the relocation + type is base-relative. + */ + + switch (type) { +#if defined(ANDROID_ARM_LINKER) + case R_ARM_JUMP_SLOT: + case R_ARM_GLOB_DAT: + case R_ARM_ABS32: + case R_ARM_RELATIVE: /* Don't care. */ + case R_ARM_NONE: /* Don't care. */ +#elif defined(ANDROID_X86_LINKER) + case R_386_JUMP_SLOT: + case R_386_GLOB_DAT: + case R_386_32: + case R_386_RELATIVE: /* Dont' care. */ +#endif /* ANDROID_*_LINKER */ + /* sym_addr was initialized to be zero above or relocation + code below does not care about value of sym_addr. + No need to do anything. */ + break; + +#if defined(ANDROID_X86_LINKER) + case R_386_PC32: + sym_addr = reloc; + break; +#endif /* ANDROID_X86_LINKER */ + +#if defined(ANDROID_ARM_LINKER) + case R_ARM_COPY: + /* Fall through. Can't really copy if weak symbol is + not found in run-time. */ +#endif /* ANDROID_ARM_LINKER */ + default: + DL_ERR("%5d unknown weak reloc type %d @ %p (%d)\n", + pid, type, rel, (int) (rel - start)); + return -1; + } + } else { + /* We got a definition. */ +#if 0 + if((base == 0) && (si->base != 0)){ + /* linking from libraries to main image is bad */ + DL_ERR("%5d cannot locate '%s'...", + pid, strtab + symtab[sym].st_name); + return -1; + } +#endif + sym_addr = (unsigned)(s->st_value + base); + } + COUNT_RELOC(RELOC_SYMBOL); + } else { + s = NULL; + } + +/* TODO: This is ugly. Split up the relocations by arch into + * different files. + */ + switch(type){ +#if defined(ANDROID_ARM_LINKER) + case R_ARM_JUMP_SLOT: + COUNT_RELOC(RELOC_ABSOLUTE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO JMP_SLOT %08x <- %08x %s\n", pid, + reloc, sym_addr, sym_name); + *((unsigned*)reloc) = sym_addr; + break; + case R_ARM_GLOB_DAT: + COUNT_RELOC(RELOC_ABSOLUTE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO GLOB_DAT %08x <- %08x %s\n", pid, + reloc, sym_addr, sym_name); + *((unsigned*)reloc) = sym_addr; + break; + case R_ARM_ABS32: + COUNT_RELOC(RELOC_ABSOLUTE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO ABS %08x <- %08x %s\n", pid, + reloc, sym_addr, sym_name); + *((unsigned*)reloc) += sym_addr; + break; + case R_ARM_REL32: + COUNT_RELOC(RELOC_RELATIVE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO REL32 %08x <- %08x - %08x %s\n", pid, + reloc, sym_addr, rel->r_offset, sym_name); + *((unsigned*)reloc) += sym_addr - rel->r_offset; + break; +#elif defined(ANDROID_X86_LINKER) + case R_386_JUMP_SLOT: + COUNT_RELOC(RELOC_ABSOLUTE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO JMP_SLOT %08x <- %08x %s\n", pid, + reloc, sym_addr, sym_name); + *((unsigned*)reloc) = sym_addr; + break; + case R_386_GLOB_DAT: + COUNT_RELOC(RELOC_ABSOLUTE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO GLOB_DAT %08x <- %08x %s\n", pid, + reloc, sym_addr, sym_name); + *((unsigned*)reloc) = sym_addr; + break; +#endif /* ANDROID_*_LINKER */ + +#if defined(ANDROID_ARM_LINKER) + case R_ARM_RELATIVE: +#elif defined(ANDROID_X86_LINKER) + case R_386_RELATIVE: +#endif /* ANDROID_*_LINKER */ + COUNT_RELOC(RELOC_RELATIVE); + MARK(rel->r_offset); + if(sym){ + DL_ERR("%5d odd RELATIVE form...", pid); + return -1; + } + TRACE_TYPE(RELO, "%5d RELO RELATIVE %08x <- +%08x\n", pid, + reloc, si->base); + *((unsigned*)reloc) += si->base; + break; + +#if defined(ANDROID_X86_LINKER) + case R_386_32: + COUNT_RELOC(RELOC_RELATIVE); + MARK(rel->r_offset); + + TRACE_TYPE(RELO, "%5d RELO R_386_32 %08x <- +%08x %s\n", pid, + reloc, sym_addr, sym_name); + *((unsigned *)reloc) += (unsigned)sym_addr; + break; + + case R_386_PC32: + COUNT_RELOC(RELOC_RELATIVE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO R_386_PC32 %08x <- " + "+%08x (%08x - %08x) %s\n", pid, reloc, + (sym_addr - reloc), sym_addr, reloc, sym_name); + *((unsigned *)reloc) += (unsigned)(sym_addr - reloc); + break; +#endif /* ANDROID_X86_LINKER */ + +#ifdef ANDROID_ARM_LINKER + case R_ARM_COPY: + COUNT_RELOC(RELOC_COPY); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO %08x <- %d @ %08x %s\n", pid, + reloc, s->st_size, sym_addr, sym_name); + memcpy((void*)reloc, (void*)sym_addr, s->st_size); + break; + case R_ARM_NONE: + break; +#endif /* ANDROID_ARM_LINKER */ + + default: + DL_ERR("%5d unknown reloc type %d @ %p (%d)", + pid, type, rel, (int) (rel - start)); + return -1; + } + rel++; + } + return 0; +} + +/* Please read the "Initialization and Termination functions" functions. + * of the linker design note in bionic/linker/README.TXT to understand + * what the following code is doing. + * + * The important things to remember are: + * + * DT_PREINIT_ARRAY must be called first for executables, and should + * not appear in shared libraries. + * + * DT_INIT should be called before DT_INIT_ARRAY if both are present + * + * DT_FINI should be called after DT_FINI_ARRAY if both are present + * + * DT_FINI_ARRAY must be parsed in reverse order. + */ + +static void call_array(unsigned *ctor, int count, int reverse) +{ + int n, inc = 1; + + if (reverse) { + ctor += (count-1); + inc = -1; + } + + for(n = count; n > 0; n--) { + TRACE("[ %5d Looking at %s *0x%08x == 0x%08x ]\n", pid, + reverse ? "dtor" : "ctor", + (unsigned)ctor, (unsigned)*ctor); + void (*func)() = (void (*)()) *ctor; + ctor += inc; + if(((int) func == 0) || ((int) func == -1)) continue; + TRACE("[ %5d Calling func @ 0x%08x ]\n", pid, (unsigned)func); + func(); + } +} + +void call_constructors_recursive(soinfo *si) +{ + if (si->constructors_called) + return; + if (strcmp(si->name,"libc.so") == 0) { + INFO("HYBRIS: =============> Skipping libc.so\n"); + return; + } + + // Set this before actually calling the constructors, otherwise it doesn't + // protect against recursive constructor calls. One simple example of + // constructor recursion is the libc debug malloc, which is implemented in + // libc_malloc_debug_leak.so: + // 1. The program depends on libc, so libc's constructor is called here. + // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so. + // 3. dlopen() calls call_constructors_recursive() with the newly created + // soinfo for libc_malloc_debug_leak.so. + // 4. The debug so depends on libc, so call_constructors_recursive() is + // called again with the libc soinfo. If it doesn't trigger the early- + // out above, the libc constructor will be called again (recursively!). + si->constructors_called = 1; + + if (si->flags & FLAG_EXE) { + TRACE("[ %5d Calling preinit_array @ 0x%08x [%d] for '%s' ]\n", + pid, (unsigned)si->preinit_array, si->preinit_array_count, + si->name); + call_array(si->preinit_array, si->preinit_array_count, 0); + TRACE("[ %5d Done calling preinit_array for '%s' ]\n", pid, si->name); + } else { + if (si->preinit_array) { + DL_ERR("%5d Shared library '%s' has a preinit_array table @ 0x%08x." + " This is INVALID.", pid, si->name, + (unsigned)si->preinit_array); + } + } + + if (si->dynamic) { + unsigned *d; + for(d = si->dynamic; *d; d += 2) { + if(d[0] == DT_NEEDED){ + soinfo* lsi = (soinfo *)d[1]; + if (!validate_soinfo(lsi)) { + DL_ERR("%5d bad DT_NEEDED pointer in %s", + pid, si->name); + } else { + call_constructors_recursive(lsi); + } + } + } + } + + if (si->init_func) { + TRACE("[ %5d Calling init_func @ 0x%08x for '%s' ]\n", pid, + (unsigned)si->init_func, si->name); + si->init_func(); + TRACE("[ %5d Done calling init_func for '%s' ]\n", pid, si->name); + } + + if (si->init_array) { + TRACE("[ %5d Calling init_array @ 0x%08x [%d] for '%s' ]\n", pid, + (unsigned)si->init_array, si->init_array_count, si->name); + call_array(si->init_array, si->init_array_count, 0); + TRACE("[ %5d Done calling init_array for '%s' ]\n", pid, si->name); + } + +} + +static void call_destructors(soinfo *si) +{ + if (si->fini_array) { + TRACE("[ %5d Calling fini_array @ 0x%08x [%d] for '%s' ]\n", pid, + (unsigned)si->fini_array, si->fini_array_count, si->name); + call_array(si->fini_array, si->fini_array_count, 1); + TRACE("[ %5d Done calling fini_array for '%s' ]\n", pid, si->name); + } + + if (si->fini_func) { + TRACE("[ %5d Calling fini_func @ 0x%08x for '%s' ]\n", pid, + (unsigned)si->fini_func, si->name); + si->fini_func(); + TRACE("[ %5d Done calling fini_func for '%s' ]\n", pid, si->name); + } +} + +/* Force any of the closed stdin, stdout and stderr to be associated with + /dev/null. */ +static int nullify_closed_stdio (void) +{ + int dev_null, i, status; + int return_value = 0; + + dev_null = open("/dev/null", O_RDWR); + if (dev_null < 0) { + DL_ERR("Cannot open /dev/null."); + return -1; + } + TRACE("[ %5d Opened /dev/null file-descriptor=%d]\n", pid, dev_null); + + /* If any of the stdio file descriptors is valid and not associated + with /dev/null, dup /dev/null to it. */ + for (i = 0; i < 3; i++) { + /* If it is /dev/null already, we are done. */ + if (i == dev_null) + continue; + + TRACE("[ %5d Nullifying stdio file descriptor %d]\n", pid, i); + /* The man page of fcntl does not say that fcntl(..,F_GETFL) + can be interrupted but we do this just to be safe. */ + do { + status = fcntl(i, F_GETFL); + } while (status < 0 && errno == EINTR); + + /* If file is openned, we are good. */ + if (status >= 0) + continue; + + /* The only error we allow is that the file descriptor does not + exist, in which case we dup /dev/null to it. */ + if (errno != EBADF) { + DL_ERR("nullify_stdio: unhandled error %s", strerror(errno)); + return_value = -1; + continue; + } + + /* Try dupping /dev/null to this stdio file descriptor and + repeat if there is a signal. Note that any errors in closing + the stdio descriptor are lost. */ + do { + status = dup2(dev_null, i); + } while (status < 0 && errno == EINTR); + + if (status < 0) { + DL_ERR("nullify_stdio: dup2 error %s", strerror(errno)); + return_value = -1; + continue; + } + } + + /* If /dev/null is not one of the stdio file descriptors, close it. */ + if (dev_null > 2) { + TRACE("[ %5d Closing /dev/null file-descriptor=%d]\n", pid, dev_null); + do { + status = close(dev_null); + } while (status < 0 && errno == EINTR); + + if (status < 0) { + DL_ERR("nullify_stdio: close error %s", strerror(errno)); + return_value = -1; + } + } + + close(dev_null); + return return_value; +} + +static int link_image(soinfo *si, unsigned wr_offset) +{ + unsigned *d; + Elf32_Phdr *phdr = si->phdr; + int phnum = si->phnum; + + INFO("[ %5d linking %s ]\n", pid, si->name); + DEBUG("%5d si->base = 0x%08x si->flags = 0x%08x\n", pid, + si->base, si->flags); + + if (si->flags & (FLAG_EXE | FLAG_LINKER)) { + /* Locate the needed program segments (DYNAMIC/ARM_EXIDX) for + * linkage info if this is the executable or the linker itself. + * If this was a dynamic lib, that would have been done at load time. + * + * TODO: It's unfortunate that small pieces of this are + * repeated from the load_library routine. Refactor this just + * slightly to reuse these bits. + */ + si->size = 0; + for(; phnum > 0; --phnum, ++phdr) { +#ifdef ANDROID_ARM_LINKER + if(phdr->p_type == PT_ARM_EXIDX) { + /* exidx entries (used for stack unwinding) are 8 bytes each. + */ + si->ARM_exidx = (unsigned *)phdr->p_vaddr; + si->ARM_exidx_count = phdr->p_memsz / 8; + } +#endif + if (phdr->p_type == PT_LOAD) { + /* For the executable, we use the si->size field only in + dl_unwind_find_exidx(), so the meaning of si->size + is not the size of the executable; it is the distance + between the load location of the executable and the last + address of the loadable part of the executable. + We use the range [si->base, si->base + si->size) to + determine whether a PC value falls within the executable + section. Of course, if a value is between si->base and + (si->base + phdr->p_vaddr), it's not in the executable + section, but a) we shouldn't be asking for such a value + anyway, and b) if we have to provide an EXIDX for such a + value, then the executable's EXIDX is probably the better + choice. + */ + DEBUG_DUMP_PHDR(phdr, "PT_LOAD", pid); + if (phdr->p_vaddr + phdr->p_memsz > si->size) + si->size = phdr->p_vaddr + phdr->p_memsz; + /* try to remember what range of addresses should be write + * protected */ + if (!(phdr->p_flags & PF_W)) { + unsigned _end; + + if (si->base + phdr->p_vaddr < si->wrprotect_start) + si->wrprotect_start = si->base + phdr->p_vaddr; + _end = (((si->base + phdr->p_vaddr + phdr->p_memsz + PAGE_SIZE - 1) & + (~PAGE_MASK))); + if (_end > si->wrprotect_end) + si->wrprotect_end = _end; + /* Make the section writable just in case we'll have to + * write to it during relocation (i.e. text segment). + * However, we will remember what range of addresses + * should be write protected. + */ + mprotect((void *) (si->base + phdr->p_vaddr), + phdr->p_memsz, + PFLAGS_TO_PROT(phdr->p_flags) | PROT_WRITE); + } + } else if (phdr->p_type == PT_DYNAMIC) { + if (si->dynamic != (unsigned *)-1) { + DL_ERR("%5d multiple PT_DYNAMIC segments found in '%s'. " + "Segment at 0x%08x, previously one found at 0x%08x", + pid, si->name, si->base + phdr->p_vaddr, + (unsigned)si->dynamic); + goto fail; + } + DEBUG_DUMP_PHDR(phdr, "PT_DYNAMIC", pid); + si->dynamic = (unsigned *) (si->base + phdr->p_vaddr); + } else if (phdr->p_type == PT_GNU_RELRO) { + if ((phdr->p_vaddr >= si->size) + || ((phdr->p_vaddr + phdr->p_memsz) > si->size) + || ((si->base + phdr->p_vaddr + phdr->p_memsz) < si->base)) { + DL_ERR("%d invalid GNU_RELRO in '%s' " + "p_vaddr=0x%08x p_memsz=0x%08x", pid, si->name, + phdr->p_vaddr, phdr->p_memsz); + goto fail; + } + si->gnu_relro_start = (Elf32_Addr) (si->base + phdr->p_vaddr); + si->gnu_relro_len = (unsigned) phdr->p_memsz; + } + } + } + + if (si->dynamic == (unsigned *)-1) { + DL_ERR("%5d missing PT_DYNAMIC?!", pid); + goto fail; + } + + DEBUG("%5d dynamic = %p\n", pid, si->dynamic); + + /* extract useful information from dynamic section */ + for(d = si->dynamic; *d; d++){ + DEBUG("%5d d = %p, d[0] = 0x%08x d[1] = 0x%08x\n", pid, d, d[0], d[1]); + switch(*d++){ + case DT_HASH: + si->nbucket = ((unsigned *) (si->base + *d))[0]; + si->nchain = ((unsigned *) (si->base + *d))[1]; + si->bucket = (unsigned *) (si->base + *d + 8); + si->chain = (unsigned *) (si->base + *d + 8 + si->nbucket * 4); + break; + case DT_STRTAB: + si->strtab = (const char *) (si->base + *d); + break; + case DT_SYMTAB: + si->symtab = (Elf32_Sym *) (si->base + *d); + break; + case DT_PLTREL: + if(*d != DT_REL) { + DL_ERR("DT_RELA not supported"); + goto fail; + } + break; + case DT_JMPREL: + si->plt_rel = (Elf32_Rel*) (si->base + *d); + break; + case DT_PLTRELSZ: + si->plt_rel_count = *d / 8; + break; + case DT_REL: + si->rel = (Elf32_Rel*) (si->base + *d); + break; + case DT_RELSZ: + si->rel_count = *d / 8; + break; + case DT_PLTGOT: + /* Save this in case we decide to do lazy binding. We don't yet. */ + si->plt_got = (unsigned *)(si->base + *d); + break; + case DT_DEBUG: + // Set the DT_DEBUG entry to the addres of _r_debug for GDB + *d = (int) &_r_debug; + break; + case DT_RELA: + DL_ERR("%5d DT_RELA not supported", pid); + goto fail; + case DT_INIT: + si->init_func = (void (*)(void))(si->base + *d); + DEBUG("%5d %s constructors (init func) found at %p\n", + pid, si->name, si->init_func); + break; + case DT_FINI: + si->fini_func = (void (*)(void))(si->base + *d); + DEBUG("%5d %s destructors (fini func) found at %p\n", + pid, si->name, si->fini_func); + break; + case DT_INIT_ARRAY: + si->init_array = (unsigned *)(si->base + *d); + DEBUG("%5d %s constructors (init_array) found at %p\n", + pid, si->name, si->init_array); + break; + case DT_INIT_ARRAYSZ: + si->init_array_count = ((unsigned)*d) / sizeof(Elf32_Addr); + break; + case DT_FINI_ARRAY: + si->fini_array = (unsigned *)(si->base + *d); + DEBUG("%5d %s destructors (fini_array) found at %p\n", + pid, si->name, si->fini_array); + break; + case DT_FINI_ARRAYSZ: + si->fini_array_count = ((unsigned)*d) / sizeof(Elf32_Addr); + break; + case DT_PREINIT_ARRAY: + si->preinit_array = (unsigned *)(si->base + *d); + DEBUG("%5d %s constructors (preinit_array) found at %p\n", + pid, si->name, si->preinit_array); + break; + case DT_PREINIT_ARRAYSZ: + si->preinit_array_count = ((unsigned)*d) / sizeof(Elf32_Addr); + break; + case DT_TEXTREL: + /* TODO: make use of this. */ + /* this means that we might have to write into where the text + * segment was loaded during relocation... Do something with + * it. + */ + DEBUG("%5d Text segment should be writable during relocation.\n", + pid); + break; + } + } + + DEBUG("%5d si->base = 0x%08x, si->strtab = %p, si->symtab = %p\n", + pid, si->base, si->strtab, si->symtab); + + if((si->strtab == 0) || (si->symtab == 0)) { + DL_ERR("%5d missing essential tables", pid); + goto fail; + } + + /* if this is the main executable, then load all of the preloads now */ + if(si->flags & FLAG_EXE) { + int i; + memset(preloads, 0, sizeof(preloads)); + for(i = 0; ldpreload_names[i] != NULL; i++) { + soinfo *lsi = find_library(ldpreload_names[i]); + if(lsi == 0) { + strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); + DL_ERR("%5d could not load needed library '%s' for '%s' (%s)", + pid, ldpreload_names[i], si->name, tmp_err_buf); + goto fail; + } + lsi->refcount++; + preloads[i] = lsi; + } + } + + for(d = si->dynamic; *d; d += 2) { + if(d[0] == DT_NEEDED){ + DEBUG("%5d %s needs %s\n", pid, si->name, si->strtab + d[1]); + soinfo *lsi = find_library(si->strtab + d[1]); + if(lsi == 0) { + strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); + DL_ERR("%5d could not load needed library '%s' for '%s' (%s)", + pid, si->strtab + d[1], si->name, tmp_err_buf); + goto fail; + } + /* Save the soinfo of the loaded DT_NEEDED library in the payload + of the DT_NEEDED entry itself, so that we can retrieve the + soinfo directly later from the dynamic segment. This is a hack, + but it allows us to map from DT_NEEDED to soinfo efficiently + later on when we resolve relocations, trying to look up a symbol + with dlsym(). + */ + d[1] = (unsigned)lsi; + lsi->refcount++; + } + } + + if(si->plt_rel) { + DEBUG("[ %5d relocating %s plt ]\n", pid, si->name ); + if(reloc_library(si, si->plt_rel, si->plt_rel_count)) + goto fail; + } + if(si->rel) { + DEBUG("[ %5d relocating %s ]\n", pid, si->name ); + if(reloc_library(si, si->rel, si->rel_count)) + goto fail; + } + + si->flags |= FLAG_LINKED; + DEBUG("[ %5d finished linking %s ]\n", pid, si->name); + +#if 0 + /* This is the way that the old dynamic linker did protection of + * non-writable areas. It would scan section headers and find where + * .text ended (rather where .data/.bss began) and assume that this is + * the upper range of the non-writable area. This is too coarse, + * and is kept here for reference until we fully move away from single + * segment elf objects. See the code in get_wr_offset (also #if'd 0) + * that made this possible. + */ + if(wr_offset < 0xffffffff){ + mprotect((void*) si->base, wr_offset, PROT_READ | PROT_EXEC); + } +#else + /* TODO: Verify that this does the right thing in all cases, as it + * presently probably does not. It is possible that an ELF image will + * come with multiple read-only segments. What we ought to do is scan + * the program headers again and mprotect all the read-only segments. + * To prevent re-scanning the program header, we would have to build a + * list of loadable segments in si, and then scan that instead. */ + if (si->wrprotect_start != 0xffffffff && si->wrprotect_end != 0) { + mprotect((void *)si->wrprotect_start, + si->wrprotect_end - si->wrprotect_start, + PROT_READ | PROT_WRITE | PROT_EXEC); // NOTE: added PROT_WRITE there so we can patch the library + } +#endif + + if (si->gnu_relro_start != 0 && si->gnu_relro_len != 0) { + Elf32_Addr start = (si->gnu_relro_start & ~PAGE_MASK); + unsigned len = (si->gnu_relro_start - start) + si->gnu_relro_len; + if (mprotect((void *) start, len, PROT_READ | PROT_WRITE) < 0) { // NOTE: added PROT_WRITE + DL_ERR("%5d GNU_RELRO mprotect of library '%s' failed: %d (%s)\n", + pid, si->name, errno, strerror(errno)); + goto fail; + } + } + + /* If this is a SET?ID program, dup /dev/null to opened stdin, + stdout and stderr to close a security hole described in: + + ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc + + */ + if (program_is_setuid) + nullify_closed_stdio (); + notify_gdb_of_load(si); + return 0; + +fail: + ERROR("failed to link %s\n", si->name); + si->flags |= FLAG_ERROR; + return -1; +} + +static void parse_library_path(const char *path, char *delim) +{ + size_t len; + char *ldpaths_bufp = ldpaths_buf; + int i = 0; + + len = strlcpy(ldpaths_buf, path, sizeof(ldpaths_buf)); + + while (i < LDPATH_MAX && (ldpaths[i] = strsep(&ldpaths_bufp, delim))) { + if (*ldpaths[i] != '\0') + ++i; + } + + /* Forget the last path if we had to truncate; this occurs if the 2nd to + * last char isn't '\0' (i.e. not originally a delim). */ + if (i > 0 && len >= sizeof(ldpaths_buf) && + ldpaths_buf[sizeof(ldpaths_buf) - 2] != '\0') { + ldpaths[i - 1] = NULL; + } else { + ldpaths[i] = NULL; + } +} + +static void parse_preloads(const char *path, char *delim) +{ + size_t len; + char *ldpreloads_bufp = ldpreloads_buf; + int i = 0; + + len = strlcpy(ldpreloads_buf, path, sizeof(ldpreloads_buf)); + + while (i < LDPRELOAD_MAX && (ldpreload_names[i] = strsep(&ldpreloads_bufp, delim))) { + if (*ldpreload_names[i] != '\0') { + ++i; + } + } + + /* Forget the last path if we had to truncate; this occurs if the 2nd to + * last char isn't '\0' (i.e. not originally a delim). */ + if (i > 0 && len >= sizeof(ldpreloads_buf) && + ldpreloads_buf[sizeof(ldpreloads_buf) - 2] != '\0') { + ldpreload_names[i - 1] = NULL; + } else { + ldpreload_names[i] = NULL; + } +} + +/* + * This code is called after the linker has linked itself and + * fixed it's own GOT. It is safe to make references to externs + * and other non-local data at this point. + */ +static unsigned __linker_init_post_relocation(unsigned **elfdata) +{ + static soinfo linker_soinfo; + + int argc = (int) *elfdata; + char **argv = (char**) (elfdata + 1); + unsigned *vecs = (unsigned*) (argv + argc + 1); + unsigned *v; + soinfo *si; + struct link_map * map; + const char *ldpath_env = NULL; + const char *ldpreload_env = NULL; + + /* NOTE: we store the elfdata pointer on a special location + * of the temporary TLS area in order to pass it to + * the C Library's runtime initializer. + * + * The initializer must clear the slot and reset the TLS + * to point to a different location to ensure that no other + * shared library constructor can access it. + */ + //__libc_init_tls(elfdata); + + pid = getpid(); + +#if TIMING + struct timeval t0, t1; + gettimeofday(&t0, 0); +#endif + + /* Initialize environment functions, and get to the ELF aux vectors table */ + vecs = linker_env_init(vecs); + + /* Check auxv for AT_SECURE first to see if program is setuid, setgid, + has file caps, or caused a SELinux/AppArmor domain transition. */ + for (v = vecs; v[0]; v += 2) { + if (v[0] == AT_SECURE) { + /* kernel told us whether to enable secure mode */ + program_is_setuid = v[1]; + goto sanitize; + } + } + + /* Kernel did not provide AT_SECURE - fall back on legacy test. */ + program_is_setuid = (getuid() != geteuid()) || (getgid() != getegid()); + +sanitize: + /* Sanitize environment if we're loading a setuid program */ + if (program_is_setuid) + linker_env_secure(); + + //debugger_init(); + + /* Get a few environment variables */ + { +#if LINKER_DEBUG + const char* env; + env = linker_env_get("DEBUG"); /* XXX: TODO: Change to LD_DEBUG */ + if (env) + debug_verbosity = atoi(env); +#endif + + /* Normally, these are cleaned by linker_env_secure, but the test + * against program_is_setuid doesn't cost us anything */ + if (!program_is_setuid) { + ldpath_env = linker_env_get("LD_LIBRARY_PATH"); + ldpreload_env = linker_env_get("LD_PRELOAD"); + } + } + + INFO("[ android linker & debugger ]\n"); + DEBUG("%5d elfdata @ 0x%08x\n", pid, (unsigned)elfdata); + + si = alloc_info(argv[0]); + if(si == 0) { + exit(-1); + } + + /* bootstrap the link map, the main exe always needs to be first */ + si->flags |= FLAG_EXE; + map = &(si->linkmap); + + map->l_addr = 0; + map->l_name = argv[0]; + map->l_prev = NULL; + map->l_next = NULL; + + _r_debug.r_map = map; + r_debug_tail = map; + + /* gdb expects the linker to be in the debug shared object list, + * and we need to make sure that the reported load address is zero. + * Without this, gdb gets the wrong idea of where rtld_db_dlactivity() + * is. Don't use alloc_info(), because the linker shouldn't + * be on the soinfo list. + */ + strlcpy((char*) linker_soinfo.name, "/system/bin/linker", sizeof linker_soinfo.name); + linker_soinfo.flags = 0; + linker_soinfo.base = 0; // This is the important part; must be zero. + insert_soinfo_into_debug_map(&linker_soinfo); + + /* extract information passed from the kernel */ + while(vecs[0] != 0){ + switch(vecs[0]){ + case AT_PHDR: + si->phdr = (Elf32_Phdr*) vecs[1]; + break; + case AT_PHNUM: + si->phnum = (int) vecs[1]; + break; + case AT_ENTRY: + si->entry = vecs[1]; + break; + } + vecs += 2; + } + + /* Compute the value of si->base. We can't rely on the fact that + * the first entry is the PHDR because this will not be true + * for certain executables (e.g. some in the NDK unit test suite) + */ + int nn; + si->base = 0; + for ( nn = 0; nn < si->phnum; nn++ ) { + if (si->phdr[nn].p_type == PT_PHDR) { + si->base = (Elf32_Addr) si->phdr - si->phdr[nn].p_vaddr; + break; + } + } + si->dynamic = (unsigned *)-1; + si->wrprotect_start = 0xffffffff; + si->wrprotect_end = 0; + si->refcount = 1; + si->gnu_relro_start = 0; + si->gnu_relro_len = 0; + + /* Use LD_LIBRARY_PATH if we aren't setuid/setgid */ + if (ldpath_env) + parse_library_path(ldpath_env, ":"); + + if (ldpreload_env) { + parse_preloads(ldpreload_env, " :"); + } + + if(link_image(si, 0)) { + char errmsg[] = "CANNOT LINK EXECUTABLE\n"; + write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf)); + write(2, errmsg, sizeof(errmsg)); + exit(-1); + } + + call_constructors_recursive(si); + +#if ALLOW_SYMBOLS_FROM_MAIN + /* Set somain after we've loaded all the libraries in order to prevent + * linking of symbols back to the main image, which is not set up at that + * point yet. + */ + somain = si; +#endif + +#if TIMING + gettimeofday(&t1,NULL); + PRINT("LINKER TIME: %s: %d microseconds\n", argv[0], (int) ( + (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) - + (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec) + )); +#endif +#if STATS + PRINT("RELO STATS: %s: %d abs, %d rel, %d copy, %d symbol\n", argv[0], + linker_stats.reloc[RELOC_ABSOLUTE], + linker_stats.reloc[RELOC_RELATIVE], + linker_stats.reloc[RELOC_COPY], + linker_stats.reloc[RELOC_SYMBOL]); +#endif +#if COUNT_PAGES + { + unsigned n; + unsigned i; + unsigned count = 0; + for(n = 0; n < 4096; n++){ + if(bitmask[n]){ + unsigned x = bitmask[n]; + for(i = 0; i < 8; i++){ + if(x & 1) count++; + x >>= 1; + } + } + } + PRINT("PAGES MODIFIED: %s: %d (%dKB)\n", argv[0], count, count * 4); + } +#endif + +#if TIMING || STATS || COUNT_PAGES + fflush(stdout); +#endif + + TRACE("[ %5d Ready to execute '%s' @ 0x%08x ]\n", pid, si->name, + si->entry); + return si->entry; +} + +/* + * Find the value of AT_BASE passed to us by the kernel. This is the load + * location of the linker. + */ +static unsigned find_linker_base(unsigned **elfdata) { + int argc = (int) *elfdata; + char **argv = (char**) (elfdata + 1); + unsigned *vecs = (unsigned*) (argv + argc + 1); + while (vecs[0] != 0) { + vecs++; + } + + /* The end of the environment block is marked by two NULL pointers */ + vecs++; + + while(vecs[0]) { + if (vecs[0] == AT_BASE) { + return vecs[1]; + } + vecs += 2; + } + + return 0; // should never happen +} + +/* + * This is the entry point for the linker, called from begin.S. This + * method is responsible for fixing the linker's own relocations, and + * then calling __linker_init_post_relocation(). + * + * Because this method is called before the linker has fixed it's own + * relocations, any attempt to reference an extern variable, extern + * function, or other GOT reference will generate a segfault. + */ +unsigned __linker_init(unsigned **elfdata) { + unsigned linker_addr = find_linker_base(elfdata); + Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *) linker_addr; + Elf32_Phdr *phdr = + (Elf32_Phdr *)((unsigned char *) linker_addr + elf_hdr->e_phoff); + + soinfo linker_so; + memset(&linker_so, 0, sizeof(soinfo)); + + linker_so.base = linker_addr; + linker_so.dynamic = (unsigned *) -1; + linker_so.phdr = phdr; + linker_so.phnum = elf_hdr->e_phnum; + linker_so.flags |= FLAG_LINKER; + linker_so.wrprotect_start = 0xffffffff; + linker_so.wrprotect_end = 0; + linker_so.gnu_relro_start = 0; + linker_so.gnu_relro_len = 0; + + if (link_image(&linker_so, 0)) { + // It would be nice to print an error message, but if the linker + // can't link itself, there's no guarantee that we'll be able to + // call write() (because it involves a GOT reference). + // + // This situation should never occur unless the linker itself + // is corrupt. + exit(-1); + } + + // We have successfully fixed our own relocations. It's safe to run + // the main part of the linker now. + return __linker_init_post_relocation(elfdata); +} diff --git a/hybris/src/jb/linker.h b/hybris/src/jb/linker.h new file mode 100644 index 0000000..e6426ae --- /dev/null +++ b/hybris/src/jb/linker.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LINKER_H_ +#define _LINKER_H_ + +#include +#include +#include + +#undef PAGE_MASK +#undef PAGE_SIZE +#define PAGE_SIZE 4096 +#define PAGE_MASK 4095 + +void debugger_init(); +const char *addr_to_name(unsigned addr); + +/* magic shared structures that GDB knows about */ + +struct link_map +{ + uintptr_t l_addr; + char * l_name; + uintptr_t l_ld; + struct link_map * l_next; + struct link_map * l_prev; +}; + +/* needed for dl_iterate_phdr to be passed to the callbacks provided */ +struct dl_phdr_info +{ + Elf32_Addr dlpi_addr; + const char *dlpi_name; + const Elf32_Phdr *dlpi_phdr; + Elf32_Half dlpi_phnum; +}; + + +// Values for r_debug->state +enum { + RT_CONSISTENT, + RT_ADD, + RT_DELETE +}; + +struct r_debug +{ + int32_t r_version; + struct link_map * r_map; + void (*r_brk)(void); + int32_t r_state; + uintptr_t r_ldbase; +}; + +typedef struct soinfo soinfo; + +#define FLAG_LINKED 0x00000001 +#define FLAG_ERROR 0x00000002 +#define FLAG_EXE 0x00000004 // The main executable +#define FLAG_LINKER 0x00000010 // The linker itself + +#define SOINFO_NAME_LEN 128 + +struct soinfo +{ + const char name[SOINFO_NAME_LEN]; + Elf32_Phdr *phdr; + int phnum; + unsigned entry; + unsigned base; + unsigned size; + + int unused; // DO NOT USE, maintained for compatibility. + + unsigned *dynamic; + + unsigned wrprotect_start; + unsigned wrprotect_end; + + soinfo *next; + unsigned flags; + + const char *strtab; + Elf32_Sym *symtab; + + unsigned nbucket; + unsigned nchain; + unsigned *bucket; + unsigned *chain; + + unsigned *plt_got; + + Elf32_Rel *plt_rel; + unsigned plt_rel_count; + + Elf32_Rel *rel; + unsigned rel_count; + + unsigned *preinit_array; + unsigned preinit_array_count; + + unsigned *init_array; + unsigned init_array_count; + unsigned *fini_array; + unsigned fini_array_count; + + void (*init_func)(void); + void (*fini_func)(void); + +#ifdef ANDROID_ARM_LINKER + /* ARM EABI section used for stack unwinding. */ + unsigned *ARM_exidx; + unsigned ARM_exidx_count; +#endif + + unsigned refcount; + struct link_map linkmap; + + int constructors_called; + + Elf32_Addr gnu_relro_start; + unsigned gnu_relro_len; + +}; + + +extern soinfo libdl_info; + +#ifdef ANDROID_ARM_LINKER + +#define R_ARM_COPY 20 +#define R_ARM_GLOB_DAT 21 +#define R_ARM_JUMP_SLOT 22 +#define R_ARM_RELATIVE 23 + +/* According to the AAPCS specification, we only + * need the above relocations. However, in practice, + * the following ones turn up from time to time. + */ +#define R_ARM_ABS32 2 +#define R_ARM_REL32 3 + +#elif defined(ANDROID_X86_LINKER) + +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GLOB_DAT 6 +#define R_386_JUMP_SLOT 7 +#define R_386_RELATIVE 8 + +#endif + +#ifndef DT_INIT_ARRAY +#define DT_INIT_ARRAY 25 +#endif + +#ifndef DT_FINI_ARRAY +#define DT_FINI_ARRAY 26 +#endif + +#ifndef DT_INIT_ARRAYSZ +#define DT_INIT_ARRAYSZ 27 +#endif + +#ifndef DT_FINI_ARRAYSZ +#define DT_FINI_ARRAYSZ 28 +#endif + +#ifndef DT_PREINIT_ARRAY +#define DT_PREINIT_ARRAY 32 +#endif + +#ifndef DT_PREINIT_ARRAYSZ +#define DT_PREINIT_ARRAYSZ 33 +#endif + +soinfo *find_library(const char *name); +unsigned unload_library(soinfo *si); +Elf32_Sym *lookup_in_library(soinfo *si, const char *name); +Elf32_Sym *lookup(const char *name, soinfo **found, soinfo *start); +soinfo *find_containing_library(const void *addr); +Elf32_Sym *find_containing_symbol(const void *addr, soinfo *si); +const char *linker_get_error(void); +void call_constructors_recursive(soinfo *si); + +#ifdef ANDROID_ARM_LINKER +typedef long unsigned int *_Unwind_Ptr; +_Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount); +#elif defined(ANDROID_X86_LINKER) +int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *), void *); +#endif + +#endif diff --git a/hybris/src/jb/linker_debug.h b/hybris/src/jb/linker_debug.h new file mode 100644 index 0000000..f1f8695 --- /dev/null +++ b/hybris/src/jb/linker_debug.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2008-2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LINKER_DEBUG_H_ +#define _LINKER_DEBUG_H_ + +#include + +#ifndef LINKER_DEBUG +#error LINKER_DEBUG should be defined to either 1 or 0 in Android.mk +#endif + +#define TRACE_DEBUG 1 +#define DO_TRACE_LOOKUP 1 +#define DO_TRACE_RELO 1 +#define TIMING 0 +#define STATS 0 +#define COUNT_PAGES 0 + +/********************************************************************* + * You shouldn't need to modify anything below unless you are adding + * more debugging information. + * + * To enable/disable specific debug options, change the defines above + *********************************************************************/ + + +/*********************************************************************/ +#undef TRUE +#undef FALSE +#define TRUE 1 +#define FALSE 0 + +/* Only use printf() during debugging. We have seen occasional memory + * corruption when the linker uses printf(). + */ +#if LINKER_DEBUG +#include "linker_format.h" +extern int debug_verbosity; +extern int debug_stdout; +extern int format_log(int, const char *, const char *, ...); +extern int format_fd(int, const char *, ...); +#define _PRINTVF(v,f,x...) \ + do { \ + if (debug_verbosity > (v)) \ + if (debug_stdout) \ + format_fd(1, x); \ + else \ + format_log(5-(v),"linker",x); \ + } while (0) +#else /* !LINKER_DEBUG */ +#define _PRINTVF(v,f,x...) do {} while(0) +#endif /* LINKER_DEBUG */ + +#define PRINT(x...) _PRINTVF(-1, FALSE, x) +#define INFO(x...) _PRINTVF(0, TRUE, x) +#define TRACE(x...) _PRINTVF(1, TRUE, x) +#define WARN(fmt,args...) \ + _PRINTVF(-1, TRUE, "%s:%d| WARNING: " fmt, __FILE__, __LINE__, ## args) +#define ERROR(fmt,args...) \ + _PRINTVF(-1, TRUE, "%s:%d| ERROR: " fmt, __FILE__, __LINE__, ## args) + + +#if TRACE_DEBUG +#define DEBUG(x...) _PRINTVF(2, TRUE, "DEBUG: " x) +#else /* !TRACE_DEBUG */ +#define DEBUG(x...) do {} while (0) +#endif /* TRACE_DEBUG */ + +#if LINKER_DEBUG +#define TRACE_TYPE(t,x...) do { if (DO_TRACE_##t) { TRACE(x); } } while (0) +#else /* !LINKER_DEBUG */ +#define TRACE_TYPE(t,x...) do {} while (0) +#endif /* LINKER_DEBUG */ + +#if STATS +#define RELOC_ABSOLUTE 0 +#define RELOC_RELATIVE 1 +#define RELOC_COPY 2 +#define RELOC_SYMBOL 3 +#define NUM_RELOC_STATS 4 + +struct _link_stats { + int reloc[NUM_RELOC_STATS]; +}; +extern struct _link_stats linker_stats; + +#define COUNT_RELOC(type) \ + do { if (type >= 0 && type < NUM_RELOC_STATS) { \ + linker_stats.reloc[type] += 1; \ + } else { \ + PRINT("Unknown reloc stat requested\n"); \ + } \ + } while(0) +#else /* !STATS */ +#define COUNT_RELOC(type) do {} while(0) +#endif /* STATS */ + +#if TIMING +#undef WARN +#define WARN(x...) do {} while (0) +#endif /* TIMING */ + +#if COUNT_PAGES +extern unsigned bitmask[]; +#define MARK(offset) do { \ + bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \ + } while(0) +#else +#define MARK(x) do {} while (0) +#endif + +#define DEBUG_DUMP_PHDR(phdr, name, pid) do { \ + DEBUG("%5d %s (phdr = 0x%08x)\n", (pid), (name), (unsigned)(phdr)); \ + DEBUG("\t\tphdr->offset = 0x%08x\n", (unsigned)((phdr)->p_offset)); \ + DEBUG("\t\tphdr->p_vaddr = 0x%08x\n", (unsigned)((phdr)->p_vaddr)); \ + DEBUG("\t\tphdr->p_paddr = 0x%08x\n", (unsigned)((phdr)->p_paddr)); \ + DEBUG("\t\tphdr->p_filesz = 0x%08x\n", (unsigned)((phdr)->p_filesz)); \ + DEBUG("\t\tphdr->p_memsz = 0x%08x\n", (unsigned)((phdr)->p_memsz)); \ + DEBUG("\t\tphdr->p_flags = 0x%08x\n", (unsigned)((phdr)->p_flags)); \ + DEBUG("\t\tphdr->p_align = 0x%08x\n", (unsigned)((phdr)->p_align)); \ + } while (0) + +#endif /* _LINKER_DEBUG_H_ */ diff --git a/hybris/src/jb/linker_environ.c b/hybris/src/jb/linker_environ.c new file mode 100644 index 0000000..b71dd80 --- /dev/null +++ b/hybris/src/jb/linker_environ.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "linker_environ.h" +#include + +static char** _envp; + +/* Returns 1 if 'str' points to a valid environment variable definition. + * For now, we check that: + * - It is smaller than MAX_ENV_LEN (to detect non-zero terminated strings) + * - It contains at least one equal sign that is not the first character + */ +static int +_is_valid_definition(const char* str) +{ + int pos = 0; + int first_equal_pos = -1; + + /* According to its sources, the kernel uses 32*PAGE_SIZE by default + * as the maximum size for an env. variable definition. + */ + const int MAX_ENV_LEN = 32*4096; + + if (str == NULL) + return 0; + + /* Parse the string, looking for the first '=' there, and its size */ + do { + if (str[pos] == '\0') + break; + if (str[pos] == '=' && first_equal_pos < 0) + first_equal_pos = pos; + pos++; + } while (pos < MAX_ENV_LEN); + + if (pos >= MAX_ENV_LEN) /* Too large */ + return 0; + + if (first_equal_pos < 1) /* No equal sign, or it is the first character */ + return 0; + + return 1; +} + +unsigned* +linker_env_init(unsigned* vecs) +{ + /* Store environment pointer - can't be NULL */ + _envp = (char**) vecs; + + /* Skip over all definitions */ + while (vecs[0] != 0) + vecs++; + /* The end of the environment block is marked by two NULL pointers */ + vecs++; + + /* As a sanity check, we're going to remove all invalid variable + * definitions from the environment array. + */ + { + char** readp = _envp; + char** writep = _envp; + for ( ; readp[0] != NULL; readp++ ) { + if (!_is_valid_definition(readp[0])) + continue; + writep[0] = readp[0]; + writep++; + } + writep[0] = NULL; + } + + /* Return the address of the aux vectors table */ + return vecs; +} + +/* Check if the environment variable definition at 'envstr' + * starts with '=', and if so return the address of the + * first character after the equal sign. Otherwise return NULL. + */ +static char* +env_match(char* envstr, const char* name) +{ + size_t cnt = 0; + + while (envstr[cnt] == name[cnt] && name[cnt] != '\0') + cnt++; + + if (name[cnt] == '\0' && envstr[cnt] == '=') + return envstr + cnt + 1; + + return NULL; +} + +#define MAX_ENV_LEN (16*4096) + +const char* +linker_env_get(const char* name) +{ + char** readp = _envp; + + if (name == NULL || name[0] == '\0') + return NULL; + + for ( ; readp[0] != NULL; readp++ ) { + char* val = env_match(readp[0], name); + if (val != NULL) { + /* Return NULL for empty strings, or if it is too large */ + if (val[0] == '\0') + val = NULL; + return val; + } + } + return NULL; +} + + +void +linker_env_unset(const char* name) +{ + char** readp = _envp; + char** writep = readp; + + if (name == NULL || name[0] == '\0') + return; + + for ( ; readp[0] != NULL; readp++ ) { + if (env_match(readp[0], name)) + continue; + writep[0] = readp[0]; + writep++; + } + /* end list with a NULL */ + writep[0] = NULL; +} + + + +/* Remove unsafe environment variables. This should be used when + * running setuid programs. */ +void +linker_env_secure(void) +{ + /* The same list than GLibc at this point */ + static const char* const unsec_vars[] = { + "GCONV_PATH", + "GETCONF_DIR", + "HOSTALIASES", + "LD_AUDIT", + "LD_DEBUG", + "LD_DEBUG_OUTPUT", + "LD_DYNAMIC_WEAK", + "LD_LIBRARY_PATH", + "LD_ORIGIN_PATH", + "LD_PRELOAD", + "LD_PROFILE", + "LD_SHOW_AUXV", + "LD_USE_LOAD_BIAS", + "LOCALDOMAIN", + "LOCPATH", + "MALLOC_TRACE", + "MALLOC_CHECK_", + "NIS_PATH", + "NLSPATH", + "RESOLV_HOST_CONF", + "RES_OPTIONS", + "TMPDIR", + "TZDIR", + "LD_AOUT_LIBRARY_PATH", + "LD_AOUT_PRELOAD", + }; + + const char* const* cp = unsec_vars; + const char* const* endp = cp + sizeof(unsec_vars)/sizeof(unsec_vars[0]); + + while (cp < endp) { + linker_env_unset(*cp); + cp++; + } +} diff --git a/hybris/src/jb/linker_environ.h b/hybris/src/jb/linker_environ.h new file mode 100644 index 0000000..98ad1de --- /dev/null +++ b/hybris/src/jb/linker_environ.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef LINKER_ENVIRON_H +#define LINKER_ENVIRON_H + +/* Call this function before anything else. 'vecs' must be the pointer + * to the environment block in the ELF data block. The function returns + * the start of the aux vectors after the env block. + */ +extern unsigned* linker_env_init(unsigned* vecs); + +/* Unset a given environment variable. In case the variable is defined + * multiple times, unset all instances. This modifies the environment + * block, so any pointer returned by linker_env_get() after this call + * might become invalid */ +extern void linker_env_unset(const char* name); + + +/* Returns the value of environment variable 'name' if defined and not + * empty, or NULL otherwise. Note that the returned pointer may become + * invalid if linker_env_unset() or linker_env_secure() are called + * after this function. */ +extern const char* linker_env_get(const char* name); + +/* Remove unsecure environment variables. This should be used when + * running setuid programs. */ +extern void linker_env_secure(void); + +#endif /* LINKER_ENVIRON_H */ diff --git a/hybris/src/jb/linker_format.c b/hybris/src/jb/linker_format.c new file mode 100644 index 0000000..36b73d8 --- /dev/null +++ b/hybris/src/jb/linker_format.c @@ -0,0 +1,703 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include "linker_format.h" +#include "linker_debug.h" + +/* define UNIT_TESTS to build this file as a single executable that runs + * the formatter's unit tests + */ +#define xxUNIT_TESTS + +/*** Generic output sink + ***/ + +typedef struct { + void *opaque; + void (*send)(void *opaque, const char *data, int len); +} Out; + +static void +out_send(Out *o, const void *data, size_t len) +{ + o->send(o->opaque, data, (int)len); +} + +static void +out_send_repeat(Out *o, char ch, int count) +{ + char pad[8]; + const int padSize = (int)sizeof(pad); + + memset(pad, ch, sizeof(pad)); + while (count > 0) { + int avail = count; + if (avail > padSize) { + avail = padSize; + } + o->send(o->opaque, pad, avail); + count -= avail; + } +} + +/* forward declaration */ +static void +out_vformat(Out *o, const char *format, va_list args); + +/*** Bounded buffer output + ***/ + +typedef struct { + Out out[1]; + char *buffer; + char *pos; + char *end; + int total; +} BufOut; + +static void +buf_out_send(void *opaque, const char *data, int len) +{ + BufOut *bo = opaque; + + if (len < 0) + len = strlen(data); + + bo->total += len; + + while (len > 0) { + int avail = bo->end - bo->pos; + if (avail == 0) + break; + if (avail > len) + avail = len; + memcpy(bo->pos, data, avail); + bo->pos += avail; + bo->pos[0] = '\0'; + len -= avail; + } +} + +static Out* +buf_out_init(BufOut *bo, char *buffer, size_t size) +{ + if (size == 0) + return NULL; + + bo->out->opaque = bo; + bo->out->send = buf_out_send; + bo->buffer = buffer; + bo->end = buffer + size - 1; + bo->pos = bo->buffer; + bo->pos[0] = '\0'; + bo->total = 0; + + return bo->out; +} + +static int +buf_out_length(BufOut *bo) +{ + return bo->total; +} + +static int +vformat_buffer(char *buff, size_t buffsize, const char *format, va_list args) +{ + BufOut bo; + Out *out; + + out = buf_out_init(&bo, buff, buffsize); + if (out == NULL) + return 0; + + out_vformat(out, format, args); + + return buf_out_length(&bo); +} + +int +format_buffer(char *buff, size_t buffsize, const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = vformat_buffer(buff, buffsize, format, args); + va_end(args); + + return ret; +} + +/* The __stack_chk_fail() function calls __libc_android_log_print() + * which calls vsnprintf(). + * + * We define our version of the function here to avoid dragging + * about 25 KB of C library routines related to formatting. + */ +#if 0 +int +vsnprintf(char *buff, size_t bufsize, const char *format, va_list args) +{ + return format_buffer(buff, bufsize, format, args); +} +#endif + +#if LINKER_DEBUG + +/*** File descriptor output + ***/ + +typedef struct { + Out out[1]; + int fd; + int total; +} FdOut; + +static void +fd_out_send(void *opaque, const char *data, int len) +{ + FdOut *fdo = opaque; + + if (len < 0) + len = strlen(data); + + while (len > 0) { + int ret = write(fdo->fd, data, len); + if (ret < 0) { + if (errno == EINTR) + continue; + break; + } + data += ret; + len -= ret; + fdo->total += ret; + } +} + +static Out* +fd_out_init(FdOut *fdo, int fd) +{ + fdo->out->opaque = fdo; + fdo->out->send = fd_out_send; + fdo->fd = fd; + fdo->total = 0; + + return fdo->out; +} + +static int +fd_out_length(FdOut *fdo) +{ + return fdo->total; +} + + +int +format_fd(int fd, const char *format, ...) +{ + FdOut fdo; + Out* out; + va_list args; + + out = fd_out_init(&fdo, fd); + if (out == NULL) + return 0; + + va_start(args, format); + out_vformat(out, format, args); + va_end(args); + + return fd_out_length(&fdo); +} + +/*** Log output + ***/ + +/* We need our own version of __libc_android_log_vprint, otherwise + * the log output is completely broken. Probably due to the fact + * that the C library is not initialized yet. + * + * You can test that by setting CUSTOM_LOG_VPRINT to 0 + */ +#define CUSTOM_LOG_VPRINT 1 + +#if CUSTOM_LOG_VPRINT + +#include +#include +#include + +static int log_vprint(int prio, const char *tag, const char *fmt, va_list args) +{ + char buf[1024]; + int result; + static int log_fd = -1; + + result = vformat_buffer(buf, sizeof buf, fmt, args); + + if (log_fd < 0) { + log_fd = open("/dev/log/main", O_WRONLY); + if (log_fd < 0) { + log_fd = fileno(stdout); // kernel doesn't have android log + return result; + } + } + + { + ssize_t ret; + struct iovec vec[3]; + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) buf; + vec[2].iov_len = strlen(buf) + 1; + + do { + ret = writev(log_fd, vec, 3); + } while ((ret < 0) && (errno == EINTR)); + } + return result; +} + +#define __libc_android_log_vprint log_vprint + +#else /* !CUSTOM_LOG_VPRINT */ + +extern int __libc_android_log_vprint(int prio, const char* tag, const char* format, va_list ap); + +#endif /* !CUSTOM_LOG_VPRINT */ + +int +format_log(int prio, const char *tag, const char *format, ...) +{ + int ret; + va_list args; + va_start(args, format); + ret = __libc_android_log_vprint(prio, tag, format, args); + va_end(args); + return ret; +} + +#endif /* LINKER_DEBUG */ + +/*** formatted output implementation + ***/ + +/* Parse a decimal string from 'format + *ppos', + * return the value, and writes the new position past + * the decimal string in '*ppos' on exit. + * + * NOTE: Does *not* handle a sign prefix. + */ +static unsigned +parse_decimal(const char *format, int *ppos) +{ + const char* p = format + *ppos; + unsigned result = 0; + + for (;;) { + int ch = *p; + unsigned d = (unsigned)(ch - '0'); + + if (d >= 10U) + break; + + result = result*10 + d; + p++; + } + *ppos = p - format; + return result; +} + +/* write an octal/decimal/number into a bounded buffer. + * assumes that bufsize > 0, and 'digits' is a string of + * digits of at least 'base' values. + */ +static void +format_number(char *buffer, size_t bufsize, uint64_t value, int base, const char *digits) +{ + char *pos = buffer; + char *end = buffer + bufsize - 1; + + /* generate digit string in reverse order */ + while (value) { + unsigned d = value % base; + value /= base; + if (pos < end) { + *pos++ = digits[d]; + } + } + + /* special case for 0 */ + if (pos == buffer) { + if (pos < end) { + *pos++ = '0'; + } + } + pos[0] = '\0'; + + /* now reverse digit string in-place */ + end = pos - 1; + pos = buffer; + while (pos < end) { + int ch = pos[0]; + pos[0] = end[0]; + end[0] = (char) ch; + pos++; + end--; + } +} + +/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */ +static void +format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned) +{ + if (isSigned && (int64_t)value < 0) { + buffer[0] = '-'; + buffer += 1; + buffsize -= 1; + value = (uint64_t)(-(int64_t)value); + } + + format_number(buffer, buffsize, value, base, "0123456789"); +} + +/* Write an octal into a buffer, assumes buffsize > 2 */ +static void +format_octal(char *buffer, size_t buffsize, uint64_t value, int isSigned) +{ + format_integer(buffer, buffsize, value, 8, isSigned); +} + +/* Write a decimal into a buffer, assumes buffsize > 2 */ +static void +format_decimal(char *buffer, size_t buffsize, uint64_t value, int isSigned) +{ + format_integer(buffer, buffsize, value, 10, isSigned); +} + +/* Write an hexadecimal into a buffer, isCap is true for capital alphas. + * Assumes bufsize > 2 */ +static void +format_hex(char *buffer, size_t buffsize, uint64_t value, int isCap) +{ + const char *digits = isCap ? "0123456789ABCDEF" : "0123456789abcdef"; + + format_number(buffer, buffsize, value, 16, digits); +} + + +/* Perform formatted output to an output target 'o' */ +static void +out_vformat(Out *o, const char *format, va_list args) +{ + int nn = 0; + + for (;;) { + int mm; + int padZero = 0; + int padLeft = 0; + char sign = '\0'; + int width = -1; + int prec = -1; + size_t bytelen = sizeof(int); + const char* str; + int slen; + char buffer[32]; /* temporary buffer used to format numbers */ + + char c; + + /* first, find all characters that are not 0 or '%' */ + /* then send them to the output directly */ + mm = nn; + do { + c = format[mm]; + if (c == '\0' || c == '%') + break; + mm++; + } while (1); + + if (mm > nn) { + out_send(o, format+nn, mm-nn); + nn = mm; + } + + /* is this it ? then exit */ + if (c == '\0') + break; + + /* nope, we are at a '%' modifier */ + nn++; // skip it + + /* parse flags */ + for (;;) { + c = format[nn++]; + if (c == '\0') { /* single trailing '%' ? */ + c = '%'; + out_send(o, &c, 1); + return; + } + else if (c == '0') { + padZero = 1; + continue; + } + else if (c == '-') { + padLeft = 1; + continue; + } + else if (c == ' ' || c == '+') { + sign = c; + continue; + } + break; + } + + /* parse field width */ + if ((c >= '0' && c <= '9')) { + nn --; + width = (int)parse_decimal(format, &nn); + c = format[nn++]; + } + + /* parse precision */ + if (c == '.') { + prec = (int)parse_decimal(format, &nn); + c = format[nn++]; + } + + /* length modifier */ + switch (c) { + case 'h': + bytelen = sizeof(short); + if (format[nn] == 'h') { + bytelen = sizeof(char); + nn += 1; + } + c = format[nn++]; + break; + case 'l': + bytelen = sizeof(long); + if (format[nn] == 'l') { + bytelen = sizeof(long long); + nn += 1; + } + c = format[nn++]; + break; + case 'z': + bytelen = sizeof(size_t); + c = format[nn++]; + break; + case 't': + bytelen = sizeof(ptrdiff_t); + c = format[nn++]; + break; + default: + ; + } + + /* conversion specifier */ + if (c == 's') { + /* string */ + str = va_arg(args, const char*); + } else if (c == 'c') { + /* character */ + /* NOTE: char is promoted to int when passed through the stack */ + buffer[0] = (char) va_arg(args, int); + buffer[1] = '\0'; + str = buffer; + } else if (c == 'p') { + uint64_t value = (uintptr_t) va_arg(args, void*); + buffer[0] = '0'; + buffer[1] = 'x'; + format_hex(buffer + 2, sizeof buffer-2, value, 0); + str = buffer; + } else { + /* integers - first read value from stack */ + uint64_t value; + int isSigned = (c == 'd' || c == 'i' || c == 'o'); + + /* NOTE: int8_t and int16_t are promoted to int when passed + * through the stack + */ + switch (bytelen) { + case 1: value = (uint8_t) va_arg(args, int); break; + case 2: value = (uint16_t) va_arg(args, int); break; + case 4: value = va_arg(args, uint32_t); break; + case 8: value = va_arg(args, uint64_t); break; + default: return; /* should not happen */ + } + + /* sign extension, if needed */ + if (isSigned) { + int shift = 64 - 8*bytelen; + value = (uint64_t)(((int64_t)(value << shift)) >> shift); + } + + /* format the number properly into our buffer */ + switch (c) { + case 'i': case 'd': + format_integer(buffer, sizeof buffer, value, 10, isSigned); + break; + case 'o': + format_integer(buffer, sizeof buffer, value, 8, isSigned); + break; + case 'x': case 'X': + format_hex(buffer, sizeof buffer, value, (c == 'X')); + break; + default: + buffer[0] = '\0'; + } + /* then point to it */ + str = buffer; + } + + /* if we are here, 'str' points to the content that must be + * outputted. handle padding and alignment now */ + + slen = strlen(str); + + if (slen < width && !padLeft) { + char padChar = padZero ? '0' : ' '; + out_send_repeat(o, padChar, width - slen); + } + + out_send(o, str, slen); + + if (slen < width && padLeft) { + char padChar = padZero ? '0' : ' '; + out_send_repeat(o, padChar, width - slen); + } + } +} + + +#ifdef UNIT_TESTS + +#include + +static int gFails = 0; + +#define MARGIN 40 + +#define UTEST_CHECK(condition,message) \ + printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \ + if (!(condition)) { \ + printf("KO\n"); \ + gFails += 1; \ + } else { \ + printf("ok\n"); \ + } + +static void +utest_BufOut(void) +{ + char buffer[16]; + BufOut bo[1]; + Out* out; + int ret; + + buffer[0] = '1'; + out = buf_out_init(bo, buffer, sizeof buffer); + UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte"); + out_send(out, "abc", 3); + UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut"); + out_send_repeat(out, 'X', 4); + UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut"); + buffer[sizeof buffer-1] = 'x'; + out_send_repeat(out, 'Y', 2*sizeof(buffer)); + UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates"); + + out = buf_out_init(bo, buffer, sizeof buffer); + out_send_repeat(out, 'X', 2*sizeof(buffer)); + ret = buf_out_length(bo); + UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow"); +} + +static void +utest_expect(const char* result, const char* format, ...) +{ + va_list args; + BufOut bo[1]; + char buffer[256]; + Out* out = buf_out_init(bo, buffer, sizeof buffer); + + printf("Checking %-*s: ", MARGIN, format); fflush(stdout); + va_start(args, format); + out_vformat(out, format, args); + va_end(args); + + if (strcmp(result, buffer)) { + printf("KO. got '%s' expecting '%s'\n", buffer, result); + gFails += 1; + } else { + printf("ok. got '%s'\n", result); + } +} + +int main(void) +{ + utest_BufOut(); + utest_expect("", ""); + utest_expect("a", "a"); + utest_expect("01234", "01234", ""); + utest_expect("01234", "%s", "01234"); + utest_expect("aabbcc", "aa%scc", "bb"); + utest_expect("a", "%c", 'a'); + utest_expect("1234", "%d", 1234); + utest_expect("-8123", "%d", -8123); + utest_expect("16", "%hd", 0x7fff0010); + utest_expect("16", "%hhd", 0x7fffff10); + utest_expect("68719476736", "%lld", 0x1000000000LL); + utest_expect("70000", "%ld", 70000); + utest_expect("0xb0001234", "%p", (void*)0xb0001234); + utest_expect("12ab", "%x", 0x12ab); + utest_expect("12AB", "%X", 0x12ab); + utest_expect("00123456", "%08x", 0x123456); + utest_expect("01234", "0%d", 1234); + utest_expect(" 1234", "%5d", 1234); + utest_expect("01234", "%05d", 1234); + utest_expect(" 1234", "%8d", 1234); + utest_expect("1234 ", "%-8d", 1234); + utest_expect("abcdef ", "%-11s", "abcdef"); + utest_expect("something:1234", "%s:%d", "something", 1234); + utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5); + utest_expect("5,0x0", "%d,%p", 5, NULL); + utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8); + return gFails != 0; +} + +#endif /* UNIT_TESTS */ diff --git a/hybris/src/jb/linker_format.h b/hybris/src/jb/linker_format.h new file mode 100644 index 0000000..6ae2bad --- /dev/null +++ b/hybris/src/jb/linker_format.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _LINKER_FORMAT_H +#define _LINKER_FORMAT_H + +#include +#include + +/* Formatting routines for the dynamic linker's debug traces */ +/* We want to avoid dragging the whole C library fprintf() */ +/* implementation into the dynamic linker since this creates */ +/* issues (it uses malloc()/free()) and increases code size */ + +int format_buffer(char *buffer, size_t bufsize, const char *format, ...); + +#endif /* _LINKER_FORMAT_H */ diff --git a/hybris/src/jb/rt.c b/hybris/src/jb/rt.c new file mode 100644 index 0000000..30d5a48 --- /dev/null +++ b/hybris/src/jb/rt.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This function is an empty stub where GDB locates a breakpoint to get notified + * about linker activity. + */ +void __attribute__((noinline)) rtld_db_dlactivity(void) +{ +} + diff --git a/hybris/src/logging.c b/hybris/src/logging.c new file mode 100644 index 0000000..0c4c79b --- /dev/null +++ b/hybris/src/logging.c @@ -0,0 +1,133 @@ + +/* + * Copyright (c) 2013 Thomas Perl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#include "logging.h" + +#include +#include +#include +#include +#include + +FILE *hybris_logging_target = NULL; + +pthread_mutex_t hybris_logging_mutex; + +static enum hybris_log_level +hybris_minimum_log_level = HYBRIS_LOG_WARN; + +static enum hybris_log_format _hybris_logging_format = HYBRIS_LOG_FORMAT_NORMAL; + +static int _hybris_should_trace = 0; + +static int +hybris_logging_initialized = 0; + +static void +hybris_logging_initialize() +{ + const char *env = getenv("HYBRIS_LOGGING_LEVEL"); + + if (env == NULL) { + /* Nothing to do - use default level */ + } else if (strcmp(env, "debug") == 0) { + hybris_minimum_log_level = HYBRIS_LOG_DEBUG; + } else if (strcmp(env, "info") == 0) { + hybris_minimum_log_level = HYBRIS_LOG_INFO; + } else if (strcmp(env, "warn") == 0) { + hybris_minimum_log_level = HYBRIS_LOG_WARN; + } else if (strcmp(env, "error") == 0) { + hybris_minimum_log_level = HYBRIS_LOG_ERROR; + } else if (strcmp(env, "disabled") == 0) { + hybris_minimum_log_level = HYBRIS_LOG_DISABLED; + } + + env = getenv("HYBRIS_LOGGING_TARGET"); + if (env != NULL) + { + hybris_logging_target = fopen(env, "a"); + } + if (hybris_logging_target == NULL) + hybris_logging_target = stderr; + + env = getenv("HYBRIS_LOGGING_FORMAT"); + if (env != NULL) + { + if (strcmp(env, "systrace") == 0) { + _hybris_logging_format = HYBRIS_LOG_FORMAT_SYSTRACE; + } + else + _hybris_logging_format = HYBRIS_LOG_FORMAT_NORMAL; + } + + env = getenv("HYBRIS_TRACE"); + if (env != NULL) + { + if (strcmp(env, "1") == 0) { + _hybris_should_trace = 1; + } + } + pthread_mutex_init(&hybris_logging_mutex, NULL); +} + +int +hybris_should_log(enum hybris_log_level level) +{ + /* Initialize logging level from environment */ + if (!hybris_logging_initialized) { + hybris_logging_initialized = 1; + hybris_logging_initialize(); + } + + return (level >= hybris_minimum_log_level); +} + +void +hybris_set_log_level(enum hybris_log_level level) +{ + hybris_minimum_log_level = level; +} + +void * +hybris_get_thread_id() +{ + return (void *)pthread_self(); +} + +double +hybris_get_thread_time() +{ + struct timespec now; + if(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now) == 0) { + return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0; + } else { + return -1.0; + } +} + +int +hybris_should_trace(const char *module, const char *tracepoint) +{ + return _hybris_should_trace; +} + +enum hybris_log_format hybris_logging_format() +{ + return _hybris_logging_format; +} diff --git a/hybris/src/logging.h b/hybris/src/logging.h new file mode 100644 index 0000000..f4841b9 --- /dev/null +++ b/hybris/src/logging.h @@ -0,0 +1,165 @@ + +/* + * Copyright (c) 2013 Thomas Perl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef HYBRIS_LOGGING_H +#define HYBRIS_LOGGING_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum hybris_log_level { + /* Most verbose logging level */ + HYBRIS_LOG_DEBUG = 0, + + /* Normal logging levels */ + HYBRIS_LOG_INFO, + HYBRIS_LOG_WARN, + HYBRIS_LOG_ERROR, + + /** + * "Fake" level at which no messages are logged. + * Can be used with hybris_set_log_level(). + **/ + HYBRIS_LOG_DISABLED, +}; + +enum hybris_log_format { + HYBRIS_LOG_FORMAT_NORMAL, + HYBRIS_LOG_FORMAT_SYSTRACE +}; + +/** + * Returns nonzero if messages at level "level" should be logged. + * Only used by the HYBRIS_LOG() macro, no need to call it manually. + **/ +int +hybris_should_log(enum hybris_log_level level); + + +/** + * Sets the minimum log level that is logged, for example a minimum + * log level of HYBRIS_LOG_DEBUG would print all log messages, a + * minimum log level of HYBRIS_LOG_WARN would only print warnings and + * errors. The default log level is HYBRIS_LOG_WARN. + **/ +void +hybris_set_log_level(enum hybris_log_level level); + +void * +hybris_get_thread_id(); + +double +hybris_get_thread_time(); + +enum hybris_log_format hybris_logging_format(); + +int hybris_should_trace(const char *module, const char *tracepoint); + +extern pthread_mutex_t hybris_logging_mutex; + +#ifdef __cplusplus +} +#endif + +extern FILE *hybris_logging_target; + +#if defined(DEBUG) +# define HYBRIS_LOG_(level, module, message, ...) do { \ + if (hybris_should_log(level)) { \ + pthread_mutex_lock(&hybris_logging_mutex); \ + if (hybris_logging_format() == HYBRIS_LOG_FORMAT_NORMAL) \ + { \ + fprintf(hybris_logging_target, "%s %s:%d (%s) %s: " message "\n", \ + module, __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + #level + 11 /* + 11 = strip leading "HYBRIS_LOG_" */, \ + ##__VA_ARGS__); \ + fflush(hybris_logging_target); \ + } else if (hybris_logging_format() == HYBRIS_LOG_FORMAT_SYSTRACE) { \ + fprintf(hybris_logging_target, "B|%i|%.9f|%s(%s) %s:%d (%s) " message "\n", \ + getpid(), hybris_get_thread_time(), module, __PRETTY_FUNCTION__, __FILE__, __LINE__, \ + #level + 11 /* + 11 = strip leading "HYBRIS_LOG_" */, \ + ##__VA_ARGS__); \ + fflush(hybris_logging_target); \ + fprintf(hybris_logging_target, "E|%i|%.9f|%s(%s) %s:%d (%s) " message "\n", \ + getpid(), hybris_get_thread_time(), module, __PRETTY_FUNCTION__, __FILE__, __LINE__, \ + #level + 11 /* + 11 = strip leading "HYBRIS_LOG_" */, \ + ##__VA_ARGS__); \ + fflush(hybris_logging_target); \ + } \ + pthread_mutex_unlock(&hybris_logging_mutex); \ + } \ + } while(0) + +#define HYBRIS_TRACE_RECORD(module, what, tracepoint, message, ...) do { \ + if (hybris_should_trace(module, tracepoint)) { \ + pthread_mutex_lock(&hybris_logging_mutex); \ + if (hybris_logging_format() == HYBRIS_LOG_FORMAT_NORMAL) \ + { \ + fprintf(hybris_logging_target, "PID: %i TTIME: %.9f Tracepoint-%c/%s::%s" message "\n", \ + getpid(), hybris_get_thread_time(), what, tracepoint, module, \ + ##__VA_ARGS__); \ + fflush(hybris_logging_target); \ + } else if (hybris_logging_format() == HYBRIS_LOG_FORMAT_SYSTRACE) { \ + if (what == 'B') \ + fprintf(hybris_logging_target, "B|%i|%.9f|%s::%s" message "", \ + getpid(), hybris_get_thread_time(), tracepoint, module, ##__VA_ARGS__); \ + else if (what == 'E') \ + fprintf(hybris_logging_target, "E"); \ + else \ + fprintf(hybris_logging_target, "C|%i|%.9f|%s::%s-%i|" message "", \ + getpid(), hybris_get_thread_time(), tracepoint, module, getpid(), ##__VA_ARGS__); \ + fflush(hybris_logging_target); \ + } \ + pthread_mutex_unlock(&hybris_logging_mutex); \ + } \ + } while(0) +# define HYBRIS_TRACE_BEGIN(module, tracepoint, message, ...) HYBRIS_TRACE_RECORD(module, 'B', tracepoint, message, ##__VA_ARGS__) +# define HYBRIS_TRACE_END(module, tracepoint, message, ...) HYBRIS_TRACE_RECORD(module, 'E', tracepoint, message, ##__VA_ARGS__) +# define HYBRIS_TRACE_COUNTER(module, tracepoint, message, ...) HYBRIS_TRACE_RECORD(module, 'C', tracepoint, message, ##__VA_ARGS__) +#else +# define HYBRIS_LOG_(level, module, message, ...) while (0) {} +# define HYBRIS_TRACE_BEGIN(module, tracepoint, message, ...) while (0) {} +# define HYBRIS_TRACE_END(module, tracepoint, message, ...) while (0) {} +# define HYBRIS_TRACE_COUNTER(module, tracepoint, message, ...) while (0) {} +#endif + + +/* Generic logging functions taking the module name and a message */ +#define HYBRIS_DEBUG_LOG(module, message, ...) HYBRIS_LOG_(HYBRIS_LOG_DEBUG, #module, message, ##__VA_ARGS__) +#define HYBRIS_WARN_LOG(module, message, ...) HYBRIS_LOG_(HYBRIS_LOG_WARN, #module, message, ##__VA_ARGS__) +#define HYBRIS_INFO_LOG(module, message, ...) HYBRIS_LOG_(HYBRIS_LOG_INFO, #module, message, ##__VA_ARGS__) +#define HYBRIS_ERROR_LOG(module, message, ...) HYBRIS_LOG_(HYBRIS_LOG_ERROR, #module, message, ##__VA_ARGS__) + + +/* Module-specific logging functions can be defined like this */ +#define HYBRIS_DEBUG(message, ...) HYBRIS_DEBUG_LOG(HYBRIS, message, ##__VA_ARGS__) +#define HYBRIS_WARN(message, ...) HYBRIS_WARN_LOG(HYBRIS, message, ##__VA_ARGS__) +#define HYBRIS_INFO(message, ...) HYBRIS_INFO_LOG(HYBRIS, message, ##__VA_ARGS__) +#define HYBRIS_ERROR(message, ...) HYBRIS_ERROR_LOG(HYBRIS, message, ##__VA_ARGS__) + +/* for compatibility reasons */ +#define TRACE(message, ...) HYBRIS_DEBUG_LOG(EGL, message, ##__VA_ARGS__) + +#endif /* HYBRIS_LOGGING_H */ +// vim: noai:ts=4:sw=4:ss=4:expandtab diff --git a/hybris/src/properties.c b/hybris/src/properties.c new file mode 100644 index 0000000..f6076fd --- /dev/null +++ b/hybris/src/properties.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2012 Carsten Munk + * 2008 The Android Open Source Project + * 2013 Simon Busch + * 2013 Canonical Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define __USE_GNU +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../include/hybris/properties.h" +#include "properties_p.h" + + +static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; +static int send_prop_msg_no_reply = 0; + +/* Get/Set a property from the Android Init property socket */ +static int send_prop_msg(prop_msg_t *msg, + void (*propfn)(const char *, const char *, void *), + void *cookie) +{ + struct pollfd pollfds[1]; + union { + struct sockaddr_un addr; + struct sockaddr addr_g; + } addr; + socklen_t alen; + size_t namelen; + int s; + int r; + int result = -1; + int patched_init = 0; + + /* if we tried to talk to the server in the past and didn't get a reply, + * it's fairly safe to say that init is not patched and this is all + * hopeless, so we should just quit while we're ahead + */ + if (send_prop_msg_no_reply == 1) + return -EIO; + + s = socket(AF_LOCAL, SOCK_STREAM, 0); + if (s < 0) { + return result; + } + + memset(&addr, 0, sizeof(addr)); + namelen = strlen(property_service_socket); + strncpy(addr.addr.sun_path, property_service_socket, + sizeof(addr.addr.sun_path)); + addr.addr.sun_family = AF_LOCAL; + alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; + + if (TEMP_FAILURE_RETRY(connect(s, &addr.addr_g, alen) < 0)) { + close(s); + return result; + } + + r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg_t), 0)); + + if (r == sizeof(prop_msg_t)) { + pollfds[0].fd = s; + pollfds[0].events = 0; + // We successfully wrote to the property server, so use recv + // in case we need to get a property. Once the other side is + // finished, the socket is closed. + while ((r = recv(s, msg, sizeof(prop_msg_t), 0)) > 0) { + if (r != sizeof(prop_msg_t)) { + close(s); + return result; + } + + /* If we got a reply, this is a patched init */ + if (!patched_init) + patched_init = 1; + + if (propfn) + propfn(msg->name, msg->value, cookie); + } + + /* We also just get a close in case of setprop */ + if ((r >= 0) && (patched_init || + (msg->cmd == PROP_MSG_SETPROP))) { + result = 0; + } else { + send_prop_msg_no_reply = 1; + } + } + + close(s); + return result; +} + +int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie) +{ + int err; + prop_msg_t msg; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = PROP_MSG_LISTPROP; + + err = send_prop_msg(&msg, propfn, cookie); + if (err < 0) + /* fallback to property cache */ + hybris_propcache_list((hybris_propcache_list_cb) propfn, cookie); + + return 0; +} + +static int property_get_socket(const char *key, char *value, const char *default_value) +{ + int err; + prop_msg_t msg; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = PROP_MSG_GETPROP; + + if (key) { + strncpy(msg.name, key, sizeof(msg.name)); + err = send_prop_msg(&msg, NULL, NULL); + if (err < 0) + return err; + } + + /* In case it's null, just use the default */ + if ((strlen(msg.value) == 0) && (default_value)) { + if (strlen(default_value) >= PROP_VALUE_MAX -1) return -1; + strcpy(msg.value, default_value); + } + + strcpy(value, msg.value); + + return 0; +} + +int property_get(const char *key, char *value, const char *default_value) +{ + char *ret = NULL; + + if ((key) && (strlen(key) >= PROP_NAME_MAX -1)) return -1; + if (value == NULL) return -1; + + if (property_get_socket(key, value, default_value) == 0) + return strlen(value); + + /* In case the socket is not available, search the property file cache by hand */ + ret = hybris_propcache_find(key); + + if (ret) { + strcpy(value, ret); + return strlen(value); + } else if (default_value != NULL) { + strcpy(value, default_value); + return strlen(value); + } else { + value = '\0'; + } + + return 0; +} + +int property_set(const char *key, const char *value) +{ + int err; + prop_msg_t msg; + + if (key == 0) return -1; + if (value == 0) value = ""; + if (strlen(key) >= PROP_NAME_MAX -1) return -1; + if (strlen(value) >= PROP_VALUE_MAX -1) return -1; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = PROP_MSG_SETPROP; + strncpy(msg.name, key, sizeof(msg.name)); + strncpy(msg.value, value, sizeof(msg.value)); + + err = send_prop_msg(&msg, NULL, NULL); + if (err < 0) { + return err; + } + + return 0; +} + +// vim:ts=4:sw=4:noexpandtab diff --git a/hybris/src/properties_p.h b/hybris/src/properties_p.h new file mode 100644 index 0000000..5b2de85 --- /dev/null +++ b/hybris/src/properties_p.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Jolla Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef HYBRIS_PROPERTIES +#define HYBRIS_PROPERTIES + +typedef void (*hybris_propcache_list_cb)(const char *key, const char *value, void *cookie); + +void hybris_propcache_list(hybris_propcache_list_cb cb, void *cookie); +char *hybris_propcache_find(const char *key); + +#endif diff --git a/hybris/src/strlcpy.c b/hybris/src/strlcpy.c new file mode 100644 index 0000000..d32b659 --- /dev/null +++ b/hybris/src/strlcpy.c @@ -0,0 +1,51 @@ +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/hybris/src/sysconf.c b/hybris/src/sysconf.c new file mode 100644 index 0000000..93535cc --- /dev/null +++ b/hybris/src/sysconf.c @@ -0,0 +1,376 @@ +#include + +/* + * bionic/libc/include/sys/sysconf.h processed with s/define _\(\w*\)\(.*\)/define \1\2\r#ifdef _\1\rMAP_TO_UNISTD(\1),\r#endif/g + */ +#define MAP_TO_UNISTD(a) [a]=_##a +static int sysconf_map[]= { +#define SC_ARG_MAX 0x0000 +#ifdef _SC_ARG_MAX +MAP_TO_UNISTD(SC_ARG_MAX), +#endif +#define SC_BC_BASE_MAX 0x0001 +#ifdef _SC_BC_BASE_MAX +MAP_TO_UNISTD(SC_BC_BASE_MAX), +#endif +#define SC_BC_DIM_MAX 0x0002 +#ifdef _SC_BC_DIM_MAX +MAP_TO_UNISTD(SC_BC_DIM_MAX), +#endif +#define SC_BC_SCALE_MAX 0x0003 +#ifdef _SC_BC_SCALE_MAX +MAP_TO_UNISTD(SC_BC_SCALE_MAX), +#endif +#define SC_BC_STRING_MAX 0x0004 +#ifdef _SC_BC_STRING_MAX +MAP_TO_UNISTD(SC_BC_STRING_MAX), +#endif +#define SC_CHILD_MAX 0x0005 +#ifdef _SC_CHILD_MAX +MAP_TO_UNISTD(SC_CHILD_MAX), +#endif +#define SC_CLK_TCK 0x0006 +#ifdef _SC_CLK_TCK +MAP_TO_UNISTD(SC_CLK_TCK), +#endif +#define SC_COLL_WEIGHTS_MAX 0x0007 +#ifdef _SC_COLL_WEIGHTS_MAX +MAP_TO_UNISTD(SC_COLL_WEIGHTS_MAX), +#endif +#define SC_EXPR_NEST_MAX 0x0008 +#ifdef _SC_EXPR_NEST_MAX +MAP_TO_UNISTD(SC_EXPR_NEST_MAX), +#endif +#define SC_LINE_MAX 0x0009 +#ifdef _SC_LINE_MAX +MAP_TO_UNISTD(SC_LINE_MAX), +#endif +#define SC_NGROUPS_MAX 0x000a +#ifdef _SC_NGROUPS_MAX +MAP_TO_UNISTD(SC_NGROUPS_MAX), +#endif +#define SC_OPEN_MAX 0x000b +#ifdef _SC_OPEN_MAX +MAP_TO_UNISTD(SC_OPEN_MAX), +#endif +#define SC_PASS_MAX 0x000c +#ifdef _SC_PASS_MAX +MAP_TO_UNISTD(SC_PASS_MAX), +#endif +#define SC_2_C_BIND 0x000d +#ifdef _SC_2_C_BIND +MAP_TO_UNISTD(SC_2_C_BIND), +#endif +#define SC_2_C_DEV 0x000e +#ifdef _SC_2_C_DEV +MAP_TO_UNISTD(SC_2_C_DEV), +#endif +#define SC_2_C_VERSION 0x000f +#ifdef _SC_2_C_VERSION +MAP_TO_UNISTD(SC_2_C_VERSION), +#endif +#define SC_2_CHAR_TERM 0x0010 +#ifdef _SC_2_CHAR_TERM +MAP_TO_UNISTD(SC_2_CHAR_TERM), +#endif +#define SC_2_FORT_DEV 0x0011 +#ifdef _SC_2_FORT_DEV +MAP_TO_UNISTD(SC_2_FORT_DEV), +#endif +#define SC_2_FORT_RUN 0x0012 +#ifdef _SC_2_FORT_RUN +MAP_TO_UNISTD(SC_2_FORT_RUN), +#endif +#define SC_2_LOCALEDEF 0x0013 +#ifdef _SC_2_LOCALEDEF +MAP_TO_UNISTD(SC_2_LOCALEDEF), +#endif +#define SC_2_SW_DEV 0x0014 +#ifdef _SC_2_SW_DEV +MAP_TO_UNISTD(SC_2_SW_DEV), +#endif +#define SC_2_UPE 0x0015 +#ifdef _SC_2_UPE +MAP_TO_UNISTD(SC_2_UPE), +#endif +#define SC_2_VERSION 0x0016 +#ifdef _SC_2_VERSION +MAP_TO_UNISTD(SC_2_VERSION), +#endif +#define SC_JOB_CONTROL 0x0017 +#ifdef _SC_JOB_CONTROL +MAP_TO_UNISTD(SC_JOB_CONTROL), +#endif +#define SC_SAVED_IDS 0x0018 +#ifdef _SC_SAVED_IDS +MAP_TO_UNISTD(SC_SAVED_IDS), +#endif +#define SC_VERSION 0x0019 +#ifdef _SC_VERSION +MAP_TO_UNISTD(SC_VERSION), +#endif +#define SC_RE_DUP_MAX 0x001a +#ifdef _SC_RE_DUP_MAX +MAP_TO_UNISTD(SC_RE_DUP_MAX), +#endif +#define SC_STREAM_MAX 0x001b +#ifdef _SC_STREAM_MAX +MAP_TO_UNISTD(SC_STREAM_MAX), +#endif +#define SC_TZNAME_MAX 0x001c +#ifdef _SC_TZNAME_MAX +MAP_TO_UNISTD(SC_TZNAME_MAX), +#endif +#define SC_XOPEN_CRYPT 0x001d +#ifdef _SC_XOPEN_CRYPT +MAP_TO_UNISTD(SC_XOPEN_CRYPT), +#endif +#define SC_XOPEN_ENH_I18N 0x001e +#ifdef _SC_XOPEN_ENH_I18N +MAP_TO_UNISTD(SC_XOPEN_ENH_I18N), +#endif +#define SC_XOPEN_SHM 0x001f +#ifdef _SC_XOPEN_SHM +MAP_TO_UNISTD(SC_XOPEN_SHM), +#endif +#define SC_XOPEN_VERSION 0x0020 +#ifdef _SC_XOPEN_VERSION +MAP_TO_UNISTD(SC_XOPEN_VERSION), +#endif +#define SC_XOPEN_XCU_VERSION 0x0021 +#ifdef _SC_XOPEN_XCU_VERSION +MAP_TO_UNISTD(SC_XOPEN_XCU_VERSION), +#endif +#define SC_XOPEN_REALTIME 0x0022 +#ifdef _SC_XOPEN_REALTIME +MAP_TO_UNISTD(SC_XOPEN_REALTIME), +#endif +#define SC_XOPEN_REALTIME_THREADS 0x0023 +#ifdef _SC_XOPEN_REALTIME_THREADS +MAP_TO_UNISTD(SC_XOPEN_REALTIME_THREADS), +#endif +#define SC_XOPEN_LEGACY 0x0024 +#ifdef _SC_XOPEN_LEGACY +MAP_TO_UNISTD(SC_XOPEN_LEGACY), +#endif +#define SC_ATEXIT_MAX 0x0025 +#ifdef _SC_ATEXIT_MAX +MAP_TO_UNISTD(SC_ATEXIT_MAX), +#endif +#define SC_IOV_MAX 0x0026 +#ifdef _SC_IOV_MAX +MAP_TO_UNISTD(SC_IOV_MAX), +#endif +#define SC_PAGESIZE 0x0027 +#ifdef _SC_PAGESIZE +MAP_TO_UNISTD(SC_PAGESIZE), +#endif +#define SC_PAGE_SIZE 0x0028 +#ifdef _SC_PAGE_SIZE +MAP_TO_UNISTD(SC_PAGE_SIZE), +#endif +#define SC_XOPEN_UNIX 0x0029 +#ifdef _SC_XOPEN_UNIX +MAP_TO_UNISTD(SC_XOPEN_UNIX), +#endif +#define SC_XBS5_ILP32_OFF32 0x002a +#ifdef _SC_XBS5_ILP32_OFF32 +MAP_TO_UNISTD(SC_XBS5_ILP32_OFF32), +#endif +#define SC_XBS5_ILP32_OFFBIG 0x002b +#ifdef _SC_XBS5_ILP32_OFFBIG +MAP_TO_UNISTD(SC_XBS5_ILP32_OFFBIG), +#endif +#define SC_XBS5_LP64_OFF64 0x002c +#ifdef _SC_XBS5_LP64_OFF64 +MAP_TO_UNISTD(SC_XBS5_LP64_OFF64), +#endif +#define SC_XBS5_LPBIG_OFFBIG 0x002d +#ifdef _SC_XBS5_LPBIG_OFFBIG +MAP_TO_UNISTD(SC_XBS5_LPBIG_OFFBIG), +#endif +#define SC_AIO_LISTIO_MAX 0x002e +#ifdef _SC_AIO_LISTIO_MAX +MAP_TO_UNISTD(SC_AIO_LISTIO_MAX), +#endif +#define SC_AIO_MAX 0x002f +#ifdef _SC_AIO_MAX +MAP_TO_UNISTD(SC_AIO_MAX), +#endif +#define SC_AIO_PRIO_DELTA_MAX 0x0030 +#ifdef _SC_AIO_PRIO_DELTA_MAX +MAP_TO_UNISTD(SC_AIO_PRIO_DELTA_MAX), +#endif +#define SC_DELAYTIMER_MAX 0x0031 +#ifdef _SC_DELAYTIMER_MAX +MAP_TO_UNISTD(SC_DELAYTIMER_MAX), +#endif +#define SC_MQ_OPEN_MAX 0x0032 +#ifdef _SC_MQ_OPEN_MAX +MAP_TO_UNISTD(SC_MQ_OPEN_MAX), +#endif +#define SC_MQ_PRIO_MAX 0x0033 +#ifdef _SC_MQ_PRIO_MAX +MAP_TO_UNISTD(SC_MQ_PRIO_MAX), +#endif +#define SC_RTSIG_MAX 0x0034 +#ifdef _SC_RTSIG_MAX +MAP_TO_UNISTD(SC_RTSIG_MAX), +#endif +#define SC_SEM_NSEMS_MAX 0x0035 +#ifdef _SC_SEM_NSEMS_MAX +MAP_TO_UNISTD(SC_SEM_NSEMS_MAX), +#endif +#define SC_SEM_VALUE_MAX 0x0036 +#ifdef _SC_SEM_VALUE_MAX +MAP_TO_UNISTD(SC_SEM_VALUE_MAX), +#endif +#define SC_SIGQUEUE_MAX 0x0037 +#ifdef _SC_SIGQUEUE_MAX +MAP_TO_UNISTD(SC_SIGQUEUE_MAX), +#endif +#define SC_TIMER_MAX 0x0038 +#ifdef _SC_TIMER_MAX +MAP_TO_UNISTD(SC_TIMER_MAX), +#endif +#define SC_ASYNCHRONOUS_IO 0x0039 +#ifdef _SC_ASYNCHRONOUS_IO +MAP_TO_UNISTD(SC_ASYNCHRONOUS_IO), +#endif +#define SC_FSYNC 0x003a +#ifdef _SC_FSYNC +MAP_TO_UNISTD(SC_FSYNC), +#endif +#define SC_MAPPED_FILES 0x003b +#ifdef _SC_MAPPED_FILES +MAP_TO_UNISTD(SC_MAPPED_FILES), +#endif +#define SC_MEMLOCK 0x003c +#ifdef _SC_MEMLOCK +MAP_TO_UNISTD(SC_MEMLOCK), +#endif +#define SC_MEMLOCK_RANGE 0x003d +#ifdef _SC_MEMLOCK_RANGE +MAP_TO_UNISTD(SC_MEMLOCK_RANGE), +#endif +#define SC_MEMORY_PROTECTION 0x003e +#ifdef _SC_MEMORY_PROTECTION +MAP_TO_UNISTD(SC_MEMORY_PROTECTION), +#endif +#define SC_MESSAGE_PASSING 0x003f +#ifdef _SC_MESSAGE_PASSING +MAP_TO_UNISTD(SC_MESSAGE_PASSING), +#endif +#define SC_PRIORITIZED_IO 0x0040 +#ifdef _SC_PRIORITIZED_IO +MAP_TO_UNISTD(SC_PRIORITIZED_IO), +#endif +#define SC_PRIORITY_SCHEDULING 0x0041 +#ifdef _SC_PRIORITY_SCHEDULING +MAP_TO_UNISTD(SC_PRIORITY_SCHEDULING), +#endif +#define SC_REALTIME_SIGNALS 0x0042 +#ifdef _SC_REALTIME_SIGNALS +MAP_TO_UNISTD(SC_REALTIME_SIGNALS), +#endif +#define SC_SEMAPHORES 0x0043 +#ifdef _SC_SEMAPHORES +MAP_TO_UNISTD(SC_SEMAPHORES), +#endif +#define SC_SHARED_MEMORY_OBJECTS 0x0044 +#ifdef _SC_SHARED_MEMORY_OBJECTS +MAP_TO_UNISTD(SC_SHARED_MEMORY_OBJECTS), +#endif +#define SC_SYNCHRONIZED_IO 0x0045 +#ifdef _SC_SYNCHRONIZED_IO +MAP_TO_UNISTD(SC_SYNCHRONIZED_IO), +#endif +#define SC_TIMERS 0x0046 +#ifdef _SC_TIMERS +MAP_TO_UNISTD(SC_TIMERS), +#endif +#define SC_GETGR_R_SIZE_MAX 0x0047 +#ifdef _SC_GETGR_R_SIZE_MAX +MAP_TO_UNISTD(SC_GETGR_R_SIZE_MAX), +#endif +#define SC_GETPW_R_SIZE_MAX 0x0048 +#ifdef _SC_GETPW_R_SIZE_MAX +MAP_TO_UNISTD(SC_GETPW_R_SIZE_MAX), +#endif +#define SC_LOGIN_NAME_MAX 0x0049 +#ifdef _SC_LOGIN_NAME_MAX +MAP_TO_UNISTD(SC_LOGIN_NAME_MAX), +#endif +#define SC_THREAD_DESTRUCTOR_ITERATIONS 0x004a +#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS +MAP_TO_UNISTD(SC_THREAD_DESTRUCTOR_ITERATIONS), +#endif +#define SC_THREAD_KEYS_MAX 0x004b +#ifdef _SC_THREAD_KEYS_MAX +MAP_TO_UNISTD(SC_THREAD_KEYS_MAX), +#endif +#define SC_THREAD_STACK_MIN 0x004c +#ifdef _SC_THREAD_STACK_MIN +MAP_TO_UNISTD(SC_THREAD_STACK_MIN), +#endif +#define SC_THREAD_THREADS_MAX 0x004d +#ifdef _SC_THREAD_THREADS_MAX +MAP_TO_UNISTD(SC_THREAD_THREADS_MAX), +#endif +#define SC_TTY_NAME_MAX 0x004e +#ifdef _SC_TTY_NAME_MAX +MAP_TO_UNISTD(SC_TTY_NAME_MAX), +#endif + +#define SC_THREADS 0x004f +#ifdef _SC_THREADS +MAP_TO_UNISTD(SC_THREADS), +#endif +#define SC_THREAD_ATTR_STACKADDR 0x0050 +#ifdef _SC_THREAD_ATTR_STACKADDR +MAP_TO_UNISTD(SC_THREAD_ATTR_STACKADDR), +#endif +#define SC_THREAD_ATTR_STACKSIZE 0x0051 +#ifdef _SC_THREAD_ATTR_STACKSIZE +MAP_TO_UNISTD(SC_THREAD_ATTR_STACKSIZE), +#endif +#define SC_THREAD_PRIORITY_SCHEDULING 0x0052 +#ifdef _SC_THREAD_PRIORITY_SCHEDULING +MAP_TO_UNISTD(SC_THREAD_PRIORITY_SCHEDULING), +#endif +#define SC_THREAD_PRIO_INHERIT 0x0053 +#ifdef _SC_THREAD_PRIO_INHERIT +MAP_TO_UNISTD(SC_THREAD_PRIO_INHERIT), +#endif +#define SC_THREAD_PRIO_PROTECT 0x0054 +#ifdef _SC_THREAD_PRIO_PROTECT +MAP_TO_UNISTD(SC_THREAD_PRIO_PROTECT), +#endif +#define SC_THREAD_SAFE_FUNCTIONS 0x0055 +#ifdef _SC_THREAD_SAFE_FUNCTIONS +MAP_TO_UNISTD(SC_THREAD_SAFE_FUNCTIONS), +#endif + +#define SC_NPROCESSORS_CONF 0x0060 +#ifdef _SC_NPROCESSORS_CONF +MAP_TO_UNISTD(SC_NPROCESSORS_CONF), +#endif +#define SC_NPROCESSORS_ONLN 0x0061 +#ifdef _SC_NPROCESSORS_ONLN +MAP_TO_UNISTD(SC_NPROCESSORS_ONLN), +#endif +#define SC_PHYS_PAGES 0x0062 +#ifdef _SC_PHYS_PAGES +MAP_TO_UNISTD(SC_PHYS_PAGES), +#endif +#define SC_AVPHYS_PAGES 0x0063 +#ifdef _SC_AVPHYS_PAGES +MAP_TO_UNISTD(SC_AVPHYS_PAGES), +#endif +}; +#undef MAP_TO_UNISTD + +long my_sysconf(int name) +{ + return sysconf(sysconf_map[name]); +} diff --git a/mcpe/App.h b/mcpe/App.h new file mode 100644 index 0000000..b3065c4 --- /dev/null +++ b/mcpe/App.h @@ -0,0 +1,29 @@ +#pragma once + +class AppPlatform; +struct AppContext { + char filler[0x10]; + AppPlatform* platform; + bool doRender; +}; + +class App { + +public: + static void (*App_init)(App*, AppContext&); + + void** vtable; + + void init(AppContext& ctx) { + App_init(this, ctx); + } + + void quit() { + ((void (*)(App*)) vtable[23])(this); + } + + bool wantToQuit() { + return ((bool (*)(App*)) vtable[24])(this); + } + +}; \ No newline at end of file diff --git a/mcpe/AppPlatform.h b/mcpe/AppPlatform.h new file mode 100644 index 0000000..14d7313 --- /dev/null +++ b/mcpe/AppPlatform.h @@ -0,0 +1,27 @@ +#pragma once + +class AppPlatform { + +public: + static void** myVtable; + + static void (*AppPlatform_construct)(AppPlatform*); + static void (*AppPlatform_initialize)(AppPlatform*); + static void (*AppPlatform__fireAppFocusGained)(AppPlatform*); + + void** vtable; + char filler [0x1000]; + + AppPlatform() { + AppPlatform_construct(this); + } + + static AppPlatform** _singleton; + void _fireAppFocusGained() { + AppPlatform__fireAppFocusGained(this); + } + void initialize() { + AppPlatform_initialize(this); + } + +}; \ No newline at end of file diff --git a/mcpe/FilePickerSettings.h b/mcpe/FilePickerSettings.h new file mode 100644 index 0000000..05a2f2b --- /dev/null +++ b/mcpe/FilePickerSettings.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +struct FilePickerSettings { + + enum class PickerType { + NONE, OPEN, SAVE + }; + struct FileDescription { + std::string ext, desc; + }; + + char filler [0x20]; // 20 + std::function pickedCallback; // 30 + std::vector fileDescriptions; // 3c + int filler3; // 40 + PickerType type; // 44 + std::string defaultFileName; // 48 + std::string pickerTitle; // 52 + +}; \ No newline at end of file diff --git a/mcpe/ImagePickingCallback.h b/mcpe/ImagePickingCallback.h new file mode 100644 index 0000000..3a96c22 --- /dev/null +++ b/mcpe/ImagePickingCallback.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class ImagePickingCallback { + +public: + virtual ~ImagePickingCallback(); + virtual void onImagePickingSuccess(const std::string&); + virtual void onImagePickingCanceled(); + +}; \ No newline at end of file diff --git a/mcpe/Keyboard.h b/mcpe/Keyboard.h new file mode 100644 index 0000000..d8456e2 --- /dev/null +++ b/mcpe/Keyboard.h @@ -0,0 +1,17 @@ +#pragma once + +struct KeyboardAction { + int action; + int keyCode; +}; + +class Keyboard { + +public: + + static void (*Keyboard_feedText)(const std::string&, bool, unsigned char); + + static std::vector* inputs; + static int* states; + +}; \ No newline at end of file diff --git a/mcpe/MinecraftGame.h b/mcpe/MinecraftGame.h new file mode 100644 index 0000000..2901b22 --- /dev/null +++ b/mcpe/MinecraftGame.h @@ -0,0 +1,38 @@ +#pragma once + +#include "App.h" + +class Options; + +class MinecraftGame : public App { + +public: + static void (*MinecraftGame_construct)(MinecraftGame*, int, char**); + static void (*MinecraftGame_update)(MinecraftGame*); + static void (*MinecraftGame_setRenderingSize)(MinecraftGame*, int, int); + static void (*MinecraftGame_setUISizeAndScale)(MinecraftGame*, int, int, float); + static Options* (*MinecraftGame_getOptions)(MinecraftGame*); + + char filler [0x4000-4]; + + MinecraftGame(int carg, char** args) { + MinecraftGame_construct(this, carg, args); + } + + void update() { + MinecraftGame_update(this); + } + + void setRenderingSize(int w, int h) { + MinecraftGame_setRenderingSize(this, w, h); + } + + void setUISizeAndScale(int w, int h, float px) { + MinecraftGame_setUISizeAndScale(this, w, h, px); + } + + Options* getOptions() { + return MinecraftGame_getOptions(this); + } + +}; diff --git a/mcpe/Mouse.h b/mcpe/Mouse.h new file mode 100644 index 0000000..61b47e4 --- /dev/null +++ b/mcpe/Mouse.h @@ -0,0 +1,8 @@ +#pragma once + +class Mouse { + +public: + static void (*feed)(char button, char type, short x, short y, short dx, short dy); + +}; \ No newline at end of file diff --git a/mcpe/Options.h b/mcpe/Options.h new file mode 100644 index 0000000..380f7af --- /dev/null +++ b/mcpe/Options.h @@ -0,0 +1,16 @@ +#pragma once + +class Options { + +public: + static bool (*Options_getFullscreen)(Options*); + static void (*Options_setFullscreen)(Options*, bool); + + bool getFullscreen() { + return Options_getFullscreen(this); + } + void setFullscreen(bool b) { + Options_setFullscreen(this, b); + } + +}; \ No newline at end of file diff --git a/mcpe/gl.h b/mcpe/gl.h new file mode 100644 index 0000000..1cc1217 --- /dev/null +++ b/mcpe/gl.h @@ -0,0 +1,23 @@ +#pragma once + +struct gl { + static std::string (*getOpenGLVendor)(); + static std::string (*getOpenGLRenderer)(); + static std::string (*getOpenGLVersion)(); + static std::string (*getOpenGLExtensions)(); +}; + +namespace mce { + +namespace Platform { + +struct OGL { + + static void (*OGL_initBindings)(); + static void initBindings() { OGL_initBindings(); } + +}; + +} + +} \ No newline at end of file diff --git a/mcpe/types.cpp b/mcpe/types.cpp new file mode 100644 index 0000000..8218b3e --- /dev/null +++ b/mcpe/types.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include "AppPlatform.h" + +AppPlatform** AppPlatform::_singleton = nullptr; +void** AppPlatform::myVtable = nullptr; +void (*AppPlatform::AppPlatform_construct)(AppPlatform*); +void (*AppPlatform::AppPlatform__fireAppFocusGained)(AppPlatform*); +void (*AppPlatform::AppPlatform_initialize)(AppPlatform*); + +#include "App.h" + +void (*App::App_init)(App*, AppContext&); + +#include "MinecraftGame.h" + +void (*MinecraftGame::MinecraftGame_construct)(MinecraftGame*, int, char**); +void (*MinecraftGame::MinecraftGame_update)(MinecraftGame*); +void (*MinecraftGame::MinecraftGame_setRenderingSize)(MinecraftGame*, int, int); +void (*MinecraftGame::MinecraftGame_setUISizeAndScale)(MinecraftGame*, int, int, float); +Options* (*MinecraftGame::MinecraftGame_getOptions)(MinecraftGame*); + +#include "Options.h" + +bool (*Options::Options_getFullscreen)(Options*); +void (*Options::Options_setFullscreen)(Options*, bool); + +#include "gl.h" + +std::string (*gl::getOpenGLVendor)(); +std::string (*gl::getOpenGLRenderer)(); +std::string (*gl::getOpenGLVersion)(); +std::string (*gl::getOpenGLExtensions)(); +void (*mce::Platform::OGL::OGL_initBindings)(); + +#include "Mouse.h" + +void (*Mouse::feed)(char, char, short, short, short, short); + +#include "Keyboard.h" + +void (*Keyboard::Keyboard_feedText)(const std::string&, bool, unsigned char); +std::vector* Keyboard::inputs; +int* Keyboard::states; \ No newline at end of file diff --git a/mod_example/README.md b/mod_example/README.md new file mode 100644 index 0000000..6769a9e --- /dev/null +++ b/mod_example/README.md @@ -0,0 +1,4 @@ +Example modification +==================== + +To compile this modification, use ndk-build in this directory. Then you should copy the output .so from libs/x86/ to the launcher's mods/ directory. diff --git a/mod_example/jni/Android.mk b/mod_example/jni/Android.mk new file mode 100644 index 0000000..e77d74e --- /dev/null +++ b/mod_example/jni/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH := $(call my-dir) +CWD := $(shell pwd) + +include $(CLEAR_VARS) + +LOCAL_MODULE := mcpelauncher_testmod +LOCAL_SRC_FILES := main.cpp +LOCAL_LDLIBS := -L$(LOCAL_PATH)/../../libs/ -ldl -lmcpelauncher_mod -lminecraftpe + +include $(BUILD_SHARED_LIBRARY) diff --git a/mod_example/jni/Application.mk b/mod_example/jni/Application.mk new file mode 100644 index 0000000..f3df8d7 --- /dev/null +++ b/mod_example/jni/Application.mk @@ -0,0 +1,5 @@ +APP_ABI := x86 +APP_PLATFORM := android-21 +APP_CPPFLAGS += -std=c++11 +APP_STL := gnustl_static +NDK_TOOLCHAIN_VERSION := 4.9 diff --git a/mod_example/jni/main.cpp b/mod_example/jni/main.cpp new file mode 100644 index 0000000..71f016c --- /dev/null +++ b/mod_example/jni/main.cpp @@ -0,0 +1,21 @@ +#include + +#include "mcpelauncher_api.h" + +struct Common { + static std::string getGameVersionString(); + static std::string (*$getGameVersionString)(); + static std::string $$getGameVersionString() { + return "Hello world!"; + } +}; +std::string (*Common::$getGameVersionString)(); + +extern "C" { + +void mod_init() { + std::cout << "init test mod\n"; + mcpelauncher_hook((void*) &Common::getGameVersionString, (void*) &Common::$$getGameVersionString, (void**) &Common::$getGameVersionString); +} + +} \ No newline at end of file diff --git a/mod_example/jni/mcpelauncher_api.h b/mod_example/jni/mcpelauncher_api.h new file mode 100644 index 0000000..df70699 --- /dev/null +++ b/mod_example/jni/mcpelauncher_api.h @@ -0,0 +1,12 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void* mcpelauncher_hook(void* symbol, void* hook, void** original); +void mcpelauncher_unhook(void* hook); + +#ifdef __cplusplus +} +#endif diff --git a/src/LinuxAppPlatform.cpp b/src/LinuxAppPlatform.cpp new file mode 100644 index 0000000..9210de5 --- /dev/null +++ b/src/LinuxAppPlatform.cpp @@ -0,0 +1,196 @@ +#include "LinuxAppPlatform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../mcpe/ImagePickingCallback.h" +#include "../mcpe/FilePickerSettings.h" +#include "../hybris/src/jb/linker.h" + +extern "C" { +#include +#include "../hybris/include/hybris/dlfcn.h" +} + +void** LinuxAppPlatform::myVtable = nullptr; +bool LinuxAppPlatform::mousePointerHidden = false; +bool enablePocketGuis = false; +bool serverMode = false; + +LinuxAppPlatform::LinuxAppPlatform() : AppPlatform() { + this->vtable = myVtable; + internalStorage = "data/private/"; + externalStorage = "data/public/"; + currentStorage = "data/current/"; + userdata = "data/user/"; + userdataPathForLevels = "data/user/"; + region = "0xdeadbeef"; + tmpPath = "tmp/"; +} + +#include +#include +#include + +void LinuxAppPlatform::replaceVtableEntry(void* lib, void** vtable, const char* sym, void* nw) { + void* sm = hybris_dlsym(lib, sym); + for (int i = 0; ; i++) { + if (vtable[i] == nullptr) + break; + if (vtable[i] == sm) { + myVtable[i] = nw; + return; + } + } +} + +void LinuxAppPlatform::initVtable(void* lib) { + void** vt = AppPlatform::myVtable; + void** vta = &((void**) hybris_dlsym(lib, "_ZTV19AppPlatform_android"))[2]; + // get vtable size + int size; + for (size = 2; ; size++) { + if (vt[size] == nullptr) + break; + } + printf("AppPlatform size = %i\n", size); + + myVtable = (void**) ::operator new(size * sizeof(void*)); + memcpy(&myVtable[0], &vt[2], (size - 2) * sizeof(void*)); + + replaceVtableEntry(lib, vta, "_ZNK19AppPlatform_android10getDataUrlEv", (void*) &LinuxAppPlatform::getDataUrl); + replaceVtableEntry(lib, vta, "_ZNK19AppPlatform_android14getUserDataUrlEv", (void*) &LinuxAppPlatform::getUserDataUrl); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android14getPackagePathEv", (void*) &LinuxAppPlatform::getPackagePath); + replaceVtableEntry(lib, vta, "_ZN11AppPlatform16hideMousePointerEv", (void*) &LinuxAppPlatform::hideMousePointer); + replaceVtableEntry(lib, vta, "_ZN11AppPlatform16showMousePointerEv", (void*) &LinuxAppPlatform::showMousePointer); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android11swapBuffersEv", (void*) &LinuxAppPlatform::swapBuffers); + replaceVtableEntry(lib, vta, "_ZNK19AppPlatform_android15getSystemRegionEv", (void*) &LinuxAppPlatform::getSystemRegion); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android25getGraphicsTearingSupportEv", (void*) &LinuxAppPlatform::getGraphicsTearingSupport); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android9pickImageER20ImagePickingCallback", (void*) &LinuxAppPlatform::pickImage); + replaceVtableEntry(lib, vta, "_ZN11AppPlatform8pickFileER18FilePickerSettings", (void*) &LinuxAppPlatform::pickFile); + replaceVtableEntry(lib, vta, "_ZNK11AppPlatform19supportsFilePickingEv", (void*) &LinuxAppPlatform::supportsFilePicking); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android22getExternalStoragePathEv", (void*) &LinuxAppPlatform::getExternalStoragePath); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android22getInternalStoragePathEv", (void*) &LinuxAppPlatform::getInternalStoragePath); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android21getCurrentStoragePathEv", (void*) &LinuxAppPlatform::getCurrentStoragePath); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android15getUserdataPathEv", (void*) &LinuxAppPlatform::getUserdataPath); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android24getUserdataPathForLevelsEv", (void*) &LinuxAppPlatform::getUserdataPathForLevels); + replaceVtableEntry(lib, vta, "_ZN11AppPlatform20getAssetFileFullPathERKSs", (void*) &LinuxAppPlatform::getAssetFileFullPath); + replaceVtableEntry(lib, vta, "_ZNK11AppPlatform14useCenteredGUIEv", (void*) &LinuxAppPlatform::useCenteredGUI); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android16getApplicationIdEv", (void*) &LinuxAppPlatform::getApplicationId); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android18getAvailableMemoryEv", (void*) &LinuxAppPlatform::getAvailableMemory); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android11getDeviceIdEv", (void*) &LinuxAppPlatform::getDeviceId); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android10createUUIDEv", (void*) &LinuxAppPlatform::createUUID); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android18isFirstSnoopLaunchEv", (void*) &LinuxAppPlatform::isFirstSnoopLaunch); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android29hasHardwareInformationChangedEv", (void*) &LinuxAppPlatform::hasHardwareInformationChanged); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android8isTabletEv", (void*) &LinuxAppPlatform::isTablet); + replaceVtableEntry(lib, vta, "_ZN11AppPlatform17setFullscreenModeE14FullscreenMode", (void*) &LinuxAppPlatform::setFullscreenMode); + replaceVtableEntry(lib, vta, "_ZNK11AppPlatform10getEditionEv", (void*) &LinuxAppPlatform::getEdition); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android31calculateAvailableDiskFreeSpaceERKSs", (void*) &LinuxAppPlatform::calculateAvailableDiskFreeSpace); + replaceVtableEntry(lib, vta, "_ZNK19AppPlatform_android25getPlatformUIScalingRulesEv", (void*) &LinuxAppPlatform::getPlatformUIScalingRules); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android19getPlatformTempPathEv", (void*) &LinuxAppPlatform::getPlatformTempPath); + replaceVtableEntry(lib, vta, "_ZN19AppPlatform_android14createDeviceIDEv", (void*) &LinuxAppPlatform::createDeviceID); +} + +void LinuxAppPlatform::hideMousePointer() { + mousePointerHidden = true; + moveMouseToCenter = true; + eglutSetMousePointerVisiblity(EGLUT_POINTER_INVISIBLE); +} +void LinuxAppPlatform::showMousePointer() { + mousePointerHidden = false; + eglutSetMousePointerVisiblity(EGLUT_POINTER_VISIBLE); +} + +std::string LinuxAppPlatform::_pickFile(std::string commandLine) { + std::cout << "Launching file picker with args: " << commandLine << "\n"; + char file[1024]; + FILE *f = popen(commandLine.c_str(), "r"); + if (fgets(file, 1024, f) == nullptr) { + std::cout << "No file selected\n"; + return ""; + } + file[strlen(file) - 1] = '\0'; + std::cout << "Selected file: " << file << "\n"; + return std::string(file); +} + +void LinuxAppPlatform::pickImage(ImagePickingCallback &callback) { + std::cout << "pickImage\n"; + std::string file = _pickFile("zenity --file-selection --title 'Select image' --file-filter *.png"); + if (file.empty()) { + callback.onImagePickingCanceled(); + } else { + callback.onImagePickingSuccess(file); + } +} + +std::string replaceAll(std::string s, std::string a, std::string b) { + while (true) { + size_t p = s.find(a); + if (p == std::string::npos) + break; + s.replace(p, a.length(), b); + } + return s; +} + +void LinuxAppPlatform::pickFile(FilePickerSettings &settings) { + std::cout << "pickFile\n"; + std::cout << "- title: " << settings.pickerTitle << "\n"; + std::cout << "- type: " << (int) settings.type << "\n"; + std::cout << "- file descriptions:\n"; + for (FilePickerSettings::FileDescription &d : settings.fileDescriptions) { + std::cout << " - " << d.ext << " " << d.desc << "\n"; + } + std::stringstream ss; + ss << "zenity --file-selection --title '" << replaceAll(settings.pickerTitle, "'", "\\'") << "'"; + if (settings.type == FilePickerSettings::PickerType::SAVE) + ss << " --save"; + if (settings.fileDescriptions.size() > 0) { + ss << " --file-filter '"; + bool first = true; + for (FilePickerSettings::FileDescription &d : settings.fileDescriptions) { + if (first) + first = false; + else + ss << "|"; + ss << "*." << d.ext; + } + ss << "'"; + } + std::string file = _pickFile(ss.str()); + settings.pickedCallback(settings, file); +} + +void LinuxAppPlatform::setFullscreenMode(int mode) { + std::cout << "set fullscreen mode: " << mode << "\n"; + int newMode = mode == 1 ? EGLUT_FULLSCREEN : EGLUT_WINDOWED; + if (eglutGet(EGLUT_FULLSCREEN_MODE) != newMode) + eglutToggleFullscreen(); +} + +std::string LinuxAppPlatform::createUUID() { + srand(time(NULL)); + + uuid_t id; + uuid_generate(id); + char out [256]; + uuid_unparse(id, out); + printf("uuid: %s\n", out); + return std::string(out); +} + +long long LinuxAppPlatform::getAvailableMemory() { + struct sysinfo memInfo; + sysinfo (&memInfo); + long long totalVirtualMem = memInfo.totalram; + totalVirtualMem += memInfo.totalswap; + totalVirtualMem *= memInfo.mem_unit; + return totalVirtualMem; +} \ No newline at end of file diff --git a/src/LinuxAppPlatform.h b/src/LinuxAppPlatform.h new file mode 100644 index 0000000..cd2c10a --- /dev/null +++ b/src/LinuxAppPlatform.h @@ -0,0 +1,145 @@ +#pragma once + +#include +#include +#include +#include "../mcpe/gl.h" +#include "../mcpe/AppPlatform.h" +#include "../mcpe/ImagePickingCallback.h" + +class ImageData; +class ImagePickingCallback; +class FilePickerSettings; + +extern bool enablePocketGuis; +extern bool moveMouseToCenter; +extern bool serverMode; + +class LinuxAppPlatform : public AppPlatform { + +private: + static std::string _pickFile(std::string commandLine); + + static void replaceVtableEntry(void* lib, void** vtable, const char* sym, void* nw); + +public: + static void** myVtable; + static void initVtable(void* lib); + + static bool mousePointerHidden; + + std::string region; + std::string internalStorage, externalStorage, currentStorage, userdata, userdataPathForLevels, tmpPath; + + LinuxAppPlatform(); + + std::string getDataUrl() { // this is used only for sounds + printf("get data url: assets/\n"); + return "assets/"; + } + std::string getUserDataUrl() { // this is used only for sounds + printf("get user data url: data/user/\n"); + return "data/user/"; + } + + std::string getPackagePath() { + return "assets/"; + } + + void hideMousePointer(); + void showMousePointer(); + + void swapBuffers() { + //printf("swap buffers\n"); + } + std::string const& getSystemRegion() { + printf("get system region: %s\n", region.c_str()); + return region; + } + + bool getGraphicsTearingSupport() { + return false; + } + + void pickImage(ImagePickingCallback& callback); + void pickFile(FilePickerSettings& callback); + bool supportsFilePicking() { + return true; + } + std::string& getExternalStoragePath() { + printf("external storage path = %s\n", externalStorage.c_str()); + return externalStorage; + } + std::string& getInternalStoragePath() { + printf("internal storage path = %s\n", internalStorage.c_str()); + return internalStorage; + } + std::string& getCurrentStoragePath() { + printf("current storage path = %s\n", currentStorage.c_str()); + return currentStorage; + } + std::string& getUserdataPath() { + printf("userdata path = %s\n", userdata.c_str()); + return userdata; + } + std::string& getUserdataPathForLevels() { + printf("userdata path for levels = %s\n", userdata.c_str()); + return userdataPathForLevels; + } + std::string getAssetFileFullPath(std::string const& s) { + printf("get assert full path: %s\n", s.c_str()); + return "assets/" + s; + } + int getScreenType() { + if (enablePocketGuis) + return 1; + return 0; // Win 10 Ed. GUIs + } + bool useCenteredGUI() { + return (enablePocketGuis ? false : true); + } + std::string getApplicationId() { + printf("application id = com.mojang.minecraftpe\n"); + return "com.mojang.minecraftpe"; + } + std::string getDeviceId() { + printf("device id = linux\n"); + return "linux"; + } + std::string createUUID(); + bool isFirstSnoopLaunch() { + printf("is first snoop launch = true\n"); + return true; + } + bool hasHardwareInformationChanged() { + printf("has hardware information change = false\n"); + return false; + } + bool isTablet() { + printf("is tablet = true\n"); + return true; + } + void setFullscreenMode(int mode); + std::string getEdition() { + if (enablePocketGuis) + return "pocket"; + return "win10"; + } + int getPlatformUIScalingRules() { + return 2; + } + long long getAvailableMemory(); + + long long calculateAvailableDiskFreeSpace() { + return 100000000L; + } + + std::string &getPlatformTempPath() { + return tmpPath; + } + + std::string createDeviceID() { + return "linux"; + } + +}; diff --git a/src/LinuxStore.h b/src/LinuxStore.h new file mode 100644 index 0000000..96b5a48 --- /dev/null +++ b/src/LinuxStore.h @@ -0,0 +1,94 @@ +#pragma once + +#include + +struct StoreListener; +struct PurchaseInfo; +struct LinuxStore { + virtual ~LinuxStore() { + std::cout << "destroying store\n"; + } + virtual bool requiresRestorePurchasesButton() { + std::cout << "requires restore purchases button: false\n"; + return false; + } + virtual bool allowsSubscriptions() { + std::cout << "allows subscriptions: false\n"; + return false; + } + virtual std::string getStoreId() { + std::cout << "get store id: LinuxStore\n"; + return "LinuxStore"; + } + virtual std::string getSubPlatformStoreId() { + std::cout << "get sub platform store id: LinuxStore\n"; + return "LinuxStore"; + } + virtual std::string getProductSkuPrefix() { + std::cout << "get product sku prefix: linux"; + return "linux"; + } + virtual std::string getRealmsSkuPrefix() { + std::cout << "get product sku prefix: realms"; + return "realms"; + } + virtual void queryProducts(std::vector const& arr) { + std::cout << "query products\n"; + } + virtual void purchase(std::string const& name) { + std::cout << "purchase: " << name << "\n"; + } + virtual void acknowledgePurchase(PurchaseInfo const& info, int type) { + std::cout << "acknowledge purchase: type=" << type << "\n"; + } + virtual void queryPurchases() { + std::cout << "query purchases\n"; + } + virtual void restorePurchases() { + std::cout << "restore purchases\n"; + } + virtual bool isTrial() { + //std::cout << "is trial: false\n"; + return false; + } + virtual void purchaseGame() { + std::cout << "purchase game\n"; + } + virtual bool isGameLicensed() { + std::cout << "is game purchased: true\n"; + return true; + } + virtual void getAppReceipt() { + std::cout << "get app receipt\n"; + } + virtual void registerLicenseChangeCallback() { + std::cout << "register license change callback\n"; + } + virtual void handleLicenseChange() { + std::cout << "handle license changed\n"; + } + virtual void restoreFromCache() { + std::cout << "restore from cache\n"; + } + virtual void getUserAccessTokenAsync() { + std::cout << "get user access token async\n"; + } + virtual void getFullSKUWithMetadataFromProductSku() { + std::cout << "get full sku with metadata from product sku\n"; + } + virtual std::string getFullGameProductSku() { + std::cout << "get full game product sku\n"; + return "idk"; + } + virtual std::string getLanguageCode() { + std::cout << "get language code\n"; + return "idk"; + } + virtual std::string getRegionCode() { + std::cout << "get region code\n"; + return "idk"; + } + virtual void refreshLicenses() { + std::cout << "refresh licenses\n"; + } +}; \ No newline at end of file diff --git a/src/amdfix.s b/src/amdfix.s new file mode 100644 index 0000000..16b4370 --- /dev/null +++ b/src/amdfix.s @@ -0,0 +1,23 @@ +.section .text +.global pshufb_xmm4_xmm0 +.type pshufb_xmm4_xmm0, @function + +pshufb_xmm4_xmm0: + push %eax + push %ebx + subl $32, %esp + movl %esp, %eax + movdqu %xmm4, (%esp) + movdqu %xmm0, 16(%esp) + movl %esp, %ebx + addl $16, %ebx + push %ebx + push %eax + call pshufb + pop %eax + pop %ebx + movdqu (%esp), %xmm4 + addl $32, %esp + pop %eax + pop %ebx +ret diff --git a/src/android_symbols.h b/src/android_symbols.h new file mode 100644 index 0000000..6aa34ee --- /dev/null +++ b/src/android_symbols.h @@ -0,0 +1,46 @@ +#pragma once + +const char* android_symbols[] = { + "ANativeWindow_setBuffersGeometry", + "AAssetManager_open", + "AAsset_getLength", + "AAsset_getBuffer", + "AAsset_close", + "AAsset_read", + "AAsset_seek64", + "AAsset_getLength64", + "AAsset_getRemainingLength64", + "ALooper_pollAll", + "ANativeActivity_finish", + "AInputQueue_getEvent", + "AKeyEvent_getKeyCode", + "AInputQueue_preDispatchEvent", + "AInputQueue_finishEvent", + "AKeyEvent_getAction", + "AMotionEvent_getAxisValue", + "AKeyEvent_getRepeatCount", + "AKeyEvent_getMetaState", + "AInputEvent_getDeviceId", + "AInputEvent_getType", + "AInputEvent_getSource", + "AMotionEvent_getAction", + "AMotionEvent_getPointerId", + "AMotionEvent_getX", + "AMotionEvent_getRawX", + "AMotionEvent_getY", + "AMotionEvent_getRawY", + "AMotionEvent_getPointerCount", + "AConfiguration_new", + "AConfiguration_fromAssetManager", + "AConfiguration_getLanguage", + "AConfiguration_getCountry", + "ALooper_prepare", + "ALooper_addFd", + "AInputQueue_detachLooper", + "AConfiguration_delete", + "AInputQueue_attachLooper", + "AAssetManager_openDir", + "AAssetDir_getNextFileName", + "AAssetDir_close", + nullptr +}; \ No newline at end of file diff --git a/src/common.cpp b/src/common.cpp new file mode 100644 index 0000000..970c734 --- /dev/null +++ b/src/common.cpp @@ -0,0 +1,160 @@ +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "../hybris/include/hybris/hook.h" +#include "../hybris/include/hybris/dlfcn.h" +} + +std::string getCWD() { + char _cwd[MAXPATHLEN]; + getcwd(_cwd, MAXPATHLEN); + return std::string(_cwd) + "/"; +} + +bool loadLibrary(std::string path) { + void* handle = hybris_dlopen((getCWD() + "libs/" + path).c_str(), RTLD_LAZY); + if (handle == nullptr) { + printf("failed to load library %s: %s\n", path.c_str(), hybris_dlerror()); + return false; + } + return true; +} + +void* loadLibraryOS(std::string path, const char** symbols) { + void* handle = dlopen(path.c_str(), RTLD_LAZY); + if (handle == nullptr) { + printf("failed to load library %s: %s\n", path.c_str(), dlerror()); + return nullptr; + } + printf("oslib: %s: %i\n", path.c_str(), (int) handle); + int i = 0; + while (true) { + const char* sym = symbols[i]; + if (sym == nullptr) + break; + void* ptr = dlsym(handle, sym); + hybris_hook(sym, ptr); + i++; + } + return handle; +} + +void* loadMod(std::string path) { + void* handle = hybris_dlopen((getCWD() + "mods/" + path).c_str(), RTLD_LAZY); + if (handle == nullptr) { + printf("failed to load mod: %s\n", path.c_str()); + return nullptr; + } + + void (*initFunc)(); + initFunc = (void (*)()) hybris_dlsym(handle, "mod_init"); + if (((void*) initFunc) == nullptr) { + printf("warn: mod %s doesn't have a init function\n", path.c_str()); + return handle; + } + initFunc(); + + return handle; +} + +void stubSymbols(const char** symbols, void* stubfunc) { + int i = 0; + while (true) { + const char* sym = symbols[i]; + if (sym == nullptr) + break; + hybris_hook(sym, stubfunc); + i++; + } +} + +void __android_log_vprint(int prio, const char *tag, const char *fmt, va_list args) { + printf("[%s] ", tag); + vprintf(fmt, args); + printf("\n"); +} +void __android_log_print(int prio, const char *tag, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + __android_log_vprint(prio, tag, fmt, args); + va_end(args); +} +void __android_log_write(int prio, const char *tag, const char *text) { + printf("[%s] %s\n", tag, text); +} + +void hookAndroidLog() { + hybris_hook("__android_log_print", (void*) __android_log_print); + hybris_hook("__android_log_vprint", (void*) __android_log_vprint); + hybris_hook("__android_log_write", (void*) __android_log_write); +} + +void patchCallInstruction(void* patchOff, void* func, bool jump) { + unsigned char* data = (unsigned char*) patchOff; + printf("original: %i %i %i %i %i\n", data[0], data[1], data[2], data[3], data[4]); + data[0] = (unsigned char) (jump ? 0xe9 : 0xe8); + int ptr = ((int) func) - (int) patchOff - 5; + memcpy(&data[1], &ptr, sizeof(int)); + printf("post patch: %i %i %i %i %i\n", data[0], data[1], data[2], data[3], data[4]); +} + +bool hasCrashed = false; +void handleSignal(int signal, void* aptr) { + printf("Signal %i received\n", signal); + if (hasCrashed) + return; + hasCrashed = true; + void** ptr = &aptr; + void *array[25]; + int count = backtrace(array, 25); + char **symbols = backtrace_symbols(array, count); + char *nameBuf = (char*) malloc(256); + size_t nameBufLen = 256; + printf("Backtrace elements: %i\n", count); + for (int i = 0; i < count; i++) { + if (symbols[i] == nullptr) { + printf("#%i unk [0x%04x]\n", i, (int)array[i]); + continue; + } + if (symbols[i][0] == '[') { // unknown symbol + Dl_info symInfo; + if (hybris_dladdr(array[i], &symInfo)) { + int status = 0; + nameBuf = abi::__cxa_demangle(symInfo.dli_sname, nameBuf, &nameBufLen, &status); + printf("#%i HYBRIS %s+%i in %s+0x%04x [0x%04x]\n", i, nameBuf, (unsigned int) array[i] - (unsigned int) symInfo.dli_saddr, symInfo.dli_fname, (unsigned int) array[i] - (unsigned int) symInfo.dli_fbase, (int)array[i]); + continue; + } + } + printf("#%i %s\n", i, symbols[i]); + } + printf("Dumping stack...\n"); + for (int i = 0; i < 1000; i++) { + void* pptr = *ptr; + Dl_info symInfo; + if (hybris_dladdr(pptr, &symInfo) && symInfo.dli_sname != nullptr && strlen(symInfo.dli_sname) > 0) { + int status = 0; + nameBuf = abi::__cxa_demangle(symInfo.dli_sname, nameBuf, &nameBufLen, &status); + printf("#%i HYBRIS %s+%i in %s+0x%04x [0x%04x]\n", i, nameBuf, (unsigned int) pptr - (unsigned int) symInfo.dli_saddr, symInfo.dli_fname, (unsigned int) pptr - (unsigned int) symInfo.dli_fbase, (int)pptr); + } + ptr++; + } + abort(); +} + +void registerCrashHandler() { + struct sigaction act; + act.sa_handler = (void (*)(int)) handleSignal; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGSEGV, &act, 0); + sigaction(SIGABRT, &act, 0); +} \ No newline at end of file diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..6e22845 --- /dev/null +++ b/src/common.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +std::string getCWD(); +bool loadLibrary(std::string path); +void* loadLibraryOS(std::string path, const char** symbols); +void* loadMod(std::string path); +void stubSymbols(const char** symbols, void* stubfunc); +void hookAndroidLog(); +void patchCallInstruction(void* patchOff, void* func, bool jump); +void registerCrashHandler(); \ No newline at end of file diff --git a/src/egl_symbols.h b/src/egl_symbols.h new file mode 100644 index 0000000..8cbdb99 --- /dev/null +++ b/src/egl_symbols.h @@ -0,0 +1,20 @@ +#pragma once + +const char* egl_symbols[] = { + "eglChooseConfig", + "eglGetError", + "eglCreateWindowSurface", + "eglGetConfigAttrib", + "eglCreateContext", + "eglDestroySurface", + "eglSwapBuffers", + "eglMakeCurrent", + "eglDestroyContext", + "eglTerminate", + "eglGetDisplay", + "eglInitialize", + "eglQuerySurface", + "eglSwapInterval", + "eglQueryString", + nullptr +}; \ No newline at end of file diff --git a/src/fmod_symbols.h b/src/fmod_symbols.h new file mode 100644 index 0000000..9b1ff9d --- /dev/null +++ b/src/fmod_symbols.h @@ -0,0 +1,42 @@ +#pragma once + +const char* fmod_symbols[] = { + "_ZN4FMOD6System12mixerSuspendEv", + "_ZN4FMOD6System11mixerResumeEv", + "_ZN4FMOD14ChannelControl7setMuteEb", + "_ZN4FMOD14ChannelControl9setVolumeEf", + "_ZN4FMOD14ChannelControl9isPlayingEPb", + "_ZN4FMOD14ChannelControl4stopEv", + "_ZN4FMOD6System9playSoundEPNS_5SoundEPNS_12ChannelGroupEbPPNS_7ChannelE", + "_ZN4FMOD5Sound15getNumSubSoundsEPi", + "_ZN4FMOD5Sound11getSubSoundEiPPS0_", + "_ZN4FMOD14ChannelControl15set3DAttributesEPK11FMOD_VECTORS3_S3_", + "_ZN4FMOD14ChannelControl8setPitchEf", + "_ZN4FMOD14ChannelControl9setPausedEb", + "_ZN4FMOD5Sound7releaseEv", + "_ZN4FMOD6System5closeEv", + "_ZN4FMOD6System7releaseEv", + "FMOD_System_Create", + "_ZN4FMOD6System10getVersionEPj", + "_ZN4FMOD6System9setOutputE15FMOD_OUTPUTTYPE", + "_ZN4FMOD6System4initEijPv", + "_ZN4FMOD6System13set3DSettingsEfff", + "_ZN4FMOD6System18createChannelGroupEPKcPPNS_12ChannelGroupE", + "_ZN4FMOD6System21getMasterChannelGroupEPPNS_12ChannelGroupE", + "_ZN4FMOD12ChannelGroup8addGroupEPS0_bPPNS_13DSPConnectionE", + "_ZN4FMOD6System23set3DListenerAttributesEiPK11FMOD_VECTORS3_S3_S3_", + "_ZN4FMOD6System6updateEv", + "_ZN4FMOD6System12createStreamEPKcjP22FMOD_CREATESOUNDEXINFOPPNS_5SoundE", + "_ZN4FMOD6System11createSoundEPKcjP22FMOD_CREATESOUNDEXINFOPPNS_5SoundE", + "_ZN4FMOD5Sound19set3DMinMaxDistanceEff", + "_ZN4FMOD6System13getNumDriversEPi", + "_ZN4FMOD6System13getDriverInfoEiPciP9FMOD_GUIDPiP16FMOD_SPEAKERMODES4_", + "_ZN4FMOD6System9setDriverEi", + "_ZN4FMOD5Sound7setModeEj", + "_ZN4FMOD5Sound9getFormatEP15FMOD_SOUND_TYPEP17FMOD_SOUND_FORMATPiS5_", + "_ZN4FMOD6System17getSoftwareFormatEPiP16FMOD_SPEAKERMODES1_", + "_ZN4FMOD14ChannelControl11getDSPClockEPyS1_", + "_ZN4FMOD14ChannelControl12addFadePointEyf", + "_ZN4FMOD14ChannelControl8setDelayEyyb", + nullptr +}; \ No newline at end of file diff --git a/src/gles_symbols.h b/src/gles_symbols.h new file mode 100644 index 0000000..d940cda --- /dev/null +++ b/src/gles_symbols.h @@ -0,0 +1,540 @@ +#pragma once + +const char* gles_symbols[] = { + "glActiveTexture", + "glAttachShader", + "glBindAttribLocation", + "glBindBuffer", + "glBindFramebuffer", + "glBindRenderbuffer", + "glBindTexture", + "glBlendColor", + "glBlendEquation", + "glBlendEquationSeparate", + "glBlendFunc", + "glBlendFuncSeparate", + "glBufferData", + "glBufferSubData", + "glCheckFramebufferStatus", + "glClear", + "glClearColor", + "glClearDepthf", + "glClearStencil", + "glColorMask", + "glCompileShader", + "glCompressedTexImage2D", + "glCompressedTexSubImage2D", + "glCopyTexImage2D", + "glCopyTexSubImage2D", + "glCreateProgram", + "glCreateShader", + "glCullFace", + "glDeleteBuffers", + "glDeleteFramebuffers", + "glDeleteProgram", + "glDeleteRenderbuffers", + "glDeleteShader", + "glDeleteTextures", + "glDepthFunc", + "glDepthMask", + "glDepthRangef", + "glDetachShader", + "glDisable", + "glDisableVertexAttribArray", + "glDrawArrays", + "glDrawElements", + "glEnable", + "glEnableVertexAttribArray", + "glFinish", + "glFlush", + "glFramebufferRenderbuffer", + "glFramebufferTexture2D", + "glFrontFace", + "glGenBuffers", + "glGenerateMipmap", + "glGenFramebuffers", + "glGenRenderbuffers", + "glGenTextures", + "glGetActiveAttrib", + "glGetActiveUniform", + "glGetAttachedShaders", + "glGetAttribLocation", + "glGetBooleanv", + "glGetBufferParameteriv", + "glGetError", + "glGetFloatv", + "glGetFramebufferAttachmen", + "glGetIntegerv", + "glGetProgramiv", + "glGetProgramInfoLog", + "glGetRenderbufferParamete", + "glGetShaderiv", + "glGetShaderInfoLog", + "glGetShaderPrecisionFormat", + "glGetShaderSource", + "glGetTexParameterfv", + "glGetTexParameteriv", + "glGetUniformfv", + "glGetUniformiv", + "glGetUniformLocation", + "glGetVertexAttribfv", + "glGetVertexAttribiv", + "glGetVertexAttribPointerv", + "glHint", + "glIsBuffer", + "glIsEnabled", + "glIsFramebuffer", + "glIsProgram", + "glIsRenderbuffer", + "glIsShader", + "glIsTexture", + "glLineWidth", + "glLinkProgram", + "glPixelStorei", + "glPolygonOffset", + "glReadPixels", + "glReleaseShaderCompiler", + "glRenderbufferStorage", + "glSampleCoverage", + "glScissor", + "glShaderBinary", + "glShaderSource", + "glStencilFunc", + "glStencilFuncSeparate", + "glStencilMask", + "glStencilMaskSeparate", + "glStencilOp", + "glStencilOpSeparate", + "glTexImage2D", + "glTexParameterf", + "glTexParameterfv", + "glTexParameteri", + "glTexParameteriv", + "glTexSubImage2D", + "glUniform1f", + "glUniform1fv", + "glUniform1i", + "glUniform1iv", + "glUniform2f", + "glUniform2fv", + "glUniform2i", + "glUniform2iv", + "glUniform3f", + "glUniform3fv", + "glUniform3i", + "glUniform3iv", + "glUniform4f", + "glUniform4fv", + "glUniform4i", + "glUniform4iv", + "glUniformMatrix2fv", + "glUniformMatrix3fv", + "glUniformMatrix4fv", + "glUseProgram", + "glValidateProgram", + "glVertexAttrib1f", + "glVertexAttrib1fv", + "glVertexAttrib2f", + "glVertexAttrib2fv", + "glVertexAttrib3f", + "glVertexAttrib3fv", + "glVertexAttrib4f", + "glVertexAttrib4fv", + "glVertexAttribPointer", + "glViewport", + "glReadBuffer", + "glDrawRangeElements", + "glTexImage3D", + "glTexSubImage3D", + "glCopyTexSubImage3D", + "glCompressedTexImage3D", + "glCompressedTexSubImage3D", + "glGenQueries", + "glDeleteQueries", + "glIsQuery", + "glBeginQuery", + "glEndQuery", + "glGetQueryiv", + "glGetQueryObjectuiv", + "glUnmapBuffer", + "glGetBufferPointerv", + "glDrawBuffers", + "glUniformMatrix2x3fv", + "glUniformMatrix3x2fv", + "glUniformMatrix2x4fv", + "glUniformMatrix4x2fv", + "glUniformMatrix3x4fv", + "glUniformMatrix4x3fv", + "glBlitFramebuffer", + "glRenderbufferStorageMult", + "glFramebufferTextureLayer", + "glMapBufferRange", + "glFlushMappedBufferRange", + "glBindVertexArray", + "glDeleteVertexArrays", + "glGenVertexArrays", + "glIsVertexArray", + "glGetIntegeri_v", + "glBeginTransformFeedback", + "glEndTransformFeedback", + "glBindBufferRange", + "glBindBufferBase", + "glTransformFeedbackVaryin", + "glGetTransformFeedbackVar", + "glVertexAttribIPointer", + "glGetVertexAttribIiv", + "glGetVertexAttribIuiv", + "glVertexAttribI4i", + "glVertexAttribI4ui", + "glVertexAttribI4iv", + "glVertexAttribI4uiv", + "glGetUniformuiv", + "glGetFragDataLocation", + "glUniform1ui", + "glUniform2ui", + "glUniform3ui", + "glUniform4ui", + "glUniform1uiv", + "glUniform2uiv", + "glUniform3uiv", + "glUniform4uiv", + "glClearBufferiv", + "glClearBufferuiv", + "glClearBufferfv", + "glClearBufferfi", + "glGetStringi", + "glCopyBufferSubData", + "glGetUniformIndices", + "glGetActiveUniformsiv", + "glGetUniformBlockIndex", + "glGetActiveUniformBlockiv", + "glGetActiveUniformBlockNa", + "glUniformBlockBinding", + "glDrawArraysInstanced", + "glDrawElementsInstanced", + "glFenceSync", + "glIsSync", + "glDeleteSync", + "glClientWaitSync", + "glWaitSync", + "glGetInteger64v", + "glGetSynciv", + "glGetInteger64i_v", + "glGetBufferParameteri64v", + "glGenSamplers", + "glDeleteSamplers", + "glIsSampler", + "glBindSampler", + "glSamplerParameteri", + "glSamplerParameteriv", + "glSamplerParameterf", + "glSamplerParameterfv", + "glGetSamplerParameteriv", + "glGetSamplerParameterfv", + "glVertexAttribDivisor", + "glBindTransformFeedback", + "glDeleteTransformFeedback", + "glGenTransformFeedbacks", + "glIsTransformFeedback", + "glPauseTransformFeedback", + "glResumeTransformFeedback", + "glGetProgramBinary", + "glProgramBinary", + "glProgramParameteri", + "glInvalidateFramebuffer", + "glInvalidateSubFramebuffe", + "glTexStorage2D", + "glTexStorage3D", + "glGetInternalformativ", + "glDispatchCompute", + "glDispatchComputeIndirect", + "glDrawArraysIndirect", + "glDrawElementsIndirect", + "glFramebufferParameteri", + "glGetFramebufferParameter", + "glGetProgramInterfaceiv", + "glGetProgramResourceIndex", + "glGetProgramResourceName", + "glGetProgramResourceiv", + "glGetProgramResourceLocat", + "glUseProgramStages", + "glActiveShaderProgram", + "glCreateShaderProgramv", + "glBindProgramPipeline", + "glDeleteProgramPipelines", + "glGenProgramPipelines", + "glIsProgramPipeline", + "glGetProgramPipelineiv", + "glProgramUniform1i", + "glProgramUniform2i", + "glProgramUniform3i", + "glProgramUniform4i", + "glProgramUniform1ui", + "glProgramUniform2ui", + "glProgramUniform3ui", + "glProgramUniform4ui", + "glProgramUniform1f", + "glProgramUniform2f", + "glProgramUniform3f", + "glProgramUniform4f", + "glProgramUniform1iv", + "glProgramUniform2iv", + "glProgramUniform3iv", + "glProgramUniform4iv", + "glProgramUniform1uiv", + "glProgramUniform2uiv", + "glProgramUniform3uiv", + "glProgramUniform4uiv", + "glProgramUniform1fv", + "glProgramUniform2fv", + "glProgramUniform3fv", + "glProgramUniform4fv", + "glProgramUniformMatrix2fv", + "glProgramUniformMatrix3fv", + "glProgramUniformMatrix4fv", + "glProgramUniformMatrix2x3", + "glProgramUniformMatrix3x2", + "glProgramUniformMatrix2x4", + "glProgramUniformMatrix4x2", + "glProgramUniformMatrix3x4", + "glProgramUniformMatrix4x3", + "glValidateProgramPipeline", + "glGetProgramPipelineInfoL", + "glBindImageTexture", + "glGetBooleani_v", + "glMemoryBarrier", + "glMemoryBarrierByRegion", + "glTexStorage2DMultisample", + "glGetMultisamplefv", + "glSampleMaski", + "glGetTexLevelParameteriv", + "glGetTexLevelParameterfv", + "glBindVertexBuffer", + "glVertexAttribFormat", + "glVertexAttribIFormat", + "glVertexAttribBinding", + "glVertexBindingDivisor", + "glBlendBarrierKHR", + "glDebugMessageControlKHR", + "glDebugMessageInsertKHR", + "glDebugMessageCallbackKHR", + "glGetDebugMessageLogKHR", + "glPushDebugGroupKHR", + "glPopDebugGroupKHR", + "glObjectLabelKHR", + "glGetObjectLabelKHR", + "glObjectPtrLabelKHR", + "glGetObjectPtrLabelKHR", + "glGetPointervKHR", + "glEGLImageTargetTexture2D", + "glEGLImageTargetRenderbuf", + "glGetProgramBinaryOES", + "glProgramBinaryOES", + "glMapBufferOES", + "glUnmapBufferOES", + "glGetBufferPointervOES", + "glMinSampleShadingOES", + "glTexImage3DOES", + "glTexSubImage3DOES", + "glCopyTexSubImage3DOES", + "glCompressedTexImage3DOES", + "glCompressedTexSubImage3D", + "glFramebufferTexture3DOES", + "glTexStorage3DMultisample", + "glBindVertexArrayOES", + "glDeleteVertexArraysOES", + "glGenVertexArraysOES", + "glIsVertexArrayOES", + "glGetPerfMonitorGroupsAMD", + "glGetPerfMonitorCountersA", + "glGetPerfMonitorGroupStri", + "glGetPerfMonitorCounterSt", + "glGetPerfMonitorCounterIn", + "glGenPerfMonitorsAMD", + "glDeletePerfMonitorsAMD", + "glSelectPerfMonitorCounte", + "glBeginPerfMonitorAMD", + "glEndPerfMonitorAMD", + "glGetPerfMonitorCounterDa", + "glBlitFramebufferANGLE", + "glRenderbufferStorageMult", + "glDrawArraysInstancedANGL", + "glDrawElementsInstancedAN", + "glVertexAttribDivisorANGL", + "glGetTranslatedShaderSour", + "glCopyTextureLevelsAPPLE", + "glRenderbufferStorageMult", + "glResolveMultisampleFrame", + "glFenceSyncAPPLE", + "glIsSyncAPPLE", + "glDeleteSyncAPPLE", + "glClientWaitSyncAPPLE", + "glWaitSyncAPPLE", + "glGetInteger64vAPPLE", + "glGetSyncivAPPLE", + "glCopyImageSubDataEXT", + "glLabelObjectEXT", + "glGetObjectLabelEXT", + "glInsertEventMarkerEXT", + "glPushGroupMarkerEXT", + "glPopGroupMarkerEXT", + "glDiscardFramebufferEXT", + "glGenQueriesEXT", + "glDeleteQueriesEXT", + "glIsQueryEXT", + "glBeginQueryEXT", + "glEndQueryEXT", + "glQueryCounterEXT", + "glGetQueryivEXT", + "glGetQueryObjectivEXT", + "glGetQueryObjectuivEXT", + "glGetQueryObjecti64vEXT", + "glGetQueryObjectui64vEXT", + "glDrawBuffersEXT", + "glEnableiEXT", + "glDisableiEXT", + "glBlendEquationiEXT", + "glBlendEquationSeparateiE", + "glBlendFunciEXT", + "glBlendFuncSeparateiEXT", + "glColorMaskiEXT", + "glIsEnablediEXT", + "glDrawArraysInstancedEXT", + "glDrawElementsInstancedEX", + "glFramebufferTextureEXT", + "glVertexAttribDivisorEXT", + "glMapBufferRangeEXT", + "glFlushMappedBufferRangeE", + "glMultiDrawArraysEXT", + "glMultiDrawElementsEXT", + "glRenderbufferStorageMult", + "glFramebufferTexture2DMul", + "glReadBufferIndexedEXT", + "glDrawBuffersIndexedEXT", + "glGetIntegeri_vEXT", + "glPrimitiveBoundingBoxEXT", + "glGetGraphicsResetStatusE", + "glReadnPixelsEXT", + "glGetnUniformfvEXT", + "glGetnUniformivEXT", + "glActiveShaderProgramEXT", + "glBindProgramPipelineEXT", + "glCreateShaderProgramvEXT", + "glDeleteProgramPipelinesE", + "glGenProgramPipelinesEXT", + "glGetProgramPipelineInfoL", + "glGetProgramPipelineivEXT", + "glIsProgramPipelineEXT", + "glProgramParameteriEXT", + "glProgramUniform1fEXT", + "glProgramUniform1fvEXT", + "glProgramUniform1iEXT", + "glProgramUniform1ivEXT", + "glProgramUniform2fEXT", + "glProgramUniform2fvEXT", + "glProgramUniform2iEXT", + "glProgramUniform2ivEXT", + "glProgramUniform3fEXT", + "glProgramUniform3fvEXT", + "glProgramUniform3iEXT", + "glProgramUniform3ivEXT", + "glProgramUniform4fEXT", + "glProgramUniform4fvEXT", + "glProgramUniform4iEXT", + "glProgramUniform4ivEXT", + "glProgramUniformMatrix2fv", + "glProgramUniformMatrix3fv", + "glProgramUniformMatrix4fv", + "glUseProgramStagesEXT", + "glValidateProgramPipeline", + "glProgramUniform1uiEXT", + "glProgramUniform2uiEXT", + "glProgramUniform3uiEXT", + "glProgramUniform4uiEXT", + "glProgramUniform1uivEXT", + "glProgramUniform2uivEXT", + "glProgramUniform3uivEXT", + "glProgramUniform4uivEXT", + "glProgramUniformMatrix2x3", + "glProgramUniformMatrix3x2", + "glProgramUniformMatrix2x4", + "glProgramUniformMatrix4x2", + "glProgramUniformMatrix3x4", + "glProgramUniformMatrix4x3", + "glPatchParameteriEXT", + "glTexParameterIivEXT", + "glTexParameterIuivEXT", + "glGetTexParameterIivEXT", + "glGetTexParameterIuivEXT", + "glSamplerParameterIivEXT", + "glSamplerParameterIuivEXT", + "glGetSamplerParameterIivE", + "glGetSamplerParameterIuiv", + "glTexBufferEXT", + "glTexBufferRangeEXT", + "glTexStorage1DEXT", + "glTexStorage2DEXT", + "glTexStorage3DEXT", + "glTextureStorage1DEXT", + "glTextureStorage2DEXT", + "glTextureStorage3DEXT", + "glTextureViewEXT", + "glRenderbufferStorageMult", + "glFramebufferTexture2DMul", + "glBeginPerfQueryINTEL", + "glCreatePerfQueryINTEL", + "glDeletePerfQueryINTEL", + "glEndPerfQueryINTEL", + "glGetFirstPerfQueryIdINTE", + "glGetNextPerfQueryIdINTEL", + "glGetPerfCounterInfoINTEL", + "glGetPerfQueryDataINTEL", + "glGetPerfQueryIdByNameINT", + "glGetPerfQueryInfoINTEL", + "glBlendParameteriNV", + "glBlendBarrierNV", + "glCopyBufferSubDataNV", + "glCoverageMaskNV", + "glCoverageOperationNV", + "glDrawBuffersNV", + "glDrawArraysInstancedNV", + "glDrawElementsInstancedNV", + "glDeleteFencesNV", + "glGenFencesNV", + "glIsFenceNV", + "glTestFenceNV", + "glGetFenceivNV", + "glFinishFenceNV", + "glSetFenceNV", + "glBlitFramebufferNV", + "glRenderbufferStorageMult", + "glVertexAttribDivisorNV", + "glUniformMatrix2x3fvNV", + "glUniformMatrix3x2fvNV", + "glUniformMatrix2x4fvNV", + "glUniformMatrix4x2fvNV", + "glUniformMatrix3x4fvNV", + "glUniformMatrix4x3fvNV", + "glReadBufferNV", + "glAlphaFuncQCOM", + "glGetDriverControlsQCOM", + "glGetDriverControlStringQ", + "glEnableDriverControlQCOM", + "glDisableDriverControlQCO", + "glExtGetTexturesQCOM", + "glExtGetBuffersQCOM", + "glExtGetRenderbuffersQCOM", + "glExtGetFramebuffersQCOM", + "glExtGetTexLevelParameter", + "glExtTexObjectStateOverri", + "glExtGetTexSubImageQCOM", + "glExtGetBufferPointervQCO", + "glExtGetShadersQCOM", + "glExtGetProgramsQCOM", + "glExtIsProgramBinaryQCOM", + "glExtGetProgramBinarySour", + "glStartTilingQCOM", + "glEndTilingQCOM", + "glGetString", + nullptr +}; \ No newline at end of file diff --git a/src/hook.cpp b/src/hook.cpp new file mode 100644 index 0000000..ef7f6f0 --- /dev/null +++ b/src/hook.cpp @@ -0,0 +1,117 @@ +#include "hook.h" + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "../hybris/include/hybris/dlfcn.h" +#include "../hybris/src/jb/linker.h" +} + +struct soinfo_hookinfo { + struct hook_section { + Elf32_Off addr, size; + }; + std::vector hookSections; +}; +std::map hookLibraries; + +void addHookLibrary(void* ptr, std::string const& fileName) { + soinfo* lib = (soinfo*) ptr; + + if (hookLibraries.count(lib) > 0) + return; + + Elf32_Ehdr header; + FILE* file = fopen(fileName.c_str(), "r"); + if (file == nullptr) { + printf("addHookLibrary: failed to open file\n"); + return; + } + if (fread(&header, sizeof(Elf32_Ehdr), 1, file) != 1) { + printf("addHookLibrary: failed to read header\n"); + fclose(file); + return; + } + + fseek(file, (long) header.e_shoff, SEEK_SET); + + char shdr[header.e_shentsize * header.e_shnum]; + if (fread(&shdr, header.e_shentsize, header.e_shnum, file) != header.e_shnum) { + printf("addHookLibrary: failed to read shdr\n"); + fclose(file); + return; + } + + // find strtab + char* strtab = nullptr; + for (int i = 0; i < header.e_shnum; i++) { + Elf32_Shdr& entry = *((Elf32_Shdr*) &shdr[header.e_shentsize * i]); + if (entry.sh_type == SHT_STRTAB) { + strtab = new char[entry.sh_size]; + fseek(file, (long) entry.sh_offset, SEEK_SET); + if (fread(strtab, 1, entry.sh_size, file) != entry.sh_size) { + printf("addHookLibrary: failed to read strtab\n"); + fclose(file); + delete[] strtab; + return; + } + } + } + if (strtab == nullptr) { + printf("addHookLibrary: couldn't find strtab\n"); + fclose(file); + return; + } + soinfo_hookinfo hi; + for (int i = 0; i < header.e_shnum; i++) { + Elf32_Shdr& entry = *((Elf32_Shdr*) &shdr[header.e_shentsize * i]); + char* entryName = &strtab[entry.sh_name]; + if (strcmp(entryName, ".got") == 0 || strcmp(entryName, ".got.plt") == 0 || + strcmp(entryName, ".data.rel.ro") == 0) { + hi.hookSections.push_back({entry.sh_addr, entry.sh_size}); + } + } + hookLibraries[lib] = hi; + fclose(file); + delete[] strtab; +} + +inline bool patchSection(Elf32_Addr base, Elf32_Word off, Elf32_Word size, void* sym, void* override) { + bool foundEntry = false; + unsigned long addr = base + off + 4; + while (addr < base + off + size) { + if (*((void**) addr) == sym) { + *((void**) addr) = override; + foundEntry = true; + } + addr += sizeof(void*); + } + return foundEntry; +} +bool patchLibrary(void* lib, void* sym, void* override) { + soinfo* si = (soinfo*) lib; + if (si == nullptr || hookLibraries.count(si) <= 0) + return false; + soinfo_hookinfo& hi = hookLibraries.at(si); + bool foundEntry = false; + for (auto& se : hi.hookSections) { + if (patchSection(si->base, se.addr, se.size, sym, override)) + foundEntry = true; + } + return foundEntry; +} + +void hookFunction(void* symbol, void* hook, void** original) { + *original = symbol; + bool foundEntry = false; + for (auto& handle : hookLibraries) + if (patchLibrary(handle.first, symbol, hook)) + foundEntry = true; + if (!foundEntry) + printf("Failed to hook a symbol (%llu)\n", (long long int) symbol); +} \ No newline at end of file diff --git a/src/hook.h b/src/hook.h new file mode 100644 index 0000000..9399259 --- /dev/null +++ b/src/hook.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void addHookLibrary(void* ptr, const std::string& path); +void hookFunction(void* symbol, void* hook, void** original); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..526c8e1 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,461 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gles_symbols.h" +#include "android_symbols.h" +#include "egl_symbols.h" +#include "fmod_symbols.h" +#include "../mcpe/gl.h" +#include "../mcpe/AppPlatform.h" +#include "../mcpe/MinecraftGame.h" +#include "LinuxAppPlatform.h" +#include "LinuxStore.h" +#include "../mcpe/Mouse.h" +#include "../mcpe/Keyboard.h" +#include "../mcpe/Options.h" +#include "common.h" +#include "hook.h" + +extern "C" { + +#include +#include "../hybris/include/hybris/dlfcn.h" +#include "../hybris/include/hybris/hook.h" +#include "../hybris/src/jb/linker.h" + +} + +void commonStub() {} + +void androidStub() { + std::cout << "warn: android call\n"; +} + +void eglStub() { + std::cout << "warn: egl call\n"; +} + +std::unique_ptr createStoreHookFunc(const std::string& idk, StoreListener& listener) { + std::cout << "creating fake store <" << idk << ">\n"; + return std::unique_ptr(new LinuxStore()); +} + +class HTTPRequest; + +class LinuxHttpRequestInternal { +public: + void* vtable; + int filler1; + HTTPRequest* request; + + void destroy() { + std::cout << "destroying http request\n"; + } +}; +void** linuxHttpRequestInternalVtable; + +void constructLinuxHttpRequestInternal(LinuxHttpRequestInternal* requestInternal, HTTPRequest* request) { + requestInternal->vtable = linuxHttpRequestInternalVtable; + requestInternal->request = request; +} + +void sendLinuxHttpRequestInternal(LinuxHttpRequestInternal* requestInternal) { + std::cout << "send http request\n"; + // TODO: Implement it +} + +void abortLinuxHttpRequestInternal(LinuxHttpRequestInternal* requestInternal) { + std::cout << "abort http request\n"; + // TODO: Implement it +} + + +static MinecraftGame* client; + +int winId = 0; +bool moveMouseToCenter = false; + +static void minecraft_idle() { + if (client->wantToQuit()) { + eglutDestroyWindow(winId); + eglutFini(); + return; + } + int cx = eglutGetWindowWidth() / 2; + int cy = eglutGetWindowHeight() / 2; + if (moveMouseToCenter) { + eglutWarpMousePointer(cx, cy); + moveMouseToCenter = false; + } + eglutPostRedisplay(); +} +static void minecraft_draw() { + client->update(); +} +float pixelSize = 2.f; +static void minecraft_reshape(int w, int h) { + client->setRenderingSize(w, h); + client->setUISizeAndScale(w, h, pixelSize); +} +static void minecraft_mouse(int x, int y) { + if (LinuxAppPlatform::mousePointerHidden) { + int cx = eglutGetWindowWidth() / 2; + int cy = eglutGetWindowHeight() / 2; + if (x != cy || y != cy) { + Mouse::feed(0, 0, x, y, x - cx, y - cy); + moveMouseToCenter = true; + } + } else { + Mouse::feed(0, 0, x, y, 0, 0); + } +} +static void minecraft_mouse_button(int x, int y, int btn, int action) { + int mcBtn = (btn == 1 ? 1 : (btn == 2 ? 3 : (btn == 3 ? 2 : (btn == 5 ? 4 : btn)))); + Mouse::feed((char) mcBtn, (char) (action == EGLUT_MOUSE_PRESS ? (btn == 5 ? -120 : (btn == 4 ? 120 : 1)) : 0), x, y, 0, 0); +} + +int getKeyMinecraft(int keyCode) { + if (keyCode == 65505) + return 16; + if (keyCode >= 97 && keyCode <= 122) + return (keyCode + 65 - 97); + if (keyCode >= 65470 && keyCode <= 65481) + return (keyCode + 112 - 65470); + + return keyCode; +} +static void minecraft_keyboard(char str[5], int action) { + if (strcmp(str, "\t") == 0) + return; + if (action == EGLUT_KEY_PRESS || action == EGLUT_KEY_REPEAT) { + if (str[0] == 13) { + str[0] = 10; + str[1] = 0; + } + std::stringstream ss; + ss << str; + Keyboard::Keyboard_feedText(ss.str(), false, 0); + } +} +static void minecraft_keyboard_special(int key, int action) { + if (key == 65480) { + if (action == EGLUT_KEY_PRESS) { + client->getOptions()->setFullscreen(!client->getOptions()->getFullscreen()); + } + return; + } + int mKey = getKeyMinecraft(key); + if (action == EGLUT_KEY_PRESS) { + Keyboard::inputs->push_back({1, mKey}); + Keyboard::states[mKey] = 1; + } else if (action == EGLUT_KEY_RELEASE) { + Keyboard::inputs->push_back({0, mKey}); + Keyboard::states[mKey] = 0; + } +} +static void minecraft_close() { + client->quit(); +} + +void detachFromJavaStub() { + std::cout << "detach from java\n"; +} +void* getJVMEnvStub() { + std::cout << "getjvmenv\n"; + return nullptr; +} +bool verifyCertChainStub() { + std::cout << "verifycertchain\n"; + return true; +} +struct xboxSingleton { + char filler[8]; +}; +xboxSingleton xboxGetAppConfigSingleton() { + std::cout << "xbox get app config singleton\n"; + return xboxSingleton(); +} +void xboxConfigSetSandboxStub() { + std::cout << "xbox config: set sandbox (stub)\n"; +} +void patchNotesModelStub() { + std::cout << "fetch patch notes\n"; +} + +extern "C" +void pshufb(char* dest, char* src) { + char new_dest[16]; + for (int i = 0; i < 16; i++) + new_dest[i] = (src[i] & 0x80) ? 0 : dest[src[i] & 15]; + memcpy(dest, new_dest, 16); +} +extern "C" +void pshufb_xmm4_xmm0(); +#include +#include +#include +#include + +using namespace std; +int main(int argc, char *argv[]) { + bool enableStackTracePrinting = true; + bool workaroundAMD = false; + + int windowWidth = 720; + int windowHeight = 480; + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) { + i++; + pixelSize = std::stof(argv[i]); + } else if (strcmp(argv[i], "-sw") == 0 || strcmp(argv[i], "--width") == 0) { + i++; + windowWidth = std::stoi(argv[i]); + } else if (strcmp(argv[i], "-sh") == 0 || strcmp(argv[i], "--height") == 0) { + i++; + windowHeight = std::stoi(argv[i]); + } else if (strcmp(argv[i], "-ns") == 0 || strcmp(argv[i], "--no-stacktrace") == 0) { + enableStackTracePrinting = false; + } else if (strcmp(argv[i], "--pocket-guis") == 0) { + enablePocketGuis = true; + } else if (strcmp(argv[i], "--server") == 0) { + serverMode = true; + } else if (strcmp(argv[i], "--amd-fix") == 0) { + std::cout << "--amd-fix: Enabling AMD Workaround.\n"; + workaroundAMD = true; + } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + std::cout << "Help\n"; + std::cout << "--help Shows this help information\n"; + std::cout << "--scale Sets the pixel scale\n"; + std::cout << "--width Sets the window width\n"; + std::cout << "--height Sets the window height\n"; + std::cout << "--pocket-guis Switches to Pocket Edition GUIs\n"; + std::cout << "--no-stacktrace Disables stack trace printing\n"; + std::cout << "--amd-workaround Fixes crashes on pre-i686 and AMD CPUs\n\n"; + std::cout << "--server Enable dedicated server mode\n\n"; + std::cout << "EGL Options\n"; + std::cout << "-display Sets the display\n"; + std::cout << "-info Shows info about the display\n\n"; + std::cout << "MCPE arguments:\n"; + std::cout << "edu \n"; + std::cout << "mcworld \n"; + return 0; + } + } + + if (enableStackTracePrinting) { + registerCrashHandler(); + } + + setenv("LC_ALL", "C", 1); // HACK: Force set locale to one recognized by MCPE so that the outdated C++ standard library MCPE uses doesn't fail to find one + + std::cout << "loading native libraries\n"; + void* glesLib = loadLibraryOS("libGLESv2.so", gles_symbols); + void* fmodLib = loadLibraryOS((getCWD() + "libs/native/libfmod.so.8.2").c_str(), fmod_symbols); + if (glesLib == nullptr || fmodLib == nullptr) + return -1; + + std::cout << "loading hybris libraries\n"; + stubSymbols(android_symbols, (void*) androidStub); + stubSymbols(egl_symbols, (void*) eglStub); + hybris_hook("eglGetProcAddress", (void*) eglGetProcAddress); + hybris_hook("mcpelauncher_hook", (void*) hookFunction); + hookAndroidLog(); + + if (!loadLibrary("libc.so") || !loadLibrary("libstdc++.so") || !loadLibrary("libm.so") || !loadLibrary("libz.so")) + return -1; + // load stub libraries + if (!loadLibrary("libandroid.so") || !loadLibrary("liblog.so") || !loadLibrary("libEGL.so") || !loadLibrary("libGLESv2.so") || !loadLibrary("libOpenSLES.so") || !loadLibrary("libfmod.so") || !loadLibrary("libGLESv1_CM.so")) + return -1; + if (!loadLibrary("libmcpelauncher_mod.so")) + return -1; + std::cout << "loading MCPE\n"; + std::string mcpePath = getCWD() + "libs/libminecraftpe.so"; + void* handle = hybris_dlopen(mcpePath.c_str(), RTLD_LAZY); + if (handle == nullptr) { + std::cout << "failed to load MCPE: " << hybris_dlerror() << "\n"; + return -1; + } + addHookLibrary(handle, mcpePath); + + unsigned int libBase = ((soinfo*) handle)->base; + std::cout << "loaded MCPE (at " << libBase << ")\n"; + + DIR *dir; + struct dirent *ent; + std::vector mods; + if ((dir = opendir ("mods/")) != NULL) { + std::cout << "loading mods\n"; + while ((ent = readdir (dir)) != NULL) { + if (ent->d_name[0] == '.') + continue; + std::string fileName (ent->d_name); + int len = fileName.length(); + if (len < 4 || fileName[len - 3] != '.' || fileName[len - 2] != 's' || fileName[len - 1] != 'o') + continue; + std::cout << "loading: " << fileName << "\n"; + void* mod = loadMod(fileName); + if (mod != nullptr) + mods.push_back(mod); + } + closedir(dir); + std::cout << "loaded " << mods.size() << " mods\n"; + } + + std::cout << "apply patches\n"; + + /* + unsigned int patchOff = (unsigned int) hybris_dlsym(handle, "_ZN12StoreFactory11createStoreER13StoreListener") + 66; + patchCallInstruction((void*) patchOff, (void*) &createStoreHookFunc, false); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN11HTTPRequestC2ERKSs") + 154; + patchCallInstruction((void*) patchOff, (void*) &constructLinuxHttpRequestInternal, false); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN11HTTPRequest4sendEv") + 26; + patchCallInstruction((void*) patchOff, (void*) &sendLinuxHttpRequestInternal, false); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN11HTTPRequest5abortEv") + 26; + patchCallInstruction((void*) patchOff, (void*) &abortLinuxHttpRequestInternal, false); + */ + unsigned int patchOff = (unsigned int) hybris_dlsym(handle, "_ZN12AndroidStore21createGooglePlayStoreERKSsR13StoreListener"); + patchCallInstruction((void*) patchOff, (void*) &createStoreHookFunc, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN26HTTPRequestInternalAndroidC2ER11HTTPRequest"); + patchCallInstruction((void*) patchOff, (void*) &constructLinuxHttpRequestInternal, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN26HTTPRequestInternalAndroid4sendEv"); + patchCallInstruction((void*) patchOff, (void*) &sendLinuxHttpRequestInternal, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN26HTTPRequestInternalAndroid5abortEv"); + patchCallInstruction((void*) patchOff, (void*) &abortLinuxHttpRequestInternal, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN9crossplat10threadpool16detach_from_javaEPv"); + patchCallInstruction((void*) patchOff, (void*) &detachFromJavaStub, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN9crossplat11get_jvm_envEv"); + patchCallInstruction((void*) patchOff, (void*) &getJVMEnvStub, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN3web4http6client7details22verify_X509_cert_chainERKSt6vectorISsSaISsEERKSs"); + patchCallInstruction((void*) patchOff, (void*) &verifyCertChainStub, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN4xbox8services20xbox_live_app_config24get_app_config_singletonEv"); + patchCallInstruction((void*) patchOff, (void*) &xboxGetAppConfigSingleton, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN4xbox8services20xbox_live_app_config11set_sandboxESs"); + patchCallInstruction((void*) patchOff, (void*) &xboxConfigSetSandboxStub, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN4xbox8services20xbox_live_app_config29set_title_telemetry_device_idERKSs"); + patchCallInstruction((void*) patchOff, (void*) &xboxConfigSetSandboxStub, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN15PatchNotesModel17preloadPatchNotesEv"); + patchCallInstruction((void*) patchOff, (void*) &patchNotesModelStub, true); + + linuxHttpRequestInternalVtable = (void**) ::operator new(8); + linuxHttpRequestInternalVtable[0] = (void*) &LinuxHttpRequestInternal::destroy; + linuxHttpRequestInternalVtable[1] = (void*) &LinuxHttpRequestInternal::destroy; + + if (workaroundAMD) {/* + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN21BlockTessallatorCache5resetER11BlockSourceRK8BlockPos") + + (0x40AD97 - 0x40ACD0); + for (unsigned int i = 0; i < 0x40ADA0 - 0x40AD97; i++) + ((char *) (void *) patchOff)[i] = 0x90;*/ + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN21BlockTessallatorCache5resetER11BlockSourceRK8BlockPos") + (0x40AD9B - 0x40ACD0); + patchCallInstruction((void*) patchOff, (void*) &pshufb_xmm4_xmm0, false); + } + + std::cout << "patches applied!\n"; + + // load symbols for gl + gl::getOpenGLVendor = (std::string (*)()) hybris_dlsym(handle, "_ZN2gl15getOpenGLVendorEv"); + gl::getOpenGLRenderer = (std::string (*)()) hybris_dlsym(handle, "_ZN2gl17getOpenGLRendererEv"); + gl::getOpenGLVersion = (std::string (*)()) hybris_dlsym(handle, "_ZN2gl16getOpenGLVersionEv"); + gl::getOpenGLExtensions = (std::string (*)()) hybris_dlsym(handle, "_ZN2gl19getOpenGLExtensionsEv"); + mce::Platform::OGL::OGL_initBindings = (void (*)()) hybris_dlsym(handle, "_ZN3mce8Platform3OGL12InitBindingsEv"); + + // init linux app platform + AppPlatform::myVtable = (void**) hybris_dlsym(handle, "_ZTV11AppPlatform"); + AppPlatform::_singleton = (AppPlatform**) hybris_dlsym(handle, "_ZN11AppPlatform10mSingletonE"); + AppPlatform::AppPlatform_construct = (void (*)(AppPlatform*)) hybris_dlsym(handle, "_ZN11AppPlatformC2Ev"); + AppPlatform::AppPlatform_initialize = (void (*)(AppPlatform*)) hybris_dlsym(handle, "_ZN11AppPlatform10initializeEv"); + AppPlatform::AppPlatform__fireAppFocusGained = (void (*)(AppPlatform*)) hybris_dlsym(handle, "_ZN11AppPlatform19_fireAppFocusGainedEv"); + + void** ptr = (void**) hybris_dlsym(handle, "_ZN9crossplat3JVME"); + *ptr = (void*) 1; // this just needs not to be null + + std::cout << "init app platform vtable\n"; + LinuxAppPlatform::initVtable(handle); + std::cout << "init app platform\n"; + LinuxAppPlatform* platform = new LinuxAppPlatform(); + std::cout << "app platform initialized\n"; + + Mouse::feed = (void (*)(char, char, short, short, short, short)) hybris_dlsym(handle, "_ZN5Mouse4feedEccssss"); + + Keyboard::inputs = (std::vector*) hybris_dlsym(handle, "_ZN8Keyboard7_inputsE"); + Keyboard::states = (int*) hybris_dlsym(handle, "_ZN8Keyboard7_statesE"); + Keyboard::Keyboard_feedText = (void (*)(const std::string&, bool, unsigned char)) hybris_dlsym(handle, "_ZN8Keyboard8feedTextERKSsbh"); + + Options::Options_getFullscreen = (bool (*)(Options*)) hybris_dlsym(handle, "_ZNK7Options13getFullscreenEv"); + Options::Options_setFullscreen = (void (*)(Options*, bool)) hybris_dlsym(handle, "_ZN7Options13setFullscreenEb"); + + std::cout << "init window\n"; + eglutInitWindowSize(windowWidth, windowHeight); + eglutInitAPIMask(EGLUT_OPENGL_ES2_BIT); + eglutInit(argc, argv); + + winId = eglutCreateWindow("Minecraft"); + + // init MinecraftGame + App::App_init = (void (*)(App*, AppContext&)) hybris_dlsym(handle, "_ZN3App4initER10AppContext"); + MinecraftGame::MinecraftGame_construct = (void (*)(MinecraftGame*, int, char**)) hybris_dlsym(handle, "_ZN13MinecraftGameC2EiPPc"); + MinecraftGame::MinecraftGame_update = (void (*)(MinecraftGame*)) hybris_dlsym(handle, "_ZN13MinecraftGame6updateEv"); + MinecraftGame::MinecraftGame_setRenderingSize = (void (*)(MinecraftGame*, int, int)) hybris_dlsym(handle, "_ZN13MinecraftGame16setRenderingSizeEii"); + MinecraftGame::MinecraftGame_setUISizeAndScale = (void (*)(MinecraftGame*, int, int, float)) hybris_dlsym(handle, "_ZN13MinecraftGame17setUISizeAndScaleEiif"); + MinecraftGame::MinecraftGame_getOptions = (Options* (*)(MinecraftGame*)) hybris_dlsym(handle, "_ZN13MinecraftGame10getOptionsEv"); + AppContext ctx; + ctx.platform = platform; + ctx.doRender = true; + + platform->initialize(); + + mce::Platform::OGL::initBindings(); + + std::cout << "create minecraft client\n"; + client = new MinecraftGame(argc, argv); + std::cout << "init minecraft client\n"; + client->init(ctx); + std::cout << "initialized lib\n"; + + if (client->getOptions()->getFullscreen()) + eglutToggleFullscreen(); + + for (void* mod : mods) { + void (*initFunc)(MinecraftGame*) = (void (*)(MinecraftGame*)) hybris_dlsym(mod, "mod_set_minecraft"); + if ((void*) initFunc != nullptr) + initFunc(client); + } + + eglutIdleFunc(minecraft_idle); + eglutReshapeFunc(minecraft_reshape); + eglutDisplayFunc(minecraft_draw); + eglutMouseFunc(minecraft_mouse); + eglutMouseButtonFunc(minecraft_mouse_button); + eglutKeyboardFunc(minecraft_keyboard); + eglutSpecialFunc(minecraft_keyboard_special); + eglutCloseWindowFunc(minecraft_close); + std::cout << "initialized display\n"; + + // init + //(*AppPlatform::_singleton)->_fireAppFocusGained(); + client->setRenderingSize(windowWidth, windowHeight); + client->setUISizeAndScale(windowWidth, windowHeight, pixelSize); + eglutMainLoop(); + + return 0; +} diff --git a/src/server.cpp b/src/server.cpp new file mode 100644 index 0000000..e69de29