androidfw: Add support for 16-bit entry offsets
Bug: 237583012
Most offsets to the entries can be well encoded in 16-bit,
and given entries are 4-byte aligned, this gives us a range
of entry offsets from 0x00000 to 0xfffe * 4u, with 0xffffu
to represent ResTable_type::NO_ENTRY.
For now, 16-bit entry offset will be enabled only when:
* all the entry offsets can be represented in 16-bit
* --enable-compact-entries switch is turned on
Change-Id: I1c815c052aa5fba6eab2529434d31d7714c13694
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index e78f91e..386f718 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -88,7 +88,9 @@
// Make sure that there is enough room for the entry offsets.
const size_t offsets_offset = dtohs(header->header.headerSize);
const size_t entries_offset = dtohl(header->entriesStart);
- const size_t offsets_length = sizeof(uint32_t) * entry_count;
+ const size_t offsets_length = header->flags & ResTable_type::FLAG_OFFSET16
+ ? sizeof(uint16_t) * entry_count
+ : sizeof(uint32_t) * entry_count;
if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) {
LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data.";
@@ -247,14 +249,13 @@
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const size_t entry_count = dtohl(type_chunk->entryCount);
- const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
+ const auto offsets = type_chunk.offset(dtohs(type_chunk->header.headerSize));
// Check if there is the desired entry in this type.
if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
// This is encoded as a sparse map, so perform a binary search.
bool error = false;
- auto sparse_indices = type_chunk.offset(offsets_offset)
- .convert<ResTable_sparseTypeEntry>().iterator();
+ auto sparse_indices = offsets.convert<ResTable_sparseTypeEntry>().iterator();
auto sparse_indices_end = sparse_indices + entry_count;
auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
[&error](const incfs::map_ptr<ResTable_sparseTypeEntry>& entry,
@@ -289,17 +290,26 @@
return base::unexpected(std::nullopt);
}
- const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + entry_index;
- if (UNLIKELY(!entry_offset_ptr)) {
- return base::unexpected(IOError::PAGES_MISSING);
+ uint32_t result;
+
+ if (type_chunk->flags & ResTable_type::FLAG_OFFSET16) {
+ const auto entry_offset_ptr = offsets.convert<uint16_t>() + entry_index;
+ if (UNLIKELY(!entry_offset_ptr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ result = offset_from16(entry_offset_ptr.value());
+ } else {
+ const auto entry_offset_ptr = offsets.convert<uint32_t>() + entry_index;
+ if (UNLIKELY(!entry_offset_ptr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ result = dtohl(entry_offset_ptr.value());
}
- const uint32_t value = dtohl(entry_offset_ptr.value());
- if (value == ResTable_type::NO_ENTRY) {
+ if (result == ResTable_type::NO_ENTRY) {
return base::unexpected(std::nullopt);
}
-
- return value;
+ return result;
}
base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
@@ -382,24 +392,35 @@
for (const auto& type_entry : type_spec->type_entries) {
const incfs::verified_map_ptr<ResTable_type>& type = type_entry.type;
- size_t entry_count = dtohl(type->entryCount);
- for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
- auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert<uint32_t>() +
- entry_idx;
- if (!entry_offset_ptr) {
- return base::unexpected(IOError::PAGES_MISSING);
- }
+ const size_t entry_count = dtohl(type->entryCount);
+ const auto entry_offsets = type.offset(dtohs(type->header.headerSize));
+ for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
uint32_t offset;
uint16_t res_idx;
if (type->flags & ResTable_type::FLAG_SPARSE) {
- auto sparse_entry = entry_offset_ptr.convert<ResTable_sparseTypeEntry>();
+ auto sparse_entry = entry_offsets.convert<ResTable_sparseTypeEntry>() + entry_idx;
+ if (!sparse_entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
offset = dtohs(sparse_entry->offset) * 4u;
res_idx = dtohs(sparse_entry->idx);
+ } else if (type->flags & ResTable_type::FLAG_OFFSET16) {
+ auto entry = entry_offsets.convert<uint16_t>() + entry_idx;
+ if (!entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ offset = offset_from16(entry.value());
+ res_idx = entry_idx;
} else {
- offset = dtohl(entry_offset_ptr.value());
+ auto entry = entry_offsets.convert<uint32_t>() + entry_idx;
+ if (!entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ offset = dtohl(entry.value());
res_idx = entry_idx;
}
+
if (offset != ResTable_type::NO_ENTRY) {
auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
if (!entry) {