| #include "idmap.h" |
| |
| #include <androidfw/AssetManager.h> |
| #include <androidfw/ResourceTypes.h> |
| #include <utils/String8.h> |
| |
| #include <fcntl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| |
| using namespace android; |
| |
| namespace { |
| static const uint32_t IDMAP_MAGIC = 0x504D4449; |
| static const size_t PATH_LENGTH = 256; |
| |
| void printe(const char *fmt, ...); |
| |
| class IdmapBuffer { |
| private: |
| const char* buf_; |
| size_t len_; |
| size_t pos_; |
| public: |
| IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {} |
| |
| ~IdmapBuffer() { |
| if (buf_ != MAP_FAILED) { |
| munmap(const_cast<char*>(buf_), len_); |
| } |
| } |
| |
| status_t init(const char *idmap_path) { |
| struct stat st; |
| int fd; |
| |
| if (stat(idmap_path, &st) < 0) { |
| printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno)); |
| return UNKNOWN_ERROR; |
| } |
| len_ = st.st_size; |
| if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) { |
| printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno)); |
| return UNKNOWN_ERROR; |
| } |
| if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { |
| close(fd); |
| printe("failed to mmap idmap: %s\n", strerror(errno)); |
| return UNKNOWN_ERROR; |
| } |
| close(fd); |
| return NO_ERROR; |
| } |
| |
| status_t nextUint32(uint32_t* i) { |
| if (!buf_) { |
| printe("failed to read next uint32_t: buffer not initialized\n"); |
| return UNKNOWN_ERROR; |
| } |
| |
| if (pos_ + sizeof(uint32_t) > len_) { |
| printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n", |
| pos_); |
| return UNKNOWN_ERROR; |
| } |
| |
| if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) { |
| printe("failed to read next uint32_t: not aligned on 4-byte boundary\n"); |
| return UNKNOWN_ERROR; |
| } |
| |
| *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_)); |
| pos_ += sizeof(uint32_t); |
| return NO_ERROR; |
| } |
| |
| status_t nextUint16(uint16_t* i) { |
| if (!buf_) { |
| printe("failed to read next uint16_t: buffer not initialized\n"); |
| return UNKNOWN_ERROR; |
| } |
| |
| if (pos_ + sizeof(uint16_t) > len_) { |
| printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n", |
| pos_); |
| return UNKNOWN_ERROR; |
| } |
| |
| if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) { |
| printe("failed to read next uint32_t: not aligned on 2-byte boundary\n"); |
| return UNKNOWN_ERROR; |
| } |
| |
| *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_)); |
| pos_ += sizeof(uint16_t); |
| return NO_ERROR; |
| } |
| |
| status_t nextPath(char *b) { |
| if (!buf_) { |
| printe("failed to read next path: buffer not initialized\n"); |
| return UNKNOWN_ERROR; |
| } |
| if (pos_ + PATH_LENGTH > len_) { |
| printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_); |
| return UNKNOWN_ERROR; |
| } |
| memcpy(b, buf_ + pos_, PATH_LENGTH); |
| pos_ += PATH_LENGTH; |
| return NO_ERROR; |
| } |
| }; |
| |
| void printe(const char *fmt, ...) { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| fprintf(stderr, "error: "); |
| vfprintf(stderr, fmt, ap); |
| va_end(ap); |
| } |
| |
| void print_header() { |
| printf("SECTION ENTRY VALUE COMMENT\n"); |
| } |
| |
| void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| printf("%-12s %-12s 0x%08x ", section, subsection, value); |
| vprintf(fmt, ap); |
| printf("\n"); |
| va_end(ap); |
| } |
| |
| void print_path(const char *section, const char *subsection, const char *fmt, ...) { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| printf("%-12s %-12s .......... ", section, subsection); |
| vprintf(fmt, ap); |
| printf("\n"); |
| va_end(ap); |
| } |
| |
| status_t resource_metadata(const AssetManager& am, uint32_t res_id, |
| String8 *package, String8 *type, String8 *name) { |
| const ResTable& rt = am.getResources(); |
| struct ResTable::resource_name data; |
| if (!rt.getResourceName(res_id, false, &data)) { |
| printe("failed to get resource name id=0x%08x\n", res_id); |
| return UNKNOWN_ERROR; |
| } |
| if (package) { |
| *package = String8(String16(data.package, data.packageLen)); |
| } |
| if (type) { |
| *type = String8(String16(data.type, data.typeLen)); |
| } |
| if (name) { |
| *name = String8(String16(data.name, data.nameLen)); |
| } |
| return NO_ERROR; |
| } |
| |
| status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) { |
| uint32_t i; |
| char path[PATH_LENGTH]; |
| |
| status_t err = buf.nextUint32(&i); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| |
| if (i != IDMAP_MAGIC) { |
| printe("not an idmap file: actual magic constant 0x%08x does not match expected magic " |
| "constant 0x%08x\n", i, IDMAP_MAGIC); |
| return UNKNOWN_ERROR; |
| } |
| |
| print_header(); |
| print("IDMAP HEADER", "magic", i, ""); |
| |
| err = buf.nextUint32(&i); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| print("", "version", i, ""); |
| |
| err = buf.nextUint32(&i); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| print("", "base crc", i, ""); |
| |
| err = buf.nextUint32(&i); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| print("", "overlay crc", i, ""); |
| |
| err = buf.nextPath(path); |
| if (err != NO_ERROR) { |
| // printe done from IdmapBuffer::nextPath |
| return err; |
| } |
| print_path("", "base path", "%s", path); |
| |
| if (!am.addAssetPath(String8(path), NULL)) { |
| printe("failed to add '%s' as asset path\n", path); |
| return UNKNOWN_ERROR; |
| } |
| |
| err = buf.nextPath(path); |
| if (err != NO_ERROR) { |
| // printe done from IdmapBuffer::nextPath |
| return err; |
| } |
| print_path("", "overlay path", "%s", path); |
| |
| return NO_ERROR; |
| } |
| |
| status_t parse_data(IdmapBuffer& buf, const AssetManager& am) { |
| const uint32_t packageId = am.getResources().getBasePackageId(0); |
| |
| uint16_t data16; |
| status_t err = buf.nextUint16(&data16); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), ""); |
| |
| err = buf.nextUint16(&data16); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| print("", "types count", static_cast<uint32_t>(data16), ""); |
| |
| uint32_t typeCount = static_cast<uint32_t>(data16); |
| while (typeCount > 0) { |
| typeCount--; |
| |
| err = buf.nextUint16(&data16); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| const uint32_t targetTypeId = static_cast<uint32_t>(data16); |
| print("DATA BLOCK", "target type", targetTypeId, ""); |
| |
| err = buf.nextUint16(&data16); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| print("", "overlay type", static_cast<uint32_t>(data16), ""); |
| |
| err = buf.nextUint16(&data16); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| const uint32_t entryCount = static_cast<uint32_t>(data16); |
| print("", "entry count", entryCount, ""); |
| |
| err = buf.nextUint16(&data16); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| const uint32_t entryOffset = static_cast<uint32_t>(data16); |
| print("", "entry offset", entryOffset, ""); |
| |
| for (uint32_t i = 0; i < entryCount; i++) { |
| uint32_t data32; |
| err = buf.nextUint32(&data32); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| |
| uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i); |
| String8 type; |
| String8 name; |
| err = resource_metadata(am, resID, NULL, &type, &name); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| print("", "entry", data32, "%s/%s", type.string(), name.string()); |
| } |
| } |
| |
| return NO_ERROR; |
| } |
| } |
| |
| int idmap_inspect(const char *idmap_path) { |
| IdmapBuffer buf; |
| if (buf.init(idmap_path) < 0) { |
| // printe done from IdmapBuffer::init |
| return EXIT_FAILURE; |
| } |
| AssetManager am; |
| if (parse_idmap_header(buf, am) != NO_ERROR) { |
| // printe done from parse_idmap_header |
| return EXIT_FAILURE; |
| } |
| if (parse_data(buf, am) != NO_ERROR) { |
| // printe done from parse_data_header |
| return EXIT_FAILURE; |
| } |
| return EXIT_SUCCESS; |
| } |