402 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			402 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
/*
 | 
						|
 * Copyright (C) 2015 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.
 | 
						|
 */
 | 
						|
 | 
						|
#ifndef ART_LIBDEXFILE_DEX_TEST_DEX_FILE_BUILDER_H_
 | 
						|
#define ART_LIBDEXFILE_DEX_TEST_DEX_FILE_BUILDER_H_
 | 
						|
 | 
						|
#include <zlib.h>
 | 
						|
 | 
						|
#include <cstring>
 | 
						|
#include <map>
 | 
						|
#include <set>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
#include <android-base/logging.h>
 | 
						|
 | 
						|
#include "base/array_ref.h"
 | 
						|
#include "base/bit_utils.h"
 | 
						|
#include "dex/dex_file_loader.h"
 | 
						|
#include "dex/standard_dex_file.h"
 | 
						|
 | 
						|
namespace art {
 | 
						|
 | 
						|
class TestDexFileBuilder {
 | 
						|
 public:
 | 
						|
  TestDexFileBuilder()
 | 
						|
      : strings_(), types_(), fields_(), protos_() {
 | 
						|
  }
 | 
						|
 | 
						|
  void AddString(const std::string& str) {
 | 
						|
    CHECK_LT(str.length(), 128u);  // Don't allow multi-byte length in uleb128.
 | 
						|
    strings_.emplace(str, IdxAndDataOffset());
 | 
						|
  }
 | 
						|
 | 
						|
  void AddType(const std::string& descriptor) {
 | 
						|
    AddString(descriptor);
 | 
						|
    types_.emplace(descriptor, 0u);
 | 
						|
  }
 | 
						|
 | 
						|
  void AddField(const std::string& class_descriptor,
 | 
						|
                const std::string& type,
 | 
						|
                const std::string& name) {
 | 
						|
    AddType(class_descriptor);
 | 
						|
    AddType(type);
 | 
						|
    AddString(name);
 | 
						|
    FieldKey key = { class_descriptor, type, name };
 | 
						|
    fields_.emplace(key, 0u);
 | 
						|
  }
 | 
						|
 | 
						|
  void AddMethod(const std::string& class_descriptor,
 | 
						|
                 const std::string& signature,
 | 
						|
                 const std::string& name) {
 | 
						|
    AddType(class_descriptor);
 | 
						|
    AddString(name);
 | 
						|
 | 
						|
    ProtoKey proto_key = CreateProtoKey(signature);
 | 
						|
    AddString(proto_key.shorty);
 | 
						|
    AddType(proto_key.return_type);
 | 
						|
    for (const auto& arg_type : proto_key.args) {
 | 
						|
      AddType(arg_type);
 | 
						|
    }
 | 
						|
    auto it = protos_.emplace(proto_key, IdxAndDataOffset()).first;
 | 
						|
    const ProtoKey* proto = &it->first;  // Valid as long as the element remains in protos_.
 | 
						|
 | 
						|
    MethodKey method_key = {
 | 
						|
        class_descriptor, name, proto
 | 
						|
    };
 | 
						|
    methods_.emplace(method_key, 0u);
 | 
						|
  }
 | 
						|
 | 
						|
  std::unique_ptr<const DexFile> Build(const std::string& dex_location,
 | 
						|
                                       uint32_t location_checksum = 0u) {
 | 
						|
    union {
 | 
						|
      uint8_t data[sizeof(DexFile::Header)];
 | 
						|
      uint64_t force_alignment;
 | 
						|
    } header_data;
 | 
						|
    std::memset(header_data.data, 0, sizeof(header_data.data));
 | 
						|
    DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data);
 | 
						|
    std::copy_n(StandardDexFile::kDexMagic, 4u, header->magic_);
 | 
						|
    std::copy_n(StandardDexFile::kDexMagicVersions[0], 4u, header->magic_ + 4u);
 | 
						|
    header->header_size_ = sizeof(DexFile::Header);
 | 
						|
    header->endian_tag_ = DexFile::kDexEndianConstant;
 | 
						|
    header->link_size_ = 0u;  // Unused.
 | 
						|
    header->link_off_ = 0u;  // Unused.
 | 
						|
    header->map_off_ = 0u;  // Unused. TODO: This is wrong. Dex files created by this builder
 | 
						|
                            //               cannot be verified. b/26808512
 | 
						|
 | 
						|
    uint32_t data_section_size = 0u;
 | 
						|
 | 
						|
    uint32_t string_ids_offset = sizeof(DexFile::Header);
 | 
						|
    uint32_t string_idx = 0u;
 | 
						|
    for (auto& entry : strings_) {
 | 
						|
      entry.second.idx = string_idx;
 | 
						|
      string_idx += 1u;
 | 
						|
      entry.second.data_offset = data_section_size;
 | 
						|
      data_section_size += entry.first.length() + 1u /* length */ + 1u /* null-terminator */;
 | 
						|
    }
 | 
						|
    header->string_ids_size_ = strings_.size();
 | 
						|
    header->string_ids_off_ = strings_.empty() ? 0u : string_ids_offset;
 | 
						|
 | 
						|
    uint32_t type_ids_offset = string_ids_offset + strings_.size() * sizeof(dex::StringId);
 | 
						|
    uint32_t type_idx = 0u;
 | 
						|
    for (auto& entry : types_) {
 | 
						|
      entry.second = type_idx;
 | 
						|
      type_idx += 1u;
 | 
						|
    }
 | 
						|
    header->type_ids_size_ = types_.size();
 | 
						|
    header->type_ids_off_ = types_.empty() ? 0u : type_ids_offset;
 | 
						|
 | 
						|
    uint32_t proto_ids_offset = type_ids_offset + types_.size() * sizeof(dex::TypeId);
 | 
						|
    uint32_t proto_idx = 0u;
 | 
						|
    for (auto& entry : protos_) {
 | 
						|
      entry.second.idx = proto_idx;
 | 
						|
      proto_idx += 1u;
 | 
						|
      size_t num_args = entry.first.args.size();
 | 
						|
      if (num_args != 0u) {
 | 
						|
        entry.second.data_offset = RoundUp(data_section_size, 4u);
 | 
						|
        data_section_size = entry.second.data_offset + 4u + num_args * sizeof(dex::TypeItem);
 | 
						|
      } else {
 | 
						|
        entry.second.data_offset = 0u;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    header->proto_ids_size_ = protos_.size();
 | 
						|
    header->proto_ids_off_ = protos_.empty() ? 0u : proto_ids_offset;
 | 
						|
 | 
						|
    uint32_t field_ids_offset = proto_ids_offset + protos_.size() * sizeof(dex::ProtoId);
 | 
						|
    uint32_t field_idx = 0u;
 | 
						|
    for (auto& entry : fields_) {
 | 
						|
      entry.second = field_idx;
 | 
						|
      field_idx += 1u;
 | 
						|
    }
 | 
						|
    header->field_ids_size_ = fields_.size();
 | 
						|
    header->field_ids_off_ = fields_.empty() ? 0u : field_ids_offset;
 | 
						|
 | 
						|
    uint32_t method_ids_offset = field_ids_offset + fields_.size() * sizeof(dex::FieldId);
 | 
						|
    uint32_t method_idx = 0u;
 | 
						|
    for (auto& entry : methods_) {
 | 
						|
      entry.second = method_idx;
 | 
						|
      method_idx += 1u;
 | 
						|
    }
 | 
						|
    header->method_ids_size_ = methods_.size();
 | 
						|
    header->method_ids_off_ = methods_.empty() ? 0u : method_ids_offset;
 | 
						|
 | 
						|
    // No class defs.
 | 
						|
    header->class_defs_size_ = 0u;
 | 
						|
    header->class_defs_off_ = 0u;
 | 
						|
 | 
						|
    uint32_t data_section_offset = method_ids_offset + methods_.size() * sizeof(dex::MethodId);
 | 
						|
    header->data_size_ = data_section_size;
 | 
						|
    header->data_off_ = (data_section_size != 0u) ? data_section_offset : 0u;
 | 
						|
 | 
						|
    uint32_t total_size = data_section_offset + data_section_size;
 | 
						|
    std::vector<uint8_t> dex_file_data(total_size, 0u);
 | 
						|
 | 
						|
    for (const auto& entry : strings_) {
 | 
						|
      CHECK_LT(entry.first.size(), 128u);
 | 
						|
      uint32_t raw_offset = data_section_offset + entry.second.data_offset;
 | 
						|
      dex_file_data[raw_offset] = static_cast<uint8_t>(entry.first.size());
 | 
						|
      std::memcpy(&dex_file_data[raw_offset + 1], entry.first.c_str(), entry.first.size() + 1);
 | 
						|
      Write32(dex_file_data,
 | 
						|
              string_ids_offset + entry.second.idx * sizeof(dex::StringId),
 | 
						|
              raw_offset);
 | 
						|
    }
 | 
						|
 | 
						|
    for (const auto& entry : types_) {
 | 
						|
      Write32(dex_file_data,
 | 
						|
              type_ids_offset + entry.second * sizeof(dex::TypeId),
 | 
						|
              GetStringIdx(entry.first));
 | 
						|
      ++type_idx;
 | 
						|
    }
 | 
						|
 | 
						|
    for (const auto& entry : protos_) {
 | 
						|
      size_t num_args = entry.first.args.size();
 | 
						|
      uint32_t type_list_offset =
 | 
						|
          (num_args != 0u) ? data_section_offset + entry.second.data_offset : 0u;
 | 
						|
      uint32_t raw_offset = proto_ids_offset + entry.second.idx * sizeof(dex::ProtoId);
 | 
						|
      Write32(dex_file_data, raw_offset + 0u, GetStringIdx(entry.first.shorty));
 | 
						|
      Write16(dex_file_data, raw_offset + 4u, GetTypeIdx(entry.first.return_type));
 | 
						|
      Write32(dex_file_data, raw_offset + 8u, type_list_offset);
 | 
						|
      if (num_args != 0u) {
 | 
						|
        CHECK_NE(entry.second.data_offset, 0u);
 | 
						|
        Write32(dex_file_data, type_list_offset, num_args);
 | 
						|
        for (size_t i = 0; i != num_args; ++i) {
 | 
						|
          Write16(dex_file_data,
 | 
						|
                  type_list_offset + 4u + i * sizeof(dex::TypeItem),
 | 
						|
                  GetTypeIdx(entry.first.args[i]));
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    for (const auto& entry : fields_) {
 | 
						|
      uint32_t raw_offset = field_ids_offset + entry.second * sizeof(dex::FieldId);
 | 
						|
      Write16(dex_file_data, raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor));
 | 
						|
      Write16(dex_file_data, raw_offset + 2u, GetTypeIdx(entry.first.type));
 | 
						|
      Write32(dex_file_data, raw_offset + 4u, GetStringIdx(entry.first.name));
 | 
						|
    }
 | 
						|
 | 
						|
    for (const auto& entry : methods_) {
 | 
						|
      uint32_t raw_offset = method_ids_offset + entry.second * sizeof(dex::MethodId);
 | 
						|
      Write16(dex_file_data, raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor));
 | 
						|
      auto it = protos_.find(*entry.first.proto);
 | 
						|
      CHECK(it != protos_.end());
 | 
						|
      Write16(dex_file_data, raw_offset + 2u, it->second.idx);
 | 
						|
      Write32(dex_file_data, raw_offset + 4u, GetStringIdx(entry.first.name));
 | 
						|
    }
 | 
						|
 | 
						|
    // Leave signature as zeros.
 | 
						|
 | 
						|
    header->file_size_ = dex_file_data.size();
 | 
						|
 | 
						|
    // Write the complete header early, as part of it needs to be checksummed.
 | 
						|
    std::memcpy(&dex_file_data[0], header_data.data, sizeof(DexFile::Header));
 | 
						|
 | 
						|
    // Checksum starts after the checksum field.
 | 
						|
    size_t skip = sizeof(header->magic_) + sizeof(header->checksum_);
 | 
						|
    header->checksum_ = adler32(adler32(0L, Z_NULL, 0),
 | 
						|
                                dex_file_data.data() + skip,
 | 
						|
                                dex_file_data.size() - skip);
 | 
						|
 | 
						|
    // Write the complete header again, just simpler that way.
 | 
						|
    std::memcpy(&dex_file_data[0], header_data.data, sizeof(DexFile::Header));
 | 
						|
 | 
						|
    // Do not protect the final data from writing. Some tests need to modify it.
 | 
						|
 | 
						|
    static constexpr bool kVerify = false;
 | 
						|
    static constexpr bool kVerifyChecksum = false;
 | 
						|
    std::string error_msg;
 | 
						|
    std::unique_ptr<const DexFile> dex_file(DexFileLoader::Open(
 | 
						|
        dex_location,
 | 
						|
        location_checksum,
 | 
						|
        std::move(dex_file_data),
 | 
						|
        /*oat_dex_file=*/ nullptr,
 | 
						|
        kVerify,
 | 
						|
        kVerifyChecksum,
 | 
						|
        &error_msg));
 | 
						|
    CHECK(dex_file != nullptr) << error_msg;
 | 
						|
    return dex_file;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t GetStringIdx(const std::string& type) {
 | 
						|
    auto it = strings_.find(type);
 | 
						|
    CHECK(it != strings_.end());
 | 
						|
    return it->second.idx;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t GetTypeIdx(const std::string& type) {
 | 
						|
    auto it = types_.find(type);
 | 
						|
    CHECK(it != types_.end());
 | 
						|
    return it->second;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t GetFieldIdx(const std::string& class_descriptor, const std::string& type,
 | 
						|
                       const std::string& name) {
 | 
						|
    FieldKey key = { class_descriptor, type, name };
 | 
						|
    auto it = fields_.find(key);
 | 
						|
    CHECK(it != fields_.end());
 | 
						|
    return it->second;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t GetMethodIdx(const std::string& class_descriptor, const std::string& signature,
 | 
						|
                        const std::string& name) {
 | 
						|
    ProtoKey proto_key = CreateProtoKey(signature);
 | 
						|
    MethodKey method_key = { class_descriptor, name, &proto_key };
 | 
						|
    auto it = methods_.find(method_key);
 | 
						|
    CHECK(it != methods_.end());
 | 
						|
    return it->second;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  struct IdxAndDataOffset {
 | 
						|
    uint32_t idx;
 | 
						|
    uint32_t data_offset;
 | 
						|
  };
 | 
						|
 | 
						|
  struct FieldKey {
 | 
						|
    const std::string class_descriptor;
 | 
						|
    const std::string type;
 | 
						|
    const std::string name;
 | 
						|
  };
 | 
						|
  struct FieldKeyComparator {
 | 
						|
    bool operator()(const FieldKey& lhs, const FieldKey& rhs) const {
 | 
						|
      if (lhs.class_descriptor != rhs.class_descriptor) {
 | 
						|
        return lhs.class_descriptor < rhs.class_descriptor;
 | 
						|
      }
 | 
						|
      if (lhs.name != rhs.name) {
 | 
						|
        return lhs.name < rhs.name;
 | 
						|
      }
 | 
						|
      return lhs.type < rhs.type;
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  struct ProtoKey {
 | 
						|
    std::string shorty;
 | 
						|
    std::string return_type;
 | 
						|
    std::vector<std::string> args;
 | 
						|
  };
 | 
						|
  struct ProtoKeyComparator {
 | 
						|
    bool operator()(const ProtoKey& lhs, const ProtoKey& rhs) const {
 | 
						|
      if (lhs.return_type != rhs.return_type) {
 | 
						|
        return lhs.return_type < rhs.return_type;
 | 
						|
      }
 | 
						|
      size_t min_args = std::min(lhs.args.size(), rhs.args.size());
 | 
						|
      for (size_t i = 0; i != min_args; ++i) {
 | 
						|
        if (lhs.args[i] != rhs.args[i]) {
 | 
						|
          return lhs.args[i] < rhs.args[i];
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return lhs.args.size() < rhs.args.size();
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  struct MethodKey {
 | 
						|
    std::string class_descriptor;
 | 
						|
    std::string name;
 | 
						|
    const ProtoKey* proto;
 | 
						|
  };
 | 
						|
  struct MethodKeyComparator {
 | 
						|
    bool operator()(const MethodKey& lhs, const MethodKey& rhs) const {
 | 
						|
      if (lhs.class_descriptor != rhs.class_descriptor) {
 | 
						|
        return lhs.class_descriptor < rhs.class_descriptor;
 | 
						|
      }
 | 
						|
      if (lhs.name != rhs.name) {
 | 
						|
        return lhs.name < rhs.name;
 | 
						|
      }
 | 
						|
      return ProtoKeyComparator()(*lhs.proto, *rhs.proto);
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  ProtoKey CreateProtoKey(const std::string& signature) {
 | 
						|
    CHECK_EQ(signature[0], '(');
 | 
						|
    const char* args = signature.c_str() + 1;
 | 
						|
    const char* args_end = std::strchr(args, ')');
 | 
						|
    CHECK(args_end != nullptr);
 | 
						|
    const char* return_type = args_end + 1;
 | 
						|
 | 
						|
    ProtoKey key = {
 | 
						|
        std::string() + ((*return_type == '[') ? 'L' : *return_type),
 | 
						|
        return_type,
 | 
						|
        std::vector<std::string>()
 | 
						|
    };
 | 
						|
    while (args != args_end) {
 | 
						|
      key.shorty += (*args == '[') ? 'L' : *args;
 | 
						|
      const char* arg_start = args;
 | 
						|
      while (*args == '[') {
 | 
						|
        ++args;
 | 
						|
      }
 | 
						|
      if (*args == 'L') {
 | 
						|
        do {
 | 
						|
          ++args;
 | 
						|
          CHECK_NE(args, args_end);
 | 
						|
        } while (*args != ';');
 | 
						|
      }
 | 
						|
      ++args;
 | 
						|
      key.args.emplace_back(arg_start, args);
 | 
						|
    }
 | 
						|
    return key;
 | 
						|
  }
 | 
						|
 | 
						|
  static void Write32(std::vector<uint8_t>& dex_file_data, size_t offset, uint32_t value) {
 | 
						|
    CHECK_LE(offset + 4u, dex_file_data.size());
 | 
						|
    CHECK_EQ(dex_file_data[offset + 0], 0u);
 | 
						|
    CHECK_EQ(dex_file_data[offset + 1], 0u);
 | 
						|
    CHECK_EQ(dex_file_data[offset + 2], 0u);
 | 
						|
    CHECK_EQ(dex_file_data[offset + 3], 0u);
 | 
						|
    dex_file_data[offset + 0] = static_cast<uint8_t>(value >> 0);
 | 
						|
    dex_file_data[offset + 1] = static_cast<uint8_t>(value >> 8);
 | 
						|
    dex_file_data[offset + 2] = static_cast<uint8_t>(value >> 16);
 | 
						|
    dex_file_data[offset + 3] = static_cast<uint8_t>(value >> 24);
 | 
						|
  }
 | 
						|
 | 
						|
  static void Write16(std::vector<uint8_t>& dex_file_data, size_t offset, uint32_t value) {
 | 
						|
    CHECK_LE(value, 0xffffu);
 | 
						|
    CHECK_LE(offset + 2u, dex_file_data.size());
 | 
						|
    CHECK_EQ(dex_file_data[offset + 0], 0u);
 | 
						|
    CHECK_EQ(dex_file_data[offset + 1], 0u);
 | 
						|
    dex_file_data[offset + 0] = static_cast<uint8_t>(value >> 0);
 | 
						|
    dex_file_data[offset + 1] = static_cast<uint8_t>(value >> 8);
 | 
						|
  }
 | 
						|
 | 
						|
  std::map<std::string, IdxAndDataOffset> strings_;
 | 
						|
  std::map<std::string, uint32_t> types_;
 | 
						|
  std::map<FieldKey, uint32_t, FieldKeyComparator> fields_;
 | 
						|
  std::map<ProtoKey, IdxAndDataOffset, ProtoKeyComparator> protos_;
 | 
						|
  std::map<MethodKey, uint32_t, MethodKeyComparator> methods_;
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace art
 | 
						|
 | 
						|
#endif  // ART_LIBDEXFILE_DEX_TEST_DEX_FILE_BUILDER_H_
 |