Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit a1f2bce

Browse files
author
Eric Miao
committed
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
1 parent 368cd19 commit a1f2bce

File tree

5 files changed

+90
-29
lines changed

5 files changed

+90
-29
lines changed

libs/androidfw/LoadedArsc.cpp

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
8888
// Make sure that there is enough room for the entry offsets.
8989
const size_t offsets_offset = dtohs(header->header.headerSize);
9090
const size_t entries_offset = dtohl(header->entriesStart);
91-
const size_t offsets_length = sizeof(uint32_t) * entry_count;
91+
const size_t offsets_length = header->flags & ResTable_type::FLAG_OFFSET16
92+
? sizeof(uint16_t) * entry_count
93+
: sizeof(uint32_t) * entry_count;
9294

9395
if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) {
9496
LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data.";
@@ -247,14 +249,13 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
247249
// The configuration matches and is better than the previous selection.
248250
// Find the entry value if it exists for this configuration.
249251
const size_t entry_count = dtohl(type_chunk->entryCount);
250-
const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
252+
const auto offsets = type_chunk.offset(dtohs(type_chunk->header.headerSize));
251253

252254
// Check if there is the desired entry in this type.
253255
if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
254256
// This is encoded as a sparse map, so perform a binary search.
255257
bool error = false;
256-
auto sparse_indices = type_chunk.offset(offsets_offset)
257-
.convert<ResTable_sparseTypeEntry>().iterator();
258+
auto sparse_indices = offsets.convert<ResTable_sparseTypeEntry>().iterator();
258259
auto sparse_indices_end = sparse_indices + entry_count;
259260
auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
260261
[&error](const incfs::map_ptr<ResTable_sparseTypeEntry>& entry,
@@ -289,17 +290,26 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
289290
return base::unexpected(std::nullopt);
290291
}
291292

292-
const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + entry_index;
293-
if (UNLIKELY(!entry_offset_ptr)) {
294-
return base::unexpected(IOError::PAGES_MISSING);
293+
uint32_t result;
294+
295+
if (type_chunk->flags & ResTable_type::FLAG_OFFSET16) {
296+
const auto entry_offset_ptr = offsets.convert<uint16_t>() + entry_index;
297+
if (UNLIKELY(!entry_offset_ptr)) {
298+
return base::unexpected(IOError::PAGES_MISSING);
299+
}
300+
result = offset_from16(entry_offset_ptr.value());
301+
} else {
302+
const auto entry_offset_ptr = offsets.convert<uint32_t>() + entry_index;
303+
if (UNLIKELY(!entry_offset_ptr)) {
304+
return base::unexpected(IOError::PAGES_MISSING);
305+
}
306+
result = dtohl(entry_offset_ptr.value());
295307
}
296308

297-
const uint32_t value = dtohl(entry_offset_ptr.value());
298-
if (value == ResTable_type::NO_ENTRY) {
309+
if (result == ResTable_type::NO_ENTRY) {
299310
return base::unexpected(std::nullopt);
300311
}
301-
302-
return value;
312+
return result;
303313
}
304314

305315
base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
@@ -382,24 +392,35 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
382392
for (const auto& type_entry : type_spec->type_entries) {
383393
const incfs::verified_map_ptr<ResTable_type>& type = type_entry.type;
384394

385-
size_t entry_count = dtohl(type->entryCount);
386-
for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
387-
auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert<uint32_t>() +
388-
entry_idx;
389-
if (!entry_offset_ptr) {
390-
return base::unexpected(IOError::PAGES_MISSING);
391-
}
395+
const size_t entry_count = dtohl(type->entryCount);
396+
const auto entry_offsets = type.offset(dtohs(type->header.headerSize));
392397

398+
for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
393399
uint32_t offset;
394400
uint16_t res_idx;
395401
if (type->flags & ResTable_type::FLAG_SPARSE) {
396-
auto sparse_entry = entry_offset_ptr.convert<ResTable_sparseTypeEntry>();
402+
auto sparse_entry = entry_offsets.convert<ResTable_sparseTypeEntry>() + entry_idx;
403+
if (!sparse_entry) {
404+
return base::unexpected(IOError::PAGES_MISSING);
405+
}
397406
offset = dtohs(sparse_entry->offset) * 4u;
398407
res_idx = dtohs(sparse_entry->idx);
408+
} else if (type->flags & ResTable_type::FLAG_OFFSET16) {
409+
auto entry = entry_offsets.convert<uint16_t>() + entry_idx;
410+
if (!entry) {
411+
return base::unexpected(IOError::PAGES_MISSING);
412+
}
413+
offset = offset_from16(entry.value());
414+
res_idx = entry_idx;
399415
} else {
400-
offset = dtohl(entry_offset_ptr.value());
416+
auto entry = entry_offsets.convert<uint32_t>() + entry_idx;
417+
if (!entry) {
418+
return base::unexpected(IOError::PAGES_MISSING);
419+
}
420+
offset = dtohl(entry.value());
401421
res_idx = entry_idx;
402422
}
423+
403424
if (offset != ResTable_type::NO_ENTRY) {
404425
auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
405426
if (!entry) {

libs/androidfw/ResourceTypes.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6521,8 +6521,12 @@ status_t ResTable::getEntry(
65216521
// Entry does not exist.
65226522
continue;
65236523
}
6524-
6525-
thisOffset = dtohl(eindex[realEntryIndex]);
6524+
if (thisType->flags & ResTable_type::FLAG_OFFSET16) {
6525+
auto eindex16 = reinterpret_cast<const uint16_t*>(eindex);
6526+
thisOffset = offset_from16(eindex16[realEntryIndex]);
6527+
} else {
6528+
thisOffset = dtohl(eindex[realEntryIndex]);
6529+
}
65266530
}
65276531

65286532
if (thisOffset == ResTable_type::NO_ENTRY) {
@@ -7574,6 +7578,9 @@ void ResTable::print(bool inclValues) const
75747578
if (type->flags & ResTable_type::FLAG_SPARSE) {
75757579
printf(" [sparse]");
75767580
}
7581+
if (type->flags & ResTable_type::FLAG_OFFSET16) {
7582+
printf(" [offset16]");
7583+
}
75777584
}
75787585

75797586
printf(":\n");
@@ -7605,7 +7612,13 @@ void ResTable::print(bool inclValues) const
76057612
thisOffset = static_cast<uint32_t>(dtohs(entry->offset)) * 4u;
76067613
} else {
76077614
entryId = entryIndex;
7608-
thisOffset = dtohl(eindex[entryIndex]);
7615+
if (type->flags & ResTable_type::FLAG_OFFSET16) {
7616+
const auto eindex16 =
7617+
reinterpret_cast<const uint16_t*>(eindex);
7618+
thisOffset = offset_from16(eindex16[entryIndex]);
7619+
} else {
7620+
thisOffset = dtohl(eindex[entryIndex]);
7621+
}
76097622
if (thisOffset == ResTable_type::NO_ENTRY) {
76107623
continue;
76117624
}

libs/androidfw/TypeWrappers.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
5959
+ dtohl(type->header.size);
6060
const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
6161
reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
62-
if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) > containerEnd) {
62+
const size_t indexSize = type->flags & ResTable_type::FLAG_OFFSET16 ?
63+
sizeof(uint16_t) : sizeof(uint32_t);
64+
if (reinterpret_cast<uintptr_t>(entryIndices) + (indexSize * entryCount) > containerEnd) {
6365
ALOGE("Type's entry indices extend beyond its boundaries");
6466
return NULL;
6567
}
@@ -73,6 +75,9 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
7375
}
7476

7577
entryOffset = static_cast<uint32_t>(dtohs(ResTable_sparseTypeEntry{*iter}.offset)) * 4u;
78+
} else if (type->flags & ResTable_type::FLAG_OFFSET16) {
79+
auto entryIndices16 = reinterpret_cast<const uint16_t*>(entryIndices);
80+
entryOffset = offset_from16(entryIndices16[mIndex]);
7681
} else {
7782
entryOffset = dtohl(entryIndices[mIndex]);
7883
}

libs/androidfw/include/androidfw/ResourceTypes.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,10 @@ struct ResTable_type
14481448
// Mark any types that use this with a v26 qualifier to prevent runtime issues on older
14491449
// platforms.
14501450
FLAG_SPARSE = 0x01,
1451+
1452+
// If set, the offsets to the entries are encoded in 16-bit, real_offset = offset * 4u
1453+
// An 16-bit offset of 0xffffu means a NO_ENTRY
1454+
FLAG_OFFSET16 = 0x02,
14511455
};
14521456
uint8_t flags;
14531457

