upload android base code part3
This commit is contained in:
parent
71b83c22f1
commit
b9e30e05b1
15122 changed files with 2089659 additions and 0 deletions
38
android/art/dexdump/Android.bp
Normal file
38
android/art/dexdump/Android.bp
Normal file
|
@ -0,0 +1,38 @@
|
|||
// 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.
|
||||
|
||||
// TODO(ajcbik): rename dexdump2 into dexdump when Dalvik version is removed
|
||||
|
||||
art_cc_binary {
|
||||
name: "dexdump2",
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"dexdump_cfg.cc",
|
||||
"dexdump_main.cc",
|
||||
"dexdump.cc",
|
||||
],
|
||||
cflags: ["-Wall"],
|
||||
shared_libs: [
|
||||
"libart",
|
||||
"libbase",
|
||||
],
|
||||
}
|
||||
|
||||
art_cc_test {
|
||||
name: "art_dexdump_tests",
|
||||
defaults: [
|
||||
"art_gtest_defaults",
|
||||
],
|
||||
srcs: ["dexdump_test.cc"],
|
||||
}
|
1892
android/art/dexdump/dexdump.cc
Normal file
1892
android/art/dexdump/dexdump.cc
Normal file
File diff suppressed because it is too large
Load diff
61
android/art/dexdump/dexdump.h
Normal file
61
android/art/dexdump/dexdump.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header file of the dexdump utility.
|
||||
*
|
||||
* This is a re-implementation of the original dexdump utility that was
|
||||
* based on Dalvik functions in libdex into a new dexdump that is now
|
||||
* based on Art functions in libart instead. The output is identical to
|
||||
* the original for correct DEX files. Error messages may differ, however.
|
||||
* Also, ODEX files are no longer supported.
|
||||
*/
|
||||
|
||||
#ifndef ART_DEXDUMP_DEXDUMP_H_
|
||||
#define ART_DEXDUMP_DEXDUMP_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace art {
|
||||
|
||||
/* Supported output formats. */
|
||||
enum OutputFormat {
|
||||
OUTPUT_PLAIN = 0, // default
|
||||
OUTPUT_XML, // XML-style
|
||||
};
|
||||
|
||||
/* Command-line options. */
|
||||
struct Options {
|
||||
bool checksumOnly;
|
||||
bool disassemble;
|
||||
bool exportsOnly;
|
||||
bool ignoreBadChecksum;
|
||||
bool showAnnotations;
|
||||
bool showCfg;
|
||||
bool showFileHeaders;
|
||||
bool showSectionHeaders;
|
||||
bool verbose;
|
||||
OutputFormat outputFormat;
|
||||
const char* outputFileName;
|
||||
};
|
||||
|
||||
/* Prototypes. */
|
||||
extern struct Options gOptions;
|
||||
extern FILE* gOutFile;
|
||||
int processFile(const char* fileName);
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_DEXDUMP_DEXDUMP_H_
|
392
android/art/dexdump/dexdump_cfg.cc
Normal file
392
android/art/dexdump/dexdump_cfg.cc
Normal file
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*
|
||||
* Implementation file for control flow graph dumping for the dexdump utility.
|
||||
*/
|
||||
|
||||
#include "dexdump_cfg.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <ostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "dex_file-inl.h"
|
||||
#include "dex_instruction-inl.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
static void dumpMethodCFGImpl(const DexFile* dex_file,
|
||||
uint32_t dex_method_idx,
|
||||
const DexFile::CodeItem* code_item,
|
||||
std::ostream& os) {
|
||||
os << "digraph {\n";
|
||||
os << " # /* " << dex_file->PrettyMethod(dex_method_idx, true) << " */\n";
|
||||
|
||||
std::set<uint32_t> dex_pc_is_branch_target;
|
||||
{
|
||||
// Go and populate.
|
||||
const Instruction* inst = Instruction::At(code_item->insns_);
|
||||
for (uint32_t dex_pc = 0;
|
||||
dex_pc < code_item->insns_size_in_code_units_;
|
||||
dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
|
||||
if (inst->IsBranch()) {
|
||||
dex_pc_is_branch_target.insert(dex_pc + inst->GetTargetOffset());
|
||||
} else if (inst->IsSwitch()) {
|
||||
const uint16_t* insns = code_item->insns_ + dex_pc;
|
||||
int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
|
||||
const uint16_t* switch_insns = insns + switch_offset;
|
||||
uint32_t switch_count = switch_insns[1];
|
||||
int32_t targets_offset;
|
||||
if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
|
||||
/* 0=sig, 1=count, 2/3=firstKey */
|
||||
targets_offset = 4;
|
||||
} else {
|
||||
/* 0=sig, 1=count, 2..count*2 = keys */
|
||||
targets_offset = 2 + 2 * switch_count;
|
||||
}
|
||||
for (uint32_t targ = 0; targ < switch_count; targ++) {
|
||||
int32_t offset =
|
||||
static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
|
||||
static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
|
||||
dex_pc_is_branch_target.insert(dex_pc + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create nodes for "basic blocks."
|
||||
std::map<uint32_t, uint32_t> dex_pc_to_node_id; // This only has entries for block starts.
|
||||
std::map<uint32_t, uint32_t> dex_pc_to_incl_id; // This has entries for all dex pcs.
|
||||
|
||||
{
|
||||
const Instruction* inst = Instruction::At(code_item->insns_);
|
||||
bool first_in_block = true;
|
||||
bool force_new_block = false;
|
||||
for (uint32_t dex_pc = 0;
|
||||
dex_pc < code_item->insns_size_in_code_units_;
|
||||
dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
|
||||
if (dex_pc == 0 ||
|
||||
(dex_pc_is_branch_target.find(dex_pc) != dex_pc_is_branch_target.end()) ||
|
||||
force_new_block) {
|
||||
uint32_t id = dex_pc_to_node_id.size();
|
||||
if (id > 0) {
|
||||
// End last node.
|
||||
os << "}\"];\n";
|
||||
}
|
||||
// Start next node.
|
||||
os << " node" << id << " [shape=record,label=\"{";
|
||||
dex_pc_to_node_id.insert(std::make_pair(dex_pc, id));
|
||||
first_in_block = true;
|
||||
force_new_block = false;
|
||||
}
|
||||
|
||||
// Register instruction.
|
||||
dex_pc_to_incl_id.insert(std::make_pair(dex_pc, dex_pc_to_node_id.size() - 1));
|
||||
|
||||
// Print instruction.
|
||||
if (!first_in_block) {
|
||||
os << " | ";
|
||||
} else {
|
||||
first_in_block = false;
|
||||
}
|
||||
|
||||
// Dump the instruction. Need to escape '"', '<', '>', '{' and '}'.
|
||||
os << "<" << "p" << dex_pc << ">";
|
||||
os << " 0x" << std::hex << dex_pc << std::dec << ": ";
|
||||
std::string inst_str = inst->DumpString(dex_file);
|
||||
size_t cur_start = 0; // It's OK to start at zero, instruction dumps don't start with chars
|
||||
// we need to escape.
|
||||
while (cur_start != std::string::npos) {
|
||||
size_t next_escape = inst_str.find_first_of("\"{}<>", cur_start + 1);
|
||||
if (next_escape == std::string::npos) {
|
||||
os << inst_str.substr(cur_start, inst_str.size() - cur_start);
|
||||
break;
|
||||
} else {
|
||||
os << inst_str.substr(cur_start, next_escape - cur_start);
|
||||
// Escape all necessary characters.
|
||||
while (next_escape < inst_str.size()) {
|
||||
char c = inst_str.at(next_escape);
|
||||
if (c == '"' || c == '{' || c == '}' || c == '<' || c == '>') {
|
||||
os << '\\' << c;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
next_escape++;
|
||||
}
|
||||
if (next_escape >= inst_str.size()) {
|
||||
next_escape = std::string::npos;
|
||||
}
|
||||
cur_start = next_escape;
|
||||
}
|
||||
}
|
||||
|
||||
// Force a new block for some fall-throughs and some instructions that terminate the "local"
|
||||
// control flow.
|
||||
force_new_block = inst->IsSwitch() || inst->IsBasicBlockEnd();
|
||||
}
|
||||
// Close last node.
|
||||
if (dex_pc_to_node_id.size() > 0) {
|
||||
os << "}\"];\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Create edges between them.
|
||||
{
|
||||
std::ostringstream regular_edges;
|
||||
std::ostringstream taken_edges;
|
||||
std::ostringstream exception_edges;
|
||||
|
||||
// Common set of exception edges.
|
||||
std::set<uint32_t> exception_targets;
|
||||
|
||||
// These blocks (given by the first dex pc) need exception per dex-pc handling in a second
|
||||
// pass. In the first pass we try and see whether we can use a common set of edges.
|
||||
std::set<uint32_t> blocks_with_detailed_exceptions;
|
||||
|
||||
{
|
||||
uint32_t last_node_id = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t old_dex_pc = 0;
|
||||
uint32_t block_start_dex_pc = std::numeric_limits<uint32_t>::max();
|
||||
const Instruction* inst = Instruction::At(code_item->insns_);
|
||||
for (uint32_t dex_pc = 0;
|
||||
dex_pc < code_item->insns_size_in_code_units_;
|
||||
old_dex_pc = dex_pc, dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
|
||||
{
|
||||
auto it = dex_pc_to_node_id.find(dex_pc);
|
||||
if (it != dex_pc_to_node_id.end()) {
|
||||
if (!exception_targets.empty()) {
|
||||
// It seems the last block had common exception handlers. Add the exception edges now.
|
||||
uint32_t node_id = dex_pc_to_node_id.find(block_start_dex_pc)->second;
|
||||
for (uint32_t handler_pc : exception_targets) {
|
||||
auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
|
||||
if (node_id_it != dex_pc_to_incl_id.end()) {
|
||||
exception_edges << " node" << node_id
|
||||
<< " -> node" << node_id_it->second << ":p" << handler_pc
|
||||
<< ";\n";
|
||||
}
|
||||
}
|
||||
exception_targets.clear();
|
||||
}
|
||||
|
||||
block_start_dex_pc = dex_pc;
|
||||
|
||||
// Seems to be a fall-through, connect to last_node_id. May be spurious edges for things
|
||||
// like switch data.
|
||||
uint32_t old_last = last_node_id;
|
||||
last_node_id = it->second;
|
||||
if (old_last != std::numeric_limits<uint32_t>::max()) {
|
||||
regular_edges << " node" << old_last << ":p" << old_dex_pc
|
||||
<< " -> node" << last_node_id << ":p" << dex_pc
|
||||
<< ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Look at the exceptions of the first entry.
|
||||
CatchHandlerIterator catch_it(*code_item, dex_pc);
|
||||
for (; catch_it.HasNext(); catch_it.Next()) {
|
||||
exception_targets.insert(catch_it.GetHandlerAddress());
|
||||
}
|
||||
}
|
||||
|
||||
// Handle instruction.
|
||||
|
||||
// Branch: something with at most two targets.
|
||||
if (inst->IsBranch()) {
|
||||
const int32_t offset = inst->GetTargetOffset();
|
||||
const bool conditional = !inst->IsUnconditional();
|
||||
|
||||
auto target_it = dex_pc_to_node_id.find(dex_pc + offset);
|
||||
if (target_it != dex_pc_to_node_id.end()) {
|
||||
taken_edges << " node" << last_node_id << ":p" << dex_pc
|
||||
<< " -> node" << target_it->second << ":p" << (dex_pc + offset)
|
||||
<< ";\n";
|
||||
}
|
||||
if (!conditional) {
|
||||
// No fall-through.
|
||||
last_node_id = std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
} else if (inst->IsSwitch()) {
|
||||
// TODO: Iterate through all switch targets.
|
||||
const uint16_t* insns = code_item->insns_ + dex_pc;
|
||||
/* make sure the start of the switch is in range */
|
||||
int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
|
||||
/* offset to switch table is a relative branch-style offset */
|
||||
const uint16_t* switch_insns = insns + switch_offset;
|
||||
uint32_t switch_count = switch_insns[1];
|
||||
int32_t targets_offset;
|
||||
if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
|
||||
/* 0=sig, 1=count, 2/3=firstKey */
|
||||
targets_offset = 4;
|
||||
} else {
|
||||
/* 0=sig, 1=count, 2..count*2 = keys */
|
||||
targets_offset = 2 + 2 * switch_count;
|
||||
}
|
||||
/* make sure the end of the switch is in range */
|
||||
/* verify each switch target */
|
||||
for (uint32_t targ = 0; targ < switch_count; targ++) {
|
||||
int32_t offset =
|
||||
static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
|
||||
static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
|
||||
int32_t abs_offset = dex_pc + offset;
|
||||
auto target_it = dex_pc_to_node_id.find(abs_offset);
|
||||
if (target_it != dex_pc_to_node_id.end()) {
|
||||
// TODO: value label.
|
||||
taken_edges << " node" << last_node_id << ":p" << dex_pc
|
||||
<< " -> node" << target_it->second << ":p" << (abs_offset)
|
||||
<< ";\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exception edges. If this is not the first instruction in the block
|
||||
if (block_start_dex_pc != dex_pc) {
|
||||
std::set<uint32_t> current_handler_pcs;
|
||||
CatchHandlerIterator catch_it(*code_item, dex_pc);
|
||||
for (; catch_it.HasNext(); catch_it.Next()) {
|
||||
current_handler_pcs.insert(catch_it.GetHandlerAddress());
|
||||
}
|
||||
if (current_handler_pcs != exception_targets) {
|
||||
exception_targets.clear(); // Clear so we don't do something at the end.
|
||||
blocks_with_detailed_exceptions.insert(block_start_dex_pc);
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->IsReturn() ||
|
||||
(inst->Opcode() == Instruction::THROW) ||
|
||||
(inst->IsBranch() && inst->IsUnconditional())) {
|
||||
// No fall-through.
|
||||
last_node_id = std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
}
|
||||
// Finish up the last block, if it had common exceptions.
|
||||
if (!exception_targets.empty()) {
|
||||
// It seems the last block had common exception handlers. Add the exception edges now.
|
||||
uint32_t node_id = dex_pc_to_node_id.find(block_start_dex_pc)->second;
|
||||
for (uint32_t handler_pc : exception_targets) {
|
||||
auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
|
||||
if (node_id_it != dex_pc_to_incl_id.end()) {
|
||||
exception_edges << " node" << node_id
|
||||
<< " -> node" << node_id_it->second << ":p" << handler_pc
|
||||
<< ";\n";
|
||||
}
|
||||
}
|
||||
exception_targets.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass for detailed exception blocks.
|
||||
// TODO
|
||||
// Exception edges. If this is not the first instruction in the block
|
||||
for (uint32_t dex_pc : blocks_with_detailed_exceptions) {
|
||||
const Instruction* inst = Instruction::At(&code_item->insns_[dex_pc]);
|
||||
uint32_t this_node_id = dex_pc_to_incl_id.find(dex_pc)->second;
|
||||
while (true) {
|
||||
CatchHandlerIterator catch_it(*code_item, dex_pc);
|
||||
if (catch_it.HasNext()) {
|
||||
std::set<uint32_t> handled_targets;
|
||||
for (; catch_it.HasNext(); catch_it.Next()) {
|
||||
uint32_t handler_pc = catch_it.GetHandlerAddress();
|
||||
auto it = handled_targets.find(handler_pc);
|
||||
if (it == handled_targets.end()) {
|
||||
auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
|
||||
if (node_id_it != dex_pc_to_incl_id.end()) {
|
||||
exception_edges << " node" << this_node_id << ":p" << dex_pc
|
||||
<< " -> node" << node_id_it->second << ":p" << handler_pc
|
||||
<< ";\n";
|
||||
}
|
||||
|
||||
// Mark as done.
|
||||
handled_targets.insert(handler_pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inst->IsBasicBlockEnd()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Loop update. Have a break-out if the next instruction is a branch target and thus in
|
||||
// another block.
|
||||
dex_pc += inst->SizeInCodeUnits();
|
||||
if (dex_pc >= code_item->insns_size_in_code_units_) {
|
||||
break;
|
||||
}
|
||||
if (dex_pc_to_node_id.find(dex_pc) != dex_pc_to_node_id.end()) {
|
||||
break;
|
||||
}
|
||||
inst = inst->Next();
|
||||
}
|
||||
}
|
||||
|
||||
// Write out the sub-graphs to make edges styled.
|
||||
os << "\n";
|
||||
os << " subgraph regular_edges {\n";
|
||||
os << " edge [color=\"#000000\",weight=.3,len=3];\n\n";
|
||||
os << " " << regular_edges.str() << "\n";
|
||||
os << " }\n\n";
|
||||
|
||||
os << " subgraph taken_edges {\n";
|
||||
os << " edge [color=\"#00FF00\",weight=.3,len=3];\n\n";
|
||||
os << " " << taken_edges.str() << "\n";
|
||||
os << " }\n\n";
|
||||
|
||||
os << " subgraph exception_edges {\n";
|
||||
os << " edge [color=\"#FF0000\",weight=.3,len=3];\n\n";
|
||||
os << " " << exception_edges.str() << "\n";
|
||||
os << " }\n\n";
|
||||
}
|
||||
|
||||
os << "}\n";
|
||||
}
|
||||
|
||||
void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostream& os) {
|
||||
// This is painful, we need to find the code item. That means finding the class, and then
|
||||
// iterating the table.
|
||||
if (dex_method_idx >= dex_file->NumMethodIds()) {
|
||||
os << "Could not find method-idx.";
|
||||
return;
|
||||
}
|
||||
const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx);
|
||||
|
||||
const DexFile::ClassDef* class_def = dex_file->FindClassDef(method_id.class_idx_);
|
||||
if (class_def == nullptr) {
|
||||
os << "Could not find class-def.";
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t* class_data = dex_file->GetClassData(*class_def);
|
||||
if (class_data == nullptr) {
|
||||
os << "No class data.";
|
||||
return;
|
||||
}
|
||||
|
||||
ClassDataItemIterator it(*dex_file, class_data);
|
||||
it.SkipAllFields();
|
||||
|
||||
// Find method, and dump it.
|
||||
while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
|
||||
uint32_t method_idx = it.GetMemberIndex();
|
||||
if (method_idx == dex_method_idx) {
|
||||
dumpMethodCFGImpl(dex_file, dex_method_idx, it.GetMethodCodeItem(), os);
|
||||
return;
|
||||
}
|
||||
it.Next();
|
||||
}
|
||||
|
||||
// Otherwise complain.
|
||||
os << "Something went wrong, didn't find the method in the class data.";
|
||||
}
|
||||
|
||||
} // namespace art
|
31
android/art/dexdump/dexdump_cfg.h
Normal file
31
android/art/dexdump/dexdump_cfg.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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_DEXDUMP_DEXDUMP_CFG_H_
|
||||
#define ART_DEXDUMP_DEXDUMP_CFG_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <ostream>
|
||||
|
||||
namespace art {
|
||||
|
||||
class DexFile;
|
||||
|
||||
void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostream& os);
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_DEXDUMP_DEXDUMP_CFG_H_
|
156
android/art/dexdump/dexdump_main.cc
Normal file
156
android/art/dexdump/dexdump_main.cc
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Main driver of the dexdump utility.
|
||||
*
|
||||
* This is a re-implementation of the original dexdump utility that was
|
||||
* based on Dalvik functions in libdex into a new dexdump that is now
|
||||
* based on Art functions in libart instead. The output is very similar to
|
||||
* to the original for correct DEX files. Error messages may differ, however.
|
||||
* Also, ODEX files are no longer supported.
|
||||
*/
|
||||
|
||||
#include "dexdump.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "runtime.h"
|
||||
#include "mem_map.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
static const char* gProgName = "dexdump";
|
||||
|
||||
/*
|
||||
* Shows usage.
|
||||
*/
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
|
||||
fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile]"
|
||||
" dexfile...\n\n", gProgName);
|
||||
fprintf(stderr, " -a : display annotations\n");
|
||||
fprintf(stderr, " -c : verify checksum and exit\n");
|
||||
fprintf(stderr, " -d : disassemble code sections\n");
|
||||
fprintf(stderr, " -e : display exported items only\n");
|
||||
fprintf(stderr, " -f : display summary information from file header\n");
|
||||
fprintf(stderr, " -g : display CFG for dex\n");
|
||||
fprintf(stderr, " -h : display file header details\n");
|
||||
fprintf(stderr, " -i : ignore checksum failures\n");
|
||||
fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
|
||||
fprintf(stderr, " -o : output file name (defaults to stdout)\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Main driver of the dexdump utility.
|
||||
*/
|
||||
int dexdumpDriver(int argc, char** argv) {
|
||||
// Art specific set up.
|
||||
InitLogging(argv, Runtime::Abort);
|
||||
MemMap::Init();
|
||||
|
||||
// Reset options.
|
||||
bool wantUsage = false;
|
||||
memset(&gOptions, 0, sizeof(gOptions));
|
||||
gOptions.verbose = true;
|
||||
|
||||
// Parse all arguments.
|
||||
while (1) {
|
||||
const int ic = getopt(argc, argv, "acdefghil:o:");
|
||||
if (ic < 0) {
|
||||
break; // done
|
||||
}
|
||||
switch (ic) {
|
||||
case 'a': // display annotations
|
||||
gOptions.showAnnotations = true;
|
||||
break;
|
||||
case 'c': // verify the checksum then exit
|
||||
gOptions.checksumOnly = true;
|
||||
break;
|
||||
case 'd': // disassemble Dalvik instructions
|
||||
gOptions.disassemble = true;
|
||||
break;
|
||||
case 'e': // exported items only
|
||||
gOptions.exportsOnly = true;
|
||||
break;
|
||||
case 'f': // display outer file header
|
||||
gOptions.showFileHeaders = true;
|
||||
break;
|
||||
case 'g': // display cfg
|
||||
gOptions.showCfg = true;
|
||||
break;
|
||||
case 'h': // display section headers, i.e. all meta-data
|
||||
gOptions.showSectionHeaders = true;
|
||||
break;
|
||||
case 'i': // continue even if checksum is bad
|
||||
gOptions.ignoreBadChecksum = true;
|
||||
break;
|
||||
case 'l': // layout
|
||||
if (strcmp(optarg, "plain") == 0) {
|
||||
gOptions.outputFormat = OUTPUT_PLAIN;
|
||||
} else if (strcmp(optarg, "xml") == 0) {
|
||||
gOptions.outputFormat = OUTPUT_XML;
|
||||
gOptions.verbose = false;
|
||||
} else {
|
||||
wantUsage = true;
|
||||
}
|
||||
break;
|
||||
case 'o': // output file
|
||||
gOptions.outputFileName = optarg;
|
||||
break;
|
||||
default:
|
||||
wantUsage = true;
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
|
||||
// Detect early problems.
|
||||
if (optind == argc) {
|
||||
fprintf(stderr, "%s: no file specified\n", gProgName);
|
||||
wantUsage = true;
|
||||
}
|
||||
if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
|
||||
fprintf(stderr, "Can't specify both -c and -i\n");
|
||||
wantUsage = true;
|
||||
}
|
||||
if (wantUsage) {
|
||||
usage();
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Open alternative output file.
|
||||
if (gOptions.outputFileName) {
|
||||
gOutFile = fopen(gOptions.outputFileName, "w");
|
||||
if (!gOutFile) {
|
||||
fprintf(stderr, "Can't open %s\n", gOptions.outputFileName);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Process all files supplied on command line.
|
||||
int result = 0;
|
||||
while (optind < argc) {
|
||||
result |= processFile(argv[optind++]);
|
||||
} // while
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
} // namespace art
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return art::dexdumpDriver(argc, argv);
|
||||
}
|
82
android/art/dexdump/dexdump_test.cc
Normal file
82
android/art/dexdump/dexdump_test.cc
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common_runtime_test.h"
|
||||
#include "runtime/arch/instruction_set.h"
|
||||
#include "runtime/exec_utils.h"
|
||||
#include "runtime/os.h"
|
||||
#include "runtime/utils.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
class DexDumpTest : public CommonRuntimeTest {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
CommonRuntimeTest::SetUp();
|
||||
// Dogfood our own lib core dex file.
|
||||
dex_file_ = GetLibCoreDexFileNames()[0];
|
||||
}
|
||||
|
||||
// Runs test with given arguments.
|
||||
bool Exec(const std::vector<std::string>& args, std::string* error_msg) {
|
||||
// TODO(ajcbik): dexdump2 -> dexdump
|
||||
std::string file_path = GetTestAndroidRoot() + "/bin/dexdump2";
|
||||
EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
|
||||
std::vector<std::string> exec_argv = { file_path };
|
||||
exec_argv.insert(exec_argv.end(), args.begin(), args.end());
|
||||
return ::art::Exec(exec_argv, error_msg);
|
||||
}
|
||||
|
||||
std::string dex_file_;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(DexDumpTest, NoInputFileGiven) {
|
||||
std::string error_msg;
|
||||
ASSERT_FALSE(Exec({}, &error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexDumpTest, CantOpenOutput) {
|
||||
std::string error_msg;
|
||||
ASSERT_FALSE(Exec({"-o", "/joho", dex_file_}, &error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexDumpTest, BadFlagCombination) {
|
||||
std::string error_msg;
|
||||
ASSERT_FALSE(Exec({"-c", "-i", dex_file_}, &error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexDumpTest, FullPlainOutput) {
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(Exec({"-d", "-f", "-h", "-l", "plain", "-o", "/dev/null",
|
||||
dex_file_}, &error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexDumpTest, XMLOutput) {
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(Exec({"-l", "xml", "-o", "/dev/null",
|
||||
dex_file_}, &error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
} // namespace art
|
Loading…
Add table
Add a link
Reference in a new issue