diff --git a/libc-test/src/functionalext/ldso_cfi/BUILD.gn b/libc-test/src/functionalext/ldso_cfi/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..7f19ba69d32d44d7f210b884a53527fc25eb27d0 --- /dev/null +++ b/libc-test/src/functionalext/ldso_cfi/BUILD.gn @@ -0,0 +1,96 @@ +# Copyright (c) 2022 Huawei Device Co., 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. + +import("../../../test_template.gni") + +group("ldso_cfi_test") { + testonly = true + deps = [ + ":ldso_cfi_check", + ":ldso_cfi_test_exe", + ":ldso_cfi_test_lib", + ":ldso_cfi_test_bad_lib", + ] +} + +ohos_executable("ldso_cfi_check") { +# cflags = [ +# "-fsanitize=cfi", +# "-fsanitize-trap=all", +# "-fno-sanitize-trap=cfi", +# "-flto", +# "-fvisibility=hidden" +# ] + +# ldflags = [ +# "-fsanitize=cfi", +# "-flto", +# "-fsanitize-trap=all", +# "-fno-sanitize-trap=cfi", +# ] +# cflags_c = ["--coverage"] +# ldflags = ["--coverage"] + subsystem_name = "musl" + part_name = "libc-test" + include_dirs = [ + "../common", + "//third_party/musl/porting/linux/user/include", + "//third_party/musl/porting/linux/user/ldso", + "//third_party/musl/libc-test/src/common", + ] + + sources = [ + "ldso_cfi_check.c", + ] + configs = [ "//third_party/musl/libc-test/src/common:config_runtest" ] +} + +ohos_executable("ldso_cfi_test_exe") { + #cflags = [ "-fPIE","-fvisibility=default" ] + #ldflags = [ "-pie","-fvisibility=default" ] + subsystem_name = "musl" + part_name = "libc-test" + include_dirs = [ + "../common", + "//third_party/musl/porting/linux/user/include", + "//third_party/musl/porting/linux/user/ldso", + "//third_party/musl/libc-test/src/common", + ] + + sources = [ + "ldso_cfi_test_exe.c", + ] + configs = [ "//third_party/musl/libc-test/src/common:config_runtest" ] +} + +ohos_shared_library("ldso_cfi_test_lib") { + include_dirs = [ "." ] + sources = [ "ldso_cfi_test_lib.c" ] +# cflags_c = ["--coverage"] +# ldflags = ["--coverage"] + output_name = "ldso_cfi_test_lib" + output_extension = "so" + subsystem_name = "musl" + part_name = "libc-test-lib" +} + +ohos_shared_library("ldso_cfi_test_bad_lib") { + include_dirs = [ "." ] + sources = [ "ldso_cfi_test_bad_lib.c" ] +# cflags_c = ["--coverage"] +# ldflags = ["--coverage"] + output_name = "ldso_cfi_test_bad_lib" + output_extension = "so" + subsystem_name = "musl" + part_name = "libc-test-lib" +} diff --git a/libc-test/src/functionalext/ldso_cfi/ldso_cfi_check.c b/libc-test/src/functionalext/ldso_cfi/ldso_cfi_check.c new file mode 100644 index 0000000000000000000000000000000000000000..c561a81276c02ab210305499dff2a48ac8bd3a92 --- /dev/null +++ b/libc-test/src/functionalext/ldso_cfi/ldso_cfi_check.c @@ -0,0 +1,346 @@ +/** + * Copyright (c) 2022 Huawei Device Co., 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 "functionalext.h" + +#define LIB_PATH "/data/tests/libc-test/src/libldso_cfi_test_lib.so" +#define BAD_LIB_PATH "/data/tests/libc-test/src/libldso_cfi_test_bad_lib.so" + +struct dso { + char *mock; +}; + +extern bool init_cfi_shadow(struct dso *dso_list); +extern bool map_dso_to_cfi_shadow(struct dso *dso); +extern void unmap_dso_from_cfi_shadow(struct dso *dso); +extern void __cfi_slowpath(uint64_t CallSiteTypeId, void *Ptr); +extern void __cfi_slowpath_diag(uint64_t CallSiteTypeId, void *Ptr, void *DiagData); + +typedef void(*TEST_FUN)(void); +typedef size_t (*GET_COUNT)(); +typedef uint64_t (*GET_TP_ID)(); +typedef void* (*GET_LAST_ADDR)(); +typedef void* (*GET_LAST_DIAG)(); +typedef void* (*GET_GL_ADDR)(); +typedef void (*CFI_CHECK)(); +typedef char* (*BUF_CHECK)(); + +GET_COUNT get_count = NULL; +GET_TP_ID get_type_id = NULL; +GET_LAST_ADDR get_address = NULL; +GET_LAST_DIAG get_diag = NULL; +GET_GL_ADDR get_global_address = NULL; +CFI_CHECK cfi_check = NULL; +BUF_CHECK buf_check = NULL; + +static void test_func() {} + +/** + * @tc.name : cfi_init_test_0001 + * @tc.desc : If no dso while initializing the CFI shadow, do nothing and return true. + * @tc.level : Level 1 + */ +void cfi_init_test_0001(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + EXPECT_TRUE("cfi_init_test_0001", init_cfi_shadow(NULL)); +} + +/** + * @tc.name : cfi_init_test_0002 + * @tc.desc : If dso is NULL while mapping to the CFI shadow, do nothing and return true. + * @tc.level : Level 2 + */ +void cfi_init_test_0002(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + EXPECT_TRUE("cfi_init_test_0002", map_dso_to_cfi_shadow(NULL)); +} + +/** + * @tc.name : cfi_init_test_0003 + * @tc.desc : If dso is NULL while unmapping from the CFI shadow, do nothing. + * @tc.level : Level 2 + */ +void cfi_init_test_0003(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + unmap_dso_from_cfi_shadow(NULL); +} + +/** + * @tc.name : cfi_slowpath_function_test_0001 + * @tc.desc : Loading a dso that contains __cfi_check() symbol, call the __cfi_slowpath() function with + * address inside the DSO, the __cfi_check() function is called. + * @tc.level : Level 1 + */ +void cfi_slowpath_function_test_0002(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + void* handle = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTRNE("cfi_slowpath_function_test_0002", handle, NULL); + + *(void **)(&get_count) = dlsym(handle, "get_count"); + *(void **)(&get_type_id) = dlsym(handle, "get_type_id"); + *(void **)(&get_address) = dlsym(handle, "get_address"); + *(void **)(&get_diag) = dlsym(handle, "get_diag"); + *(void **)(&get_global_address) = dlsym(handle, "get_global_address"); + + size_t count = (*get_count)(); + + __cfi_slowpath(20, (*get_global_address)()); + EXPECT_EQ("cfi_slowpath_function_test_0002", 20, (*get_type_id)()); + EXPECT_EQ("cfi_slowpath_function_test_0002", (*get_global_address)(), (*get_address)()); + EXPECT_EQ("cfi_slowpath_function_test_0002", NULL, (*get_diag)()); + EXPECT_EQ("cfi_slowpath_function_test_0002", ++count, (*get_count)()); + + dlclose(handle); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +/** + * @tc.name : cfi_slowpath_function_test_0003 + * @tc.desc : Loading a dso that contains __cfi_check() symbol, call the __cfi_slowpath() function with + * address belongs to other dso that does not enable Cross-DSO, the __cfi_check() function is + * not called. + * @tc.level : Level 1 + */ +void cfi_slowpath_function_test_0003(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + void* handle = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTRNE("cfi_slowpath_function_test_0003", handle, NULL); + + *(void **)(&get_count) = dlsym(handle, "get_count"); + *(void **)(&get_type_id) = dlsym(handle, "get_type_id"); + *(void **)(&get_address) = dlsym(handle, "get_address"); + *(void **)(&get_diag) = dlsym(handle, "get_diag"); + *(void **)(&get_global_address) = dlsym(handle, "get_global_address"); + + __cfi_slowpath(30, (*get_global_address)()); + size_t count = (*get_count)(); + __cfi_slowpath(40, (void*)(&test_func)); + EXPECT_EQ("cfi_slowpath_function_test_0003", 30, (*get_type_id)()); + EXPECT_EQ("cfi_slowpath_function_test_0003", (*get_global_address)(), (*get_address)()); + EXPECT_EQ("cfi_slowpath_function_test_0003", NULL, (*get_diag)()); + EXPECT_EQ("cfi_slowpath_function_test_0003", count, (*get_count)()); + + dlclose(handle); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +/** + * @tc.name : cfi_slowpath_function_test_0004 + * @tc.desc : Calling __cfi_slowpath() with target_addr = NULL + * @tc.level : Level 2 + */ +void cfi_slowpath_function_test_0004(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + void* handle = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTRNE("cfi_slowpath_function_test_0004", handle, NULL); + + *(void **)(&get_count) = dlsym(handle, "get_count"); + + size_t count = (*get_count)(); + __cfi_slowpath(30, NULL); + EXPECT_EQ("cfi_slowpath_function_test_0004", count, (*get_count)()); + + dlclose(handle); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +/** + * @tc.name : cfi_slowpath_function_test_0005 + * @tc.desc : Loading a dso that contains __cfi_check() symbol, call the __cfi_slowpath() function with + * invalid address, coredump happened. + * not called. + * @tc.level : Level 2 + */ +void cfi_slowpath_function_test_0005(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + void* handle = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTRNE("cfi_slowpath_function_test_0005", handle, NULL); + + *(void **)(&get_count) = dlsym(handle, "get_count"); + *(void **)(&get_type_id) = dlsym(handle, "get_type_id"); + *(void **)(&get_address) = dlsym(handle, "get_address"); + *(void **)(&get_diag) = dlsym(handle, "get_diag"); + *(void **)(&get_global_address) = dlsym(handle, "get_global_address"); + + size_t count = (*get_count)(); + int status; + pid_t pid; + pid = fork(); + if(pid > 0) { + printf("["__FILE__"][Line: %d][%s]: parent process pid = %d\n", __LINE__, __func__, getppid()); + wait(&status); + } else if(pid == 0) { + printf("["__FILE__"][Line: %d][%s]: child pid = %d\n", __LINE__, __func__, pid); + __cfi_slowpath(30, (void*)&count); + } else { + printf("["__FILE__"][Line: %d][%s]: fork failed!\n", __LINE__, __func__); + } + dlclose(handle); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +/** + * @tc.name : cfi_slowpath_function_test_0006 + * @tc.desc : Loading a dso which size is larger than 1 LIBRARY_ALIGNMENT(256KB), call the __cfi_slowpath() + * function with address in different LIBRARY_ALIGNMENT range and make sure __cfi_check() is called. + * @tc.level : Level 1 + */ +void cfi_slowpath_function_test_0006(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + void* handle = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTRNE("cfi_slowpath_function_test_0006", handle, NULL); + + *(void **)(&get_count) = dlsym(handle, "get_count"); + *(void **)(&buf_check) = dlsym(handle, "buf"); + + size_t count = (*get_count)(); + const size_t bss_size = 1024 * 1024; + + for (size_t i = 0; i < bss_size; ++i) { + __cfi_slowpath(50, (char*)buf_check + i); + EXPECT_EQ("cfi_slowpath_function_test_0006", ++count, (*get_count)()); + } + + dlclose(handle); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +/** + * @tc.name : cfi_slowpath_function_test_0007 + * @tc.desc : Loading a dso 2 times + * @tc.level : Level 1 + */ +void cfi_slowpath_function_test_0007(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + void* handle = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTRNE("cfi_slowpath_function_test_0007", handle, NULL); + + *(void **)(&get_count) = dlsym(handle, "get_count"); + *(void **)(&get_global_address) = dlsym(handle, "get_global_address"); + __cfi_slowpath(10, (*get_global_address)()); + + void* handle2 = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTREQ("cfi_slowpath_function_test_0007", handle, handle2); + *(void **)(&get_count) = dlsym(handle2, "get_count"); + *(void **)(&get_type_id) = dlsym(handle2, "get_type_id"); + *(void **)(&get_address) = dlsym(handle2, "get_address"); + *(void **)(&get_diag) = dlsym(handle2, "get_diag"); + *(void **)(&get_global_address) = dlsym(handle2, "get_global_address"); + + size_t count = (*get_count)(); + __cfi_slowpath(20, (*get_global_address)()); + EXPECT_EQ("cfi_slowpath_function_test_0002", 20, (*get_type_id)()); + EXPECT_EQ("cfi_slowpath_function_test_0002", (*get_global_address)(), (*get_address)()); + EXPECT_EQ("cfi_slowpath_function_test_0002", ++count, (*get_count)()); + + dlclose(handle); + dlclose(handle2); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +/** + * @tc.name : cfi_slowpath_function_test_0008 + * @tc.desc : Calling dso's function after unloading the dso. + * @tc.level : Level 1 + */ +void cfi_slowpath_function_test_0008(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + void* handle = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTRNE("cfi_slowpath_function_test_0008", handle, NULL); + *(void **)(&get_global_address) = dlsym(handle, "get_global_address"); + dlclose(handle); + + int status; + pid_t pid; + pid = fork(); + if(pid > 0) { + printf("["__FILE__"][Line: %d][%s]: parent process pid = %d\n", __LINE__, __func__, getppid()); + wait(&status); + } else if(pid == 0) { + printf("["__FILE__"][Line: %d][%s]: child pid = %d\n", __LINE__, __func__, pid); + __cfi_slowpath(30, (*get_global_address)()); + } else { + printf("["__FILE__"][Line: %d][%s]: fork failed!\n", __LINE__, __func__); + } + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +/** + * @tc.name : cfi_slowpath_diag_function_test_0001 + * @tc.desc : Loading a dso that contains __cfi_check() symbol, call the __cfi_slowpath_diag() function with + * address inside the DSO, the __cfi_check() function is called. + * @tc.level : Level 1 + */ +void cfi_slowpath_diag_function_test_0001(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + void* handle = dlopen(LIB_PATH, RTLD_LAZY); + EXPECT_PTRNE("cfi_slowpath_diag_function_test_0002", handle, NULL); + + *(void **)(&get_count) = dlsym(handle, "get_count"); + *(void **)(&get_global_address) = dlsym(handle, "get_global_address"); + + size_t count = (*get_count)(); + void* diag_ptr = (void*)(5678); + + __cfi_slowpath_diag(10, NULL, diag_ptr); + EXPECT_EQ("cfi_slowpath_diag_function_test_0002", count, (*get_count)()); + + __cfi_slowpath_diag(10, (*get_global_address)(), NULL); + EXPECT_EQ("cfi_slowpath_diag_function_test_0002", ++count, (*get_count)()); + + dlclose(handle); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +TEST_FUN G_Fun_Array[] = { + cfi_init_test_0001, + cfi_init_test_0002, + cfi_init_test_0003, + cfi_slowpath_function_test_0002, + cfi_slowpath_function_test_0003, + cfi_slowpath_function_test_0004, + cfi_slowpath_function_test_0005, + cfi_slowpath_function_test_0006, + cfi_slowpath_function_test_0007, + cfi_slowpath_function_test_0008, + cfi_slowpath_diag_function_test_0001, +}; + +int main(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + int num = sizeof(G_Fun_Array)/sizeof(TEST_FUN); + for (int pos = 0; pos < num; ++pos) { + G_Fun_Array[pos](); + } + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); + return 0; +} diff --git a/libc-test/src/functionalext/ldso_cfi/ldso_cfi_test_lib.c b/libc-test/src/functionalext/ldso_cfi/ldso_cfi_test_lib.c new file mode 100644 index 0000000000000000000000000000000000000000..b1655f54d21e8fc8090251678bcd34fd614dcb4c --- /dev/null +++ b/libc-test/src/functionalext/ldso_cfi/ldso_cfi_test_lib.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +static size_t g_count = 0; +static uint64_t g_type_id = 0; +static void *g_address = NULL; +static void *g_diag = NULL; + +// Make sure the library crosses at least one kLibraryAlignment(=256KB) boundary. +char buf[1024 * 1024]; + +// Mock a CFI-enabled library without relying on the compiler. +__attribute__((aligned(4096))) void __cfi_check(uint64_t CallSiteTypeId, + void *TargetAddr, void *Diag) { + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + ++g_count; + g_type_id = CallSiteTypeId; + g_address = TargetAddr; + g_diag = Diag; +} + +size_t get_count() { + return g_count; +} + +uint64_t get_type_id() { + return g_type_id; +} + +void *get_address() { + return g_address; +} + +void *get_diag() { + return g_diag; +} + +void *get_global_address() { + return &g_count; +} diff --git a/libc-test/src/functionalext/test_src_functionalext.gni b/libc-test/src/functionalext/test_src_functionalext.gni index e6080d8514330cf46e508e103d853435904d45d1..09e5ac5fb4540cd5cf5afc1100a93c6c1dc5ebcc 100644 --- a/libc-test/src/functionalext/test_src_functionalext.gni +++ b/libc-test/src/functionalext/test_src_functionalext.gni @@ -25,4 +25,5 @@ functionalext_list = [ "supplement:functionalext_supplement_test", "fortify:functionalext_fortify_test", "symver:functionalext_symver_test", + "ldso_cfi:ldso_cfi_test", ] diff --git a/musl_src.gni b/musl_src.gni index 6857d7a77b4ca18e6a36643c1f02a62dc1cf19c4..4e4973d82a86482334676f415a3162560ade5a1f 100644 --- a/musl_src.gni +++ b/musl_src.gni @@ -1668,6 +1668,7 @@ musl_src_ldso = [ "ldso/ns_config.c", "ldso/strops.c", "ldso/dynlink_rand.c", + "ldso/cfi.c", "ldso/ld_log.c", ] @@ -2102,11 +2103,13 @@ musl_src_porting_file = [ "crt/arm/crti.s", "crt/aarch64/crti.s", "crt/crtplus.c", + "ldso/cfi.h", "ldso/ld_log.h", "ldso/ld_log.c", "ldso/namespace.c", "ldso/ns_config.c", "ldso/strops.c", + "ldso/cfi.c", "ldso/dynlink_rand.c", "ldso/dynlink_rand.h", "src/thread/pthread_create.c", diff --git a/porting/linux/user/ldso/cfi.c b/porting/linux/user/ldso/cfi.c new file mode 100755 index 0000000000000000000000000000000000000000..b685dc427f3e7276252893a64896eaaedd85c1db --- /dev/null +++ b/porting/linux/user/ldso/cfi.c @@ -0,0 +1,427 @@ +#define _GNU_SOURCE +#include +#include +#include "cfi.h" +#include "ld_log.h" +#include "namespace.h" + +/* This module provides support for LLVM CFI Cross-DSO by implementing the __cfi_slowpath() and __cfi_slowpath_diag() + * functions. These two functions will be called before visiting other dso's resources. The responsibility is to + * calculate the __cfi_check() of the target dso, and call it. + * The relationship between __cfi_check and dso addr is: + * __cfi_check = AlignDown(addr, shadow_alignment) + shadow_alignment - (shadow_value - 2) * 4096; + * The shadow_alignment presents the size of memory mapped by one shadow value. + * The shadow_value presents the distance between the __cfi_check() and the the end address of each shadow alignment + * in the dso. It can be presented as a multiple of 4096. + * The CFI shadow is used to store shadow value(s) of each dso. + */ + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define ALIGN_UP(a, b) (((a) + (b) - 1) & -(b)) +#define ALIGN_DOWN(a, b) ((a) & -(b)) +#if DL_FDPIC +#define LADDR(p, v) laddr((p), (v)) +#else +#define LADDR(p, v) (void *)((p)->base + (v)) +#endif + +/* Function ptr for __cfi_check() */ +typedef int (*cfi_check_t)(uint64_t, void *, void *); + +static const uintptr_t shadow_granularity = LIBRARY_ALIGNMENT_BITS; +static const uintptr_t cfi_check_granularity = 12; +static const uintptr_t shadow_alignment = 1UL << shadow_granularity; +static uintptr_t shadow_size = 0; +/* Start addr of the CFI shadow */ +static char *cfi_shadow_start = NULL; +/* List head of all the DSOs loaded by the process */ +static struct dso *dso_list_head = NULL; + +/* Shadow value */ +static const uint16_t sv_invalid = 0; +static const uint16_t sv_uncheck = 1; +static const uint16_t sv_valid_min = 2; + +#if defined(__LP64__) +static const uintptr_t max_target_addr = 0xffffffffffff; +#else +static const uintptr_t max_target_addr = 0xffffffff; +#endif + +/* Create a cfi shadow */ +static bool create_cfi_shadow(void); + +/* Map dsos to CFI shadow */ +static bool add_dso_to_cfi_shadow(struct dso *dso); +static bool fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type); + +/* Find the __cfi_check() of target dso and call it */ +void __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr); +void __cfi_slowpath_diag(uint64_t call_site_type_id, void *func_ptr, void *diag_data); + +static inline uintptr_t addr_to_offset(uintptr_t addr, int bits) +{ + /* Convert addr to CFI shadow offset. + * Shift left 1 bit because the shadow value is uint16_t. + */ + return (addr >> bits) << 1; +} + +static void set_cfi_shadow_name() +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); + + return; +} + +static struct symdef find_cfi_check_sym(struct dso *p) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + if (p == NULL) { + LD_LOGE("[%{public}s] has null param!\n", __FUNCTION__); + struct symdef emptysym = {0}; + return emptysym; + } + ns_t *ns = p->namespace; + struct verinfo verinfo = { .s = "__cfi_check", .v = "", .use_vna_hash = false }; + return find_sym2(p, &verinfo, 0, 1, ns); +} + +static uintptr_t get_cfi_check_addr(uint16_t value, void* func_ptr) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + uintptr_t addr = (uintptr_t)func_ptr; + uintptr_t aligned_addr = ALIGN_DOWN(addr, shadow_alignment) + shadow_alignment; + uintptr_t cfi_check_func_addr = aligned_addr - ((uintptr_t)(value - sv_valid_min) << cfi_check_granularity); +#ifdef __arm__ + LD_LOGI("[%{public}s] __arm__ defined!\n", __FUNCTION__); + cfi_check_func_addr++; +#endif + LD_LOGI("[%{public}s] the cfi_check_func_addr is %{public}p!\n", __FUNCTION__, cfi_check_func_addr); + + return cfi_check_func_addr; +} + +static void cfi_slowpath_common(uint64_t call_site_type_id, void *func_ptr, void *diag_data) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGI("[%{public}s] func_ptr[%{public}p] !\n", __FUNCTION__, func_ptr); + + uint16_t value = sv_invalid; + +#if defined(__aarch64__) + LD_LOGI("[%{public}s] __aarch64__ defined!\n", __FUNCTION__); + uintptr_t addr = (uintptr_t)func_ptr & ((1ULL << 56) - 1); +#else + LD_LOGI("[%{public}s] __aarch64__ not defined!\n", __FUNCTION__); + uintptr_t addr = func_ptr; +#endif + + /* Get shadow value */ + uintptr_t offset = addr_to_offset(addr, shadow_granularity); + if (offset > shadow_size) { + value = sv_invalid; + } else { + value = *((uint16_t*)(cfi_shadow_start + offset)); + } + LD_LOGI("[%{public}s] the value is 0x%{public}x!\n", __FUNCTION__, value); + + struct dso *dso = NULL; + switch (value) + { + case sv_invalid: + dso = (struct dso *)addr2dso((size_t)__builtin_return_address(0)); + if (dso == NULL) { + LD_LOGE("[%{public}s] can not find the dso!\n", __FUNCTION__); + __builtin_trap(); + } + LD_LOGI("[%{public}s] dso name[%{public}s]!\n", __FUNCTION__, dso->name); + + struct symdef cfi_check_sym = find_cfi_check_sym(dso); + if (!cfi_check_sym.sym) { + LD_LOGE("[%{public}s] can not find the __cfi_check in the dso!\n", __FUNCTION__); + __builtin_trap(); + } + LD_LOGI("[%{public}s] cfi_check addr[%{public}p]!\n", + __FUNCTION__, LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value)); + ((cfi_check_t)LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value))(call_site_type_id, func_ptr, diag_data); + break; + case sv_uncheck: + break; + default: + ((cfi_check_t)get_cfi_check_addr(value, func_ptr))(call_site_type_id, func_ptr, diag_data); + break; + } + + return; +} + +bool init_cfi_shadow(struct dso *dso_list) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + if (dso_list == NULL) { + LD_LOGW("[%{public}s] has null param!\n", __FUNCTION__); + return true; + } + + /* Save the head node of dso list */ + dso_list_head = dso_list; + + return map_dso_to_cfi_shadow(dso_list); +} + +bool map_dso_to_cfi_shadow(struct dso *dso) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + bool has_cfi_check = false; + + if (dso == NULL) { + LD_LOGE("[%{public}s] has null param!\n", __FUNCTION__); + return true; + } + + /* Find __cfi_check symbol in dso list */ + for (struct dso *p = dso; p; p = p->next) { + if (find_cfi_check_sym(p).sym) { + LD_LOGI("[%{public}s] find __cfi_check function in dso %{public}s!\n", __FUNCTION__, p->name); + has_cfi_check = true; + break; + } + } + + /* If the cfi shadow does not exist, create it and map all the dsos and its dependents to it. */ + if (cfi_shadow_start == NULL) { + if (has_cfi_check) { + if (!create_cfi_shadow()) { + LD_LOGE("[%{public}s] create cfi shadow failed!\n", __FUNCTION__); + return false; + } + LD_LOGI("[%{public}s] add_dso_to_cfi_shadow with dso_list_head!\n", __FUNCTION__); + add_dso_to_cfi_shadow(dso_list_head); + set_cfi_shadow_name(); + } + /* If the cfi shadow exists, map the current dso and its dependents to it. */ + } else { + LD_LOGI("[%{public}s] add_dso_to_cfi_shadow with dso!\n", __FUNCTION__); + add_dso_to_cfi_shadow(dso); + set_cfi_shadow_name(); + } + + return true; +} + +void unmap_dso_from_cfi_shadow(struct dso *dso) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + if (dso == NULL) { + LD_LOGE("[%{public}s] has null param!\n", __FUNCTION__); + return; + } + + LD_LOGI("[%{public}s] unmap dso %{public}s from shadow!\n", __FUNCTION__, dso->name); + + if (cfi_shadow_start == NULL) + return; + + if (dso->map == 0 || dso->map_len == 0) + return; + + /* Set the dso's shadow value as invalid. */ + fill_shadow_value_to_shadow(dso->map, dso->map + dso->map_len, 0, sv_invalid); + set_cfi_shadow_name(); + + return; +} + +static bool create_cfi_shadow(void) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + /* Each process can load up to (max_target_addr >> shadow_granularity) dsos. Shift left 1 bit because the shadow + * value is uint16_t. The size passed to mmap() should be aligned with 4096, so shadow_size should be aligned. + */ + shadow_size = ALIGN_UP(((max_target_addr >> shadow_granularity) << 1), PAGE_SIZE); + + uintptr_t *mmap_addr = mmap(NULL, shadow_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + + if (mmap_addr == MAP_FAILED) { + LD_LOGE("[%{public}s] mmap failed!\n", __FUNCTION__); + return false; + } + + cfi_shadow_start = (char*)mmap_addr; + LD_LOGI("[%{public}s] the cfi_shadow_start addr is %{public}p!\n", __FUNCTION__, cfi_shadow_start); + + return true; +} + +static bool add_dso_to_cfi_shadow(struct dso *dso) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + for (struct dso *p = dso; p; p = p->next) { + LD_LOGI("[%{public}s] start to deal with dso %{public}s!\n", __FUNCTION__, p->name); + if (p->map == 0 || p->map_len == 0) { + LD_LOGI("[%{public}s] the dso has no data!\n", __FUNCTION__); + continue; + } + + struct symdef cfi_check_sym = find_cfi_check_sym(p); + /* If the dso doesn't have __cfi_check(), set it's shadow value unchecked. */ + if (!cfi_check_sym.sym) { + LD_LOGI("[%{public}s] the dso has no __cfi_check func, call fill_shadow_value_to_shadow!\n", __FUNCTION__); + if (!fill_shadow_value_to_shadow(p->map, p->map + p->map_len, 0, sv_uncheck)) { + LD_LOGE("[%{public}s] add dso to cfi shadow failed!\n", __FUNCTION__); + return false; + } + /* If the dso has __cfi_check(), set it's shadow value valid. */ + } else { + LD_LOGI("[%{public}s] the dso has __cfi_check func,call fill_shadow_value_to_shadow!\n", __FUNCTION__); + uintptr_t end = p->map + p->map_len; + uintptr_t cfi_check = LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value); + + if (cfi_check == 0) { + LD_LOGE("[%{public}s] the dso has null cfi_check func!\n", __FUNCTION__); + return false; + } + if (!fill_shadow_value_to_shadow(p->map, end, cfi_check, sv_valid_min)) { + LD_LOGE("[%{public}s] add dso to cfi shadow failed!\n", __FUNCTION__); + return false; + } + } + LD_LOGI("[%{public}s] finish to deal with dso %{public}s!\n", __FUNCTION__, p->name); + } + + return true; +} + +static bool fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGI("[%{public}s] begin[%{public}x] end[%{public}x] cfi_check[%{public}x] type[%{public}x]!\n", + __FUNCTION__, begin, end, cfi_check, type); + + if (begin == 0 || end == 0) { + LD_LOGE("[%{public}s] has error param!\n", __FUNCTION__); + return false; + } + + /* To ensure the atomicity of the CFI shadow operation, we create a temp_shadow, write the shadow value to + * the temp_shadow, and then write it back to the CFI shadow by mremap().*/ + begin = ALIGN_DOWN(MAX(begin, cfi_check), shadow_alignment); + char* shadow_begin = cfi_shadow_start + addr_to_offset(begin, LIBRARY_ALIGNMENT_BITS); + char* shadow_end = (char*)(((uint16_t*)(cfi_shadow_start + addr_to_offset(end - 1, LIBRARY_ALIGNMENT_BITS))) + 1); + char* aligned_shadow_begin = (char*)ALIGN_DOWN((uintptr_t)shadow_begin, PAGE_SIZE); + char* aligned_shadow_end = (char*)ALIGN_UP((uintptr_t)shadow_end, PAGE_SIZE); + + uint16_t tmp_shadow_size = aligned_shadow_end - aligned_shadow_begin; + uint16_t offset_begin = shadow_begin - aligned_shadow_begin; + uint16_t offset_end = shadow_end - aligned_shadow_begin; + + char* tmp_shadow_start = (char*)mmap(NULL, tmp_shadow_size, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (tmp_shadow_start == MAP_FAILED) { + LD_LOGE("[%{public}s] mmap failed!\n", __FUNCTION__); + return false; + } + + LD_LOGI("[%{public}s] tmp_shadow_start is %{public}p\t tmp_shadow_size is 0x%{public}x!\n", + __FUNCTION__, tmp_shadow_start, tmp_shadow_size); + memcpy(tmp_shadow_start, aligned_shadow_begin, offset_begin); + memcpy(tmp_shadow_start + offset_end, shadow_end, aligned_shadow_end - shadow_end); + + /* If the dso has __cfi_check(), calculate valid shadow value */ + if (type == sv_valid_min) { +#ifdef __arm__ + uint16_t shadow_value_begin = ((begin + shadow_alignment - (cfi_check - 1)) + >> cfi_check_granularity) + sv_valid_min; +#else + uint16_t shadow_value_begin = ((begin + shadow_alignment - cfi_check) + >> cfi_check_granularity) + sv_valid_min; +#endif + LD_LOGI("[%{public}s] shadow_value_begin is 0x%{public}x!\n", __FUNCTION__, shadow_value_begin); + uint16_t shadow_value_step = 1 << (shadow_granularity - cfi_check_granularity); + uint16_t shadow_value = shadow_value_begin; + + /* Set shadow_value */ + for (uint16_t *shadow_addr = tmp_shadow_start + offset_begin; + shadow_addr != tmp_shadow_start + offset_end; shadow_addr++) { + if (shadow_value < shadow_value_begin) { + *shadow_addr = sv_uncheck; + continue; + } + *shadow_addr = shadow_value; + shadow_value += shadow_value_step; + } + /* in these cases, shadow_value will always be sv_uncheck or sv_invalid */ + } else if (type == sv_uncheck || type == sv_invalid) { + /* Set shadow_value */ + for (uint16_t *shadow_addr = tmp_shadow_start + offset_begin; + shadow_addr != tmp_shadow_start + offset_end; shadow_addr++) { + *shadow_addr = type; + } + } else { + LD_LOGE("[%{public}s] has error param!\n", __FUNCTION__); + return false; + } + + mprotect(tmp_shadow_start, tmp_shadow_size, PROT_READ); + /* Remap temp_shadow to CFI shadow. */ + uint16_t* mremap_addr = mremap(tmp_shadow_start, tmp_shadow_size, tmp_shadow_size, + MREMAP_MAYMOVE | MREMAP_FIXED, aligned_shadow_begin); + + if (mremap_addr == MAP_FAILED) + { + LD_LOGE("[%{public}s] mremap failed!\n", __FUNCTION__); + return false; + } + + LD_LOGI("[%{public}s] fill completed!\n", __FUNCTION__); + return true; +} + +void __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + if (func_ptr == NULL) { + LD_LOGE("[%{public}s] has error param!\n", __FUNCTION__); + return; + } + + if (cfi_shadow_start == NULL) { + LD_LOGE("[%{public}s] the cfi_shadow_start is null!\n", __FUNCTION__); + __builtin_trap(); + } + + cfi_slowpath_common(call_site_type_id, func_ptr, NULL); + + return; +} + +void __cfi_slowpath_diag(uint64_t call_site_type_id, void *func_ptr, void *diag_data) +{ + LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + + if (func_ptr == NULL) { + LD_LOGE("[%{public}s] has error param!\n", __FUNCTION__); + return; + } + + if (cfi_shadow_start == NULL) { + LD_LOGE("[%{public}s] the cfi_shadow_start is null!\n", __FUNCTION__); + __builtin_trap(); + } + + cfi_slowpath_common(call_site_type_id, func_ptr, diag_data); + + return; +} \ No newline at end of file diff --git a/porting/linux/user/ldso/cfi.h b/porting/linux/user/ldso/cfi.h new file mode 100755 index 0000000000000000000000000000000000000000..4ed990a4eeaa162649466e21d982bc075a27590a --- /dev/null +++ b/porting/linux/user/ldso/cfi.h @@ -0,0 +1,13 @@ +#include "dynlink.h" + +/* alignment bits in memory space for dso */ +#define LIBRARY_ALIGNMENT_BITS 18 +/* minimum unit in memory space for dso */ +#define LIBRARY_ALIGNMENT (1UL << LIBRARY_ALIGNMENT_BITS) + +/* map all the dso and the dependents to cfi shadow */ +bool init_cfi_shadow(struct dso *dso_list); +/* map a dso and the dependents to cfi shadow */ +bool map_dso_to_cfi_shadow(struct dso *dso); +/* unmap a dso from cfi shadow */ +void unmap_dso_from_cfi_shadow(struct dso *dso); \ No newline at end of file diff --git a/porting/linux/user/ldso/dynlink.c b/porting/linux/user/ldso/dynlink.c index 6a4c474a7ba318f159890f8a04993789ef605fb4..b0388e2f800749eaa842562b61d558169332dc76 100644 --- a/porting/linux/user/ldso/dynlink.c +++ b/porting/linux/user/ldso/dynlink.c @@ -26,10 +26,12 @@ #include #include #include +#include #include "dlfcn_ext.h" #include "dynlink_rand.h" #include "ld_log.h" +#include "cfi.h" #include "libc.h" #include "malloc_impl.h" #include "namespace.h" @@ -88,105 +90,11 @@ struct reserved_address_params { #endif }; -struct td_index { - size_t args[2]; - struct td_index *next; -}; - -struct verinfo { - const char *s; - const char *v; - bool use_vna_hash; - uint32_t vna_hash; -}; - struct sym_info_pair { uint_fast32_t sym_h; uint32_t sym_l; }; -struct dso { -#if DL_FDPIC - struct fdpic_loadmap *loadmap; -#else - unsigned char *base; -#endif - char *name; - size_t *dynv; - struct dso *next, *prev; - /* add namespace */ - ns_t *namespace; - /* mark the dso status */ - unsigned int flags; - - int cache_sym_index; - struct dso *cache_dso; - Sym *cache_sym; - - Phdr *phdr; - int phnum; - size_t phentsize; - Sym *syms; - Elf_Symndx *hashtab; - uint32_t *ghashtab; - int16_t *versym; - Verdef *verdef; - Verneed *verneed; - char *strings; - struct dso *syms_next, *lazy_next; - size_t *lazy, lazy_cnt; - unsigned char *map; - size_t map_len; - dev_t dev; - ino_t ino; - uint64_t file_offset; - char relocated; - char constructed; - char kernel_mapped; - char mark; - char bfs_built; - char runtime_loaded; - char by_dlopen; - struct dso **deps, *needed_by; - size_t ndeps_direct; - size_t next_dep; - int ctor_visitor; - int nr_dlopen; - char *rpath_orig, *rpath; - struct tls_module tls; - size_t tls_id; - size_t relro_start, relro_end; - uintptr_t *new_dtv; - unsigned char *new_tls; - struct td_index *td_index; - struct dso *fini_next; - char *shortname; -#if DL_FDPIC - unsigned char *base; -#else - struct fdpic_loadmap *loadmap; -#endif - struct funcdesc { - void *addr; - size_t *got; - } *funcdescs; - size_t *got; - struct dso **parents; - size_t parents_count; - size_t parents_capacity; - bool is_global; - bool is_reloc_head_so_dep; - struct dso **reloc_can_search_dso_list; - size_t reloc_can_search_dso_count; - size_t reloc_can_search_dso_capacity; - char buf[]; -}; - -struct symdef { - Sym *sym; - struct dso *dso; -}; - typedef void (*stage3_func)(size_t *, size_t *); static struct builtin_tls { @@ -264,7 +172,6 @@ static void handle_relro_sharing(struct dso *p, const dl_extinfo *extinfo, ssize int handle_asan_path_open(int fd, const char *name, ns_t *namespace, char *buf, size_t buf_size); /* add namespace function */ -static void *addr2dso(size_t a); static void get_sys_path(ns_configor *conf); static void dlclose_ns(struct dso *p); static bool get_app_path(char *path, size_t size) @@ -429,7 +336,7 @@ static void init_namespace(struct dso *app) /* Compute load address for a virtual address in a given dso. */ #if DL_FDPIC -static void *laddr(const struct dso *p, size_t v) +void *laddr(const struct dso *p, size_t v) { size_t j=0; if (!p->loadmap) return p->base + v; @@ -799,7 +706,7 @@ static void add_can_search_so_list_in_dso(struct dso *dso_relocating, struct dso #if defined(__GNUC__) __attribute__((always_inline)) #endif -static inline struct symdef find_sym2(struct dso *dso, struct verinfo *verinfo, int need_def, int use_deps, ns_t *ns) +struct symdef find_sym2(struct dso *dso, struct verinfo *verinfo, int need_def, int use_deps, ns_t *ns) { struct sym_info_pair s_info_p = gnu_hash(verinfo->s); uint32_t h = 0, gh = s_info_p.sym_h, gho = gh / (8*sizeof(size_t)), *ght; @@ -1373,19 +1280,45 @@ static void *map_library(int fd, struct dso *dso, struct reserved_address_params map_flags |= MAP_FIXED; } } - /* The first time, we map too much, possibly even more than - * the length of the file. This is okay because we will not - * use the invalid part; we just need to reserve the right - * amount of virtual address space to map over later. */ - map = DL_NOMMU_SUPPORT - ? mmap((void *)start_addr, map_len, PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) - : mmap((void *)start_addr, map_len, prot, - map_flags, fd, off_start); - if (map==MAP_FAILED) goto error; - if (reserved_params && map_len < reserved_params->reserved_size) { - reserved_params->reserved_size -= (map_len + (start_addr - (size_t)reserved_params->start_addr)); - reserved_params->start_addr = (void *)((uint8_t *)map + map_len); + + /* we will find a LIBRARY_ALIGNMENT aligned address as the start of dso + * so we need a tmp_map_len as map_len + LIBRARY_ALIGNMENT to make sure + * we have enough space to shift the dso to the correct location*/ + size_t tmp_map_len = ALIGN(map_len, LIBRARY_ALIGNMENT) + LIBRARY_ALIGNMENT - PAGE_SIZE; + + /* if reserved_params exists, we should use start_addr as prefered result to do the mmap operation */ + if (reserved_params) { + map = DL_NOMMU_SUPPORT + ? mmap((void *)start_addr, map_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + : mmap((void *)start_addr, map_len, prot, map_flags, fd, off_start); + if (map == MAP_FAILED) { + goto error; + } + if (reserved_params && map_len < reserved_params->reserved_size) { + reserved_params->reserved_size -= (map_len + (start_addr - (size_t)reserved_params->start_addr)); + reserved_params->start_addr = (void *)((uint8_t *)map + map_len); + } + /* if reserved_params does not exist, we should use real_map as prefered result to do the mmap operation */ + } else { + /* use tmp_map_len to mmap enough space for the dso with anonymous mapping */ + unsigned char *temp_map = mmap((void *)NULL, tmp_map_len, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (temp_map == MAP_FAILED) { + goto error; + } + + /* find the LIBRARY_ALIGNMENT aligned address */ + unsigned char *real_map = (unsigned char*)ALIGN((uintptr_t)temp_map, LIBRARY_ALIGNMENT); + + /* mummap the space we mmap before so that we can mmap correct space again */ + munmap(temp_map, tmp_map_len); + + map = DL_NOMMU_SUPPORT + ? mmap(real_map, map_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + /* use map_len to mmap correct space for the dso with file mapping */ + : mmap(real_map, map_len, prot, map_flags, fd, off_start); + if (map == MAP_FAILED) { + goto error; + } } dso->map = map; dso->map_len = map_len; @@ -2902,6 +2835,13 @@ void __dls3(size_t *sp, size_t *auxv) libc.tls_size = tmp_tls_size; } + if (!init_cfi_shadow(head)) { + error("[%s] init_cfi_shadow failed: %m", __FUNCTION__); + if (runtime) { + longjmp(*rtld_fail, 1); + } + } + if (ldso_fail) _exit(127); if (ldd_mode) _exit(0); @@ -3202,6 +3142,13 @@ static void *dlopen_impl( * relocations resolved to symbol definitions that get removed. */ redo_lazy_relocs(); + if (!map_dso_to_cfi_shadow(p)) { + error("[%s] map_dso_to_cfi_shadow failed: %m", __FUNCTION__); + if (runtime) { + longjmp(*rtld_fail, 1); + } + } + if (mode & RTLD_NODELETE) { p->flags |= DSO_FLAGS_NODELETE; } @@ -3399,7 +3346,7 @@ hidden int __dl_invalid_handle(void *h) return 1; } -static void *addr2dso(size_t a) +void *addr2dso(size_t a) { struct dso *p; size_t i; @@ -3554,6 +3501,8 @@ static int dlclose_impl(struct dso *p) /* remove dso from namespace */ dlclose_ns(p); + + unmap_dso_from_cfi_shadow(p); if (p->lazy != NULL) internal_free(p->lazy); @@ -4177,20 +4126,48 @@ static bool task_map_library(struct loadtask *task, struct reserved_address_para map_flags |= MAP_FIXED; } } - /* The first time, we map too much, possibly even more than - * the length of the file. This is okay because we will not - * use the invalid part; we just need to reserve the right - * amount of virtual address space to map over later. */ - map = DL_NOMMU_SUPPORT + + /* we will find a LIBRARY_ALIGNMENT aligned address as the start of dso + * so we need a tmp_map_len as map_len + LIBRARY_ALIGNMENT to make sure + * we have enough space to shift the dso to the correct location*/ + size_t tmp_map_len = ALIGN(map_len, LIBRARY_ALIGNMENT) + LIBRARY_ALIGNMENT - PAGE_SIZE; + + /* if reserved_params exists, we should use start_addr as prefered result to do the mmap operation */ + if (reserved_params) { + map = DL_NOMMU_SUPPORT ? mmap((void *)start_addr, map_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) : mmap((void *)start_addr, map_len, prot, map_flags, task->fd, off_start + task->file_offset); - if (map == MAP_FAILED) { - LD_LOGE("Error mapping library %{public}s: failed to map fd", task->name); - goto error; - } - if (reserved_params && map_len < reserved_params->reserved_size) { - reserved_params->reserved_size -= (map_len + (start_addr - (size_t)reserved_params->start_addr)); - reserved_params->start_addr = (void *)((uint8_t *)map + map_len); + if (map == MAP_FAILED) { + LD_LOGE("Error mapping library %{public}s: failed to map fd", task->name); + goto error; + } + if (reserved_params && map_len < reserved_params->reserved_size) { + reserved_params->reserved_size -= (map_len + (start_addr - (size_t)reserved_params->start_addr)); + reserved_params->start_addr = (void *)((uint8_t *)map + map_len); + } + /* if reserved_params does not exist, we should use real_map as prefered result to do the mmap operation */ + } else { + /* use tmp_map_len to mmap enough space for the dso with anonymous mapping */ + unsigned char *temp_map = mmap((void *)NULL, tmp_map_len, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (temp_map == MAP_FAILED) { + LD_LOGE("Error mapping library 1 %{public}s: failed to map fd", task->name); + goto error; + } + + /* find the LIBRARY_ALIGNMENT aligned address */ + unsigned char *real_map = (unsigned char*)ALIGN((uintptr_t)temp_map, LIBRARY_ALIGNMENT); + + /* mummap the space we mmap before so that we can mmap correct space again */ + munmap(temp_map, tmp_map_len); + + map = DL_NOMMU_SUPPORT + ? mmap(real_map, map_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + /* use map_len to mmap correct space for the dso with file mapping */ + : mmap(real_map, map_len, prot, map_flags, task->fd, off_start + task->file_offset); + if (map == MAP_FAILED) { + LD_LOGE("Error mapping library 3 %{public}s: failed to map fd", task->name); + goto error; + } } task->p->map = map; task->p->map_len = map_len; diff --git a/porting/linux/user/src/internal/dynlink.h b/porting/linux/user/src/internal/dynlink.h index b5e15a6619f5fd2931bb6b19a6198ef48de0a47a..ccfe2447d910008adaa9359dbb29ff7e81e61830 100644 --- a/porting/linux/user/src/internal/dynlink.h +++ b/porting/linux/user/src/internal/dynlink.h @@ -6,6 +6,11 @@ #include #include #include +#include +#include +#include +#include "libc.h" +#include "../../ldso/namespace.h" #if UINTPTR_MAX == 0xffffffff typedef Elf32_Ehdr Ehdr; @@ -53,6 +58,100 @@ enum { REL_FUNCDESC_VAL, }; +struct dso { +#if DL_FDPIC + struct fdpic_loadmap *loadmap; +#else + unsigned char *base; +#endif + char *name; + size_t *dynv; + struct dso *next, *prev; + /* add namespace */ + ns_t *namespace; + /* mark the dso status */ + unsigned int flags; + + int cache_sym_index; + struct dso *cache_dso; + Sym *cache_sym; + + Phdr *phdr; + int phnum; + size_t phentsize; + Sym *syms; + Elf_Symndx *hashtab; + uint32_t *ghashtab; + int16_t *versym; + Verdef *verdef; + Verneed *verneed; + char *strings; + struct dso *syms_next, *lazy_next; + size_t *lazy, lazy_cnt; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + uint64_t file_offset; + char relocated; + char constructed; + char kernel_mapped; + char mark; + char bfs_built; + char runtime_loaded; + char by_dlopen; + struct dso **deps, *needed_by; + size_t ndeps_direct; + size_t next_dep; + int ctor_visitor; + int nr_dlopen; + char *rpath_orig, *rpath; + struct tls_module tls; + size_t tls_id; + size_t relro_start, relro_end; + uintptr_t *new_dtv; + unsigned char *new_tls; + struct td_index *td_index; + struct dso *fini_next; + char *shortname; +#if DL_FDPIC + unsigned char *base; +#else + struct fdpic_loadmap *loadmap; +#endif + struct funcdesc { + void *addr; + size_t *got; + } *funcdescs; + size_t *got; + struct dso **parents; + size_t parents_count; + size_t parents_capacity; + bool is_global; + bool is_reloc_head_so_dep; + struct dso **reloc_can_search_dso_list; + size_t reloc_can_search_dso_count; + size_t reloc_can_search_dso_capacity; + char buf[]; +}; + +struct symdef { + Sym *sym; + struct dso *dso; +}; + +struct verinfo { + const char *s; + const char *v; + bool use_vna_hash; + uint32_t vna_hash; +}; + +struct td_index { + size_t args[2]; + struct td_index *next; +}; + struct fdpic_loadseg { uintptr_t addr, p_vaddr, p_memsz; }; @@ -118,6 +217,13 @@ struct fdpic_dummy_loadmap { typedef void (*stage2_func)(unsigned char *, size_t *); +#if DL_FDPIC +void *laddr(const struct dso *p, size_t v); +#endif + +void *addr2dso(size_t a); +struct symdef find_sym2(struct dso *dso, struct verinfo *verinfo, int need_def, int use_deps, ns_t *ns); + hidden void *__dlsym(void *restrict, const char *restrict, void *restrict); hidden void *__dlvsym(void *restrict, const char *restrict, const char *restrict, void *restrict); hidden int __dlclose(void *p);