1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "agent.h"
18 
19 #include "android-base/stringprintf.h"
20 #include "nativehelper/scoped_local_ref.h"
21 #include "nativeloader/native_loader.h"
22 
23 #include "base/logging.h"
24 #include "base/strlcpy.h"
25 #include "jni/java_vm_ext.h"
26 #include "runtime.h"
27 #include "thread-current-inl.h"
28 #include "scoped_thread_state_change-inl.h"
29 
30 namespace art {
31 namespace ti {
32 
33 using android::base::StringPrintf;
34 
35 const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
36 const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
37 const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
38 
AgentSpec(const std::string & arg)39 AgentSpec::AgentSpec(const std::string& arg) {
40   size_t eq = arg.find_first_of('=');
41   if (eq == std::string::npos) {
42     name_ = arg;
43   } else {
44     name_ = arg.substr(0, eq);
45     args_ = arg.substr(eq + 1, arg.length());
46   }
47 }
48 
Load(jint * call_res,LoadError * error,std::string * error_msg)49 std::unique_ptr<Agent> AgentSpec::Load(/*out*/jint* call_res,
50                                        /*out*/LoadError* error,
51                                        /*out*/std::string* error_msg) {
52   VLOG(agents) << "Loading agent: " << name_ << " " << args_;
53   return DoLoadHelper(nullptr, false, nullptr, call_res, error, error_msg);
54 }
55 
56 // Tries to attach the agent using its OnAttach method. Returns true on success.
Attach(JNIEnv * env,jobject class_loader,jint * call_res,LoadError * error,std::string * error_msg)57 std::unique_ptr<Agent> AgentSpec::Attach(JNIEnv* env,
58                                          jobject class_loader,
59                                          /*out*/jint* call_res,
60                                          /*out*/LoadError* error,
61                                          /*out*/std::string* error_msg) {
62   VLOG(agents) << "Attaching agent: " << name_ << " " << args_;
63   return DoLoadHelper(env, true, class_loader, call_res, error, error_msg);
64 }
65 
66 
67 // TODO We need to acquire some locks probably.
DoLoadHelper(JNIEnv * env,bool attaching,jobject class_loader,jint * call_res,LoadError * error,std::string * error_msg)68 std::unique_ptr<Agent> AgentSpec::DoLoadHelper(JNIEnv* env,
69                                                bool attaching,
70                                                jobject class_loader,
71                                                /*out*/jint* call_res,
72                                                /*out*/LoadError* error,
73                                                /*out*/std::string* error_msg) {
74   ScopedThreadStateChange stsc(Thread::Current(), ThreadState::kNative);
75   DCHECK(call_res != nullptr);
76   DCHECK(error_msg != nullptr);
77 
78   std::unique_ptr<Agent> agent = DoDlOpen(env, class_loader, error, error_msg);
79   if (agent == nullptr) {
80     VLOG(agents) << "err: " << *error_msg;
81     return nullptr;
82   }
83   AgentOnLoadFunction callback = attaching ? agent->onattach_ : agent->onload_;
84   if (callback == nullptr) {
85     *error_msg = StringPrintf("Unable to start agent %s: No %s callback found",
86                               (attaching ? "attach" : "load"),
87                               name_.c_str());
88     VLOG(agents) << "err: " << *error_msg;
89     *error = kLoadingError;
90     return nullptr;
91   }
92   // Need to let the function fiddle with the array.
93   std::unique_ptr<char[]> copied_args(new char[args_.size() + 1]);
94   strlcpy(copied_args.get(), args_.c_str(), args_.size() + 1);
95   // TODO Need to do some checks that we are at a good spot etc.
96   *call_res = callback(Runtime::Current()->GetJavaVM(),
97                        copied_args.get(),
98                        nullptr);
99   if (*call_res != 0) {
100     *error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
101                               name_.c_str(), *call_res);
102     VLOG(agents) << "err: " << *error_msg;
103     *error = kInitializationError;
104     return nullptr;
105   }
106   return agent;
107 }
108 
DoDlOpen(JNIEnv * env,jobject class_loader,LoadError * error,std::string * error_msg)109 std::unique_ptr<Agent> AgentSpec::DoDlOpen(JNIEnv* env,
110                                            jobject class_loader,
111                                            /*out*/LoadError* error,
112                                            /*out*/std::string* error_msg) {
113   DCHECK(error_msg != nullptr);
114 
115   ScopedLocalRef<jstring> library_path(env,
116                                        class_loader == nullptr
117                                            ? nullptr
118                                            : JavaVMExt::GetLibrarySearchPath(env, class_loader));
119 
120   bool needs_native_bridge = false;
121   char* nativeloader_error_msg = nullptr;
122   void* dlopen_handle = android::OpenNativeLibrary(env,
123                                                    Runtime::Current()->GetTargetSdkVersion(),
124                                                    name_.c_str(),
125                                                    class_loader,
126                                                    nullptr,
127                                                    library_path.get(),
128                                                    &needs_native_bridge,
129                                                    &nativeloader_error_msg);
130   if (dlopen_handle == nullptr) {
131     *error_msg = StringPrintf("Unable to dlopen %s: %s",
132                               name_.c_str(),
133                               nativeloader_error_msg);
134     android::NativeLoaderFreeErrorMessage(nativeloader_error_msg);
135     *error = kLoadingError;
136     return nullptr;
137   }
138   if (needs_native_bridge) {
139     // TODO: Consider support?
140     // The result of this call and error_msg is ignored because the most
141     // relevant error is that native bridge is unsupported.
142     android::CloseNativeLibrary(dlopen_handle, needs_native_bridge, &nativeloader_error_msg);
143     android::NativeLoaderFreeErrorMessage(nativeloader_error_msg);
144     *error_msg = StringPrintf("Native-bridge agents unsupported: %s", name_.c_str());
145     *error = kLoadingError;
146     return nullptr;
147   }
148 
149   std::unique_ptr<Agent> agent(new Agent(name_, dlopen_handle));
150   agent->PopulateFunctions();
151   *error = kNoError;
152   return agent;
153 }
154 
operator <<(std::ostream & os,AgentSpec const & m)155 std::ostream& operator<<(std::ostream &os, AgentSpec const& m) {
156   return os << "AgentSpec { name=\"" << m.name_ << "\", args=\"" << m.args_ << "\" }";
157 }
158 
159 
FindSymbol(const std::string & name) const160 void* Agent::FindSymbol(const std::string& name) const {
161   CHECK(dlopen_handle_ != nullptr) << "Cannot find symbols in an unloaded agent library " << this;
162   return dlsym(dlopen_handle_, name.c_str());
163 }
164 
165 // TODO Lock some stuff probably.
Unload()166 void Agent::Unload() {
167   if (dlopen_handle_ != nullptr) {
168     if (onunload_ != nullptr) {
169       onunload_(Runtime::Current()->GetJavaVM());
170     }
171     // Don't actually android::CloseNativeLibrary since some agents assume they will never get
172     // unloaded. Since this only happens when the runtime is shutting down anyway this isn't a big
173     // deal.
174     dlopen_handle_ = nullptr;
175     onload_ = nullptr;
176     onattach_ = nullptr;
177     onunload_ = nullptr;
178   } else {
179     VLOG(agents) << this << " is not currently loaded!";
180   }
181 }
182 
Agent(Agent && other)183 Agent::Agent(Agent&& other) noexcept
184     : dlopen_handle_(nullptr),
185       onload_(nullptr),
186       onattach_(nullptr),
187       onunload_(nullptr) {
188   *this = std::move(other);
189 }
190 
operator =(Agent && other)191 Agent& Agent::operator=(Agent&& other) noexcept {
192   if (this != &other) {
193     if (dlopen_handle_ != nullptr) {
194       Unload();
195     }
196     name_ = std::move(other.name_);
197     dlopen_handle_ = other.dlopen_handle_;
198     onload_ = other.onload_;
199     onattach_ = other.onattach_;
200     onunload_ = other.onunload_;
201     other.dlopen_handle_ = nullptr;
202     other.onload_ = nullptr;
203     other.onattach_ = nullptr;
204     other.onunload_ = nullptr;
205   }
206   return *this;
207 }
208 
PopulateFunctions()209 void Agent::PopulateFunctions() {
210   onload_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_LOAD_FUNCTION_NAME));
211   if (onload_ == nullptr) {
212     VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
213   }
214   onattach_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_ATTACH_FUNCTION_NAME));
215   if (onattach_ == nullptr) {
216     VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
217   }
218   onunload_ = reinterpret_cast<AgentOnUnloadFunction>(FindSymbol(AGENT_ON_UNLOAD_FUNCTION_NAME));
219   if (onunload_ == nullptr) {
220     VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this;
221   }
222 }
223 
~Agent()224 Agent::~Agent() {
225   if (dlopen_handle_ != nullptr) {
226     Unload();
227   }
228 }
229 
operator <<(std::ostream & os,const Agent * m)230 std::ostream& operator<<(std::ostream &os, const Agent* m) {
231   return os << *m;
232 }
233 
operator <<(std::ostream & os,Agent const & m)234 std::ostream& operator<<(std::ostream &os, Agent const& m) {
235   return os << "Agent { name=\"" << m.name_ << "\", handle=" << m.dlopen_handle_ << " }";
236 }
237 
238 }  // namespace ti
239 }  // namespace art
240