@@ -1464,6 +1468,11 @@ struct ResTable_type
14641468
ResTable_config config;
14651469
};
14661470

1471+
// Convert a 16-bit offset to 32-bit if FLAG_OFFSET16 is set
1472+
static inline uint32_t offset_from16(uint16_t off16) {
1473+
return dtohs(off16) == 0xffffu ? ResTable_type::NO_ENTRY : dtohs(off16) * 4u;
1474+
}
1475+
14671476
// The minimum size required to read any version of ResTable_type.
14681477
constexpr size_t kResTableTypeMinSize =
14691478
sizeof(ResTable_type) - sizeof(ResTable_config) + sizeof(ResTable_config::size);

tools/aapt2/format/binary/TableFlattener.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "format/binary/TableFlattener.h"
1818

19+
#include <limits>
1920
#include <sstream>
2021
#include <type_traits>
2122
#include <variant>
@@ -191,6 +192,9 @@ class PackageFlattener {
191192
offsets[flat_entry.entry->id.value()] = res_entry_writer->Write(&flat_entry);
192193
}
193194

195+
// whether the offsets can be represented in 2 bytes
196+
bool short_offsets = (values_buffer.size() / 4u) < std::numeric_limits<uint16_t>::max();
197+
194198
bool sparse_encode = sparse_entries_ == SparseEntriesMode::Enabled ||
195199
sparse_entries_ == SparseEntriesMode::Forced;
196200

