From: Sergey Melnikov Date: Fri, 25 Jan 2013 12:40:13 +0000 (+0400) Subject: Kernel dso support for 'dl_iterate_phdr' function X-Git-Tag: android-x86-4.4-r1~19^2~58^2^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=c45087bffa528c0809f0df2e0a3708eba7018b33;p=android-x86%2Fbionic.git Kernel dso support for 'dl_iterate_phdr' function Kernel provides virtual DSO for stack unwinding/exception handlind info for signal usage case. Stack unwinding routines use 'dl_iterate_phdr' function for additional DWARF info gathering from DSOs. Patch enables virtual DSO enumeration via dl_iterate_phdr function. Signed-off-by: Sergey Melnikov Change-Id: Ic2882b28f40b456a088bc1e63c50cbfda7e4a102 --- diff --git a/libc/bionic/dl_iterate_phdr_static.c b/libc/bionic/dl_iterate_phdr_static.c index 90ed1b7ad..fc79ce5a6 100644 --- a/libc/bionic/dl_iterate_phdr_static.c +++ b/libc/bionic/dl_iterate_phdr_static.c @@ -27,39 +27,56 @@ */ #include +#include #include #include -/* Dynamic binaries get this from the dynamic linker (system/linker), which - * we don't pull in for static bins. We also don't have a list of so's to - * iterate over, since there's really only a single monolithic blob of - * code/data. - * - * All we need to do is to find where the executable is in memory, and grab the - * phdr and phnum from there. - */ - /* ld provides this to us in the default link script */ -extern void *__executable_start; +extern void* __executable_start; -int -dl_iterate_phdr(int (*cb)(struct dl_phdr_info *info, size_t size, void *data), - void *data) -{ - struct dl_phdr_info dl_info; - Elf32_Ehdr *ehdr = (Elf32_Ehdr *) &__executable_start; - Elf32_Phdr *phdr = (Elf32_Phdr *)((unsigned long)ehdr + ehdr->e_phoff); +int dl_iterate_phdr(int (*cb)(struct dl_phdr_info* info, size_t size, void* data), void* data) { + Elf32_Ehdr* ehdr = (Elf32_Ehdr*) &__executable_start; - /* TODO: again, copied from linker.c. Find a better home for this - * later. */ + // TODO: again, copied from linker.c. Find a better home for this later. if (ehdr->e_ident[EI_MAG0] != ELFMAG0) return -1; if (ehdr->e_ident[EI_MAG1] != ELFMAG1) return -1; if (ehdr->e_ident[EI_MAG2] != ELFMAG2) return -1; if (ehdr->e_ident[EI_MAG3] != ELFMAG3) return -1; - dl_info.dlpi_addr = 0; - dl_info.dlpi_name = NULL; - dl_info.dlpi_phdr = phdr; - dl_info.dlpi_phnum = ehdr->e_phnum; - return cb(&dl_info, sizeof (struct dl_phdr_info), data); + // Dynamic binaries get their dl_iterate_phdr from the dynamic linker, but + // static binaries get this. We don't have a list of shared objects to + // iterate over, since there's really only a single monolithic blob of + // code/data, plus optionally a VDSO. + + struct dl_phdr_info exe_info; + exe_info.dlpi_addr = 0; + exe_info.dlpi_name = NULL; + exe_info.dlpi_phdr = (Elf32_Phdr*) ((unsigned long) ehdr + ehdr->e_phoff); + exe_info.dlpi_phnum = ehdr->e_phnum; + +#ifdef AT_SYSINFO_EHDR + // Try the executable first. + int rc = cb(&exe_info, sizeof(exe_info), data); + if (rc != 0) { + return rc; + } + + // Try the VDSO if that didn't work. + Elf32_Ehdr* ehdr_vdso = (Elf32_Ehdr*) getauxval(AT_SYSINFO_EHDR); + struct dl_phdr_info vdso_info; + vdso_info.dlpi_addr = 0; + vdso_info.dlpi_name = NULL; + vdso_info.dlpi_phdr = (Elf32_Phdr*) ((char*) ehdr_vdso + ehdr_vdso->e_phoff); + vdso_info.dlpi_phnum = ehdr_vdso->e_phnum; + for (size_t i = 0; i < vdso_info.dlpi_phnum; ++i) { + if (vdso_info.dlpi_phdr[i].p_type == PT_LOAD) { + vdso_info.dlpi_addr = (Elf32_Addr) ehdr_vdso - vdso_info.dlpi_phdr[i].p_vaddr; + break; + } + } + return cb(&vdso_info, sizeof(vdso_info), data); +#else + // There's only the executable to try. + return cb(&exe_info, sizeof(exe_info), data); +#endif } diff --git a/linker/linker.cpp b/linker/linker.cpp index 9c35cfcfc..386f6dcbd 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1586,6 +1586,28 @@ static bool soinfo_link_image(soinfo* si) { } /* + * This function add vdso to internal dso list. + * It helps to stack unwinding through signal handlers. + * Also, it makes bionic more like glibc. + */ +static void add_vdso(KernelArgumentBlock& args UNUSED) { +#ifdef AT_SYSINFO_EHDR + Elf32_Ehdr* ehdr_vdso = reinterpret_cast(args.getauxval(AT_SYSINFO_EHDR)); + + soinfo* si = soinfo_alloc("[vdso]"); + si->phdr = reinterpret_cast(reinterpret_cast(ehdr_vdso) + ehdr_vdso->e_phoff); + si->phnum = ehdr_vdso->e_phnum; + si->link_map.l_name = si->name; + for (size_t i = 0; i < si->phnum; ++i) { + if (si->phdr[i].p_type == PT_LOAD) { + si->link_map.l_addr = reinterpret_cast(ehdr_vdso) - si->phdr[i].p_vaddr; + break; + } + } +#endif +} + +/* * 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. @@ -1710,6 +1732,8 @@ static Elf32_Addr __linker_init_post_relocation(KernelArgumentBlock& args, Elf32 exit(EXIT_FAILURE); } + add_vdso(args); + si->CallPreInitConstructors(); for (size_t i = 0; gLdPreloads[i] != NULL; ++i) { diff --git a/tests/Android.mk b/tests/Android.mk index fe794c47f..d94c77cdf 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -74,6 +74,7 @@ test_src_files = \ regex_test.cpp \ signal_test.cpp \ stack_protector_test.cpp \ + stack_unwinding_test.cpp \ stdio_test.cpp \ stdlib_test.cpp \ string_test.cpp \ @@ -87,6 +88,18 @@ test_dynamic_ldflags = -Wl,--export-dynamic -Wl,-u,DlSymTestFunction test_dynamic_src_files = \ dlfcn_test.cpp \ +include $(CLEAR_VARS) +LOCAL_MODULE := bionic-unit-tests-unwind-test-impl +LOCAL_CFLAGS += $(test_c_flags) -fexceptions -fnon-call-exceptions +LOCAL_SRC_FILES := stack_unwinding_test_impl.c +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := bionic-unit-tests-unwind-test-impl-host +LOCAL_CFLAGS += $(test_c_flags) -fexceptions -fnon-call-exceptions +LOCAL_SRC_FILES := stack_unwinding_test_impl.c +include $(BUILD_HOST_STATIC_LIBRARY) + # Build tests for the device (with bionic's .so). Run with: # adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests include $(CLEAR_VARS) @@ -97,6 +110,7 @@ LOCAL_LDFLAGS += $(test_dynamic_ldflags) LOCAL_SHARED_LIBRARIES += libdl LOCAL_SRC_FILES := $(test_src_files) $(test_dynamic_src_files) LOCAL_WHOLE_STATIC_LIBRARIES := bionic-unit-tests-clang +LOCAL_STATIC_LIBRARIES += bionic-unit-tests-unwind-test-impl include $(BUILD_NATIVE_TEST) # Build tests for the device (with bionic's .a). Run with: @@ -107,7 +121,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_CFLAGS += $(test_c_flags) LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SRC_FILES := $(test_src_files) -LOCAL_STATIC_LIBRARIES += libstlport_static libstdc++ libm libc +LOCAL_STATIC_LIBRARIES += libstlport_static libstdc++ libm libc bionic-unit-tests-unwind-test-impl LOCAL_WHOLE_STATIC_LIBRARIES := bionic-unit-tests-clang include $(BUILD_NATIVE_TEST) @@ -142,6 +156,7 @@ LOCAL_CFLAGS += $(test_c_flags) LOCAL_LDFLAGS += -lpthread -ldl LOCAL_LDFLAGS += $(test_dynamic_ldflags) LOCAL_SRC_FILES := $(test_src_files) $(test_dynamic_src_files) +LOCAL_STATIC_LIBRARIES += bionic-unit-tests-unwind-test-impl-host include $(BUILD_HOST_NATIVE_TEST) endif diff --git a/tests/stack_unwinding_test.cpp b/tests/stack_unwinding_test.cpp new file mode 100644 index 000000000..3b18daa58 --- /dev/null +++ b/tests/stack_unwinding_test.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 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. + */ + +/* + * Contributed by: Intel Corporation + */ + +#include + +#if defined(i386) // Only our x86 unwinding is good enough. Switch to libunwind? + +extern "C" { + void do_test(); +} + +// We have to say "DeathTest" here so gtest knows to run this test (which exits) +// in its own process. +TEST(stack_unwinding_DeathTest, unwinding_through_signal_frame) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + ASSERT_EXIT(do_test(), ::testing::ExitedWithCode(42), ""); +} + +#endif diff --git a/tests/stack_unwinding_test_impl.c b/tests/stack_unwinding_test_impl.c new file mode 100644 index 000000000..b0099f043 --- /dev/null +++ b/tests/stack_unwinding_test_impl.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 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. + */ + +/* + * Contributed by: Intel Corporation + */ + +#include +#include +#include +#include +#include + +#define noinline __attribute__((__noinline__)) +#define unused __attribute__((__unused__)) + +static noinline _Unwind_Reason_Code stop_fn(int a unused, + _Unwind_Action action, + _Unwind_Exception_Class b unused, struct _Unwind_Exception* c unused, + struct _Unwind_Context* d unused, void* e unused) { + if ((action & _UA_END_OF_STACK) != 0) { + // We reached the end of the stack without executing foo_cleanup. Test failed. + abort(); + } + return _URC_NO_REASON; +} + +static void noinline foo_cleanup(char* param unused) { + exit(42); +} + +static void noinline do_crash() { + char* ptr = NULL; + *ptr = 0; // Deliberately cause a SIGSEGV. +} + +static void noinline foo() { + char c1 __attribute__((cleanup(foo_cleanup))); + do_crash(); +} + +// It's SEGSEGV handler. We start forced stack unwinding here. +// If libgcc don't find dso for signal frame stack unwinding will be finished. +// libgcc pass to stop_fn _UA_END_OF_STACK flag. +// Test pass condition: stack unwinding through signal frame and foo1_handler execution. +static void noinline sigsegv_handler(int param unused) { + struct _Unwind_Exception* exception = (struct _Unwind_Exception*) malloc(sizeof(*exception)); + memset(&exception->exception_class, 0, sizeof(exception->exception_class)); + exception->exception_cleanup = 0; + _Unwind_ForcedUnwind(exception, stop_fn, 0); +} + +void do_test() { + signal(SIGSEGV, &sigsegv_handler); + foo(); +}