Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 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_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ |
| 18 | #define ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ |
| 19 | |
Vladimir Marko | 9bdf108 | 2016-01-21 12:15:52 +0000 | [diff] [blame] | 20 | #include <zlib.h> |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 21 | |
Andreas Gampe | 8cf9cb3 | 2017-07-19 09:28:38 -0700 | [diff] [blame] | 22 | #include <cstring> |
| 23 | #include <map> |
| 24 | #include <set> |
| 25 | #include <vector> |
| 26 | |
Andreas Gampe | 5794381 | 2017-12-06 21:39:13 -0800 | [diff] [blame] | 27 | #include <android-base/logging.h> |
| 28 | |
Vladimir Marko | 80afd02 | 2015-05-19 18:08:00 +0100 | [diff] [blame] | 29 | #include "base/bit_utils.h" |
David Sehr | 013fd80 | 2018-01-11 22:55:24 -0800 | [diff] [blame] | 30 | #include "dex/art_dex_file_loader.h" |
David Sehr | 9e734c7 | 2018-01-04 17:56:19 -0800 | [diff] [blame] | 31 | #include "dex/dex_file_loader.h" |
| 32 | #include "dex/standard_dex_file.h" |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 33 | |
| 34 | namespace art { |
| 35 | |
| 36 | class TestDexFileBuilder { |
| 37 | public: |
| 38 | TestDexFileBuilder() |
| 39 | : strings_(), types_(), fields_(), protos_(), dex_file_data_() { |
| 40 | } |
| 41 | |
| 42 | void AddString(const std::string& str) { |
| 43 | CHECK(dex_file_data_.empty()); |
| 44 | auto it = strings_.emplace(str, IdxAndDataOffset()).first; |
| 45 | CHECK_LT(it->first.length(), 128u); // Don't allow multi-byte length in uleb128. |
| 46 | } |
| 47 | |
| 48 | void AddType(const std::string& descriptor) { |
| 49 | CHECK(dex_file_data_.empty()); |
| 50 | AddString(descriptor); |
| 51 | types_.emplace(descriptor, 0u); |
| 52 | } |
| 53 | |
| 54 | void AddField(const std::string& class_descriptor, const std::string& type, |
| 55 | const std::string& name) { |
| 56 | CHECK(dex_file_data_.empty()); |
| 57 | AddType(class_descriptor); |
| 58 | AddType(type); |
| 59 | AddString(name); |
| 60 | FieldKey key = { class_descriptor, type, name }; |
| 61 | fields_.emplace(key, 0u); |
| 62 | } |
| 63 | |
| 64 | void AddMethod(const std::string& class_descriptor, const std::string& signature, |
| 65 | const std::string& name) { |
| 66 | CHECK(dex_file_data_.empty()); |
| 67 | AddType(class_descriptor); |
| 68 | AddString(name); |
| 69 | |
| 70 | ProtoKey proto_key = CreateProtoKey(signature); |
| 71 | AddString(proto_key.shorty); |
| 72 | AddType(proto_key.return_type); |
| 73 | for (const auto& arg_type : proto_key.args) { |
| 74 | AddType(arg_type); |
| 75 | } |
| 76 | auto it = protos_.emplace(proto_key, IdxAndDataOffset()).first; |
| 77 | const ProtoKey* proto = &it->first; // Valid as long as the element remains in protos_. |
| 78 | |
| 79 | MethodKey method_key = { |
| 80 | class_descriptor, name, proto |
| 81 | }; |
| 82 | methods_.emplace(method_key, 0u); |
| 83 | } |
| 84 | |
| 85 | // NOTE: The builder holds the actual data, so it must live as long as the dex file. |
| 86 | std::unique_ptr<const DexFile> Build(const std::string& dex_location) { |
| 87 | CHECK(dex_file_data_.empty()); |
| 88 | union { |
| 89 | uint8_t data[sizeof(DexFile::Header)]; |
| 90 | uint64_t force_alignment; |
| 91 | } header_data; |
| 92 | std::memset(header_data.data, 0, sizeof(header_data.data)); |
| 93 | DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data); |
Mathieu Chartier | 292567e | 2017-10-12 13:24:38 -0700 | [diff] [blame] | 94 | std::copy_n(StandardDexFile::kDexMagic, 4u, header->magic_); |
| 95 | std::copy_n(StandardDexFile::kDexMagicVersions[0], 4u, header->magic_ + 4u); |
Andreas Gampe | 3a2bd29 | 2016-01-26 17:23:47 -0800 | [diff] [blame] | 96 | header->header_size_ = sizeof(DexFile::Header); |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 97 | header->endian_tag_ = DexFile::kDexEndianConstant; |
| 98 | header->link_size_ = 0u; // Unused. |
| 99 | header->link_off_ = 0u; // Unused. |
Andreas Gampe | 3a2bd29 | 2016-01-26 17:23:47 -0800 | [diff] [blame] | 100 | header->map_off_ = 0u; // Unused. TODO: This is wrong. Dex files created by this builder |
| 101 | // cannot be verified. b/26808512 |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 102 | |
| 103 | uint32_t data_section_size = 0u; |
| 104 | |
| 105 | uint32_t string_ids_offset = sizeof(DexFile::Header); |
| 106 | uint32_t string_idx = 0u; |
| 107 | for (auto& entry : strings_) { |
| 108 | entry.second.idx = string_idx; |
| 109 | string_idx += 1u; |
| 110 | entry.second.data_offset = data_section_size; |
| 111 | data_section_size += entry.first.length() + 1u /* length */ + 1u /* null-terminator */; |
| 112 | } |
| 113 | header->string_ids_size_ = strings_.size(); |
| 114 | header->string_ids_off_ = strings_.empty() ? 0u : string_ids_offset; |
| 115 | |
| 116 | uint32_t type_ids_offset = string_ids_offset + strings_.size() * sizeof(DexFile::StringId); |
| 117 | uint32_t type_idx = 0u; |
| 118 | for (auto& entry : types_) { |
| 119 | entry.second = type_idx; |
| 120 | type_idx += 1u; |
| 121 | } |
| 122 | header->type_ids_size_ = types_.size(); |
| 123 | header->type_ids_off_ = types_.empty() ? 0u : type_ids_offset; |
| 124 | |
| 125 | uint32_t proto_ids_offset = type_ids_offset + types_.size() * sizeof(DexFile::TypeId); |
| 126 | uint32_t proto_idx = 0u; |
| 127 | for (auto& entry : protos_) { |
| 128 | entry.second.idx = proto_idx; |
| 129 | proto_idx += 1u; |
| 130 | size_t num_args = entry.first.args.size(); |
| 131 | if (num_args != 0u) { |
| 132 | entry.second.data_offset = RoundUp(data_section_size, 4u); |
| 133 | data_section_size = entry.second.data_offset + 4u + num_args * sizeof(DexFile::TypeItem); |
| 134 | } else { |
| 135 | entry.second.data_offset = 0u; |
| 136 | } |
| 137 | } |
| 138 | header->proto_ids_size_ = protos_.size(); |
| 139 | header->proto_ids_off_ = protos_.empty() ? 0u : proto_ids_offset; |
| 140 | |
| 141 | uint32_t field_ids_offset = proto_ids_offset + protos_.size() * sizeof(DexFile::ProtoId); |
| 142 | uint32_t field_idx = 0u; |
| 143 | for (auto& entry : fields_) { |
| 144 | entry.second = field_idx; |
| 145 | field_idx += 1u; |
| 146 | } |
| 147 | header->field_ids_size_ = fields_.size(); |
| 148 | header->field_ids_off_ = fields_.empty() ? 0u : field_ids_offset; |
| 149 | |
| 150 | uint32_t method_ids_offset = field_ids_offset + fields_.size() * sizeof(DexFile::FieldId); |
| 151 | uint32_t method_idx = 0u; |
| 152 | for (auto& entry : methods_) { |
| 153 | entry.second = method_idx; |
| 154 | method_idx += 1u; |
| 155 | } |
| 156 | header->method_ids_size_ = methods_.size(); |
| 157 | header->method_ids_off_ = methods_.empty() ? 0u : method_ids_offset; |
| 158 | |
| 159 | // No class defs. |
| 160 | header->class_defs_size_ = 0u; |
| 161 | header->class_defs_off_ = 0u; |
| 162 | |
| 163 | uint32_t data_section_offset = method_ids_offset + methods_.size() * sizeof(DexFile::MethodId); |
| 164 | header->data_size_ = data_section_size; |
| 165 | header->data_off_ = (data_section_size != 0u) ? data_section_offset : 0u; |
| 166 | |
| 167 | uint32_t total_size = data_section_offset + data_section_size; |
| 168 | |
| 169 | dex_file_data_.resize(total_size); |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 170 | |
| 171 | for (const auto& entry : strings_) { |
| 172 | CHECK_LT(entry.first.size(), 128u); |
| 173 | uint32_t raw_offset = data_section_offset + entry.second.data_offset; |
| 174 | dex_file_data_[raw_offset] = static_cast<uint8_t>(entry.first.size()); |
| 175 | std::memcpy(&dex_file_data_[raw_offset + 1], entry.first.c_str(), entry.first.size() + 1); |
| 176 | Write32(string_ids_offset + entry.second.idx * sizeof(DexFile::StringId), raw_offset); |
| 177 | } |
| 178 | |
| 179 | for (const auto& entry : types_) { |
| 180 | Write32(type_ids_offset + entry.second * sizeof(DexFile::TypeId), GetStringIdx(entry.first)); |
| 181 | ++type_idx; |
| 182 | } |
| 183 | |
| 184 | for (const auto& entry : protos_) { |
| 185 | size_t num_args = entry.first.args.size(); |
| 186 | uint32_t type_list_offset = |
| 187 | (num_args != 0u) ? data_section_offset + entry.second.data_offset : 0u; |
| 188 | uint32_t raw_offset = proto_ids_offset + entry.second.idx * sizeof(DexFile::ProtoId); |
| 189 | Write32(raw_offset + 0u, GetStringIdx(entry.first.shorty)); |
| 190 | Write16(raw_offset + 4u, GetTypeIdx(entry.first.return_type)); |
| 191 | Write32(raw_offset + 8u, type_list_offset); |
| 192 | if (num_args != 0u) { |
| 193 | CHECK_NE(entry.second.data_offset, 0u); |
| 194 | Write32(type_list_offset, num_args); |
| 195 | for (size_t i = 0; i != num_args; ++i) { |
| 196 | Write16(type_list_offset + 4u + i * sizeof(DexFile::TypeItem), |
| 197 | GetTypeIdx(entry.first.args[i])); |
| 198 | } |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | for (const auto& entry : fields_) { |
| 203 | uint32_t raw_offset = field_ids_offset + entry.second * sizeof(DexFile::FieldId); |
| 204 | Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor)); |
| 205 | Write16(raw_offset + 2u, GetTypeIdx(entry.first.type)); |
| 206 | Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); |
| 207 | } |
| 208 | |
| 209 | for (const auto& entry : methods_) { |
| 210 | uint32_t raw_offset = method_ids_offset + entry.second * sizeof(DexFile::MethodId); |
| 211 | Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor)); |
| 212 | auto it = protos_.find(*entry.first.proto); |
| 213 | CHECK(it != protos_.end()); |
| 214 | Write16(raw_offset + 2u, it->second.idx); |
| 215 | Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); |
| 216 | } |
| 217 | |
Vladimir Marko | 9bdf108 | 2016-01-21 12:15:52 +0000 | [diff] [blame] | 218 | // Leave signature as zeros. |
| 219 | |
| 220 | header->file_size_ = dex_file_data_.size(); |
Andreas Gampe | 3a2bd29 | 2016-01-26 17:23:47 -0800 | [diff] [blame] | 221 | |
| 222 | // Write the complete header early, as part of it needs to be checksummed. |
| 223 | std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); |
| 224 | |
| 225 | // Checksum starts after the checksum field. |
Vladimir Marko | 9bdf108 | 2016-01-21 12:15:52 +0000 | [diff] [blame] | 226 | size_t skip = sizeof(header->magic_) + sizeof(header->checksum_); |
Andreas Gampe | 3a2bd29 | 2016-01-26 17:23:47 -0800 | [diff] [blame] | 227 | header->checksum_ = adler32(adler32(0L, Z_NULL, 0), |
| 228 | dex_file_data_.data() + skip, |
| 229 | dex_file_data_.size() - skip); |
| 230 | |
| 231 | // Write the complete header again, just simpler that way. |
Vladimir Marko | 9bdf108 | 2016-01-21 12:15:52 +0000 | [diff] [blame] | 232 | std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 233 | |
Aart Bik | 37d6a3b | 2016-06-21 18:30:10 -0700 | [diff] [blame] | 234 | static constexpr bool kVerify = false; |
| 235 | static constexpr bool kVerifyChecksum = false; |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 236 | std::string error_msg; |
David Sehr | 013fd80 | 2018-01-11 22:55:24 -0800 | [diff] [blame] | 237 | const ArtDexFileLoader dex_file_loader; |
| 238 | std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open( |
Aart Bik | 37d6a3b | 2016-06-21 18:30:10 -0700 | [diff] [blame] | 239 | &dex_file_data_[0], |
| 240 | dex_file_data_.size(), |
| 241 | dex_location, |
| 242 | 0u, |
| 243 | nullptr, |
| 244 | kVerify, |
| 245 | kVerifyChecksum, |
| 246 | &error_msg)); |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 247 | CHECK(dex_file != nullptr) << error_msg; |
Pirama Arumuga Nainar | 0600cdc | 2015-09-14 11:00:16 -0700 | [diff] [blame] | 248 | return dex_file; |
Vladimir Marko | c91df2d | 2015-04-23 09:29:21 +0000 | [diff] [blame] | 249 | } |
| 250 | |
| 251 | uint32_t GetStringIdx(const std::string& type) { |
| 252 | auto it = strings_.find(type); |
| 253 | CHECK(it != strings_.end()); |
| 254 | return it->second.idx; |
| 255 | } |
| 256 | |
| 257 | uint32_t GetTypeIdx(const std::string& type) { |
| 258 | auto it = types_.find(type); |
| 259 | CHECK(it != types_.end()); |
| 260 | return it->second; |
| 261 | } |
| 262 | |
| 263 | uint32_t GetFieldIdx(const std::string& class_descriptor, const std::string& type, |
| 264 | const std::string& name) { |
| 265 | FieldKey key = { class_descriptor, type, name }; |
| 266 | auto it = fields_.find(key); |
| 267 | CHECK(it != fields_.end()); |
| 268 | return it->second; |
| 269 | } |
| 270 | |
| 271 | uint32_t GetMethodIdx(const std::string& class_descriptor, const std::string& signature, |
| 272 | const std::string& name) { |
| 273 | ProtoKey proto_key = CreateProtoKey(signature); |
| 274 | MethodKey method_key = { class_descriptor, name, &proto_key }; |
| 275 | auto it = methods_.find(method_key); |
| 276 | CHECK(it != methods_.end()); |
| 277 | return it->second; |
| 278 | } |
| 279 | |
| 280 | private: |
| 281 | struct IdxAndDataOffset { |
| 282 | uint32_t idx; |
| 283 | uint32_t data_offset; |
| 284 | }; |
| 285 | |
| 286 | struct FieldKey { |
| 287 | const std::string class_descriptor; |
| 288 | const std::string type; |
| 289 | const std::string name; |
| 290 | }; |
| 291 | struct FieldKeyComparator { |
| 292 | bool operator()(const FieldKey& lhs, const FieldKey& rhs) const { |
| 293 | if (lhs.class_descriptor != rhs.class_descriptor) { |
| 294 | return lhs.class_descriptor < rhs.class_descriptor; |
| 295 | } |
| 296 | if (lhs.name != rhs.name) { |
| 297 | return lhs.name < rhs.name; |
| 298 | } |
| 299 | return lhs.type < rhs.type; |
| 300 | } |
| 301 | }; |
| 302 | |
| 303 | struct ProtoKey { |
| 304 | std::string shorty; |
| 305 | std::string return_type; |
| 306 | std::vector<std::string> args; |
| 307 | }; |
| 308 | struct ProtoKeyComparator { |
| 309 | bool operator()(const ProtoKey& lhs, const ProtoKey& rhs) const { |
| 310 | if (lhs.return_type != rhs.return_type) { |
| 311 | return lhs.return_type < rhs.return_type; |
| 312 | } |
| 313 | size_t min_args = std::min(lhs.args.size(), rhs.args.size()); |
| 314 | for (size_t i = 0; i != min_args; ++i) { |
| 315 | if (lhs.args[i] != rhs.args[i]) { |
| 316 | return lhs.args[i] < rhs.args[i]; |
| 317 | } |
| 318 | } |
| 319 | return lhs.args.size() < rhs.args.size(); |
| 320 | } |
| 321 | }; |
| 322 | |
| 323 | struct MethodKey { |
| 324 | std::string class_descriptor; |
| 325 | std::string name; |
| 326 | const ProtoKey* proto; |
| 327 | }; |
| 328 | struct MethodKeyComparator { |
| 329 | bool operator()(const MethodKey& lhs, const MethodKey& rhs) const { |
| 330 | if (lhs.class_descriptor != rhs.class_descriptor) { |
| 331 | return lhs.class_descriptor < rhs.class_descriptor; |
| 332 | } |
| 333 | if (lhs.name != rhs.name) { |
| 334 | return lhs.name < rhs.name; |
| 335 | } |
| 336 | return ProtoKeyComparator()(*lhs.proto, *rhs.proto); |
| 337 | } |
| 338 | }; |
| 339 | |
| 340 | ProtoKey CreateProtoKey(const std::string& signature) { |
| 341 | CHECK_EQ(signature[0], '('); |
| 342 | const char* args = signature.c_str() + 1; |
| 343 | const char* args_end = std::strchr(args, ')'); |
| 344 | CHECK(args_end != nullptr); |
| 345 | const char* return_type = args_end + 1; |
| 346 | |
| 347 | ProtoKey key = { |
| 348 | std::string() + ((*return_type == '[') ? 'L' : *return_type), |
| 349 | return_type, |
| 350 | std::vector<std::string>() |
| 351 | }; |
| 352 | while (args != args_end) { |
| 353 | key.shorty += (*args == '[') ? 'L' : *args; |
| 354 | const char* arg_start = args; |
| 355 | while (*args == '[') { |
| 356 | ++args; |
| 357 | } |
| 358 | if (*args == 'L') { |
| 359 | do { |
| 360 | ++args; |
| 361 | CHECK_NE(args, args_end); |
| 362 | } while (*args != ';'); |
| 363 | } |
| 364 | ++args; |
| 365 | key.args.emplace_back(arg_start, args); |
| 366 | } |
| 367 | return key; |
| 368 | } |
| 369 | |
| 370 | void Write32(size_t offset, uint32_t value) { |
| 371 | CHECK_LE(offset + 4u, dex_file_data_.size()); |
| 372 | CHECK_EQ(dex_file_data_[offset + 0], 0u); |
| 373 | CHECK_EQ(dex_file_data_[offset + 1], 0u); |
| 374 | CHECK_EQ(dex_file_data_[offset + 2], 0u); |
| 375 | CHECK_EQ(dex_file_data_[offset + 3], 0u); |
| 376 | dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0); |
| 377 | dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8); |
| 378 | dex_file_data_[offset + 2] = static_cast<uint8_t>(value >> 16); |
| 379 | dex_file_data_[offset + 3] = static_cast<uint8_t>(value >> 24); |
| 380 | } |
| 381 | |
| 382 | void Write16(size_t offset, uint32_t value) { |
| 383 | CHECK_LE(value, 0xffffu); |
| 384 | CHECK_LE(offset + 2u, dex_file_data_.size()); |
| 385 | CHECK_EQ(dex_file_data_[offset + 0], 0u); |
| 386 | CHECK_EQ(dex_file_data_[offset + 1], 0u); |
| 387 | dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0); |
| 388 | dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8); |
| 389 | } |
| 390 | |
| 391 | std::map<std::string, IdxAndDataOffset> strings_; |
| 392 | std::map<std::string, uint32_t> types_; |
| 393 | std::map<FieldKey, uint32_t, FieldKeyComparator> fields_; |
| 394 | std::map<ProtoKey, IdxAndDataOffset, ProtoKeyComparator> protos_; |
| 395 | std::map<MethodKey, uint32_t, MethodKeyComparator> methods_; |
| 396 | |
| 397 | std::vector<uint8_t> dex_file_data_; |
| 398 | }; |
| 399 | |
| 400 | } // namespace art |
| 401 | |
| 402 | #endif // ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ |