Skip to content

Commit

Permalink
Add support for ARSC resource table compact encoding
Browse files Browse the repository at this point in the history
This is to support a preliminary option in AAPT2 to enable more compact
resource tables.

Add support for a ResTable type chunk encoded using FLAG_OFFSET16 and a
ResTable entry type chunk encoded using FLAG_COMPACT.

PiperOrigin-RevId: 521671209
  • Loading branch information
hoisie authored and Copybara-Service committed Apr 4, 2023
1 parent f14da31 commit 5730f50
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ private ApkAssetsCookie FindEntry(int resid, short density_override,
out_entry_.type_flags = type_flags;
out_entry_.type_string_ref = new StringPoolRef(best_package.GetTypeStringPool(), best_type.id - 1);
out_entry_.entry_string_ref =
new StringPoolRef(best_package.GetKeyStringPool(), best_entry.key.index);
new StringPoolRef(best_package.GetKeyStringPool(), best_entry.getKeyIndex());
out_entry_.dynamic_ref_table = package_group.dynamic_ref_table;
out_entry.set(out_entry_);
return best_cookie;
Expand Down
43 changes: 23 additions & 20 deletions resources/src/main/java/org/robolectric/res/android/LoadedArsc.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,9 @@ static boolean VerifyResTableType(ResTable_type header) {
// Make sure that there is enough room for the entry offsets.
int offsets_offset = dtohs(header.header.headerSize);
int entries_offset = dtohl(header.entriesStart);
int offsets_length = 4 * entry_count;

if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) {
int bytesPerEntry = isTruthy(header.flags & ResTable_type.FLAG_OFFSET16) ? 2 : 4;
int offsetsLength = bytesPerEntry * entry_count;
if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsetsLength) {
logError("RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data.");
return false;
}
Expand Down Expand Up @@ -291,54 +291,57 @@ static boolean VerifyResTableEntry(ResTable_type type, int entry_offset) {
// reinterpret_cast<uint8_t*>(type) + entry_offset);
ResTable_entry entry = new ResTable_entry(type.myBuf(), type.myOffset() + entry_offset);

int entry_size = dtohs(entry.size);
// if (entry_size < sizeof(*entry)) {
if (entry_size < ResTable_entry.SIZEOF) {
logError("ResTable_entry size " + entry_size + " at offset " + entry_offset
+ " is too small.");
int entrySize = entry.isCompact() ? ResTable_entry.SIZEOF : dtohs(entry.size);
if (entrySize < ResTable_entry.SIZEOF) {
logError(
"ResTable_entry size " + entrySize + " at offset " + entry_offset + " is too small.");
return false;
}

if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
logError("ResTable_entry size " + entry_size + " at offset " + entry_offset
+ " is too large.");
if (entrySize > chunk_size || entry_offset > chunk_size - entrySize) {
logError(
"ResTable_entry size " + entrySize + " at offset " + entry_offset + " is too large.");
return false;
}

if (entry_size < ResTable_map_entry.BASE_SIZEOF) {
// No further validations apply if the entry is compact.
if (entry.isCompact()) {
return true;
}

if (entrySize < ResTable_map_entry.BASE_SIZEOF) {
// There needs to be room for one Res_value struct.
if (entry_offset + entry_size > chunk_size - Res_value.SIZEOF) {
if (entry_offset + entrySize > chunk_size - Res_value.SIZEOF) {
logError("No room for Res_value after ResTable_entry at offset " + entry_offset
+ " for type " + (int) type.id + ".");
return false;
}

// Res_value value =
// reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(entry) + entry_size);
Res_value value =
new Res_value(entry.myBuf(), entry.myOffset() + ResTable_entry.SIZEOF);
Res_value value = entry.getResValue();
int value_size = dtohs(value.size);
if (value_size < Res_value.SIZEOF) {
logError("Res_value at offset " + entry_offset + " is too small.");
return false;
}

if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
if (value_size > chunk_size || entry_offset + entrySize > chunk_size - value_size) {
logError("Res_value size " + value_size + " at offset " + entry_offset
+ " is too large.");
return false;
}
} else {
ResTable_map_entry map = new ResTable_map_entry(entry.myBuf(), entry.myOffset());
int map_entry_count = dtohl(map.count);
int map_entries_start = entry_offset + entry_size;
if (isTruthy(map_entries_start & 0x03)) {
int mapEntriesStart = entry_offset + entrySize;
if (isTruthy(mapEntriesStart & 0x03)) {
logError("Map entries at offset " + entry_offset + " start at unaligned offset.");
return false;
}

// Each entry is sizeof(ResTable_map) big.
if (map_entry_count > ((chunk_size - map_entries_start) / ResTable_map.SIZEOF)) {
if (map_entry_count > ((chunk_size - mapEntriesStart) / ResTable_map.SIZEOF)) {
logError("Too many map entries in ResTable_map_entry at offset " + entry_offset + ".");
return false;
}
Expand Down Expand Up @@ -539,7 +542,7 @@ int FindEntryByName(String type_name, String entry_name) {
ResTable_entry entry =
new ResTable_entry(type.myBuf(), type.myOffset() +
dtohl(type.entriesStart) + offset);
if (dtohl(entry.key.index) == key_idx) {
if (dtohl(entry.getKeyIndex()) == key_idx) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
return make_resid((byte) 0x00, (byte) (type_idx + type_id_offset_ + 1), (short) entry_idx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ public Res_value(Res_value other) {
}

public Res_value(byte dataType, int data) {
this.size = 0;
this.size = SIZEOF;
// this.res0 = 0;
this.dataType = dataType;
this.data = data;
Expand Down Expand Up @@ -1165,6 +1165,11 @@ static class ResTable_type extends WithOffset
// Mark any types that use this with a v26 qualifier to prevent runtime issues on older
// platforms.
public static final int FLAG_SPARSE = 0x01;

// If set, the offsets to the entries are encoded in 16-bit, real_offset = offset * 4u
// An 16-bit offset of 0xffffu means a NO_ENTRY
public static final int FLAG_OFFSET16 = 0x02;

// };
final byte flags;

Expand Down Expand Up @@ -1208,13 +1213,14 @@ public int findEntryByResName(int stringId) {
int entryOffset(int entryIndex) {
ByteBuffer byteBuffer = myBuf();
int offset = myOffset();

// from ResTable cpp:
// const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
// reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
//
// uint32_t thisOffset = dtohl(eindex[realEntryIndex]);
return byteBuffer.getInt(offset + header.headerSize + entryIndex * 4);
boolean isOffset16 = (flags & ResTable_type.FLAG_OFFSET16) == ResTable_type.FLAG_OFFSET16;
if (isOffset16) {
short off16 = byteBuffer.getShort(offset + header.headerSize + entryIndex * 2);
// Check for no entry (0xffff short)
return dtohs(off16) == -1 ? ResTable_type.NO_ENTRY : dtohs(off16) * 4;
} else {
return byteBuffer.getInt(offset + header.headerSize + entryIndex * 4);
}
}

private int entryNameIndex(int entryIndex) {
Expand Down Expand Up @@ -1282,9 +1288,8 @@ static class ResTable_entry extends WithOffset
public static final int SIZEOF = 4 + ResStringPool_ref.SIZEOF;

// Number of bytes in this structure.
final short size;
short size;

//enum {
// If set, this is a complex entry, holding a set of name/value
// mappings. It is followed by an array of ResTable_map structures.
public static final int FLAG_COMPLEX = 0x0001;
Expand All @@ -1295,18 +1300,42 @@ static class ResTable_entry extends WithOffset
// resources of the same name/type. This is only useful during
// linking with other resource tables.
public static final int FLAG_WEAK = 0x0004;
// };
// If set, this is a compact entry with data type and value directly
// encoded in the this entry, see ResTable_entry::compact
public static final int FLAG_COMPACT = 0x0008;

final short flags;

// Reference into ResTable_package::keyStrings identifying this entry.
final ResStringPool_ref key;
ResStringPool_ref key;

int compactData;
short compactKey;

ResTable_entry(ByteBuffer buf, int offset) {
super(buf, offset);

size = buf.getShort(offset);
flags = buf.getShort(offset + 2);
key = new ResStringPool_ref(buf, offset + 4);

if (isCompact()) {
compactKey = buf.getShort(offset);
compactData = buf.getInt(offset + 4);
} else {
size = buf.getShort(offset);
key = new ResStringPool_ref(buf, offset + 4);
}
}

public int getKeyIndex() {
if (isCompact()) {
return dtohs(compactKey);
} else {
return key.index;
}
}

public boolean isCompact() {
return (flags & FLAG_COMPACT) == FLAG_COMPACT;
}

public Res_value getResValue() {
Expand All @@ -1315,7 +1344,12 @@ public Res_value getResValue() {
// final Res_value device_value = reinterpret_cast<final Res_value>(
// reinterpret_cast<final byte*>(entry) + dtohs(entry.size));

return new Res_value(myBuf(), myOffset() + dtohs(size));
if (isCompact()) {
byte type = (byte) (dtohs(flags) >> 8);
return new Res_value(type, compactData);
} else {
return new Res_value(myBuf(), myOffset() + dtohs(size));
}
}
}

Expand Down

0 comments on commit 5730f50

Please sign in to comment.