libredex/DexInstruction.cpp (1,110 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "DexInstruction.h"
#include "Debug.h"
#include "DexCallSite.h"
#include "DexIdx.h"
#include "DexMethodHandle.h"
#include "DexOutput.h"
#include "Macros.h"
#include "Show.h"
#include "Warning.h"
unsigned DexInstruction::count_from_opcode() const {
// clang-format off
static int args[] = {
0, /* FMT_f00x */
0, /* FMT_f10x */
0, /* FMT_f12x */
0, /* FMT_f12x_2 */
0, /* FMT_f11n */
0, /* FMT_f11x_d */
0, /* FMT_f11x_s */
0, /* FMT_f10t */
1, /* FMT_f20t */
0, /* FMT_f20bc */
1, /* FMT_f22x */
1, /* FMT_f21t */
1, /* FMT_f21s */
1, /* FMT_f21h */
0, /* FMT_f21c_d */
0, /* FMT_f21c_s */
1, /* FMT_f23x_d */
1, /* FMT_f23x_s */
1, /* FMT_f22b */
1, /* FMT_f22t */
1, /* FMT_f22s */
0, /* FMT_f22c_d */
0, /* FMT_f22c_s */
0, /* FMT_f22cs */
2, /* FMT_f30t */
2, /* FMT_f32x */
2, /* FMT_f31i */
2, /* FMT_f31t */
1, /* FMT_f31c */
1, /* FMT_f35c */
2, /* FMT_f35ms */
2, /* FMT_f35mi */
1, /* FMT_f3rc */
2, /* FMT_f3rms */
2, /* FMT_f3rmi */
4, /* FMT_f51l */
1, /* FMT_f41c_d */
1, /* FMT_f41c_s */
1, /* FMT_f45cc */ // TODO(T59275897)
1, /* FMT_f4rcc */ // TODO(T59275897)
2, /* FMT_f52c_d */
2, /* FMT_f52c_s */
2, /* FMT_f5rc */
2, /* FMT_f57c */
0, /* FMT_fopcode */
};
// clang-format on
return args[dex_opcode::format(opcode())];
};
DexOpcode DexInstruction::opcode() const {
auto opcode = m_opcode & 0xff;
if (opcode == OPCODE_NOP) {
// Get the full opcode for pseudo-ops.
return static_cast<DexOpcode>(m_opcode);
}
return static_cast<DexOpcode>(opcode);
}
DexInstruction* DexInstruction::set_opcode(DexOpcode op) {
if (op >= FOPCODE_PACKED_SWITCH) {
m_opcode = op;
} else {
m_opcode = (m_opcode & 0xff00) | op;
}
return this;
}
bool DexInstruction::has_dest() const { return dex_opcode::has_dest(opcode()); }
unsigned DexInstruction::srcs_size() const {
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f00x:
case FMT_f10x:
case FMT_f11n:
case FMT_f11x_d:
case FMT_f10t:
case FMT_f20t:
case FMT_f21s:
case FMT_f21h:
case FMT_f21c_d:
case FMT_f30t:
case FMT_f31i:
case FMT_f31c:
case FMT_f3rc:
case FMT_f4rcc:
case FMT_f51l:
case FMT_f5rc:
case FMT_f41c_d:
case FMT_fopcode:
return 0;
case FMT_f12x:
case FMT_f11x_s:
case FMT_f22x:
case FMT_f21t:
case FMT_f21c_s:
case FMT_f22b:
case FMT_f22s:
case FMT_f22c_d:
case FMT_f32x:
case FMT_f31t:
case FMT_f41c_s:
case FMT_f52c_d:
return 1;
case FMT_f12x_2:
case FMT_f23x_d:
case FMT_f22t:
case FMT_f22c_s:
case FMT_f52c_s:
return 2;
case FMT_f23x_s:
return 3;
case FMT_f35c:
case FMT_f45cc:
case FMT_f57c:
return arg_word_count();
case FMT_f20bc:
case FMT_f22cs:
case FMT_f35ms:
case FMT_f35mi:
case FMT_f3rms:
case FMT_f3rmi:
case FMT_iopcode:
not_reached_log("Unimplemented opcode `%s'", SHOW(this));
}
}
uint16_t DexInstruction::dest() const {
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f12x:
case FMT_f12x_2:
case FMT_f11n:
case FMT_f22s:
case FMT_f22c_d:
case FMT_f22cs:
return (m_opcode >> 8) & 0xf;
case FMT_f11x_d:
case FMT_f22x:
case FMT_f21s:
case FMT_f21h:
case FMT_f21c_d:
case FMT_f23x_d:
case FMT_f22b:
case FMT_f31i:
case FMT_f31c:
case FMT_f51l:
return (m_opcode >> 8) & 0xff;
case FMT_f32x:
return m_arg[0];
case FMT_f41c_d:
case FMT_f52c_d:
return m_arg[0];
default:
// All other formats do not define a destination register.
not_reached_log("Unhandled opcode: %s", SHOW(opcode()));
}
}
DexInstruction* DexInstruction::set_dest(uint16_t vreg) {
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f12x:
case FMT_f12x_2:
case FMT_f11n:
case FMT_f22s:
case FMT_f22c_d:
case FMT_f22cs:
redex_assert((vreg & 0xf) == vreg);
m_opcode = (m_opcode & 0xf0ff) | (vreg << 8);
return this;
case FMT_f11x_d:
case FMT_f22x:
case FMT_f21s:
case FMT_f21h:
case FMT_f21c_d:
case FMT_f23x_d:
case FMT_f22b:
case FMT_f31i:
case FMT_f31c:
case FMT_f51l:
redex_assert((vreg & 0xff) == vreg);
m_opcode = (m_opcode & 0x00ff) | (vreg << 8);
return this;
case FMT_f32x:
m_arg[0] = vreg;
return this;
case FMT_f41c_d:
case FMT_f52c_d:
m_arg[0] = vreg;
return this;
default:
// All other formats do not define a destination register.
not_reached_log("Unhandled opcode: %s", SHOW(this));
}
}
uint16_t DexInstruction::src(int i) const {
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f11x_s:
case FMT_f21t:
case FMT_f21c_s:
case FMT_f31t:
redex_assert(i == 0);
return (m_opcode >> 8) & 0xff;
case FMT_f12x:
case FMT_f22s:
case FMT_f22c_d:
redex_assert(i == 0);
return (m_opcode >> 12) & 0xf;
case FMT_f12x_2:
redex_assert(i < 2);
if (i == 0) return (m_opcode >> 8) & 0xf;
return (m_opcode >> 12) & 0xf;
case FMT_f22x:
case FMT_f3rc:
case FMT_f4rcc:
redex_assert(i == 0);
return m_arg[0];
case FMT_f23x_d:
redex_assert(i < 2);
if (i == 0) return m_arg[0] & 0xff;
return (m_arg[0] >> 8) & 0xff;
case FMT_f23x_s:
redex_assert(i < 3);
if (i == 0) return (m_opcode >> 8) & 0xff;
if (i == 1) return m_arg[0] & 0xff;
return (m_arg[0] >> 8) & 0xff;
case FMT_f22b:
redex_assert(i == 0);
return m_arg[0] & 0xff;
case FMT_f22t:
case FMT_f22c_s:
redex_assert(i < 2);
if (i == 0) return (m_opcode >> 8) & 0xf;
return (m_opcode >> 12) & 0xf; // i == 1
case FMT_f32x:
redex_assert(i == 0);
return m_arg[1];
case FMT_f35c:
case FMT_f45cc:
redex_assert(i < 5);
switch (i) {
case 0:
return m_arg[0] & 0xf;
case 1:
return (m_arg[0] >> 4) & 0xf;
case 2:
return (m_arg[0] >> 8) & 0xf;
case 3:
return (m_arg[0] >> 12) & 0xf;
case 4:
return (m_opcode >> 8) & 0xf;
}
case FMT_f41c_s:
redex_assert(i == 0);
return m_arg[0];
case FMT_f52c_d:
redex_assert(i == 0);
return m_arg[1];
case FMT_f52c_s:
redex_assert(i <= 1);
return m_arg[i];
case FMT_f5rc:
redex_assert(i == 0);
return m_arg[1];
case FMT_f57c:
redex_assert(i <= 6);
switch (i) {
case 0:
return (m_arg[0] >> 4) & 0xf;
case 1:
return (m_arg[0] >> 8) & 0xf;
case 2:
return (m_arg[0] >> 12) & 0xf;
case 3:
return m_arg[1] & 0xf;
case 4:
return (m_arg[1] >> 4) & 0xf;
case 5:
return (m_arg[1] >> 8) & 0xf;
case 6:
return (m_arg[1] >> 12) & 0xf;
}
not_reached();
default:
// All other formats do not define source registers.
not_reached_log("Unhandled opcode: %s", SHOW(this));
}
}
DexInstruction* DexInstruction::set_src(int i, uint16_t vreg) {
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f11x_s:
case FMT_f21t:
case FMT_f21c_s:
case FMT_f31t:
redex_assert(i == 0);
redex_assert((vreg & 0xff) == vreg);
m_opcode = (m_opcode & 0x00ff) | (vreg << 8);
return this;
case FMT_f12x:
case FMT_f22s:
case FMT_f22c_d:
redex_assert(i == 0);
redex_assert((vreg & 0xf) == vreg);
m_opcode = (m_opcode & 0x0fff) | (vreg << 12);
return this;
case FMT_f12x_2:
redex_assert(i < 2);
redex_assert((vreg & 0xf) == vreg);
if (i == 0) {
m_opcode = (m_opcode & 0xf0ff) | (vreg << 8);
} else {
m_opcode = (m_opcode & 0x0fff) | (vreg << 12);
}
return this;
case FMT_f22x:
redex_assert(i == 0);
m_arg[0] = vreg;
return this;
case FMT_f23x_d:
redex_assert(i < 2);
redex_assert((vreg & 0xff) == vreg);
if (i == 0) {
m_arg[0] = (m_arg[0] & 0xff00) | vreg;
return this;
}
m_arg[0] = (m_arg[0] & 0x00ff) | (vreg << 8);
return this;
case FMT_f23x_s:
redex_assert(i < 3);
redex_assert((vreg & 0xff) == vreg);
if (i == 0) {
m_opcode = (m_opcode & 0x00ff) | (vreg << 8);
} else if (i == 1) {
m_arg[0] = (m_arg[0] & 0xff00) | vreg;
} else {
m_arg[0] = (m_arg[0] & 0x00ff) | (vreg << 8);
}
return this;
case FMT_f22b:
redex_assert(i == 0);
redex_assert((vreg & 0xff) == vreg);
m_arg[0] = (m_arg[0] & 0xff00) | vreg;
return this;
case FMT_f22t:
case FMT_f22c_s:
redex_assert(i < 2);
redex_assert((vreg & 0xf) == vreg);
if (i == 0) {
m_opcode = (m_opcode & 0xf0ff) | (vreg << 8);
} else {
m_opcode = (m_opcode & 0x0fff) | (vreg << 12);
}
return this;
case FMT_f32x:
redex_assert(i == 0);
m_arg[1] = vreg;
return this;
case FMT_f35c:
case FMT_f45cc:
redex_assert(i < 5);
redex_assert((vreg & 0xf) == vreg);
switch (i) {
case 0:
m_arg[0] = (m_arg[0] & 0xfff0) | vreg;
return this;
case 1:
m_arg[0] = (m_arg[0] & 0xff0f) | (vreg << 4);
return this;
case 2:
m_arg[0] = (m_arg[0] & 0xf0ff) | (vreg << 8);
return this;
case 3:
m_arg[0] = (m_arg[0] & 0x0fff) | (vreg << 12);
return this;
case 4:
m_opcode = (m_opcode & 0xf0ff) | (vreg << 8);
return this;
}
case FMT_f41c_s:
redex_assert(i == 0);
m_arg[0] = vreg;
return this;
case FMT_f52c_d:
redex_assert(i == 0);
m_arg[1] = vreg;
return this;
case FMT_f52c_s:
redex_assert(i <= 1);
m_arg[i] = vreg;
return this;
case FMT_f57c:
redex_assert(i <= 6);
redex_assert((vreg & 0xf) == vreg);
switch (i) {
case 0:
m_arg[0] = (m_arg[0] & 0xff0f) | (vreg << 4);
return this;
case 1:
m_arg[0] = (m_arg[0] & 0xf0ff) | (vreg << 8);
return this;
case 2:
m_arg[0] = (m_arg[0] & 0x0fff) | (vreg << 12);
return this;
case 3:
m_arg[1] = (m_arg[1] & 0xfff0) | vreg;
return this;
case 4:
m_arg[0] = (m_arg[1] & 0xff0f) | (vreg << 4);
return this;
case 5:
m_arg[0] = (m_arg[1] & 0xf0ff) | (vreg << 8);
return this;
case 6:
m_arg[0] = (m_arg[1] & 0x0fff) | (vreg << 12);
return this;
}
not_reached();
default:
// All other formats do not define source registers.
not_reached_log("Unhandled opcode: %s", SHOW(this));
}
}
DexInstruction* DexInstruction::set_srcs(const std::vector<uint16_t>& vregs) {
for (size_t i = 0; i < vregs.size(); ++i) {
set_src(i, vregs[i]);
}
return this;
}
template <int Width>
int64_t signext(uint64_t uv) {
int shift = 64 - Width;
return int64_t(uint64_t(uv) << shift) >> shift;
}
int64_t DexInstruction::get_literal() const {
redex_assert(dex_opcode::has_literal(opcode()));
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f11n:
return signext<4>(m_opcode >> 12);
case FMT_f21s:
return signext<16>(m_arg[0]);
case FMT_f21h:
return int64_t(uint64_t(signext<16>(m_arg[0]))
<< (opcode() == DOPCODE_CONST_WIDE_HIGH16 ? 48 : 16));
case FMT_f22b:
return signext<8>(m_arg[0] >> 8);
case FMT_f22s:
return signext<16>(m_arg[0]);
case FMT_f31i: {
auto literal = uint32_t(m_arg[0]) | (uint32_t(m_arg[1]) << 16);
return signext<32>(literal);
}
case FMT_f51l: {
auto literal = uint64_t(m_arg[0]) | (uint64_t(m_arg[1]) << 16) |
(uint64_t(m_arg[2]) << 32) | (uint64_t(m_arg[3]) << 48);
return signext<64>(literal);
}
default:
not_reached();
}
}
DexInstruction* DexInstruction::set_literal(int64_t literal) {
redex_assert(dex_opcode::has_literal(opcode()));
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f11n:
m_opcode = (m_opcode & 0xfff) | ((literal & 0xf) << 12);
return this;
case FMT_f21s:
m_arg[0] = literal;
return this;
case FMT_f21h:
m_arg[0] = literal >> (opcode() == DOPCODE_CONST_WIDE_HIGH16 ? 48 : 16);
return this;
case FMT_f22b:
m_arg[0] = (m_arg[0] & 0xFF) | ((uint64_t(literal) << 8) & 0xFF00);
return this;
case FMT_f22s:
m_arg[0] = literal;
return this;
case FMT_f31i:
m_arg[0] = literal & 0xffff;
m_arg[1] = literal >> 16;
return this;
case FMT_f51l:
m_arg[0] = literal;
m_arg[1] = literal >> 16;
m_arg[2] = literal >> 32;
m_arg[3] = literal >> 48;
return this;
default:
not_reached();
}
}
int32_t DexInstruction::offset() const {
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f10t:
return (int32_t)signext<8>(m_opcode >> 8);
case FMT_f20t:
case FMT_f21t:
case FMT_f22t:
return (int32_t)signext<16>(m_arg[0]);
case FMT_f30t:
case FMT_f31t: {
auto offset = uint32_t(m_arg[0]) | (uint32_t(m_arg[1]) << 16);
return (int32_t)signext<32>(offset);
}
default:
not_reached();
}
}
DexInstruction* DexInstruction::set_offset(int32_t offset) {
auto format = dex_opcode::format(opcode());
switch (format) {
case FMT_f10t:
always_assert_log((int32_t)(int8_t)(offset & 0xff) == offset,
"offset %d too large for %s",
offset,
SHOW(this));
m_opcode = (m_opcode & 0xff) | ((offset & 0xff) << 8);
return this;
case FMT_f20t:
case FMT_f21t:
case FMT_f22t:
always_assert_log((int32_t)(int16_t)(offset & 0xffff) == offset,
"offset %d too large for %s",
offset,
SHOW(this));
m_arg[0] = offset;
return this;
case FMT_f30t:
case FMT_f31t:
m_arg[0] = offset;
m_arg[1] = offset >> 16;
return this;
default:
not_reached();
}
}
uint16_t DexInstruction::range_base() const {
auto format = dex_opcode::format(opcode());
redex_assert(format == FMT_f3rc || format == FMT_f4rcc || format == FMT_f5rc);
if (format == FMT_f5rc) {
return m_arg[1];
}
return m_arg[0];
}
uint16_t DexInstruction::range_size() const {
auto format = dex_opcode::format(opcode());
redex_assert(format == FMT_f3rc || format == FMT_f4rcc || format == FMT_f5rc);
if (format == FMT_f5rc) return m_arg[0];
return (m_opcode >> 8) & 0xff;
}
DexInstruction* DexInstruction::set_range_base(uint16_t base) {
auto format = dex_opcode::format(opcode());
redex_assert(format == FMT_f3rc || format == FMT_f4rcc || format == FMT_f5rc);
if (format == FMT_f5rc) {
m_arg[1] = base;
} else {
m_arg[0] = base;
}
return this;
}
DexInstruction* DexInstruction::set_range_size(uint16_t size) {
auto format = dex_opcode::format(opcode());
redex_assert(format == FMT_f3rc || format == FMT_f4rcc || format == FMT_f5rc);
if (format == FMT_f5rc) {
m_arg[0] = size;
} else {
redex_assert(size == (size & 0xff));
m_opcode = (m_opcode & 0xff) | (size << 8);
}
return this;
}
uint16_t DexInstruction::arg_word_count() const {
auto format = dex_opcode::format(opcode());
redex_assert(format == FMT_f35c || format == FMT_f45cc || format == FMT_f57c);
if (format == FMT_f57c) {
return (m_arg[0]) & 0xf;
}
return (m_opcode >> 12) & 0xf;
}
DexInstruction* DexInstruction::set_arg_word_count(uint16_t count) {
auto format = dex_opcode::format(opcode());
redex_assert(format == FMT_f35c || format == FMT_f45cc || format == FMT_f57c);
redex_assert((count & 0xf) == count);
if (format == FMT_f57c) {
m_arg[0] = (m_arg[0] & 0xfff0) | count;
} else {
m_opcode = (m_opcode & 0x0fff) | (count << 12);
}
return this;
}
void DexOpcodeString::gather_strings(
std::vector<const DexString*>& lstring) const {
lstring.push_back(m_string);
}
size_t DexOpcodeString::size() const { return jumbo() ? 3 : 2; }
void DexOpcodeString::encode(DexOutputIdx* dodx, uint16_t*& insns) const {
encode_opcode(insns);
uint32_t sidx = dodx->stringidx(m_string);
uint16_t idx = (uint16_t)sidx;
if (!jumbo()) {
always_assert_log(sidx == idx,
"Attempt to encode jumbo string in non-jumbo opcode: %s",
m_string->c_str());
*insns++ = idx;
return;
}
if (sidx == idx) {
opt_warn(NON_JUMBO_STRING, "%s\n", m_string->c_str());
}
*insns++ = idx;
idx = sidx >> 16;
*insns++ = idx;
}
size_t DexOpcodeType::size() const { return m_count + 2; }
void DexOpcodeType::gather_types(std::vector<DexType*>& ltype) const {
ltype.push_back(m_type);
}
void DexOpcodeType::encode(DexOutputIdx* dodx, uint16_t*& insns) const {
encode_opcode(insns);
uint16_t idx = dodx->typeidx(m_type);
*insns++ = idx;
encode_args(insns);
}
void DexOpcodeField::gather_fields(std::vector<DexFieldRef*>& lfield) const {
lfield.push_back(m_field);
}
size_t DexOpcodeField::size() const { return 2; }
void DexOpcodeField::encode(DexOutputIdx* dodx, uint16_t*& insns) const {
encode_opcode(insns);
uint16_t idx = dodx->fieldidx(m_field);
*insns++ = idx;
}
void DexOpcodeMethod::gather_methods(
std::vector<DexMethodRef*>& lmethod) const {
lmethod.push_back(m_method);
}
size_t DexOpcodeMethod::size() const { return 3; }
void DexOpcodeMethod::encode(DexOutputIdx* dodx, uint16_t*& insns) const {
encode_opcode(insns);
uint16_t idx = dodx->methodidx(m_method);
*insns++ = idx;
encode_args(insns);
}
size_t DexOpcodeCallSite::size() const { return 3; }
void DexOpcodeCallSite::encode(DexOutputIdx* dodx, uint16_t*& insns) const {
encode_opcode(insns);
uint16_t idx = dodx->callsiteidx(m_callsite);
*insns++ = idx;
encode_args(insns);
}
void DexOpcodeCallSite::gather_callsites(
std::vector<DexCallSite*>& lcallsite) const {
lcallsite.emplace_back(m_callsite);
}
void DexOpcodeCallSite::gather_strings(
std::vector<const DexString*>& lstring) const {
m_callsite->gather_strings(lstring);
}
void DexOpcodeCallSite::gather_methodhandles(
std::vector<DexMethodHandle*>& lmethodhandle) const {
m_callsite->gather_methodhandles(lmethodhandle);
}
void DexOpcodeCallSite::gather_methods(
std::vector<DexMethodRef*>& lmethod) const {
m_callsite->gather_methods(lmethod);
}
void DexOpcodeCallSite::gather_fields(std::vector<DexFieldRef*>& lfield) const {
m_callsite->gather_fields(lfield);
}
size_t DexOpcodeMethodHandle::size() const { return 3; }
void DexOpcodeMethodHandle::encode(DexOutputIdx* dodx, uint16_t*& insns) const {
encode_opcode(insns);
uint16_t idx = dodx->methodhandleidx(m_methodhandle);
*insns++ = idx;
encode_args(insns);
}
void DexOpcodeMethodHandle::gather_methods(
std::vector<DexMethodRef*>& lmethod) const {
m_methodhandle->gather_methods(lmethod);
}
void DexOpcodeMethodHandle::gather_fields(
std::vector<DexFieldRef*>& lfield) const {
m_methodhandle->gather_fields(lfield);
}
void DexOpcodeMethodHandle::gather_methodhandles(
std::vector<DexMethodHandle*>& lmethodhandle) const {
lmethodhandle.push_back(m_methodhandle);
}
size_t DexOpcodeData::size() const { return m_data_count + 1; }
void DexOpcodeData::encode(DexOutputIdx* /* unused */, uint16_t*& insns) const {
encode_opcode(insns);
memcpy(insns, m_data.get(), m_data_count * sizeof(uint16_t));
insns += m_data_count;
}
void DexInstruction::encode(DexOutputIdx* /* unused */,
uint16_t*& insns) const {
encode_opcode(insns);
encode_args(insns);
}
size_t DexInstruction::size() const { return m_count + 1; }
DexInstruction* DexInstruction::make_instruction(DexIdx* idx,
const uint16_t** insns_ptr) {
auto& insns = *insns_ptr;
auto fopcode = static_cast<DexOpcode>(*insns++);
DexOpcode opcode = static_cast<DexOpcode>(fopcode & 0xff);
switch (opcode) {
case DOPCODE_NOP: {
if (fopcode == FOPCODE_PACKED_SWITCH) {
int count = (*insns--) * 2 + 4;
insns += count;
return new DexOpcodeData(insns - count, count - 1);
} else if (fopcode == FOPCODE_SPARSE_SWITCH) {
int count = (*insns--) * 4 + 2;
insns += count;
return new DexOpcodeData(insns - count, count - 1);
} else if (fopcode == FOPCODE_FILLED_ARRAY) {
uint16_t ewidth = *insns++;
uint32_t size = *((uint32_t*)insns);
int count = (ewidth * size + 1) / 2 + 4;
insns += count - 2;
return new DexOpcodeData(insns - count, count - 1);
}
}
/* Format 10, fall through for NOP */
FALLTHROUGH_INTENDED;
case DOPCODE_MOVE:
case DOPCODE_MOVE_WIDE:
case DOPCODE_MOVE_OBJECT:
case DOPCODE_MOVE_RESULT:
case DOPCODE_MOVE_RESULT_WIDE:
case DOPCODE_MOVE_RESULT_OBJECT:
case DOPCODE_MOVE_EXCEPTION:
case DOPCODE_RETURN_VOID:
case DOPCODE_RETURN:
case DOPCODE_RETURN_WIDE:
case DOPCODE_RETURN_OBJECT:
case DOPCODE_CONST_4:
case DOPCODE_MONITOR_ENTER:
case DOPCODE_MONITOR_EXIT:
case DOPCODE_THROW:
case DOPCODE_GOTO:
case DOPCODE_NEG_INT:
case DOPCODE_NOT_INT:
case DOPCODE_NEG_LONG:
case DOPCODE_NOT_LONG:
case DOPCODE_NEG_FLOAT:
case DOPCODE_NEG_DOUBLE:
case DOPCODE_INT_TO_LONG:
case DOPCODE_INT_TO_FLOAT:
case DOPCODE_INT_TO_DOUBLE:
case DOPCODE_LONG_TO_INT:
case DOPCODE_LONG_TO_FLOAT:
case DOPCODE_LONG_TO_DOUBLE:
case DOPCODE_FLOAT_TO_INT:
case DOPCODE_FLOAT_TO_LONG:
case DOPCODE_FLOAT_TO_DOUBLE:
case DOPCODE_DOUBLE_TO_INT:
case DOPCODE_DOUBLE_TO_LONG:
case DOPCODE_DOUBLE_TO_FLOAT:
case DOPCODE_INT_TO_BYTE:
case DOPCODE_INT_TO_CHAR:
case DOPCODE_INT_TO_SHORT:
case DOPCODE_ADD_INT_2ADDR:
case DOPCODE_SUB_INT_2ADDR:
case DOPCODE_MUL_INT_2ADDR:
case DOPCODE_DIV_INT_2ADDR:
case DOPCODE_REM_INT_2ADDR:
case DOPCODE_AND_INT_2ADDR:
case DOPCODE_OR_INT_2ADDR:
case DOPCODE_XOR_INT_2ADDR:
case DOPCODE_SHL_INT_2ADDR:
case DOPCODE_SHR_INT_2ADDR:
case DOPCODE_USHR_INT_2ADDR:
case DOPCODE_ADD_LONG_2ADDR:
case DOPCODE_SUB_LONG_2ADDR:
case DOPCODE_MUL_LONG_2ADDR:
case DOPCODE_DIV_LONG_2ADDR:
case DOPCODE_REM_LONG_2ADDR:
case DOPCODE_AND_LONG_2ADDR:
case DOPCODE_OR_LONG_2ADDR:
case DOPCODE_XOR_LONG_2ADDR:
case DOPCODE_SHL_LONG_2ADDR:
case DOPCODE_SHR_LONG_2ADDR:
case DOPCODE_USHR_LONG_2ADDR:
case DOPCODE_ADD_FLOAT_2ADDR:
case DOPCODE_SUB_FLOAT_2ADDR:
case DOPCODE_MUL_FLOAT_2ADDR:
case DOPCODE_DIV_FLOAT_2ADDR:
case DOPCODE_REM_FLOAT_2ADDR:
case DOPCODE_ADD_DOUBLE_2ADDR:
case DOPCODE_SUB_DOUBLE_2ADDR:
case DOPCODE_MUL_DOUBLE_2ADDR:
case DOPCODE_DIV_DOUBLE_2ADDR:
case DOPCODE_REM_DOUBLE_2ADDR:
case DOPCODE_ARRAY_LENGTH:
return new DexInstruction(fopcode);
/* Format 20 */
case DOPCODE_MOVE_FROM16:
case DOPCODE_MOVE_WIDE_FROM16:
case DOPCODE_MOVE_OBJECT_FROM16:
case DOPCODE_CONST_16:
case DOPCODE_CONST_HIGH16:
case DOPCODE_CONST_WIDE_16:
case DOPCODE_CONST_WIDE_HIGH16:
case DOPCODE_GOTO_16:
case DOPCODE_CMPL_FLOAT:
case DOPCODE_CMPG_FLOAT:
case DOPCODE_CMPL_DOUBLE:
case DOPCODE_CMPG_DOUBLE:
case DOPCODE_CMP_LONG:
case DOPCODE_IF_EQ:
case DOPCODE_IF_NE:
case DOPCODE_IF_LT:
case DOPCODE_IF_GE:
case DOPCODE_IF_GT:
case DOPCODE_IF_LE:
case DOPCODE_IF_EQZ:
case DOPCODE_IF_NEZ:
case DOPCODE_IF_LTZ:
case DOPCODE_IF_GEZ:
case DOPCODE_IF_GTZ:
case DOPCODE_IF_LEZ:
case DOPCODE_AGET:
case DOPCODE_AGET_WIDE:
case DOPCODE_AGET_OBJECT:
case DOPCODE_AGET_BOOLEAN:
case DOPCODE_AGET_BYTE:
case DOPCODE_AGET_CHAR:
case DOPCODE_AGET_SHORT:
case DOPCODE_APUT:
case DOPCODE_APUT_WIDE:
case DOPCODE_APUT_OBJECT:
case DOPCODE_APUT_BOOLEAN:
case DOPCODE_APUT_BYTE:
case DOPCODE_APUT_CHAR:
case DOPCODE_APUT_SHORT:
case DOPCODE_ADD_INT:
case DOPCODE_SUB_INT:
case DOPCODE_MUL_INT:
case DOPCODE_DIV_INT:
case DOPCODE_REM_INT:
case DOPCODE_AND_INT:
case DOPCODE_OR_INT:
case DOPCODE_XOR_INT:
case DOPCODE_SHL_INT:
case DOPCODE_SHR_INT:
case DOPCODE_USHR_INT:
case DOPCODE_ADD_LONG:
case DOPCODE_SUB_LONG:
case DOPCODE_MUL_LONG:
case DOPCODE_DIV_LONG:
case DOPCODE_REM_LONG:
case DOPCODE_AND_LONG:
case DOPCODE_OR_LONG:
case DOPCODE_XOR_LONG:
case DOPCODE_SHL_LONG:
case DOPCODE_SHR_LONG:
case DOPCODE_USHR_LONG:
case DOPCODE_ADD_FLOAT:
case DOPCODE_SUB_FLOAT:
case DOPCODE_MUL_FLOAT:
case DOPCODE_DIV_FLOAT:
case DOPCODE_REM_FLOAT:
case DOPCODE_ADD_DOUBLE:
case DOPCODE_SUB_DOUBLE:
case DOPCODE_MUL_DOUBLE:
case DOPCODE_DIV_DOUBLE:
case DOPCODE_REM_DOUBLE:
case DOPCODE_ADD_INT_LIT16:
case DOPCODE_RSUB_INT:
case DOPCODE_MUL_INT_LIT16:
case DOPCODE_DIV_INT_LIT16:
case DOPCODE_REM_INT_LIT16:
case DOPCODE_AND_INT_LIT16:
case DOPCODE_OR_INT_LIT16:
case DOPCODE_XOR_INT_LIT16:
case DOPCODE_ADD_INT_LIT8:
case DOPCODE_RSUB_INT_LIT8:
case DOPCODE_MUL_INT_LIT8:
case DOPCODE_DIV_INT_LIT8:
case DOPCODE_REM_INT_LIT8:
case DOPCODE_AND_INT_LIT8:
case DOPCODE_OR_INT_LIT8:
case DOPCODE_XOR_INT_LIT8:
case DOPCODE_SHL_INT_LIT8:
case DOPCODE_SHR_INT_LIT8:
case DOPCODE_USHR_INT_LIT8: {
uint16_t arg = *insns++;
return new DexInstruction(fopcode, arg);
}
/* Format 30 */
case DOPCODE_MOVE_16:
case DOPCODE_MOVE_WIDE_16:
case DOPCODE_MOVE_OBJECT_16:
case DOPCODE_CONST:
case DOPCODE_CONST_WIDE_32:
case DOPCODE_FILL_ARRAY_DATA:
case DOPCODE_GOTO_32:
case DOPCODE_PACKED_SWITCH:
case DOPCODE_SPARSE_SWITCH: {
insns += 2;
return new DexInstruction(insns - 3, 2);
}
/* Format 50 */
case DOPCODE_CONST_WIDE: {
insns += 4;
return new DexInstruction(insns - 5, 4);
}
/* Field ref: */
case DOPCODE_IGET:
case DOPCODE_IGET_WIDE:
case DOPCODE_IGET_OBJECT:
case DOPCODE_IGET_BOOLEAN:
case DOPCODE_IGET_BYTE:
case DOPCODE_IGET_CHAR:
case DOPCODE_IGET_SHORT:
case DOPCODE_IPUT:
case DOPCODE_IPUT_WIDE:
case DOPCODE_IPUT_OBJECT:
case DOPCODE_IPUT_BOOLEAN:
case DOPCODE_IPUT_BYTE:
case DOPCODE_IPUT_CHAR:
case DOPCODE_IPUT_SHORT:
case DOPCODE_SGET:
case DOPCODE_SGET_WIDE:
case DOPCODE_SGET_OBJECT:
case DOPCODE_SGET_BOOLEAN:
case DOPCODE_SGET_BYTE:
case DOPCODE_SGET_CHAR:
case DOPCODE_SGET_SHORT:
case DOPCODE_SPUT:
case DOPCODE_SPUT_WIDE:
case DOPCODE_SPUT_OBJECT:
case DOPCODE_SPUT_BOOLEAN:
case DOPCODE_SPUT_BYTE:
case DOPCODE_SPUT_CHAR:
case DOPCODE_SPUT_SHORT: {
uint16_t fidx = *insns++;
DexFieldRef* field = idx->get_fieldidx(fidx);
return new DexOpcodeField(fopcode, field);
}
/* MethodRef: */
case DOPCODE_INVOKE_VIRTUAL:
case DOPCODE_INVOKE_SUPER:
case DOPCODE_INVOKE_DIRECT:
case DOPCODE_INVOKE_STATIC:
case DOPCODE_INVOKE_INTERFACE:
case DOPCODE_INVOKE_VIRTUAL_RANGE:
case DOPCODE_INVOKE_SUPER_RANGE:
case DOPCODE_INVOKE_DIRECT_RANGE:
case DOPCODE_INVOKE_STATIC_RANGE:
case DOPCODE_INVOKE_INTERFACE_RANGE: {
uint16_t midx = *insns++;
uint16_t arg = *insns++;
DexMethodRef* meth = idx->get_methodidx(midx);
return new DexOpcodeMethod(fopcode, meth, arg);
}
/* MethodHandle: */
case DOPCODE_INVOKE_POLYMORPHIC:
case DOPCODE_INVOKE_POLYMORPHIC_RANGE: {
uint16_t csidx = *insns++;
uint16_t arg = *insns++;
DexMethodHandle* methodhandle = idx->get_methodhandleidx(csidx);
return new DexOpcodeMethodHandle(fopcode, methodhandle, arg);
}
/* CallSite: */
case DOPCODE_INVOKE_CUSTOM:
case DOPCODE_INVOKE_CUSTOM_RANGE: {
uint16_t csidx = *insns++;
uint16_t arg = *insns++;
DexCallSite* callsite = idx->get_callsiteidx(csidx);
return new DexOpcodeCallSite(fopcode, callsite, arg);
}
/* StringRef: */
case DOPCODE_CONST_STRING: {
uint16_t sidx = *insns++;
auto str = idx->get_stringidx(sidx);
return new DexOpcodeString(fopcode, str);
}
case DOPCODE_CONST_STRING_JUMBO: {
uint32_t sidx = *insns++;
sidx |= (*insns++) << 16;
auto str = idx->get_stringidx(sidx);
return new DexOpcodeString(fopcode, str);
}
case DOPCODE_CONST_CLASS:
case DOPCODE_CHECK_CAST:
case DOPCODE_INSTANCE_OF:
case DOPCODE_NEW_INSTANCE:
case DOPCODE_NEW_ARRAY: {
uint16_t tidx = *insns++;
DexType* type = idx->get_typeidx(tidx);
return new DexOpcodeType(fopcode, type);
}
case DOPCODE_FILLED_NEW_ARRAY:
case DOPCODE_FILLED_NEW_ARRAY_RANGE: {
uint16_t tidx = *insns++;
uint16_t arg = *insns++;
DexType* type = idx->get_typeidx(tidx);
return new DexOpcodeType(fopcode, type, arg);
}
default:
fprintf(stderr, "Unknown opcode %02x\n", opcode);
return nullptr;
}
}
DexInstruction* DexInstruction::make_instruction(DexOpcode op) {
switch (op) {
/* Field ref: */
case DOPCODE_IGET:
case DOPCODE_IGET_WIDE:
case DOPCODE_IGET_OBJECT:
case DOPCODE_IGET_BOOLEAN:
case DOPCODE_IGET_BYTE:
case DOPCODE_IGET_CHAR:
case DOPCODE_IGET_SHORT:
case DOPCODE_IPUT:
case DOPCODE_IPUT_WIDE:
case DOPCODE_IPUT_OBJECT:
case DOPCODE_IPUT_BOOLEAN:
case DOPCODE_IPUT_BYTE:
case DOPCODE_IPUT_CHAR:
case DOPCODE_IPUT_SHORT:
case DOPCODE_SGET:
case DOPCODE_SGET_WIDE:
case DOPCODE_SGET_OBJECT:
case DOPCODE_SGET_BOOLEAN:
case DOPCODE_SGET_BYTE:
case DOPCODE_SGET_CHAR:
case DOPCODE_SGET_SHORT:
case DOPCODE_SPUT:
case DOPCODE_SPUT_WIDE:
case DOPCODE_SPUT_OBJECT:
case DOPCODE_SPUT_BOOLEAN:
case DOPCODE_SPUT_BYTE:
case DOPCODE_SPUT_CHAR:
case DOPCODE_SPUT_SHORT:
return new DexOpcodeField(op, nullptr);
/* MethodRef: */
case DOPCODE_INVOKE_VIRTUAL:
case DOPCODE_INVOKE_SUPER:
case DOPCODE_INVOKE_DIRECT:
case DOPCODE_INVOKE_STATIC:
case DOPCODE_INVOKE_INTERFACE:
case DOPCODE_INVOKE_CUSTOM:
case DOPCODE_INVOKE_POLYMORPHIC:
case DOPCODE_INVOKE_VIRTUAL_RANGE:
case DOPCODE_INVOKE_SUPER_RANGE:
case DOPCODE_INVOKE_DIRECT_RANGE:
case DOPCODE_INVOKE_STATIC_RANGE:
case DOPCODE_INVOKE_INTERFACE_RANGE:
case DOPCODE_INVOKE_CUSTOM_RANGE:
case DOPCODE_INVOKE_POLYMORPHIC_RANGE:
return new DexOpcodeMethod(op, nullptr);
/* StringRef: */
case DOPCODE_CONST_STRING:
case DOPCODE_CONST_STRING_JUMBO:
return new DexOpcodeString(op, nullptr);
case DOPCODE_CONST_CLASS:
case DOPCODE_CHECK_CAST:
case DOPCODE_INSTANCE_OF:
case DOPCODE_NEW_INSTANCE:
case DOPCODE_NEW_ARRAY:
case DOPCODE_FILLED_NEW_ARRAY:
case DOPCODE_FILLED_NEW_ARRAY_RANGE:
return new DexOpcodeType(op, nullptr);
default:
return new DexInstruction(op);
}
}
bool DexInstruction::operator==(const DexInstruction& that) const {
if (m_ref_type != that.m_ref_type || m_opcode != that.m_opcode ||
m_count != that.m_count) {
return false;
}
for (size_t i = 0; i < m_count; ++i) {
if (m_arg[i] != that.m_arg[i]) {
return false;
}
}
switch (m_ref_type) {
case REF_NONE:
return true;
case REF_STRING: {
auto this_ = static_cast<const DexOpcodeString*>(this);
auto that_ = static_cast<const DexOpcodeString*>(&that);
return this_->get_string() == that_->get_string();
}
case REF_TYPE: {
auto this_ = static_cast<const DexOpcodeType*>(this);
auto that_ = static_cast<const DexOpcodeType*>(&that);
return this_->get_type() == that_->get_type();
}
case REF_FIELD: {
auto this_ = static_cast<const DexOpcodeField*>(this);
auto that_ = static_cast<const DexOpcodeField*>(&that);
return this_->get_field() == that_->get_field();
}
case REF_METHOD: {
auto this_ = static_cast<const DexOpcodeMethod*>(this);
auto that_ = static_cast<const DexOpcodeMethod*>(&that);
return this_->get_method() == that_->get_method();
}
case REF_CALLSITE: {
auto this_ = static_cast<const DexOpcodeCallSite*>(this);
auto that_ = static_cast<const DexOpcodeCallSite*>(&that);
return this_->get_callsite() == that_->get_callsite();
}
case REF_METHODHANDLE: {
auto this_ = static_cast<const DexOpcodeMethodHandle*>(this);
auto that_ = static_cast<const DexOpcodeMethodHandle*>(&that);
return this_->get_methodhandle() == that_->get_methodhandle();
}
}
}