Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 1 | #include "idmap.h" |
| 2 | |
| 3 | #include <androidfw/AssetManager.h> |
| 4 | #include <androidfw/ResourceTypes.h> |
| 5 | #include <utils/String8.h> |
| 6 | |
| 7 | #include <fcntl.h> |
| 8 | #include <sys/mman.h> |
| 9 | #include <sys/stat.h> |
| 10 | |
| 11 | using namespace android; |
| 12 | |
| 13 | #define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0) |
| 14 | |
| 15 | namespace { |
| 16 | static const uint32_t IDMAP_MAGIC = 0x706d6469; |
| 17 | static const size_t PATH_LENGTH = 256; |
| 18 | static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t))); |
| 19 | |
| 20 | void printe(const char *fmt, ...); |
| 21 | |
| 22 | class IdmapBuffer { |
| 23 | private: |
| 24 | char *buf_; |
| 25 | size_t len_; |
| 26 | mutable size_t pos_; |
| 27 | public: |
| 28 | IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {} |
| 29 | |
| 30 | ~IdmapBuffer() { |
| 31 | if (buf_ != MAP_FAILED) { |
| 32 | munmap(buf_, len_); |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | int init(const char *idmap_path) |
| 37 | { |
| 38 | struct stat st; |
| 39 | int fd; |
| 40 | |
| 41 | if (stat(idmap_path, &st) < 0) { |
| 42 | printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno)); |
| 43 | return -1; |
| 44 | } |
| 45 | len_ = st.st_size; |
| 46 | if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) { |
| 47 | printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno)); |
| 48 | return -1; |
| 49 | } |
| 50 | if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { |
| 51 | close(fd); |
| 52 | printe("failed to mmap idmap: %s\n", strerror(errno)); |
| 53 | return -1; |
| 54 | } |
| 55 | close(fd); |
| 56 | return 0; |
| 57 | } |
| 58 | |
| 59 | int next(uint32_t *i, uint32_t *offset) const |
| 60 | { |
| 61 | if (!buf_) { |
| 62 | printe("failed to read next uint32_t: buffer not initialized\n"); |
| 63 | return -1; |
| 64 | } |
| 65 | if (pos_ + 4 > len_) { |
| 66 | printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n", |
| 67 | pos_); |
| 68 | return -1; |
| 69 | } |
| 70 | *offset = pos_ / sizeof(uint32_t); |
| 71 | char a = buf_[pos_++]; |
| 72 | char b = buf_[pos_++]; |
| 73 | char c = buf_[pos_++]; |
| 74 | char d = buf_[pos_++]; |
| 75 | *i = (d << 24) | (c << 16) | (b << 8) | a; |
| 76 | return 0; |
| 77 | } |
| 78 | |
| 79 | int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const |
| 80 | { |
| 81 | if (!buf_) { |
| 82 | printe("failed to read next path: buffer not initialized\n"); |
| 83 | return -1; |
| 84 | } |
| 85 | if (pos_ + PATH_LENGTH > len_) { |
| 86 | printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_); |
| 87 | return -1; |
| 88 | } |
| 89 | memcpy(b, buf_ + pos_, PATH_LENGTH); |
| 90 | *offset_start = pos_ / sizeof(uint32_t); |
| 91 | pos_ += PATH_LENGTH; |
| 92 | *offset_end = pos_ / sizeof(uint32_t) - 1; |
| 93 | return 0; |
| 94 | } |
| 95 | }; |
| 96 | |
| 97 | void printe(const char *fmt, ...) |
| 98 | { |
| 99 | va_list ap; |
| 100 | |
| 101 | va_start(ap, fmt); |
| 102 | fprintf(stderr, "error: "); |
| 103 | vfprintf(stderr, fmt, ap); |
| 104 | va_end(ap); |
| 105 | } |
| 106 | |
| 107 | void print_header() |
| 108 | { |
| 109 | printf("SECTION ENTRY VALUE OFFSET COMMENT\n"); |
| 110 | } |
| 111 | |
| 112 | void print(const char *section, const char *subsection, uint32_t value, uint32_t offset, |
| 113 | const char *fmt, ...) |
| 114 | { |
| 115 | va_list ap; |
| 116 | |
| 117 | va_start(ap, fmt); |
| 118 | printf("%-12s %-12s 0x%08x 0x%-4x ", section, subsection, value, offset); |
| 119 | vprintf(fmt, ap); |
| 120 | printf("\n"); |
| 121 | va_end(ap); |
| 122 | } |
| 123 | |
| 124 | void print_path(const char *section, const char *subsection, uint32_t offset_start, |
| 125 | uint32_t offset_end, const char *fmt, ...) |
| 126 | { |
| 127 | va_list ap; |
| 128 | |
| 129 | va_start(ap, fmt); |
| 130 | printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start, |
| 131 | offset_end); |
| 132 | vprintf(fmt, ap); |
| 133 | printf("\n"); |
| 134 | va_end(ap); |
| 135 | } |
| 136 | |
| 137 | int resource_metadata(const AssetManager& am, uint32_t res_id, |
| 138 | String8 *package, String8 *type, String8 *name) |
| 139 | { |
| 140 | const ResTable& rt = am.getResources(); |
| 141 | struct ResTable::resource_name data; |
| 142 | if (!rt.getResourceName(res_id, false, &data)) { |
| 143 | printe("failed to get resource name id=0x%08x\n", res_id); |
| 144 | return -1; |
| 145 | } |
| 146 | if (package) { |
| 147 | *package = String8(String16(data.package, data.packageLen)); |
| 148 | } |
| 149 | if (type) { |
| 150 | *type = String8(String16(data.type, data.typeLen)); |
| 151 | } |
| 152 | if (name) { |
| 153 | *name = String8(String16(data.name, data.nameLen)); |
| 154 | } |
| 155 | return 0; |
| 156 | } |
| 157 | |
| 158 | int package_id(const AssetManager& am) |
| 159 | { |
| 160 | return (am.getResources().getBasePackageId(0)) << 24; |
| 161 | } |
| 162 | |
| 163 | int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am) |
| 164 | { |
| 165 | uint32_t i, o, e; |
| 166 | char path[PATH_LENGTH]; |
| 167 | |
| 168 | NEXT(buf, i, o); |
| 169 | if (i != IDMAP_MAGIC) { |
| 170 | printe("not an idmap file: actual magic constant 0x%08x does not match expected magic " |
| 171 | "constant 0x%08x\n", i, IDMAP_MAGIC); |
| 172 | return -1; |
| 173 | } |
| 174 | print_header(); |
| 175 | print("IDMAP HEADER", "magic", i, o, ""); |
| 176 | |
| 177 | NEXT(buf, i, o); |
| 178 | print("", "base crc", i, o, ""); |
| 179 | |
| 180 | NEXT(buf, i, o); |
| 181 | print("", "overlay crc", i, o, ""); |
| 182 | |
| 183 | if (buf.nextPath(path, &o, &e) < 0) { |
| 184 | // printe done from IdmapBuffer::nextPath |
| 185 | return -1; |
| 186 | } |
| 187 | print_path("", "base path", o, e, "%s", path); |
| 188 | if (!am.addAssetPath(String8(path), NULL)) { |
| 189 | printe("failed to add '%s' as asset path\n", path); |
| 190 | return -1; |
| 191 | } |
| 192 | |
| 193 | if (buf.nextPath(path, &o, &e) < 0) { |
| 194 | // printe done from IdmapBuffer::nextPath |
| 195 | return -1; |
| 196 | } |
| 197 | print_path("", "overlay path", o, e, "%s", path); |
| 198 | |
| 199 | return 0; |
| 200 | } |
| 201 | |
| 202 | int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector<uint32_t>& types) |
| 203 | { |
| 204 | uint32_t i, o; |
| 205 | const uint32_t numeric_package = package_id(am); |
| 206 | |
| 207 | NEXT(buf, i, o); |
| 208 | print("DATA HEADER", "types count", i, o, ""); |
| 209 | const uint32_t N = i; |
| 210 | |
| 211 | for (uint32_t j = 0; j < N; ++j) { |
| 212 | NEXT(buf, i, o); |
| 213 | if (i == 0) { |
| 214 | print("", "padding", i, o, ""); |
| 215 | } else { |
| 216 | String8 type; |
| 217 | const uint32_t numeric_type = (j + 1) << 16; |
| 218 | const uint32_t res_id = numeric_package | numeric_type; |
| 219 | if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) { |
| 220 | // printe done from resource_metadata |
| 221 | return -1; |
| 222 | } |
| 223 | print("", "type offset", i, o, "absolute offset 0x%02x, %s", |
| 224 | i + IDMAP_HEADER_SIZE, type.string()); |
| 225 | types.add(numeric_type); |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | return 0; |
| 230 | } |
| 231 | |
| 232 | int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type) |
| 233 | { |
| 234 | uint32_t i, o, n, id_offset; |
| 235 | const uint32_t numeric_package = package_id(am); |
| 236 | |
| 237 | NEXT(buf, i, o); |
| 238 | print("DATA BLOCK", "entry count", i, o, ""); |
| 239 | n = i; |
| 240 | |
| 241 | NEXT(buf, i, o); |
| 242 | print("", "entry offset", i, o, ""); |
| 243 | id_offset = i; |
| 244 | |
| 245 | for ( ; n > 0; --n) { |
| 246 | String8 type, name; |
| 247 | |
| 248 | NEXT(buf, i, o); |
| 249 | if (i == 0) { |
| 250 | print("", "padding", i, o, ""); |
| 251 | } else { |
| 252 | uint32_t res_id = numeric_package | numeric_type | id_offset; |
| 253 | if (resource_metadata(am, res_id, NULL, &type, &name) < 0) { |
| 254 | // printe done from resource_metadata |
| 255 | return -1; |
| 256 | } |
| 257 | print("", "entry", i, o, "%s/%s", type.string(), name.string()); |
| 258 | } |
| 259 | ++id_offset; |
| 260 | } |
| 261 | |
| 262 | return 0; |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | int idmap_inspect(const char *idmap_path) |
| 267 | { |
| 268 | IdmapBuffer buf; |
| 269 | if (buf.init(idmap_path) < 0) { |
| 270 | // printe done from IdmapBuffer::init |
| 271 | return EXIT_FAILURE; |
| 272 | } |
| 273 | AssetManager am; |
| 274 | if (parse_idmap_header(buf, am) < 0) { |
| 275 | // printe done from parse_idmap_header |
| 276 | return EXIT_FAILURE; |
| 277 | } |
| 278 | Vector<uint32_t> types; |
| 279 | if (parse_data_header(buf, am, types) < 0) { |
| 280 | // printe done from parse_data_header |
| 281 | return EXIT_FAILURE; |
| 282 | } |
| 283 | const size_t N = types.size(); |
| 284 | for (size_t i = 0; i < N; ++i) { |
| 285 | if (parse_data_block(buf, am, types.itemAt(i)) < 0) { |
| 286 | // printe done from parse_data_block |
| 287 | return EXIT_FAILURE; |
| 288 | } |
| 289 | } |
| 290 | return EXIT_SUCCESS; |
| 291 | } |