From ac6b027b54a24568885b17b7a219ae6d32f9cf39 Mon Sep 17 00:00:00 2001 From: Chongyun Lee <45286352+licy183@users.noreply.github.com> Date: Wed, 4 May 2022 13:09:10 +0800 Subject: [PATCH] new package: libandroid-execinfo --- packages/libandroid-execinfo/LICENSE | 25 +++ packages/libandroid-execinfo/build.sh | 26 ++++ packages/libandroid-execinfo/execinfo.c | 196 ++++++++++++++++++++++++ packages/libandroid-execinfo/execinfo.h | 74 +++++++++ 4 files changed, 321 insertions(+) create mode 100644 packages/libandroid-execinfo/LICENSE create mode 100644 packages/libandroid-execinfo/build.sh create mode 100644 packages/libandroid-execinfo/execinfo.c create mode 100644 packages/libandroid-execinfo/execinfo.h diff --git a/packages/libandroid-execinfo/LICENSE b/packages/libandroid-execinfo/LICENSE new file mode 100644 index 000000000..dfc15f24f --- /dev/null +++ b/packages/libandroid-execinfo/LICENSE @@ -0,0 +1,25 @@ +Copyright (C) 2021 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. \ No newline at end of file diff --git a/packages/libandroid-execinfo/build.sh b/packages/libandroid-execinfo/build.sh new file mode 100644 index 000000000..2860612ab --- /dev/null +++ b/packages/libandroid-execinfo/build.sh @@ -0,0 +1,26 @@ +TERMUX_PKG_HOMEPAGE=https://man7.org/linux/man-pages/man3/backtrace.3.html +TERMUX_PKG_DESCRIPTION="Shared library for the backtrace system function" +TERMUX_PKG_LICENSE="BSD 2-Clause" +TERMUX_PKG_MAINTAINER="@termux" +TERMUX_PKG_VERSION=0.1 +TERMUX_PKG_SKIP_SRC_EXTRACT=true +TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_PROVIDES="libexecinfo" +TERMUX_PKG_CONFLICTS="libexecinfo" + +# Files are taken from the Bionic libc repo. +# exexinfo.h: https://android.googlesource.com/platform/bionic/+/refs/heads/master/libc/include/execinfo.h +# execinfo.c: https://android.googlesource.com/platform/bionic/+/refs/heads/master/libc/bionic/execinfo.cpp +termux_step_make() { + $CC $CFLAGS -I$TERMUX_PKG_BUILDER_DIR -c $TERMUX_PKG_BUILDER_DIR/execinfo.c + $CC $LDFLAGS -shared execinfo.o -o libandroid-execinfo.so + $AR rcu libandroid-execinfo.a execinfo.o + cp -f $TERMUX_PKG_BUILDER_DIR/LICENSE $TERMUX_PKG_SRCDIR/ +} + +termux_step_make_install() { + install -Dm600 $TERMUX_PKG_BUILDER_DIR/execinfo.h $TERMUX_PREFIX/include/execinfo.h + install -Dm600 libandroid-execinfo.a $TERMUX_PREFIX/lib/libandroid-execinfo.a + install -Dm600 libandroid-execinfo.so $TERMUX_PREFIX/lib/libandroid-execinfo.so + ln -sfr $TERMUX_PREFIX/lib/libandroid-execinfo.so $TERMUX_PREFIX/lib/libexecinfo.so +} diff --git a/packages/libandroid-execinfo/execinfo.c b/packages/libandroid-execinfo/execinfo.c new file mode 100644 index 000000000..a3b0a8b7e --- /dev/null +++ b/packages/libandroid-execinfo/execinfo.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2012 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 +#include +#include + +#if defined(__ANDROID__) && __ANDROID_API__ < 30 +#define memfd_create(name,flags) syscall(SYS_memfd_create,name,flags) +#endif +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif + +static inline int __tmpfile_crate() { + char name[] = _PATH_TMP ".backtrace-FFFFFFFF-XXXXXXXX"; + snprintf(name, sizeof(name), _PATH_TMP ".backtrace-%0x-XXXXXXXX", getpid() & 0xFFFFFFFF); + // XXX: It is almost impossible to have file conflicts, isn't it? + int fd = mkstemp(name); + unlink(name); + return fd; +} + +struct StackState { + void** frames; + int frame_count; + int cur_frame; +}; + +static _Unwind_Reason_Code TraceFunction(struct _Unwind_Context* context, void* arg) { + // The instruction pointer is pointing at the instruction after the return + // call on all architectures. + // Modify the pc to point at the real function. + uintptr_t ip = _Unwind_GetIP(context); + if (ip != 0) { +#if defined(__arm__) + // If the ip is suspiciously low, do nothing to avoid a segfault trying + // to access this memory. + if (ip >= 4096) { + // Check bits [15:11] of the first halfword assuming the instruction + // is 32 bits long. If the bits are any of these values, then our + // assumption was correct: + // b11101 + // b11110 + // b11111 + // Otherwise, this is a 16 bit instruction. + uint16_t value = (*((uint16_t*)(ip - 2))) >> 11; + if (value == 0x1f || value == 0x1e || value == 0x1d) { + ip -= 4; + } else { + ip -= 2; + } + } +#elif defined(__aarch64__) + // All instructions are 4 bytes long, skip back one instruction. + ip -= 4; +#elif defined(__i386__) || defined(__x86_64__) + // It's difficult to decode exactly where the previous instruction is, + // so subtract 1 to estimate where the instruction lives. + ip--; +#endif + } + struct StackState* state = (struct StackState*)(arg); + state->frames[state->cur_frame++] = (void*)(ip); + return (state->cur_frame >= state->frame_count) ? _URC_END_OF_STACK : _URC_NO_REASON; +} + +int backtrace(void** buffer, int size) { + if (size <= 0) { + return 0; + } + struct StackState state = {buffer, size, 0}; + _Unwind_Backtrace(TraceFunction, &state); + return state.cur_frame; +} + +char** backtrace_symbols(void* const* buffer, int size) { + if (size <= 0) { + return NULL; + } + // Do this calculation first in case the user passes in a bad value. + size_t ptr_size; + if (__builtin_mul_overflow(sizeof(char*), size, &ptr_size)) { + return NULL; + } + int fd = memfd_create("backtrace_symbols_fd", MFD_CLOEXEC); + if (fd == -1) fd = __tmpfile_crate(); + if (fd == -1) { + return NULL; + } + backtrace_symbols_fd(buffer, size, fd); + // Get the size of the file. + off_t file_size = lseek(fd, 0, SEEK_END); + if (file_size <= 0) { + close(fd); + return NULL; + } + // The interface for backtrace_symbols indicates that only the single + // returned pointer must be freed by the caller. Therefore, allocate a + // buffer that includes the memory for the strings and all of the pointers. + // Add one byte at the end just in case the file didn't end with a '\n'. + size_t symbol_data_size; + if (__builtin_add_overflow(ptr_size, file_size, &symbol_data_size) || + __builtin_add_overflow(symbol_data_size, 1, &symbol_data_size)) { + close(fd); + return NULL; + } + uint8_t* symbol_data = (uint8_t*)(malloc(symbol_data_size)); + if (symbol_data == NULL) { + close(fd); + return NULL; + } + // Copy the string data into the buffer. + char* cur_string = (char*)(&symbol_data[ptr_size]); + // If this fails, the read won't read back the correct number of bytes. + lseek(fd, 0, SEEK_SET); + ssize_t num_read = read(fd, cur_string, file_size); + close(fd); + fd = -1; + if (num_read != file_size) { + free(symbol_data); + return NULL; + } + // Make sure the last character in the file is '\n'. + if (cur_string[file_size] != '\n') { + cur_string[file_size++] = '\n'; + } + for (int i = 0; i < size; i++) { + ((char**)(symbol_data))[i] = cur_string; + cur_string = strchr(cur_string, '\n'); + if (cur_string == NULL) { + free(symbol_data); + return NULL; + } + cur_string[0] = '\0'; + cur_string++; + } + return (char**)(symbol_data); +} + +// This function should do no allocations if possible. +void backtrace_symbols_fd(void* const* buffer, int size, int fd) { + if (size <= 0 || fd < 0) { + return; + } + for (int frame_num = 0; frame_num < size; frame_num++) { + void* address = buffer[frame_num]; + Dl_info info; + if (dladdr(address, &info) != 0) { + if (info.dli_fname != NULL) { + write(fd, info.dli_fname, strlen(info.dli_fname)); + } + if (info.dli_sname != NULL) { + dprintf(fd, "(%s+0x%" PRIxPTR ") ", info.dli_sname, + (uintptr_t)(address) - (uintptr_t)(info.dli_saddr)); + } else { + dprintf(fd, "(+%p) ", info.dli_saddr); + } + } + dprintf(fd, "[%p]\n", address); + } +} diff --git a/packages/libandroid-execinfo/execinfo.h b/packages/libandroid-execinfo/execinfo.h new file mode 100644 index 000000000..aacbe23e9 --- /dev/null +++ b/packages/libandroid-execinfo/execinfo.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include + +/** + * @file execinfo.h + * @brief Functions to do in process backtracing. + */ + +__BEGIN_DECLS + +/** + * [backtrace(3)](https://man7.org/linux/man-pages/man3/backtrace.3.html) + * Saves a backtrace for the current call in the array pointed to by buffer. + * "size" indicates the maximum number of void* pointers that can be set. + * + * Returns the number of addresses stored in "buffer", which is not greater + * than "size". If the return value is equal to "size" then the number of + * addresses may have been truncated. + * + * Available since API level 24. + */ +int backtrace(void** buffer, int size) __INTRODUCED_IN(24); + +/** + * [backtrace_symbols(3)](https://man7.org/linux/man-pages/man3/backtrace_symbols.3.html) + * Given an array of void* pointers, translate the addresses into an array + * of strings that represent the backtrace. + * + * Returns a pointer to allocated memory, on error NULL is returned. It is + * the responsibility of the caller to free the returned memory. + * + * Available since API level 24. + */ +char** backtrace_symbols(void* const* buffer, int size) __INTRODUCED_IN(24); + +/** + * [backtrace_symbols_fd(3)](https://man7.org/linux/man-pages/man3/backtrace_symbols_fd.3.html) + * Given an array of void* pointers, translate the addresses into an array + * of strings that represent the backtrace and write to the file represented + * by "fd". The file is written such that one line equals one void* address. + * + * Available since API level 24. + */ +void backtrace_symbols_fd(void* const* buffer, int size, int fd) __INTRODUCED_IN(24); + +__END_DECLS