in libredex/JarLoader.cpp [364:537]
bool parse_class(uint8_t* buffer,
Scope* classes,
attribute_hook_t attr_hook,
const DexLocation* jar_location) {
uint32_t magic = read32(buffer);
uint16_t vminor DEBUG_ONLY = read16(buffer);
uint16_t vmajor DEBUG_ONLY = read16(buffer);
uint16_t cp_count = read16(buffer);
if (magic != kClassMagic) {
fprintf(stderr, "Bad class magic %08x, Bailing\n", magic);
return false;
}
std::vector<cp_entry> cpool;
cpool.resize(cp_count);
/* The zero'th entry is always empty. Java is annoying. */
for (int i = 1; i < cp_count; i++) {
if (!parse_cp_entry(buffer, cpool[i])) return false;
if (cpool[i].tag == CP_CONST_LONG || cpool[i].tag == CP_CONST_DOUBLE) {
cpool[i + 1] = cpool[i];
i++;
}
}
uint16_t aflags = read16(buffer);
uint16_t clazz = read16(buffer);
uint16_t super = read16(buffer);
uint16_t ifcount = read16(buffer);
if (is_module((DexAccessFlags)aflags)) {
// Classes with the ACC_MODULE access flag are special. They contain
// metadata for the module/package system and don't have a superclass.
// Ignore them for now.
TRACE(MAIN, 5, "Warning: ignoring module-info class in jar '%s'",
jar_location->get_file_name().c_str());
return true;
}
DexType* self = make_dextype_from_cref(cpool, clazz);
DexClass* cls = type_class(self);
if (cls) {
// We are seeing duplicate classes when parsing jar file
if (cls->is_external()) {
// Two external classes in .jar file has the same name
// Just issue an warning for now
TRACE(MAIN, 1,
"Warning: Found a duplicate class '%s' in two .jar files:\n "
" Current: '%s'\n"
" Previous: '%s'",
SHOW(self), jar_location->get_file_name().c_str(),
cls->get_location()->get_file_name().c_str());
} else if (!dup_classes::is_known_dup(cls)) {
TRACE(MAIN, 1,
"Warning: Found a duplicate class '%s' in .dex and .jar file."
" Current: '%s'\n"
" Previous: '%s'\n",
SHOW(self), jar_location->get_file_name().c_str(),
cls->get_location()->get_file_name().c_str());
// TODO: There are still blocking issues in instrumentation test that are
// blocking. We currently only fail for duplicate `android*` classes,
// we can make this throw for all the classes once they are fixed.
if (boost::starts_with(cls->str(), "Landroid")) {
throw RedexException(RedexError::DUPLICATE_CLASSES,
"Found duplicate class in two different files.",
{{"class", SHOW(self)},
{"jar", jar_location->get_file_name()},
{"dex", cls->get_location()->get_file_name()}});
}
}
return true;
}
ClassCreator cc(self, jar_location);
cc.set_external();
if (super != 0) {
DexType* sclazz = make_dextype_from_cref(cpool, super);
cc.set_super(sclazz);
}
cc.set_access((DexAccessFlags)aflags);
if (ifcount) {
for (int i = 0; i < ifcount; i++) {
uint16_t iface = read16(buffer);
DexType* iftype = make_dextype_from_cref(cpool, iface);
cc.add_interface(iftype);
}
}
uint16_t fcount = read16(buffer);
auto invoke_attr_hook =
[&](const boost::variant<DexField*, DexMethod*>& field_or_method,
uint8_t* attrPtr) {
if (attr_hook == nullptr) {
return;
}
uint16_t attributes_count = read16(attrPtr);
for (uint16_t j = 0; j < attributes_count; j++) {
uint16_t attribute_name_index = read16(attrPtr);
uint32_t attribute_length = read32(attrPtr);
char attribute_name[MAX_CLASS_NAMELEN];
auto extract_res = extract_utf8(cpool, attribute_name_index,
attribute_name, MAX_CLASS_NAMELEN);
always_assert_log(
extract_res,
"attribute hook was specified, but failed to load the attribute "
"name due to insufficient name buffer");
attr_hook(field_or_method, attribute_name, attrPtr);
attrPtr += attribute_length;
}
};
for (int i = 0; i < fcount; i++) {
cp_field_info cpfield;
cpfield.aflags = read16(buffer);
cpfield.nameNdx = read16(buffer);
cpfield.descNdx = read16(buffer);
uint8_t* attrPtr = buffer;
skip_attributes(buffer);
DexField* field = make_dexfield(cpool, self, cpfield);
if (field == nullptr) return false;
cc.add_field(field);
invoke_attr_hook({field}, attrPtr);
}
uint16_t mcount = read16(buffer);
if (mcount) {
for (int i = 0; i < mcount; i++) {
cp_method_info cpmethod;
cpmethod.aflags = read16(buffer);
cpmethod.nameNdx = read16(buffer);
cpmethod.descNdx = read16(buffer);
uint8_t* attrPtr = buffer;
skip_attributes(buffer);
DexMethod* method = make_dexmethod(cpool, self, cpmethod);
if (method == nullptr) return false;
cc.add_method(method);
invoke_attr_hook({method}, attrPtr);
}
}
DexClass* dc = cc.create();
if (classes != nullptr) {
classes->emplace_back(dc);
}
//#define DEBUG_PRINT
#ifdef DEBUG_PRINT
fprintf(stderr, "DexClass constructed from jar:\n%s\n", SHOW(dc));
if (dc->get_sfields().size()) {
fprintf(stderr, "Static Fields:\n");
for (auto const& field : dc->get_sfields()) {
fprintf(stderr, "\t%s\n", SHOW(field));
}
}
if (dc->get_ifields().size()) {
fprintf(stderr, "Instance Fields:\n");
for (auto const& field : dc->get_ifields()) {
fprintf(stderr, "\t%s\n", SHOW(field));
}
}
if (dc->get_dmethods().size()) {
fprintf(stderr, "Direct Methods:\n");
for (auto const& method : dc->get_dmethods()) {
fprintf(stderr, "\t%s\n", SHOW(method));
}
}
if (dc->get_vmethods().size()) {
fprintf(stderr, "Virtual Methods:\n");
for (auto const& method : dc->get_vmethods()) {
fprintf(stderr, "\t%s\n", SHOW(method));
}
}
#endif
return true;
}