Skip to content

Commit

Permalink
Add parsing of Proguard mapping file for resource class fields. Fixes…
Browse files Browse the repository at this point in the history
… issue with obfuscated fields being stripped because they fail to match any known resource.

--
MOS_MIGRATED_REVID=131830443
  • Loading branch information
apelle03 authored and aehlig committed Sep 1, 2016
1 parent d82f4a5 commit 193e1d4
Showing 1 changed file with 58 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import com.android.resources.FolderTypeRelationship;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.utils.Pair;
import com.android.utils.XmlUtils;

import org.objectweb.asm.ClassReader;
Expand Down Expand Up @@ -157,11 +158,12 @@ public class ResourceShrinker {
private Map<ResourceType, Map<String, Resource>> typeToName =
Maps.newEnumMap(ResourceType.class);
/**
* Map from resource class owners (VM format class) to corresponding resource types. This will
* typically be the fully qualified names of the R classes, as well as any renamed versions of
* those discovered in the mapping.txt file from ProGuard
* Map from resource class owners (VM format class) to corresponding resource entries.
* This lets us map back from code references (obfuscated class and possibly obfuscated field
* reference) back to the corresponding resource type and name.
*/
private Map<String, ResourceType> resourceClassOwners = Maps.newHashMapWithExpectedSize(20);
private final Map<String, Pair<ResourceType, Map<String, String>>> resourceObfuscation =
Maps.newHashMapWithExpectedSize(30);

public ResourceShrinker(
Set<String> resourcePackages,
Expand Down Expand Up @@ -680,9 +682,36 @@ private void recordMapping(@Nullable Path mapping) throws IOException {
}
final String arrowIndicator = " -> ";
final String resourceIndicator = ".R$";
Map<String, String> nameMap = null;
for (String line : Files.readLines(mapping.toFile(), UTF_8)) {
if (line.startsWith(" ") || line.startsWith("\t")) {
if (nameMap != null) {
// We're processing the members of a resource class: record names into the map
int n = line.length();
int i = 0;
for (; i < n; i++) {
if (!Character.isWhitespace(line.charAt(i))) {
break;
}
}
if (i < n && line.startsWith("int", i)) { // int or int[]
int start = line.indexOf(' ', i + 3) + 1;
int arrow = line.indexOf(arrowIndicator);
if (start > 0 && arrow != -1) {
int end = line.indexOf(' ', start + 1);
if (end != -1) {
String oldName = line.substring(start, end);
String newName = line.substring(arrow + arrowIndicator.length()).trim();
if (!newName.equals(oldName)) {
nameMap.put(newName, oldName);
}
}
}
}
}
continue;
} else {
nameMap = null;
}
int index = line.indexOf(resourceIndicator);
if (index == -1) {
Expand All @@ -703,7 +732,10 @@ private void recordMapping(@Nullable Path mapping) throws IOException {
}
String target = line.substring(arrow + arrowIndicator.length(), end).trim();
String ownerName = target.replace('.', '/');
resourceClassOwners.put(ownerName, type);

nameMap = Maps.newHashMap();
Pair<ResourceType, Map<String, String>> pair = Pair.of(type, nameMap);
resourceObfuscation.put(ownerName, pair);
}
}

Expand Down Expand Up @@ -740,6 +772,22 @@ private Resource getResource(@NonNull String possibleUrlReference) {
return null;
}

@VisibleForTesting
@Nullable
Resource getResourceFromCode(@NonNull String owner, @NonNull String name) {
Pair<ResourceType, Map<String, String>> pair = resourceObfuscation.get(owner);
if (pair != null) {
ResourceType type = pair.getFirst();
Map<String, String> nameMap = pair.getSecond();
String renamedField = nameMap.get(name);
if (renamedField != null) {
name = renamedField;
}
return getResource(type, name);
}
return null;
}

private void recordManifestUsages(Node node) {
short nodeType = node.getNodeType();
if (nodeType == Node.ELEMENT_NODE) {
Expand Down Expand Up @@ -945,7 +993,8 @@ private void parseResourceTxtFile(Path rTxt, Set<String> resourcePackages) throw
String[] tokens = line.split(" ");
ResourceType type = ResourceType.getEnum(tokens[1]);
for (String resourcePackage : resourcePackages) {
resourceClassOwners.put(resourcePackage.replace('.', '/') + "/R$" + type.getName(), type);
resourceObfuscation.put(resourcePackage.replace('.', '/') + "/R$" + type.getName(),
Pair.<ResourceType, Map<String, String>>of(type, Maps.<String, String>newHashMap()));
}
if (type == ResourceType.STYLEABLE) {
if (tokens[0].equals("int[]")) {
Expand Down Expand Up @@ -1113,12 +1162,9 @@ public void visitLdcInsn(Object cst) {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (opcode == Opcodes.GETSTATIC) {
ResourceType type = resourceClassOwners.get(owner);
if (type != null) {
Resource resource = getResource(type, name);
if (resource != null) {
markReachable(resource);
}
Resource resource = getResourceFromCode(owner, name);
if (resource != null) {
markReachable(resource);
}
}
}
Expand Down

0 comments on commit 193e1d4

Please sign in to comment.