diff --git a/ddprof-lib/gtest/build.gradle b/ddprof-lib/gtest/build.gradle index dc3e9e7c6..dd6b39324 100644 --- a/ddprof-lib/gtest/build.gradle +++ b/ddprof-lib/gtest/build.gradle @@ -37,42 +37,37 @@ tasks.withType(StripSymbols).configureEach { task -> } } -def buildResourcesTask = tasks.register("buildResources", Exec) { +def buildNativeLibsTask = tasks.register("buildNativeLibs") { group = 'build' - description = "Build the resources for the Google Tests" + description = "Build the native libs for the Google Tests" onlyIf { hasGtest && !project.hasProperty('skip-native') && !project.hasProperty('skip-gtest') && os().isLinux() } - def targetDir = project(':ddprof-lib').file('build/test/resources/unresolved-functions') - - commandLine "sh", "-c", "cd ${project(':ddprof-lib').projectDir}/src/test/resources/unresolved-functions && make TARGET_DIR=${targetDir}" - - inputs.files project(':ddprof-lib').files('src/test/resources/unresolved-functions') - outputs.file "${targetDir}/main" -} - -def buildSmallLibTask = tasks.register("buildSmallLib", Exec) { - group = 'build' - description = "Build the small-lib shared library for the Google Tests" - - onlyIf { - hasGtest && !project.hasProperty('skip-native') && !project.hasProperty('skip-gtest') && os().isLinux() + def srcDir = project(':ddprof-lib').file('src/test/resources/native-libs') + def targetDir = project(':ddprof-lib').file('build/test/resources/native-libs/') + + // Move the exec calls to the execution phase + doLast { + srcDir.eachDir { dir -> + def libName = dir.name + def libDir = file("${targetDir}/${libName}") + def libSrcDir = file("${srcDir}/${libName}") + + exec { + commandLine "sh", "-c", """ + echo "Processing library: ${libName} @ ${libSrcDir}" + mkdir -p ${libDir} + cd ${libSrcDir} + make TARGET_DIR=${libDir} + """ + } + } } - def sourceDir = project(':ddprof-lib').file('src/test/resources/small-lib') - def targetDir = project(':ddprof-lib').file('build/test/resources/small-lib') - - commandLine "sh", "-c", """ - mkdir -p ${targetDir} - cd ${sourceDir} && g++ -fPIC -shared -o ${targetDir}/libsmall-lib.so *.cpp - """ - - inputs.files fileTree(sourceDir) { - include '**/*.cpp' - } - outputs.file "${targetDir}/libsmall-lib.so" + inputs.files project(':ddprof-lib').files('src/test/resources/native-libs/**/*') + outputs.dir "${targetDir}" } def gtestAll = tasks.register("gtest") { @@ -204,7 +199,7 @@ tasks.whenTaskAdded { task -> gtestTask.get().dependsOn gtestExecuteTask.get() if (os().isLinux()) { // custom binaries for tests are built only on linux - gtestExecuteTask.get().dependsOn(buildResourcesTask, buildSmallLibTask) + gtestExecuteTask.get().dependsOn(buildNativeLibs) } gtestAll.get().dependsOn gtestExecuteTask.get() } diff --git a/ddprof-lib/src/main/cpp/arch.h b/ddprof-lib/src/main/cpp/arch.h index e0b898382..192273a78 100644 --- a/ddprof-lib/src/main/cpp/arch.h +++ b/ddprof-lib/src/main/cpp/arch.h @@ -1,17 +1,6 @@ /* - * Copyright 2017 Andrei Pangin - * - * 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. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #ifndef _ARCH_H @@ -19,12 +8,19 @@ #include +#ifndef unlikely +# define unlikely(x) (__builtin_expect(!!(x), 0)) +#endif + +#define callerPC() __builtin_return_address(0) + #ifdef _LP64 # define LP64_ONLY(code) code #else // !_LP64 # define LP64_ONLY(code) #endif // _LP64 + typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; diff --git a/ddprof-lib/src/main/cpp/callTraceStorage.cpp b/ddprof-lib/src/main/cpp/callTraceStorage.cpp index e8c0dd128..478d27501 100644 --- a/ddprof-lib/src/main/cpp/callTraceStorage.cpp +++ b/ddprof-lib/src/main/cpp/callTraceStorage.cpp @@ -1,17 +1,6 @@ /* - * Copyright 2020 Andrei Pangin - * - * 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. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #include "callTraceStorage.h" diff --git a/ddprof-lib/src/main/cpp/codeCache.cpp b/ddprof-lib/src/main/cpp/codeCache.cpp index 8030090b1..37582e76e 100644 --- a/ddprof-lib/src/main/cpp/codeCache.cpp +++ b/ddprof-lib/src/main/cpp/codeCache.cpp @@ -1,17 +1,6 @@ /* - * Copyright 2016 Andrei Pangin - * - * 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. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #include "codeCache.h" @@ -274,23 +263,54 @@ void CodeCache::findSymbolsByPrefix(std::vector &prefixes, } } -void CodeCache::addImport(void **entry, const char *name) { - switch (name[0]) { - case 'd': - if (strcmp(name, "dlopen") == 0) { - _imports[im_dlopen] = entry; +void CodeCache::saveImport(ImportId id, void** entry) { + for (int ty = 0; ty < NUM_IMPORT_TYPES; ty++) { + if (_imports[id][ty] == nullptr) { + _imports[id][ty] = entry; + return; + } } - break; - case 'p': - if (strcmp(name, "pthread_create") == 0) { - _imports[im_pthread_create] = entry; - } else if (strcmp(name, "pthread_exit") == 0) { - _imports[im_pthread_exit] = entry; - } else if (strcmp(name, "pthread_setspecific") == 0) { - _imports[im_pthread_setspecific] = entry; +} + +void CodeCache::addImport(void **entry, const char *name) { + switch (name[0]) { + case 'c': + if (strcmp(name, "calloc") == 0) { + saveImport(im_calloc, entry); + } + break; + case 'd': + if (strcmp(name, "dlopen") == 0) { + saveImport(im_dlopen, entry); + } + break; + case 'f': + if (strcmp(name, "free") == 0) { + saveImport(im_free, entry); + } + break; + case 'm': + if (strcmp(name, "malloc") == 0) { + saveImport(im_malloc, entry); + } + break; + case 'p': + if (strcmp(name, "pthread_create") == 0) { + saveImport(im_pthread_create, entry); + } else if (strcmp(name, "pthread_exit") == 0) { + saveImport(im_pthread_exit, entry); + } else if (strcmp(name, "pthread_setspecific") == 0) { + saveImport(im_pthread_setspecific, entry); + } else if (strcmp(name, "poll") == 0) { + saveImport(im_poll, entry); + } + break; + case 'r': + if (strcmp(name, "realloc") == 0) { + saveImport(im_realloc, entry); + } + break; } - break; - } } void **CodeCache::findImport(ImportId id) { @@ -298,24 +318,33 @@ void **CodeCache::findImport(ImportId id) { makeImportsPatchable(); _imports_patchable = true; } - return _imports[id]; + return _imports[id][PRIMARY]; } void CodeCache::patchImport(ImportId id, void *hook_func) { - void **entry = findImport(id); + if (!_imports_patchable) { + makeImportsPatchable(); + _imports_patchable = true; + } + + for (int ty = 0; ty < NUM_IMPORT_TYPES; ty++) {void **entry = _imports[id][ty]; if (entry != NULL) { *entry = hook_func; - } + }} } void CodeCache::makeImportsPatchable() { void **min_import = (void **)-1; void **max_import = NULL; for (int i = 0; i < NUM_IMPORTS; i++) { - if (_imports[i] != NULL && _imports[i] < min_import) - min_import = _imports[i]; - if (_imports[i] != NULL && _imports[i] > max_import) - max_import = _imports[i]; + for (int j = 0; j < NUM_IMPORT_TYPES; j++) { + void** entry = _imports[i][j]; + if (entry == NULL) continue; + if (entry < min_import) + min_import = entry; + if (entry > max_import) + max_import = entry; + } } if (max_import != NULL) { diff --git a/ddprof-lib/src/main/cpp/codeCache.h b/ddprof-lib/src/main/cpp/codeCache.h index 41a658e8f..2f4d4d196 100644 --- a/ddprof-lib/src/main/cpp/codeCache.h +++ b/ddprof-lib/src/main/cpp/codeCache.h @@ -1,17 +1,6 @@ /* - * Copyright 2017 Andrei Pangin - * - * 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. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #ifndef _CODECACHE_H @@ -35,9 +24,20 @@ enum ImportId { im_pthread_create, im_pthread_exit, im_pthread_setspecific, + im_poll, + im_malloc, + im_calloc, + im_realloc, + im_free, NUM_IMPORTS }; +enum ImportType { + PRIMARY, + SECONDARY, + NUM_IMPORT_TYPES +}; + class NativeFunc { private: short _lib_index; @@ -58,7 +58,7 @@ class NativeFunc { if (posix_memalign((void**)(&func), sizeof(NativeFunc*), sizeof(NativeFunc)) != 0) { return -1; } - return func->_lib_index; + return func->_lib_index; } static bool isMarked(const char *name) { return from(name)->_mark != 0; } @@ -100,7 +100,7 @@ class CodeCache { unsigned int _plt_offset; unsigned int _plt_size; - void **_imports[NUM_IMPORTS]; + void **_imports[NUM_IMPORTS][NUM_IMPORT_TYPES]; bool _imports_patchable; bool _debug_symbols; @@ -113,6 +113,7 @@ class CodeCache { void expand(); void makeImportsPatchable(); + void saveImport(ImportId id, void** entry); public: explicit CodeCache(const char *name, short lib_index = -1, diff --git a/ddprof-lib/src/main/cpp/flightRecorder.cpp b/ddprof-lib/src/main/cpp/flightRecorder.cpp index b2427ce1b..9b6fa3bd6 100644 --- a/ddprof-lib/src/main/cpp/flightRecorder.cpp +++ b/ddprof-lib/src/main/cpp/flightRecorder.cpp @@ -1,18 +1,7 @@ /* - * Copyright 2018 Andrei Pangin - * Copyright 2021, 2023 Datadog, Inc - * - * 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. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021, 2025 Datadog, Inc */ #include @@ -133,7 +122,7 @@ void Lookup::fillJavaMethodInfo(MethodInfo *mi, jmethodID method, jvmtiEnv *jvmti = VM::jvmti(); jvmtiPhase phase; - jclass method_class; + jclass method_class = NULL; // invariant: these strings must remain null, or be assigned by JVMTI char *class_name = nullptr; char *method_name = nullptr; @@ -590,10 +579,10 @@ bool Recording::parseAgentProperties() { if (get_agent_props != NULL && to_string != NULL) { jobject props = env->CallStaticObjectMethod(vm_support, get_agent_props); jniExceptionCheck(env); - if (props != NULL) { + if (props != NULL && !env->ExceptionCheck()) { jstring str = (jstring)env->CallObjectMethod(props, to_string); jniExceptionCheck(env); - if (str != NULL) { + if (str != NULL && !env->ExceptionCheck()) { _agent_properties = (char *)env->GetStringUTFChars(str, NULL); } } diff --git a/ddprof-lib/src/main/cpp/profiler.cpp b/ddprof-lib/src/main/cpp/profiler.cpp index 8adb74fb4..deec41fd0 100644 --- a/ddprof-lib/src/main/cpp/profiler.cpp +++ b/ddprof-lib/src/main/cpp/profiler.cpp @@ -1,18 +1,7 @@ /* - * Copyright 2016 Andrei Pangin - * Copyright 2024 Datadog, Inc - * - * 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. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2024, 2025 Datadog, Inc */ #include "profiler.h" diff --git a/ddprof-lib/src/main/cpp/symbols_linux.cpp b/ddprof-lib/src/main/cpp/symbols_linux.cpp index 891a4bec5..f0e35f3f2 100644 --- a/ddprof-lib/src/main/cpp/symbols_linux.cpp +++ b/ddprof-lib/src/main/cpp/symbols_linux.cpp @@ -1,17 +1,6 @@ /* - * Copyright 2017 Andrei Pangin - * - * 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. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #ifdef __linux__ @@ -196,17 +185,22 @@ void ElfParser::parseDynamicSection() { // Parse .rela.plt table for (size_t offs = 0; offs < pltrelsz; offs += relent) { ElfRelocation *r = (ElfRelocation *)(jmprel + offs); - ElfSymbol *sym = (ElfSymbol *)(symtab + ELF_R_SYM(r->r_info) * syment); - if (sym->st_name != 0) { - _cc->addImport((void **)(_base + r->r_offset), strtab + sym->st_name); + + ElfSymbol *sym = (ElfSymbol *)(symtab + ELF_R_SYM(r->r_info) * syment); + if (sym->st_name != 0) { + _cc->addImport((void **)(_base + r->r_offset), strtab + sym->st_name); + } } - } else if (rel != NULL && relsz != 0) { - // Shared library was built without PLT (-fno-plt) - // Relocation entries have been moved from .rela.plt to .rela.dyn + } + + if (rel != NULL && relsz != 0) { + // Relocation entries for imports can be found in .rela.dyn, for example + // if a shared library is built without PLT (-fno-plt). However, if both + // entries exist, addImport saves them both. for (size_t offs = relcount * relent; offs < relsz; offs += relent) { ElfRelocation *r = (ElfRelocation *)(rel + offs); - if (ELF_R_TYPE(r->r_info) == R_GLOB_DAT) { + if (ELF_R_TYPE(r->r_info) == R_GLOB_DAT || ELF_R_TYPE(r->r_info) == R_ABS64) { ElfSymbol *sym = (ElfSymbol *)(symtab + ELF_R_SYM(r->r_info) * syment); if (sym->st_name != 0) { @@ -214,9 +208,8 @@ void ElfParser::parseDynamicSection() { strtab + sym->st_name); } } - } + }} } - } } void ElfParser::parseDwarfInfo() { diff --git a/ddprof-lib/src/main/cpp/symbols_linux.h b/ddprof-lib/src/main/cpp/symbols_linux.h index b7b82b96d..9dda9704f 100644 --- a/ddprof-lib/src/main/cpp/symbols_linux.h +++ b/ddprof-lib/src/main/cpp/symbols_linux.h @@ -88,17 +88,32 @@ typedef Elf32_Dyn ElfDyn; #endif // __LP64__ #if defined(__x86_64__) -#define R_GLOB_DAT R_X86_64_GLOB_DAT +# define R_GLOB_DAT R_X86_64_GLOB_DAT +# define R_ABS64 R_X86_64_64 #elif defined(__i386__) -#define R_GLOB_DAT R_386_GLOB_DAT +# define R_GLOB_DAT R_386_GLOB_DAT +# define R_ABS64 -1 #elif defined(__arm__) || defined(__thumb__) -#define R_GLOB_DAT R_ARM_GLOB_DAT +# define R_GLOB_DAT R_ARM_GLOB_DAT +# define R_ABS64 -1 #elif defined(__aarch64__) -#define R_GLOB_DAT R_AARCH64_GLOB_DAT +# define R_GLOB_DAT R_AARCH64_GLOB_DAT +# define R_ABS64 R_AARCH64_ABS64 #elif defined(__PPC64__) -#define R_GLOB_DAT R_PPC64_GLOB_DAT +# define R_GLOB_DAT R_PPC64_GLOB_DAT +# define R_ABS64 -1 +#elif defined(__riscv) && (__riscv_xlen == 64) +// RISC-V does not have GLOB_DAT relocation, use something neutral, +// like the impossible relocation number. +# define R_GLOB_DAT -1 +# define R_ABS64 -1 +#elif defined(__loongarch_lp64) +// LOONGARCH does not have GLOB_DAT relocation, use something neutral, +// like the impossible relocation number. +# define R_GLOB_DAT -1 +# define R_ABS64 -1 #else -#error "Compiling on unsupported arch" +# error "Compiling on unsupported arch" #endif #ifdef __musl__ diff --git a/ddprof-lib/src/main/cpp/vmEntry.h b/ddprof-lib/src/main/cpp/vmEntry.h index b30c3514f..a8e861f32 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.h +++ b/ddprof-lib/src/main/cpp/vmEntry.h @@ -1,24 +1,14 @@ /* - * Copyright 2016 Andrei Pangin - * Copyright 2021, 2023 Datadog, Inc - * - * 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. + * Copyright The async-profiler authors + * Copyright 2021, 2025 Datadog, Inc + * SPDX-License-Identifier: Apache-2.0 */ #ifndef _VMENTRY_H #define _VMENTRY_H #include +#include "arch.h" #include "arch.h" #include "codeCache.h" @@ -62,10 +52,9 @@ enum ASGCT_Failure { }; typedef struct { - jint bci; - // see https://github.com/async-profiler/async-profiler/pull/1090 - LP64_ONLY(jint padding;) - jmethodID method_id; + jint bci; + LP64_ONLY(jint padding;) + jmethodID method_id; } ASGCT_CallFrame; diff --git a/ddprof-lib/src/test/cpp/elfparser_ut.cpp b/ddprof-lib/src/test/cpp/elfparser_ut.cpp index e2a9143da..8aa40b0a8 100644 --- a/ddprof-lib/src/test/cpp/elfparser_ut.cpp +++ b/ddprof-lib/src/test/cpp/elfparser_ut.cpp @@ -1,8 +1,10 @@ #ifdef __linux__ +#include #include #include "codeCache.h" +#include "libraries.h" #include "symbols_linux.h" #include "log.h" @@ -27,7 +29,7 @@ TEST(Elf, readSymTable) { exit(1); } char path[PATH_MAX]; - snprintf(path, sizeof(path) - 1, "%s/../build/test/resources/unresolved-functions/main", cwd); + snprintf(path, sizeof(path) - 1, "%s/../build/test/resources/native-libs/unresolved-functions/main", cwd); if (access(path, R_OK) != 0) { fprintf(stdout, "Missing test resource %s. Skipping the test\n", path); exit(0); @@ -36,6 +38,57 @@ TEST(Elf, readSymTable) { ElfParser::parseFile(&cc, nullptr, path, false); } +class ElfReladyn : public ::testing::Test { + protected: + Libraries* _libs = nullptr; + CodeCache* _libreladyn = nullptr; + + // This method is called before each test. + void SetUp() override { + char cwd[PATH_MAX - 64]; + if (getcwd(cwd, sizeof(cwd)) == nullptr) { + exit(1); + } + char path[PATH_MAX]; + snprintf(path, sizeof(path) - 1, "%s/../build/test/resources/native-libs/reladyn-lib/libreladyn.so", cwd); + if (access(path, R_OK) != 0) { + fprintf(stdout, "Missing test resource %s. Skipping the test\n", path); + exit(0); + } + void* handle = dlopen(path, RTLD_NOW); + ASSERT_THAT(handle, ::testing::NotNull()); + + _libs = Libraries::instance(); + _libs->updateSymbols(false); + _libreladyn = _libs->findLibraryByName("libreladyn"); + ASSERT_THAT(_libreladyn, ::testing::NotNull()); + } + + // This method is called after each test. + void TearDown() override { + // Clean up resources. + } + + CodeCache* libreladyn() { + return _libreladyn; + } +}; + +TEST_F(ElfReladyn, resolveFromRela_plt) { + void* sym = libreladyn()->findImport(im_pthread_create); + ASSERT_THAT(sym, ::testing::NotNull()); +} + +TEST_F(ElfReladyn, resolveFromRela_dyn_R_GLOB_DAT) { + void* sym = libreladyn()->findImport(im_pthread_setspecific); + ASSERT_THAT(sym, ::testing::NotNull()); +} + +TEST_F(ElfReladyn, resolveFromRela_dyn_R_ABS64) { + void* sym = libreladyn()->findImport(im_pthread_exit); + ASSERT_THAT(sym, ::testing::NotNull()); +} + class ElfTest : public ::testing::Test { protected: void SetUp() override { @@ -247,7 +300,7 @@ TEST_P(ElfTestParam, invalidElfSmallMappingAfterUnmap) { // Construct the path to the test resource char path[PATH_MAX]; - snprintf(path, sizeof(path) - 1, "%s/../build/test/resources/small-lib/libsmall-lib.so", cwd); + snprintf(path, sizeof(path) - 1, "%s/../build/test/resources/native-libs/small-lib/libsmall-lib.so", cwd); if (access(path, R_OK) != 0) { fprintf(stdout, "Missing test resource %s. Skipping the test\n", path); exit(1); diff --git a/ddprof-lib/src/test/resources/native-libs/reladyn-lib/Makefile b/ddprof-lib/src/test/resources/native-libs/reladyn-lib/Makefile new file mode 100644 index 000000000..6f50129b3 --- /dev/null +++ b/ddprof-lib/src/test/resources/native-libs/reladyn-lib/Makefile @@ -0,0 +1,3 @@ +TARGET_DIR = ../build/test/resources/native-libs/reladyn-lib +all: + g++ -fPIC -shared -o $(TARGET_DIR)/libreladyn.so reladyn.c diff --git a/ddprof-lib/src/test/resources/native-libs/reladyn-lib/reladyn.c b/ddprof-lib/src/test/resources/native-libs/reladyn-lib/reladyn.c new file mode 100644 index 000000000..4d87f57ed --- /dev/null +++ b/ddprof-lib/src/test/resources/native-libs/reladyn-lib/reladyn.c @@ -0,0 +1,28 @@ +/* + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +// Force pthread_setspecific into .rela.dyn with R_X86_64_GLOB_DAT. +int (*indirect_pthread_setspecific)(pthread_key_t, const void*); +// Force pthread_exit into .rela.dyn with R_X86_64_64. +void (*static_pthread_exit)(void*) = pthread_exit; +void* thread_function(void* arg) { + printf("Thread running\n"); + return NULL; +} +// Not indended to be executed. +int reladyn() { + pthread_t thread; + pthread_key_t key; + pthread_key_create(&key, NULL); + // Direct call, forces into .rela.plt. + pthread_create(&thread, NULL, thread_function, NULL); + // Assign to a function pointer at runtime, forces into .rela.dyn as R_X86_64_GLOB_DAT. + indirect_pthread_setspecific = pthread_setspecific; + indirect_pthread_setspecific(key, "Thread-specific value"); + // Use pthread_exit via the static pointer, forces into .rela.dyn as R_X86_64_64. + static_pthread_exit(NULL); + return 0; +} \ No newline at end of file diff --git a/ddprof-lib/src/test/resources/native-libs/small-lib/Makefile b/ddprof-lib/src/test/resources/native-libs/small-lib/Makefile new file mode 100644 index 000000000..86b19bd36 --- /dev/null +++ b/ddprof-lib/src/test/resources/native-libs/small-lib/Makefile @@ -0,0 +1,3 @@ +TARGET_DIR = ../build/test/resources/native-libs/small-lib +all: + g++ -fPIC -shared -o $(TARGET_DIR)/libsmall-lib.so small_lib.cpp diff --git a/ddprof-lib/src/test/resources/small-lib/small_lib.cpp b/ddprof-lib/src/test/resources/native-libs/small-lib/small_lib.cpp similarity index 100% rename from ddprof-lib/src/test/resources/small-lib/small_lib.cpp rename to ddprof-lib/src/test/resources/native-libs/small-lib/small_lib.cpp diff --git a/ddprof-lib/src/test/resources/small-lib/small_lib.h b/ddprof-lib/src/test/resources/native-libs/small-lib/small_lib.h similarity index 100% rename from ddprof-lib/src/test/resources/small-lib/small_lib.h rename to ddprof-lib/src/test/resources/native-libs/small-lib/small_lib.h diff --git a/ddprof-lib/src/test/resources/unresolved-functions/Makefile b/ddprof-lib/src/test/resources/native-libs/unresolved-functions/Makefile similarity index 100% rename from ddprof-lib/src/test/resources/unresolved-functions/Makefile rename to ddprof-lib/src/test/resources/native-libs/unresolved-functions/Makefile diff --git a/ddprof-lib/src/test/resources/unresolved-functions/linker.ld b/ddprof-lib/src/test/resources/native-libs/unresolved-functions/linker.ld similarity index 100% rename from ddprof-lib/src/test/resources/unresolved-functions/linker.ld rename to ddprof-lib/src/test/resources/native-libs/unresolved-functions/linker.ld diff --git a/ddprof-lib/src/test/resources/unresolved-functions/main.c b/ddprof-lib/src/test/resources/native-libs/unresolved-functions/main.c similarity index 100% rename from ddprof-lib/src/test/resources/unresolved-functions/main.c rename to ddprof-lib/src/test/resources/native-libs/unresolved-functions/main.c diff --git a/ddprof-lib/src/test/resources/unresolved-functions/readme.txt b/ddprof-lib/src/test/resources/native-libs/unresolved-functions/readme.txt similarity index 100% rename from ddprof-lib/src/test/resources/unresolved-functions/readme.txt rename to ddprof-lib/src/test/resources/native-libs/unresolved-functions/readme.txt diff --git a/ddprof-lib/src/test/resources/small-lib/Makefile b/ddprof-lib/src/test/resources/small-lib/Makefile deleted file mode 100644 index 1d484f4a3..000000000 --- a/ddprof-lib/src/test/resources/small-lib/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -TARGET_DIR = ../build/test/resources/small-lib -all: - g++ -fPIC -shared -o libsmall-lib.so small_lib.cpp diff --git a/test/native/libs/reladyn.c b/test/native/libs/reladyn.c new file mode 100644 index 000000000..2133004c5 --- /dev/null +++ b/test/native/libs/reladyn.c @@ -0,0 +1,38 @@ +/* + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +// Force pthread_setspecific into .rela.dyn with R_X86_64_GLOB_DAT. +int (*indirect_pthread_setspecific)(pthread_key_t, const void*); + +// Force pthread_exit into .rela.dyn with R_X86_64_64. +void (*static_pthread_exit)(void*) = pthread_exit; + +void* thread_function(void* arg) { + printf("Thread running\n"); + return NULL; +} + +// Not indended to be executed. +int reladyn() { + pthread_t thread; + pthread_key_t key; + + pthread_key_create(&key, NULL); + + // Direct call, forces into .rela.plt. + pthread_create(&thread, NULL, thread_function, NULL); + + // Assign to a function pointer at runtime, forces into .rela.dyn as R_X86_64_GLOB_DAT. + indirect_pthread_setspecific = pthread_setspecific; + indirect_pthread_setspecific(key, "Thread-specific value"); + + // Use pthread_exit via the static pointer, forces into .rela.dyn as R_X86_64_64. + static_pthread_exit(NULL); + + return 0; +} diff --git a/test/native/symbolsLinuxTest.cpp b/test/native/symbolsLinuxTest.cpp new file mode 100644 index 000000000..537cf808b --- /dev/null +++ b/test/native/symbolsLinuxTest.cpp @@ -0,0 +1,36 @@ +/* + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifdef __linux__ + +#include "codeCache.h" +#include "profiler.h" +#include "testRunner.hpp" +#include + +#define ASSERT_RESOLVE(id) \ + { \ + void* result = dlopen("libreladyn.so", RTLD_NOW); /* see reladyn.c */ \ + ASSERT(result); \ + Profiler::instance()->updateSymbols(false); \ + CodeCache* libreladyn = Profiler::instance()->findLibraryByName("libreladyn"); \ + ASSERT(libreladyn); \ + void* sym = libreladyn->findImport(id); \ + ASSERT(sym); \ + } + +TEST_CASE(ResolveFromRela_plt) { + ASSERT_RESOLVE(im_pthread_create); +} + +TEST_CASE(ResolveFromRela_dyn_R_GLOB_DAT) { + ASSERT_RESOLVE(im_pthread_setspecific); +} + +TEST_CASE(ResolveFromRela_dyn_R_ABS64) { + ASSERT_RESOLVE(im_pthread_exit); +} + +#endif // __linux__ diff --git a/test/test/nativemem/malloc_plt_dyn.c b/test/test/nativemem/malloc_plt_dyn.c new file mode 100644 index 000000000..d61d20e80 --- /dev/null +++ b/test/test/nativemem/malloc_plt_dyn.c @@ -0,0 +1,25 @@ +/* + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +const int MALLOC_SIZE = 1999993; +const int MALLOC_DYN_SIZE = 2000003; + +// A global pointer referencing malloc as data -> .rela.dyn +static void* (*malloc_dyn)(size_t) = malloc; + +int main(void) { + // Direct call -> .rela.plt + void* p = malloc(MALLOC_SIZE); + + void* q = malloc_dyn(MALLOC_DYN_SIZE); + + free(p); + free(q); + + return 0; +}