@@ -203,8 +207,7 @@ class PackageFlattener {
203207
}
204208

205209
// Only sparse encode if the offsets are representable in 2 bytes.
206-
sparse_encode =
207-
sparse_encode && (values_buffer.size() / 4u) <= std::numeric_limits<uint16_t>::max();
210+
sparse_encode = sparse_encode && short_offsets;
208211

209212
// Only sparse encode if the ratio of populated entries to total entries is below some
210213
// threshold.
@@ -226,12 +229,22 @@ class PackageFlattener {
226229
}
227230
} else {
228231
type_header->entryCount = android::util::HostToDevice32(num_total_entries);
229-
uint32_t* indices = type_writer.NextBlock<uint32_t>(num_total_entries);
230-
for (size_t i = 0; i < num_total_entries; i++) {
231-
indices[i] = android::util::HostToDevice32(offsets[i]);
232+
if (compact_entry && short_offsets) {
233+
// use 16-bit offset only when compact_entry is true
234+
type_header->flags |= ResTable_type::FLAG_OFFSET16;
235+
uint16_t* indices = type_writer.NextBlock<uint16_t>(num_total_entries);
236+
for (size_t i = 0; i < num_total_entries; i++) {
237+
indices[i] = android::util::HostToDevice16(offsets[i] / 4u);
238+
}
239+
} else {
240+
uint32_t* indices = type_writer.NextBlock<uint32_t>(num_total_entries);
241+
for (size_t i = 0; i < num_total_entries; i++) {
242+
indices[i] = android::util::HostToDevice32(offsets[i]);
243+
}
232244
}
233245
}
234246

247+
type_writer.buffer()->Align4();
235248
type_header->entriesStart = android::util::HostToDevice32(type_writer.size());
236249
type_writer.buffer()->AppendBuffer(std::move(values_buffer));
237250
type_writer.Finish();

0 commit comments

Comments
 (0)