1 /*
2  * Copyright (C) 2019 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 #ifndef ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_SUPPORT_H_
18 #define ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_SUPPORT_H_
19 
20 // C++ wrapper for the dex file external API.
21 
22 #include <cstring>
23 #include <memory>
24 #include <string>
25 #include <string_view>
26 #include <utility>
27 #include <vector>
28 
29 #include <android-base/macros.h>
30 
31 #include "art_api/dex_file_external.h"
32 
33 namespace art_api {
34 namespace dex {
35 
36 // Returns true if libdexfile_external.so is already loaded. Otherwise tries to
37 // load it and returns true if successful. Otherwise returns false and sets
38 // *error_msg. If false is returned then calling any function below may abort
39 // the process. Thread safe.
40 bool TryLoadLibdexfileExternal(std::string* error_msg);
41 
42 // Loads the libdexfile_external.so library and sets up function pointers.
43 // Aborts with a fatal error on any error. For internal use by the classes
44 // below.
45 void LoadLibdexfileExternal();
46 
47 // Minimal std::string look-alike for a string returned from libdexfile.
48 class DexString final {
49  public:
DexString(DexString && dex_str)50   DexString(DexString&& dex_str) noexcept : ext_string_(dex_str.ext_string_) {
51     dex_str.ext_string_ = MakeExtDexFileString("", 0);
52   }
53   explicit DexString(const char* str = "")
ext_string_(MakeExtDexFileString (str,std::strlen (str)))54       : ext_string_(MakeExtDexFileString(str, std::strlen(str))) {}
DexString(std::string_view str)55   explicit DexString(std::string_view str)
56       : ext_string_(MakeExtDexFileString(str.data(), str.size())) {}
~DexString()57   ~DexString() { g_ExtDexFileFreeString(ext_string_); }
58 
59   DexString& operator=(DexString&& dex_str) noexcept {
60     std::swap(ext_string_, dex_str.ext_string_);
61     return *this;
62   }
63 
data()64   const char* data() const {
65     size_t ignored;
66     return g_ExtDexFileGetString(ext_string_, &ignored);
67   }
c_str()68   const char* c_str() const { return data(); }
69 
size()70   size_t size() const {
71     size_t len;
72     (void)g_ExtDexFileGetString(ext_string_, &len);
73     return len;
74   }
length()75   size_t length() const { return size(); }
76 
string_view()77   operator std::string_view() const {
78     size_t len;
79     const char* chars = g_ExtDexFileGetString(ext_string_, &len);
80     return std::string_view(chars, len);
81   }
82 
83  private:
84   friend bool TryLoadLibdexfileExternal(std::string* error_msg);
85   friend class DexFile;
86   friend bool operator==(const DexString&, const DexString&);
DexString(const ExtDexFileString * ext_string)87   explicit DexString(const ExtDexFileString* ext_string) : ext_string_(ext_string) {}
88   const ExtDexFileString* ext_string_;  // Owned instance. Never nullptr.
89 
90   // These are initialized by TryLoadLibdexfileExternal.
91   static decltype(ExtDexFileMakeString)* g_ExtDexFileMakeString;
92   static decltype(ExtDexFileGetString)* g_ExtDexFileGetString;
decltype(ExtDexFileFreeString)93   static decltype(ExtDexFileFreeString)* g_ExtDexFileFreeString;
94 
95   static const struct ExtDexFileString* MakeExtDexFileString(const char* str, size_t size) {
96     if (UNLIKELY(g_ExtDexFileMakeString == nullptr)) {
97       LoadLibdexfileExternal();
98     }
99     return g_ExtDexFileMakeString(str, size);
100   }
101 
102   DISALLOW_COPY_AND_ASSIGN(DexString);
103 };
104 
105 inline bool operator==(const DexString& s1, const DexString& s2) {
106   size_t l1, l2;
107   const char* str1 = DexString::g_ExtDexFileGetString(s1.ext_string_, &l1);
108   const char* str2 = DexString::g_ExtDexFileGetString(s2.ext_string_, &l2);
109   // Use memcmp to avoid assumption about absence of null characters in the strings.
110   return l1 == l2 && !std::memcmp(str1, str2, l1);
111 }
112 
113 struct MethodInfo {
114   int32_t offset;  // Code offset relative to the start of the dex file header
115   int32_t len;  // Code length
116   DexString name;
117 };
118 
119 inline bool operator==(const MethodInfo& s1, const MethodInfo& s2) {
120   return s1.offset == s2.offset && s1.len == s2.len && s1.name == s2.name;
121 }
122 
123 // External stable API to access ordinary dex files and CompactDex. This wraps
124 // the stable C ABI and handles instance ownership. Thread-compatible but not
125 // thread-safe.
126 class DexFile {
127  public:
DexFile(DexFile && dex_file)128   DexFile(DexFile&& dex_file) noexcept {
129     ext_dex_file_ = dex_file.ext_dex_file_;
130     dex_file.ext_dex_file_ = nullptr;
131   }
132 
DexFile(std::unique_ptr<DexFile> & dex_file)133   explicit DexFile(std::unique_ptr<DexFile>& dex_file) noexcept {
134     ext_dex_file_ = dex_file->ext_dex_file_;
135     dex_file->ext_dex_file_ = nullptr;
136     dex_file.reset(nullptr);
137   }
138   virtual ~DexFile();
139 
140   // Interprets a chunk of memory as a dex file. As long as *size is too small,
141   // returns nullptr, sets *size to a new size to try again with, and sets
142   // *error_msg to "". That might happen repeatedly. Also returns nullptr
143   // on error in which case *error_msg is set to a nonempty string.
144   //
145   // location is a string that describes the dex file, and is preferably its
146   // path. It is mostly used to make error messages better, and may be "".
147   //
148   // The caller must retain the memory.
OpenFromMemory(const void * addr,size_t * size,const std::string & location,std::string * error_msg)149   static std::unique_ptr<DexFile> OpenFromMemory(const void* addr,
150                                                  size_t* size,
151                                                  const std::string& location,
152                                                  /*out*/ std::string* error_msg) {
153     if (UNLIKELY(g_ExtDexFileOpenFromMemory == nullptr)) {
154       // Load libdexfile_external.so in this factory function, so instance
155       // methods don't need to check this.
156       LoadLibdexfileExternal();
157     }
158     ExtDexFile* ext_dex_file;
159     const ExtDexFileString* ext_error_msg = nullptr;
160     if (g_ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_error_msg, &ext_dex_file)) {
161       return std::unique_ptr<DexFile>(new DexFile(ext_dex_file));
162     }
163     *error_msg = (ext_error_msg == nullptr) ? "" : std::string(DexString(ext_error_msg));
164     return nullptr;
165   }
166 
167   // mmaps the given file offset in the open fd and reads a dexfile from there.
168   // Returns nullptr on error in which case *error_msg is set.
169   //
170   // location is a string that describes the dex file, and is preferably its
171   // path. It is mostly used to make error messages better, and may be "".
OpenFromFd(int fd,off_t offset,const std::string & location,std::string * error_msg)172   static std::unique_ptr<DexFile> OpenFromFd(int fd,
173                                              off_t offset,
174                                              const std::string& location,
175                                              /*out*/ std::string* error_msg) {
176     if (UNLIKELY(g_ExtDexFileOpenFromFd == nullptr)) {
177       // Load libdexfile_external.so in this factory function, so instance
178       // methods don't need to check this.
179       LoadLibdexfileExternal();
180     }
181     ExtDexFile* ext_dex_file;
182     const ExtDexFileString* ext_error_msg = nullptr;
183     if (g_ExtDexFileOpenFromFd(fd, offset, location.c_str(), &ext_error_msg, &ext_dex_file)) {
184       return std::unique_ptr<DexFile>(new DexFile(ext_dex_file));
185     }
186     *error_msg = std::string(DexString(ext_error_msg));
187     return nullptr;
188   }
189 
190   // Given an offset relative to the start of the dex file header, if there is a
191   // method whose instruction range includes that offset then returns info about
192   // it, otherwise returns a struct with offset == 0. MethodInfo.name receives
193   // the full function signature if with_signature is set, otherwise it gets the
194   // class and method name only.
GetMethodInfoForOffset(int64_t dex_offset,bool with_signature)195   MethodInfo GetMethodInfoForOffset(int64_t dex_offset, bool with_signature) {
196     ExtDexFileMethodInfo ext_method_info;
197     if (g_ExtDexFileGetMethodInfoForOffset(ext_dex_file_,
198                                            dex_offset,
199                                            with_signature,
200                                            &ext_method_info)) {
201       return AbsorbMethodInfo(ext_method_info);
202     }
203     return {/*offset=*/0, /*len=*/0, /*name=*/DexString()};
204   }
205 
206   // Returns info structs about all methods in the dex file. MethodInfo.name
207   // receives the full function signature if with_signature is set, otherwise it
208   // gets the class and method name only.
GetAllMethodInfos(bool with_signature)209   std::vector<MethodInfo> GetAllMethodInfos(bool with_signature) {
210     MethodInfoVector res;
211     g_ExtDexFileGetAllMethodInfos(ext_dex_file_,
212                                   with_signature,
213                                   AddMethodInfoCallback,
214                                   static_cast<void*>(&res));
215     return res;
216   }
217 
218  private:
219   friend bool TryLoadLibdexfileExternal(std::string* error_msg);
DexFile(ExtDexFile * ext_dex_file)220   explicit DexFile(ExtDexFile* ext_dex_file) : ext_dex_file_(ext_dex_file) {}
221   ExtDexFile* ext_dex_file_;  // Owned instance. nullptr only in moved-from zombies.
222 
223   typedef std::vector<MethodInfo> MethodInfoVector;
224 
225   static MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info);
226   static void AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* user_data);
227 
228   // These are initialized by TryLoadLibdexfileExternal.
229   static decltype(ExtDexFileOpenFromMemory)* g_ExtDexFileOpenFromMemory;
230   static decltype(ExtDexFileOpenFromFd)* g_ExtDexFileOpenFromFd;
231   static decltype(ExtDexFileGetMethodInfoForOffset)* g_ExtDexFileGetMethodInfoForOffset;
232   static decltype(ExtDexFileGetAllMethodInfos)* g_ExtDexFileGetAllMethodInfos;
233   static decltype(ExtDexFileFree)* g_ExtDexFileFree;
234 
235   DISALLOW_COPY_AND_ASSIGN(DexFile);
236 };
237 
238 }  // namespace dex
239 }  // namespace art_api
240 
241 #endif  // ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_SUPPORT_H_
242