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
21 #include "base/strlcpy.h"
22 #include "java_vm_ext.h"
23 #include "runtime.h"
24
25 namespace art {
26 namespace ti {
27
28 using android::base::StringPrintf;
29
30 const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
31 const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
32 const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
33
34 // TODO We need to acquire some locks probably.
DoLoadHelper(bool attaching,jint * call_res,std::string * error_msg)35 Agent::LoadError Agent::DoLoadHelper(bool attaching,
36 /*out*/jint* call_res,
37 /*out*/std::string* error_msg) {
38 DCHECK(call_res != nullptr);
39 DCHECK(error_msg != nullptr);
40
41 if (IsStarted()) {
42 *error_msg = StringPrintf("the agent at %s has already been started!", name_.c_str());
43 VLOG(agents) << "err: " << *error_msg;
44 return kAlreadyStarted;
45 }
46 LoadError err = DoDlOpen(error_msg);
47 if (err != kNoError) {
48 VLOG(agents) << "err: " << *error_msg;
49 return err;
50 }
51 AgentOnLoadFunction callback = attaching ? onattach_ : onload_;
52 if (callback == nullptr) {
53 *error_msg = StringPrintf("Unable to start agent %s: No %s callback found",
54 (attaching ? "attach" : "load"),
55 name_.c_str());
56 VLOG(agents) << "err: " << *error_msg;
57 return kLoadingError;
58 }
59 // Need to let the function fiddle with the array.
60 std::unique_ptr<char[]> copied_args(new char[args_.size() + 1]);
61 strlcpy(copied_args.get(), args_.c_str(), args_.size() + 1);
62 // TODO Need to do some checks that we are at a good spot etc.
63 *call_res = callback(Runtime::Current()->GetJavaVM(),
64 copied_args.get(),
65 nullptr);
66 if (*call_res != 0) {
67 *error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
68 name_.c_str(), *call_res);
69 VLOG(agents) << "err: " << *error_msg;
70 return kInitializationError;
71 } else {
72 return kNoError;
73 }
74 }
75
FindSymbol(const std::string & name) const76 void* Agent::FindSymbol(const std::string& name) const {
77 CHECK(IsStarted()) << "Cannot find symbols in an unloaded agent library " << this;
78 return dlsym(dlopen_handle_, name.c_str());
79 }
80
DoDlOpen(std::string * error_msg)81 Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
82 DCHECK(error_msg != nullptr);
83
84 DCHECK(dlopen_handle_ == nullptr);
85 DCHECK(onload_ == nullptr);
86 DCHECK(onattach_ == nullptr);
87 DCHECK(onunload_ == nullptr);
88
89 dlopen_handle_ = dlopen(name_.c_str(), RTLD_LAZY);
90 if (dlopen_handle_ == nullptr) {
91 *error_msg = StringPrintf("Unable to dlopen %s: %s", name_.c_str(), dlerror());
92 return kLoadingError;
93 }
94
95 onload_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_LOAD_FUNCTION_NAME));
96 if (onload_ == nullptr) {
97 VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
98 }
99 onattach_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_ATTACH_FUNCTION_NAME));
100 if (onattach_ == nullptr) {
101 VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
102 }
103 onunload_= reinterpret_cast<AgentOnUnloadFunction>(FindSymbol(AGENT_ON_UNLOAD_FUNCTION_NAME));
104 if (onunload_ == nullptr) {
105 VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this;
106 }
107 return kNoError;
108 }
109
110 // TODO Lock some stuff probably.
Unload()111 void Agent::Unload() {
112 if (dlopen_handle_ != nullptr) {
113 if (onunload_ != nullptr) {
114 onunload_(Runtime::Current()->GetJavaVM());
115 }
116 dlclose(dlopen_handle_);
117 dlopen_handle_ = nullptr;
118 onload_ = nullptr;
119 onattach_ = nullptr;
120 onunload_ = nullptr;
121 } else {
122 VLOG(agents) << this << " is not currently loaded!";
123 }
124 }
125
Agent(std::string arg)126 Agent::Agent(std::string arg)
127 : dlopen_handle_(nullptr),
128 onload_(nullptr),
129 onattach_(nullptr),
130 onunload_(nullptr) {
131 size_t eq = arg.find_first_of('=');
132 if (eq == std::string::npos) {
133 name_ = arg;
134 } else {
135 name_ = arg.substr(0, eq);
136 args_ = arg.substr(eq + 1, arg.length());
137 }
138 }
139
Agent(const Agent & other)140 Agent::Agent(const Agent& other)
141 : dlopen_handle_(nullptr),
142 onload_(nullptr),
143 onattach_(nullptr),
144 onunload_(nullptr) {
145 *this = other;
146 }
147
148 // Attempting to copy to/from loaded/started agents is a fatal error
operator =(const Agent & other)149 Agent& Agent::operator=(const Agent& other) {
150 if (this != &other) {
151 if (other.dlopen_handle_ != nullptr) {
152 LOG(FATAL) << "Attempting to copy a loaded agent!";
153 }
154
155 if (dlopen_handle_ != nullptr) {
156 LOG(FATAL) << "Attempting to assign into a loaded agent!";
157 }
158
159 DCHECK(other.onload_ == nullptr);
160 DCHECK(other.onattach_ == nullptr);
161 DCHECK(other.onunload_ == nullptr);
162
163 DCHECK(onload_ == nullptr);
164 DCHECK(onattach_ == nullptr);
165 DCHECK(onunload_ == nullptr);
166
167 name_ = other.name_;
168 args_ = other.args_;
169
170 dlopen_handle_ = nullptr;
171 onload_ = nullptr;
172 onattach_ = nullptr;
173 onunload_ = nullptr;
174 }
175 return *this;
176 }
177
Agent(Agent && other)178 Agent::Agent(Agent&& other)
179 : dlopen_handle_(nullptr),
180 onload_(nullptr),
181 onattach_(nullptr),
182 onunload_(nullptr) {
183 *this = std::move(other);
184 }
185
operator =(Agent && other)186 Agent& Agent::operator=(Agent&& other) {
187 if (this != &other) {
188 if (dlopen_handle_ != nullptr) {
189 dlclose(dlopen_handle_);
190 }
191 name_ = std::move(other.name_);
192 args_ = std::move(other.args_);
193 dlopen_handle_ = other.dlopen_handle_;
194 onload_ = other.onload_;
195 onattach_ = other.onattach_;
196 onunload_ = other.onunload_;
197 other.dlopen_handle_ = nullptr;
198 other.onload_ = nullptr;
199 other.onattach_ = nullptr;
200 other.onunload_ = nullptr;
201 }
202 return *this;
203 }
204
~Agent()205 Agent::~Agent() {
206 if (dlopen_handle_ != nullptr) {
207 dlclose(dlopen_handle_);
208 }
209 }
210
operator <<(std::ostream & os,const Agent * m)211 std::ostream& operator<<(std::ostream &os, const Agent* m) {
212 return os << *m;
213 }
214
operator <<(std::ostream & os,Agent const & m)215 std::ostream& operator<<(std::ostream &os, Agent const& m) {
216 return os << "Agent { name=\"" << m.name_ << "\", args=\"" << m.args_ << "\", handle="
217 << m.dlopen_handle_ << " }";
218 }
219
220 } // namespace ti
221 } // namespace art
222