// Copyright (C) 2017 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. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace chainagents { static constexpr const char* kChainFile = "chain_agents.txt"; static constexpr const char* kOnLoad = "Agent_OnLoad"; static constexpr const char* kOnAttach = "Agent_OnAttach"; static constexpr const char* kOnUnload = "Agent_OnUnload"; using AgentLoadFunction = jint (*)(JavaVM*, const char*, void*); using AgentUnloadFunction = jint (*)(JavaVM*); // Global namespace. Shared by every usage of this wrapper unfortunately. // We need to keep track of them to call Agent_OnUnload. static std::mutex unload_mutex; struct Unloader { AgentUnloadFunction unload; }; static std::vector unload_functions; enum class StartType { OnAttach, OnLoad, }; static std::pair Split(std::string source, char delim) { std::string first(source.substr(0, source.find(delim))); if (source.find(delim) == std::string::npos) { return std::pair(first, ""); } else { return std::pair(first, source.substr(source.find(delim) + 1)); } } static jint Load(StartType start, JavaVM* vm, void* reserved, const std::pair& lib_and_args, /*out*/ std::string* err) { void* handle = dlopen(lib_and_args.first.c_str(), RTLD_LAZY); std::ostringstream oss; if (handle == nullptr) { oss << "Failed to dlopen due to " << dlerror(); *err = oss.str(); return JNI_ERR; } AgentLoadFunction alf = reinterpret_cast( dlsym(handle, start == StartType::OnLoad ? kOnLoad : kOnAttach)); if (alf == nullptr) { oss << "Failed to dlsym " << (start == StartType::OnLoad ? kOnLoad : kOnAttach) << " due to " << dlerror(); *err = oss.str(); return JNI_ERR; } jint res = alf(vm, lib_and_args.second.c_str(), reserved); if (res != JNI_OK) { *err = "load function failed!"; return res; } AgentUnloadFunction auf = reinterpret_cast(dlsym(handle, kOnUnload)); if (auf != nullptr) { unload_functions.push_back({ auf }); } return JNI_OK; } static jint AgentStart(StartType start, JavaVM* vm, char* options, void* reserved) { std::string input_file(options); input_file = input_file + "/" + kChainFile; std::ifstream input(input_file); std::string line; std::lock_guard mu(unload_mutex); while (std::getline(input, line)) { std::pair lib_and_args(Split(line, '=')); std::string err; jint new_res = Load(start, vm, reserved, lib_and_args, &err); if (new_res != JNI_OK) { PLOG(WARNING) << "Failed to load library " << lib_and_args.first << " (arguments: " << lib_and_args.second << ") due to " << err; } } return JNI_OK; } // Late attachment (e.g. 'am attach-agent'). extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { return AgentStart(StartType::OnAttach, vm, options, reserved); } // Early attachment // (e.g. 'java // -agentpath:/path/to/libwrapagentproperties.so=/path/to/propfile,/path/to/wrapped.so=[ops]'). extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) { return AgentStart(StartType::OnLoad, jvm, options, reserved); } extern "C" JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* jvm) { std::lock_guard lk(unload_mutex); for (const Unloader& u : unload_functions) { u.unload(jvm); // Don't dlclose since some agents expect to still have code loaded after this. } unload_functions.clear(); } } // namespace chainagents