diff options
Diffstat (limited to 'utils/nwztools/database/nvp/nvptool.cpp')
-rw-r--r-- | utils/nwztools/database/nvp/nvptool.cpp | 754 |
1 files changed, 754 insertions, 0 deletions
diff --git a/utils/nwztools/database/nvp/nvptool.cpp b/utils/nwztools/database/nvp/nvptool.cpp new file mode 100644 index 0000000000..1371e93b87 --- /dev/null +++ b/utils/nwztools/database/nvp/nvptool.cpp | |||
@@ -0,0 +1,754 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <cstdio> | ||
23 | #include <stdint.h> | ||
24 | #include <cstdlib> | ||
25 | #include <cstring> | ||
26 | #include <getopt.h> | ||
27 | #include <cstdarg> | ||
28 | #include <string> | ||
29 | #include <fstream> | ||
30 | #include <elf.h> | ||
31 | |||
32 | bool g_verbose = false; | ||
33 | bool g_unsafe = false; | ||
34 | |||
35 | uint8_t *read_file(const std::string& path, size_t& size) | ||
36 | { | ||
37 | std::ifstream fin(path.c_str(), std::ios::binary); | ||
38 | if(!fin) | ||
39 | { | ||
40 | printf("Error: cannot open '%s'\n", path.c_str()); | ||
41 | return 0; | ||
42 | } | ||
43 | fin.seekg(0, std::ios::end); | ||
44 | size = fin.tellg(); | ||
45 | fin.seekg(0, std::ios::beg); | ||
46 | uint8_t *buf = new uint8_t[size]; | ||
47 | fin.read((char *)buf, size); | ||
48 | return buf; | ||
49 | } | ||
50 | |||
51 | bool write_file(const std::string& path, uint8_t *buf, size_t size) | ||
52 | { | ||
53 | std::ofstream fout(path.c_str(), std::ios::binary); | ||
54 | if(!fout) | ||
55 | { | ||
56 | printf("Error: cannot open '%s'\n", path.c_str()); | ||
57 | return false; | ||
58 | } | ||
59 | fout.write((char *)buf, size); | ||
60 | fout.close(); | ||
61 | return true; | ||
62 | } | ||
63 | |||
64 | /* ELF code */ | ||
65 | uint8_t *g_elf_buf; | ||
66 | size_t g_elf_size; | ||
67 | Elf32_Shdr *g_elf_symtab; | ||
68 | Elf32_Shdr *g_elf_symtab_strtab; | ||
69 | Elf32_Shdr *g_elf_shstrtab; | ||
70 | |||
71 | Elf32_Ehdr *elf_ehdr() | ||
72 | { | ||
73 | return (Elf32_Ehdr *)g_elf_buf; | ||
74 | } | ||
75 | |||
76 | #define NTH_SHDR_OFF(n) \ | ||
77 | (elf_ehdr()->e_shoff + elf_ehdr()->e_shentsize * (n)) | ||
78 | |||
79 | Elf32_Shdr *elf_shdr(size_t index) | ||
80 | { | ||
81 | if(index >= elf_ehdr()->e_shnum) | ||
82 | { | ||
83 | printf("Warning: section index is out of bounds\n"); | ||
84 | return nullptr; | ||
85 | } | ||
86 | return (Elf32_Shdr *)(g_elf_buf + NTH_SHDR_OFF(index)); | ||
87 | } | ||
88 | |||
89 | size_t elf_shnum() | ||
90 | { | ||
91 | return elf_ehdr()->e_shnum; | ||
92 | } | ||
93 | |||
94 | const char *elf_get_str(Elf32_Shdr *strtab, Elf32_Word index) | ||
95 | { | ||
96 | /* sanity checks */ | ||
97 | if(strtab->sh_type != SHT_STRTAB) | ||
98 | { | ||
99 | printf("Warning: string access to a non-string-table section\n"); | ||
100 | return nullptr; | ||
101 | } | ||
102 | if(strtab->sh_offset + strtab->sh_size > g_elf_size) | ||
103 | { | ||
104 | printf("Warning: string table section does not fit in the file\n"); | ||
105 | return nullptr; | ||
106 | } | ||
107 | if(index >= strtab->sh_size) | ||
108 | { | ||
109 | printf("Warning: string access to string table is out of bounds\n"); | ||
110 | return nullptr; | ||
111 | } | ||
112 | char *buf = (char *)(g_elf_buf + strtab->sh_offset); | ||
113 | if(buf[strtab->sh_size - 1] != 0) | ||
114 | { | ||
115 | printf("Warning: string table is not zero terminated\n"); | ||
116 | return nullptr; | ||
117 | } | ||
118 | return buf + index; | ||
119 | } | ||
120 | |||
121 | const char *elf_get_section_name(size_t index) | ||
122 | { | ||
123 | Elf32_Shdr *shdr = elf_shdr(index); | ||
124 | return shdr ? elf_get_str(g_elf_shstrtab, shdr->sh_name) : nullptr; | ||
125 | } | ||
126 | |||
127 | const char *elf_get_symbol_name(Elf32_Sym *sym) | ||
128 | { | ||
129 | if(ELF32_ST_TYPE(sym->st_info) == STT_SECTION) | ||
130 | return elf_get_section_name(sym->st_shndx); | ||
131 | else | ||
132 | return elf_get_str(g_elf_symtab_strtab, sym->st_name); | ||
133 | } | ||
134 | |||
135 | Elf32_Sym *elf_get_symbol_by_name(const char *name) | ||
136 | { | ||
137 | Elf32_Sym *sym = (Elf32_Sym *)(g_elf_buf + g_elf_symtab->sh_offset); | ||
138 | size_t nr_syms = g_elf_symtab->sh_size / sizeof(Elf32_Sym); | ||
139 | for(size_t i = 0; i < nr_syms; i++) | ||
140 | { | ||
141 | const char *s = elf_get_symbol_name(&sym[i]); | ||
142 | if(s != nullptr && strcmp(name, s) == 0) | ||
143 | return &sym[i]; | ||
144 | } | ||
145 | return nullptr; | ||
146 | } | ||
147 | |||
148 | Elf32_Sym *elf_get_symbol_by_address(size_t shndx, Elf32_Word address) | ||
149 | { | ||
150 | Elf32_Sym *sym = (Elf32_Sym *)(g_elf_buf + g_elf_symtab->sh_offset); | ||
151 | size_t nr_syms = g_elf_symtab->sh_size / sizeof(Elf32_Sym); | ||
152 | for(size_t i = 0; i < nr_syms; i++) | ||
153 | { | ||
154 | if(sym[i].st_shndx == shndx && sym[i].st_value == address) | ||
155 | return &sym[i]; | ||
156 | } | ||
157 | return nullptr; | ||
158 | } | ||
159 | |||
160 | Elf32_Sym *elf_get_symbol_by_index(size_t index) | ||
161 | { | ||
162 | Elf32_Sym *sym = (Elf32_Sym *)(g_elf_buf + g_elf_symtab->sh_offset); | ||
163 | size_t nr_syms = g_elf_symtab->sh_size / sizeof(Elf32_Sym); | ||
164 | if(index >= nr_syms) | ||
165 | return nullptr; | ||
166 | return &sym[index]; | ||
167 | } | ||
168 | |||
169 | void *elf_get_section_ptr(size_t shndx, Elf32_Word address, size_t size) | ||
170 | { | ||
171 | Elf32_Shdr *shdr = elf_shdr(shndx); | ||
172 | if(shdr == nullptr) | ||
173 | return nullptr; | ||
174 | if(address + size > shdr->sh_size) | ||
175 | return nullptr; | ||
176 | if(shdr->sh_offset + shdr->sh_size > g_elf_size) | ||
177 | return nullptr; | ||
178 | return g_elf_buf + shdr->sh_offset + address; | ||
179 | } | ||
180 | |||
181 | /* make sure the string has a final zero in the section, optionally check characters | ||
182 | * are printable */ | ||
183 | const char *elf_get_string_ptr_safe(size_t shndx, Elf32_Word offset, bool want_print = true) | ||
184 | { | ||
185 | Elf32_Shdr *shdr = elf_shdr(shndx); | ||
186 | if(shdr == nullptr) | ||
187 | return nullptr; | ||
188 | /* address must be in the section */ | ||
189 | if(offset >= shdr->sh_size) | ||
190 | return nullptr; | ||
191 | /* determine maximum size */ | ||
192 | size_t max_sz = shdr->sh_size - offset; | ||
193 | const char *ptr = (const char *)(g_elf_buf + shdr->sh_offset + offset); | ||
194 | for(size_t i = 0; i < max_sz; i++) | ||
195 | { | ||
196 | if(ptr[i] == 0) /* found final 0, everything is fine */ | ||
197 | return ptr; | ||
198 | if(want_print && !isprint(ptr[i])) | ||
199 | return nullptr; | ||
200 | } | ||
201 | return nullptr; | ||
202 | } | ||
203 | |||
204 | size_t elf_find_reloc_section(size_t shndx) | ||
205 | { | ||
206 | /* find the relocation section */ | ||
207 | for(size_t i = 0; i < elf_ehdr()->e_shnum; i++) | ||
208 | { | ||
209 | Elf32_Shdr *shdr = elf_shdr(i); | ||
210 | if(shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) | ||
211 | continue; | ||
212 | if(shdr->sh_info != shndx) | ||
213 | continue; | ||
214 | return i; | ||
215 | } | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | void *elf_get_symbol_ptr(Elf32_Sym *sym, size_t size) | ||
220 | { | ||
221 | /* NOTE: also works for STT_SECTION since offset will be 0 */ | ||
222 | return elf_get_section_ptr(sym->st_shndx, sym->st_value, size); | ||
223 | } | ||
224 | |||
225 | /* take the position of a 32-bit address in the section and apply relocation if | ||
226 | * any */ | ||
227 | void *elf_reloc_addr32(size_t shndx, Elf32_Word offset) | ||
228 | { | ||
229 | /* read value */ | ||
230 | uint32_t *val = (uint32_t *)elf_get_section_ptr(shndx, offset, 4); | ||
231 | if(val == nullptr) | ||
232 | return 0; /* invalid */ | ||
233 | /* find reloc section if any */ | ||
234 | size_t relshndx = elf_find_reloc_section(shndx); | ||
235 | if(relshndx == 0) | ||
236 | return g_elf_buf + *val; /* no relocation applies */ | ||
237 | Elf32_Shdr *shdr = elf_shdr(relshndx); | ||
238 | /* find relocation that applies */ | ||
239 | if(shdr->sh_type == SHT_RELA) | ||
240 | { | ||
241 | printf("Warning: unsupported RELA relocation type\n"); | ||
242 | return 0; | ||
243 | } | ||
244 | Elf32_Rel *rel = (Elf32_Rel *)elf_get_section_ptr(relshndx, 0, shdr->sh_size); | ||
245 | if(rel == nullptr) | ||
246 | { | ||
247 | printf("Warning: invalid relocation section\n"); | ||
248 | return 0; | ||
249 | } | ||
250 | size_t sym_count = shdr->sh_size / sizeof(Elf32_Rel); | ||
251 | for(size_t i = 0; i < sym_count; i++) | ||
252 | { | ||
253 | /* for relocatable files, r_offset is the offset in the section */ | ||
254 | if(rel[i].r_offset != offset) | ||
255 | continue; | ||
256 | /* find symbol, ignore shdr->sh_link and assume it is g_elf_symtab | ||
257 | * since the file should have only one symbol table anyway */ | ||
258 | Elf32_Sym *sym = elf_get_symbol_by_index(ELF32_R_SYM(rel[i].r_info)); | ||
259 | /* found it! */ | ||
260 | if(g_verbose) | ||
261 | { | ||
262 | printf("[section %zu (%s) offset %#x reloc val %#x type %d sym %d (%s)]\n", | ||
263 | shndx, elf_get_section_name(shndx), offset, *val, | ||
264 | ELF32_R_TYPE(rel[i].r_info), ELF32_R_SYM(rel[i].r_info), | ||
265 | sym ? elf_get_symbol_name(sym) : "<undef>"); | ||
266 | } | ||
267 | /* apply reloc */ | ||
268 | if(ELF32_R_TYPE(rel[i].r_info) == R_ARM_ABS32) | ||
269 | { | ||
270 | if(sym == nullptr) | ||
271 | { | ||
272 | printf("Warning: R_ARM_ABS32 reloc with invalid symbol reference\n"); | ||
273 | return 0; | ||
274 | } | ||
275 | return *val + (uint8_t *)elf_get_symbol_ptr(sym, 0); | ||
276 | } | ||
277 | else | ||
278 | { | ||
279 | printf("Warning: unsupported relocation type %d\n", ELF32_R_TYPE(rel[i].r_info)); | ||
280 | return 0; | ||
281 | } | ||
282 | } | ||
283 | /* no reloc applies */ | ||
284 | if(g_verbose) | ||
285 | { | ||
286 | printf("[section %zu (%s) offset %#x no reloc found]\n", shndx, | ||
287 | elf_get_section_name(shndx), offset); | ||
288 | } | ||
289 | return g_elf_buf + *val; /* no relocation applies */ | ||
290 | } | ||
291 | |||
292 | size_t elf_map_virt_addr(uint32_t address, Elf32_Word& out_off) | ||
293 | { | ||
294 | /* for relocatable file, this is trivial */ | ||
295 | for(size_t i = 0; i < elf_ehdr()->e_shnum; i++) | ||
296 | { | ||
297 | Elf32_Shdr *shdr = elf_shdr(i); | ||
298 | if(shdr->sh_offset <= address && address < shdr->sh_offset + shdr->sh_size) | ||
299 | { | ||
300 | out_off = address - shdr->sh_offset; | ||
301 | if(g_verbose) | ||
302 | { | ||
303 | printf("[map %#x to section %zi (%s) at %#x]\n", address, i, | ||
304 | elf_get_section_name(i), out_off); | ||
305 | } | ||
306 | return i; | ||
307 | } | ||
308 | } | ||
309 | return 0; /* section 0 is always invalid */ | ||
310 | } | ||
311 | |||
312 | size_t elf_map_ptr(void *ptr, Elf32_Word& out_off) | ||
313 | { | ||
314 | uint32_t addr = (uint32_t)((uint8_t *)ptr - g_elf_buf); | ||
315 | return elf_map_virt_addr(addr, out_off); | ||
316 | } | ||
317 | |||
318 | /* same as elf_reloc_addr32 but find section automatically from pointer */ | ||
319 | void *elf_reloc_addr32_ptr(uint32_t *val) | ||
320 | { | ||
321 | Elf32_Word off; | ||
322 | size_t sec = elf_map_ptr((void *)val, off); | ||
323 | /* if it does not belong to any section, don't do anything */ | ||
324 | if(sec == 0) | ||
325 | { | ||
326 | printf("Warning: reloc addr pointer not in any section\n"); | ||
327 | return g_elf_buf + *val; | ||
328 | } | ||
329 | return elf_reloc_addr32(sec, off); | ||
330 | } | ||
331 | |||
332 | Elf32_Sym *elf_get_symbol_by_ptr(void *ptr) | ||
333 | { | ||
334 | Elf32_Word off; | ||
335 | size_t sec = elf_map_ptr(ptr, off); | ||
336 | return sec ? elf_get_symbol_by_address(sec, off) : nullptr; | ||
337 | } | ||
338 | |||
339 | /* check if a string is safe */ | ||
340 | bool elf_is_str_ptr_safe(const char *str) | ||
341 | { | ||
342 | Elf32_Word name_off; | ||
343 | /* find the section it belongs to */ | ||
344 | size_t name_shndx = elf_map_ptr((void *)str, name_off); | ||
345 | if(name_shndx == 0) | ||
346 | return false; | ||
347 | /* check the string fit in the section */ | ||
348 | return elf_get_string_ptr_safe(name_shndx, name_off) != nullptr; | ||
349 | } | ||
350 | |||
351 | bool elf_is_ptr_safe(void *ptr, size_t sz) | ||
352 | { | ||
353 | Elf32_Word ptr_off; | ||
354 | /* find the section it belongs to */ | ||
355 | size_t ptr_shndx = elf_map_ptr((void *)ptr, ptr_off); | ||
356 | if(ptr_shndx == 0) | ||
357 | return false; | ||
358 | /* check the string fit in the section */ | ||
359 | return elf_get_section_ptr(ptr_shndx, ptr_off, sz) != nullptr; | ||
360 | } | ||
361 | |||
362 | bool elf_init() | ||
363 | { | ||
364 | if(g_elf_size < sizeof(Elf32_Ehdr)) | ||
365 | { | ||
366 | printf("Invalid ELF file: too small\n"); | ||
367 | return false; | ||
368 | } | ||
369 | Elf32_Ehdr *ehdr = elf_ehdr(); | ||
370 | if(ehdr->e_ident[EI_MAG0] != ELFMAG0 || | ||
371 | ehdr->e_ident[EI_MAG1] != ELFMAG1 || | ||
372 | ehdr->e_ident[EI_MAG2] != ELFMAG2 || | ||
373 | ehdr->e_ident[EI_MAG3] != ELFMAG3) | ||
374 | { | ||
375 | printf("Invalid ELF file: invalid ident\n"); | ||
376 | return false; | ||
377 | } | ||
378 | /* we only support relocatable files */ | ||
379 | if(ehdr->e_type != ET_REL) | ||
380 | { | ||
381 | printf("Unsupported ELF file: this is not a relocatable file\n"); | ||
382 | return false; | ||
383 | } | ||
384 | if(ehdr->e_ident[EI_CLASS] != ELFCLASS32 || ehdr->e_machine != EM_ARM) | ||
385 | { | ||
386 | printf("Unsupported ELF file: this is not a 32-bit ARM ELF file\n"); | ||
387 | return false; | ||
388 | } | ||
389 | /* go through sections */ | ||
390 | if(ehdr->e_shoff == 0) | ||
391 | { | ||
392 | printf("Invalid ELF file: no sections\n"); | ||
393 | return false; | ||
394 | } | ||
395 | if(ehdr->e_shentsize < sizeof(Elf32_Shdr)) | ||
396 | { | ||
397 | printf("Invalid ELF file: section entry size too small\n"); | ||
398 | return false; | ||
399 | } | ||
400 | if(NTH_SHDR_OFF(ehdr->e_shnum) > g_elf_size) | ||
401 | { | ||
402 | printf("Invalid ELF file: sections header does not fit in the file\n"); | ||
403 | return false; | ||
404 | } | ||
405 | for(size_t i = 0; i < ehdr->e_shnum; i++) | ||
406 | { | ||
407 | Elf32_Shdr *shdr = (Elf32_Shdr *)(g_elf_buf + NTH_SHDR_OFF(i)); | ||
408 | if(shdr->sh_type == SHT_SYMTAB) | ||
409 | g_elf_symtab = shdr; | ||
410 | } | ||
411 | /* handle symbol table */ | ||
412 | if(g_elf_symtab) | ||
413 | { | ||
414 | if(g_elf_symtab->sh_offset + g_elf_symtab->sh_size > g_elf_size) | ||
415 | { | ||
416 | printf("Invalid ELF file: symtab does not file in the file\n"); | ||
417 | return false; | ||
418 | } | ||
419 | g_elf_symtab_strtab = elf_shdr(g_elf_symtab->sh_link); | ||
420 | if(g_elf_symtab_strtab == nullptr) | ||
421 | { | ||
422 | printf("Invalid ELF file: symtab's strtab is not valid\n"); | ||
423 | } | ||
424 | if(g_elf_symtab_strtab->sh_type != SHT_STRTAB) | ||
425 | { | ||
426 | printf("Invalid ELF file: symtab's strtab is not a string table\n"); | ||
427 | return false; | ||
428 | } | ||
429 | } | ||
430 | /* handle section string table */ | ||
431 | if(ehdr->e_shstrndx != SHN_UNDEF) | ||
432 | { | ||
433 | g_elf_shstrtab = elf_shdr(ehdr->e_shstrndx); | ||
434 | if(g_elf_shstrtab == nullptr) | ||
435 | { | ||
436 | printf("Invalid ELF file: section string table is invalid\n"); | ||
437 | return false; | ||
438 | } | ||
439 | } | ||
440 | |||
441 | return true; | ||
442 | } | ||
443 | |||
444 | /* main code */ | ||
445 | |||
446 | void usage() | ||
447 | { | ||
448 | printf("usage: nvptool [options] inputs...\n"); | ||
449 | printf("options:\n"); | ||
450 | printf(" -h/--help Display help\n"); | ||
451 | printf(" -x/--extract Extract nvp map from icx_nvp_emmc.ko\n"); | ||
452 | printf(" -o/--output Set output file\n"); | ||
453 | printf(" -v/--verbose Enable debug output\n"); | ||
454 | printf(" -u/--unsafe Perform potentially unsafe operations\n"); | ||
455 | exit(1); | ||
456 | } | ||
457 | |||
458 | struct zone_info_v1_t | ||
459 | { | ||
460 | uint32_t node; | ||
461 | uint32_t start; | ||
462 | uint32_t count; | ||
463 | uint32_t size; | ||
464 | uint32_t semaphore[4]; /* a 16-byte structure, useless for us */ | ||
465 | uint32_t name; /* pointer to string */ | ||
466 | } __attribute__((packed)); | ||
467 | |||
468 | struct zone_info_v2_t | ||
469 | { | ||
470 | uint32_t node; | ||
471 | uint32_t start; | ||
472 | uint32_t count; | ||
473 | uint32_t size; | ||
474 | uint32_t semaphore[3]; /* a 12-byte structure, useless for us */ | ||
475 | uint32_t name; /* pointer to string */ | ||
476 | } __attribute__((packed)); | ||
477 | |||
478 | struct area_info_v1_t | ||
479 | { | ||
480 | uint32_t type; /* 1 = large, 2 = small */ | ||
481 | uint32_t zoneinfo; /* pointer to zone_info_t[] */ | ||
482 | uint32_t zonecount; | ||
483 | uint32_t semaphore[4]; /* a 16-byte structure, useless for us */ | ||
484 | uint32_t name; /* pointer to string */ | ||
485 | } __attribute__((packed)); | ||
486 | |||
487 | struct area_info_v2_t | ||
488 | { | ||
489 | uint32_t type; /* 1 = large, 2 = small */ | ||
490 | uint32_t zoneinfo; /* pointer to zone_info_t[] */ | ||
491 | uint32_t zonecount; | ||
492 | uint32_t semaphore[3]; /* a 16-byte structure, useless for us */ | ||
493 | uint32_t name; /* pointer to string */ | ||
494 | } __attribute__((packed)); | ||
495 | |||
496 | int guess_version(void *area_info_ptr) | ||
497 | { | ||
498 | /* the "semaphore" part is always filled with zeroes, so simply check if there | ||
499 | * are 3 or 4 of them */ | ||
500 | area_info_v1_t *ai_v1 = (area_info_v1_t *)area_info_ptr; | ||
501 | if(ai_v1->semaphore[3] == 0) | ||
502 | return 1; /* v1: semaphore has 4 fields */ | ||
503 | else | ||
504 | return 2; /* v2: semaphore has 3 fields */ | ||
505 | } | ||
506 | |||
507 | int do_extract(const char *output, int argc, char **argv) | ||
508 | { | ||
509 | if(argc != 1) | ||
510 | { | ||
511 | printf("You need to specify exactly one input file to extract from.\n"); | ||
512 | return 3; | ||
513 | } | ||
514 | FILE *fout = NULL; | ||
515 | if(output) | ||
516 | { | ||
517 | fout = fopen(output, "w"); | ||
518 | if(fout == NULL) | ||
519 | { | ||
520 | printf("Cannot open output file '%s'\n", output); | ||
521 | return 4; | ||
522 | } | ||
523 | } | ||
524 | /* read elf file */ | ||
525 | g_elf_buf = read_file(argv[0], g_elf_size); | ||
526 | if(g_elf_buf == nullptr) | ||
527 | { | ||
528 | printf("Cannot open input file '%s'\n", argv[0]); | ||
529 | return 1; | ||
530 | } | ||
531 | if(!elf_init()) | ||
532 | { | ||
533 | printf("This is not a valid ELF file\n"); | ||
534 | return 1; | ||
535 | } | ||
536 | if(g_elf_symtab == nullptr) | ||
537 | { | ||
538 | printf("This ELF file does not have a symbol table\n"); | ||
539 | return 1; | ||
540 | } | ||
541 | /* look for symbol 'AreaInfo' */ | ||
542 | Elf32_Sym *sym_AreaInfo = elf_get_symbol_by_name("AreaInfo"); | ||
543 | if(sym_AreaInfo == nullptr) | ||
544 | { | ||
545 | printf("Cannot find symbol 'AreaInfo'\n"); | ||
546 | return 1; | ||
547 | } | ||
548 | printf("AreaInfo:\n"); | ||
549 | if(g_verbose) | ||
550 | { | ||
551 | printf("[%u bytes at address %#x in section %u (%s)]\n", | ||
552 | (unsigned)sym_AreaInfo->st_size, (unsigned)sym_AreaInfo->st_value, | ||
553 | (unsigned)sym_AreaInfo->st_shndx, elf_get_section_name(sym_AreaInfo->st_shndx)); | ||
554 | } | ||
555 | /* guess version */ | ||
556 | int ver = guess_version(elf_get_symbol_ptr(sym_AreaInfo, sizeof(area_info_v1_t))); | ||
557 | if(g_verbose) | ||
558 | printf("[guessed version: %d]\n", ver); | ||
559 | size_t sizeof_area_info = (ver == 1) ? sizeof(area_info_v1_t) : sizeof(area_info_v2_t); | ||
560 | size_t sizeof_zone_info = (ver == 1) ? sizeof(zone_info_v1_t) : sizeof(zone_info_v2_t); | ||
561 | /* sanity check AreaInfo */ | ||
562 | size_t area_count = sym_AreaInfo->st_size / sizeof_area_info; | ||
563 | if(!g_unsafe && (sym_AreaInfo->st_size % sizeof_area_info) != 0) | ||
564 | { | ||
565 | printf("AreaInfo size (%u) is a not a multiple of area_info_t size (%zu).\n", | ||
566 | (unsigned)sym_AreaInfo->st_size, sizeof_area_info); | ||
567 | printf("Use unsafe option to override this check\n"); | ||
568 | return 1; | ||
569 | } | ||
570 | area_info_v1_t *AreaInfo_v1 = (area_info_v1_t *)elf_get_symbol_ptr(sym_AreaInfo, | ||
571 | sym_AreaInfo->st_size); | ||
572 | area_info_v2_t *AreaInfo_v2 = (area_info_v2_t *)AreaInfo_v1; | ||
573 | if(AreaInfo_v1 == nullptr) | ||
574 | { | ||
575 | printf("Symbol does not point to a valid address\n"); | ||
576 | return 1; | ||
577 | } | ||
578 | for(size_t i = 0; i < area_count; i++) | ||
579 | { | ||
580 | uint32_t type; | ||
581 | uint32_t *zoneinfo_ptr; | ||
582 | uint32_t zonecount; | ||
583 | uint32_t *name_ptr; | ||
584 | |||
585 | if(ver == 1) | ||
586 | { | ||
587 | type = AreaInfo_v1[i].type; | ||
588 | zoneinfo_ptr = &AreaInfo_v1[i].zoneinfo; | ||
589 | zonecount = AreaInfo_v1[i].zonecount; | ||
590 | name_ptr = &AreaInfo_v1[i].name; | ||
591 | } | ||
592 | else | ||
593 | { | ||
594 | type = AreaInfo_v2[i].type; | ||
595 | zoneinfo_ptr = &AreaInfo_v2[i].zoneinfo; | ||
596 | zonecount = AreaInfo_v2[i].zonecount; | ||
597 | name_ptr = &AreaInfo_v2[i].name; | ||
598 | } | ||
599 | |||
600 | if(g_verbose) | ||
601 | { | ||
602 | printf(" [type=%u info=%#x count=%u name=%#x]\n", type, *zoneinfo_ptr, | ||
603 | zonecount, *name_ptr); | ||
604 | } | ||
605 | /* translate name address */ | ||
606 | const char *name = (const char *)elf_reloc_addr32_ptr(name_ptr); | ||
607 | if(name == nullptr || !elf_is_str_ptr_safe(name)) | ||
608 | { | ||
609 | printf(" Entry name is not a string\n"); | ||
610 | continue; | ||
611 | } | ||
612 | /* skip reserved entries */ | ||
613 | if(*zoneinfo_ptr == 0) | ||
614 | { | ||
615 | printf(" %s\n", name); | ||
616 | continue; | ||
617 | } | ||
618 | /* relocate the zoneinfo pointer */ | ||
619 | void *Zone = elf_reloc_addr32_ptr(zoneinfo_ptr);; | ||
620 | if(Zone == nullptr) | ||
621 | { | ||
622 | printf(" %s\n", name); | ||
623 | printf(" Zone info pointer is not valid\n"); | ||
624 | continue; | ||
625 | } | ||
626 | /* in safe mode, make sure the zone info pointer is a symbol */ | ||
627 | Elf32_Sym *zoneinfo_sym = elf_get_symbol_by_ptr((void *)Zone); | ||
628 | const char *zoneinfo_sym_name = "<no symbol>"; | ||
629 | if(zoneinfo_sym) | ||
630 | zoneinfo_sym_name = elf_get_symbol_name(zoneinfo_sym); | ||
631 | printf(" %s (%s)\n", name, zoneinfo_sym_name); | ||
632 | if(!g_unsafe && !zoneinfo_sym) | ||
633 | { | ||
634 | printf(" Zone info pointer does not correspond to any symbol.\n"); | ||
635 | printf(" Use unsafe option to override this check\n"); | ||
636 | continue; | ||
637 | } | ||
638 | /* if we have the symbol, make sure the claimed size match */ | ||
639 | if(!g_unsafe && zoneinfo_sym) | ||
640 | { | ||
641 | if(zoneinfo_sym->st_size != sizeof_zone_info * zonecount) | ||
642 | { | ||
643 | printf(" Zone info symbol size (%u) does not match expected size (%zu)\n", | ||
644 | (unsigned)zoneinfo_sym->st_size, sizeof_zone_info * zonecount); | ||
645 | printf(" Use unsafe option to override this check\n"); | ||
646 | continue; | ||
647 | } | ||
648 | } | ||
649 | /* sanity check */ | ||
650 | if(!elf_is_ptr_safe((void *)Zone, sizeof_zone_info * zonecount)) | ||
651 | { | ||
652 | printf(" Zone info pointer is not valid\n"); | ||
653 | continue; | ||
654 | } | ||
655 | /* read zone */ | ||
656 | zone_info_v1_t *Zone_v1 = (zone_info_v1_t *)Zone; | ||
657 | zone_info_v2_t *Zone_v2 = (zone_info_v2_t *)Zone; | ||
658 | for(size_t j = 0; j < zonecount; j++) | ||
659 | { | ||
660 | uint32_t node, start, count, size; | ||
661 | uint32_t *name_ptr; | ||
662 | |||
663 | if(ver == 1) | ||
664 | { | ||
665 | node = Zone_v1[j].node; | ||
666 | start = Zone_v1[j].start; | ||
667 | count = Zone_v1[j].count; | ||
668 | size = Zone_v1[j].size; | ||
669 | name_ptr = &Zone_v1[j].name; | ||
670 | } | ||
671 | else | ||
672 | { | ||
673 | node = Zone_v2[j].node; | ||
674 | start = Zone_v2[j].start; | ||
675 | count = Zone_v2[j].count; | ||
676 | size = Zone_v2[j].size; | ||
677 | name_ptr = &Zone_v2[j].name; | ||
678 | } | ||
679 | |||
680 | if(g_verbose) | ||
681 | { | ||
682 | printf(" [node=%u start=%#x count=%u size=%u name=%#x]\n", | ||
683 | node, start, count, size, *name_ptr); | ||
684 | } | ||
685 | /* translate name address */ | ||
686 | const char *name = (const char *)elf_reloc_addr32_ptr(name_ptr); | ||
687 | if(name == nullptr || !elf_is_str_ptr_safe(name)) | ||
688 | { | ||
689 | printf(" Entry name is not a string\n"); | ||
690 | continue; | ||
691 | } | ||
692 | printf(" %s: node %03u, size %u\n", name, node, size); | ||
693 | if(fout) | ||
694 | fprintf(fout, "%u,%u,%s\n", node, size, name); | ||
695 | } | ||
696 | } | ||
697 | if(fout) | ||
698 | fclose(fout); | ||
699 | /* success */ | ||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | int main(int argc, char **argv) | ||
704 | { | ||
705 | const char *output = NULL; | ||
706 | bool extract = false; | ||
707 | |||
708 | if(argc <= 1) | ||
709 | usage(); | ||
710 | |||
711 | while(1) | ||
712 | { | ||
713 | static struct option long_options[] = | ||
714 | { | ||
715 | {"help", no_argument, 0, 'h'}, | ||
716 | {"extract", no_argument, 0, 'x'}, | ||
717 | {"output", required_argument, 0, 'o'}, | ||
718 | {"verbose", no_argument, 0, 'v'}, | ||
719 | {"unsafe", no_argument, 0, 'u'}, | ||
720 | {0, 0, 0, 0} | ||
721 | }; | ||
722 | |||
723 | int c = getopt_long(argc, argv, "hxo:vu", long_options, NULL); | ||
724 | if(c == -1) | ||
725 | break; | ||
726 | switch(c) | ||
727 | { | ||
728 | case -1: | ||
729 | break; | ||
730 | case 'h': | ||
731 | usage(); | ||
732 | break; | ||
733 | case 'o': | ||
734 | output = optarg; | ||
735 | break; | ||
736 | case 'x': | ||
737 | extract = true; | ||
738 | break; | ||
739 | case 'v': | ||
740 | g_verbose = true; | ||
741 | break; | ||
742 | case 'u': | ||
743 | g_unsafe = true; | ||
744 | break; | ||
745 | default: | ||
746 | abort(); | ||
747 | } | ||
748 | } | ||
749 | |||
750 | if(extract) | ||
751 | return do_extract(output, argc - optind, argv + optind); | ||
752 | printf("You need to specify an operation. Run nvptool -h for help\n"); | ||
753 | return 1; | ||
754 | } | ||