| 811 |
theseven |
1 |
#!/usr/bin/env python
|
|
|
2 |
#
|
|
|
3 |
#
|
|
|
4 |
# Copyright 2011 TheSeven
|
|
|
5 |
#
|
|
|
6 |
#
|
|
|
7 |
# This file is part of emCORE.
|
|
|
8 |
#
|
|
|
9 |
# emCORE is free software: you can redistribute it and/or
|
|
|
10 |
# modify it under the terms of the GNU General Public License as
|
|
|
11 |
# published by the Free Software Foundation, either version 2 of the
|
|
|
12 |
# License, or (at your option) any later version.
|
|
|
13 |
#
|
|
|
14 |
# emCORE is distributed in the hope that it will be useful,
|
|
|
15 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
16 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
17 |
# See the GNU General Public License for more details.
|
|
|
18 |
#
|
|
|
19 |
# You should have received a copy of the GNU General Public License
|
|
|
20 |
# along with emCORE. If not, see <http://www.gnu.org/licenses/>.
|
|
|
21 |
#
|
|
|
22 |
#
|
|
|
23 |
|
|
|
24 |
|
|
|
25 |
import sys
|
|
|
26 |
import struct
|
| 816 |
theseven |
27 |
from misc import ExtendedCStruct, string_from_image
|
|
|
28 |
from libemcoredata import scheduler_thread
|
| 811 |
theseven |
29 |
from ctypes import *
|
|
|
30 |
|
|
|
31 |
|
|
|
32 |
def usage():
|
|
|
33 |
print("Usage: %s <image> [image_base] [tlsf_pool] [SL_INDEX_COUNT_LOG2] [FL_INDEX_MAX]" % (sys.argv[0]))
|
|
|
34 |
exit(2)
|
|
|
35 |
|
|
|
36 |
|
|
|
37 |
image_base = 0x08000000
|
|
|
38 |
tlsf_pool = 0x08000000
|
|
|
39 |
SL_INDEX_COUNT_LOG2 = 5
|
|
|
40 |
FL_INDEX_MAX = 30
|
|
|
41 |
|
|
|
42 |
if len(sys.argv) < 2: usage()
|
|
|
43 |
filename = sys.argv[1]
|
|
|
44 |
if len(sys.argv) > 2: image_base = int(sys.argv[2])
|
|
|
45 |
if len(sys.argv) > 3: tlsf_pool = int(sys.argv[3])
|
|
|
46 |
if len(sys.argv) > 4: SL_INDEX_COUNT_LOG2 = int(sys.argv[4])
|
|
|
47 |
if len(sys.argv) > 5: FL_INDEX_MAX = int(sys.argv[5])
|
|
|
48 |
if len(sys.argv) > 6: usage()
|
|
|
49 |
|
|
|
50 |
file = open(filename, "rb")
|
|
|
51 |
data = file.read()
|
|
|
52 |
file.close()
|
|
|
53 |
|
|
|
54 |
SL_INDEX_COUNT = 1 << SL_INDEX_COUNT_LOG2
|
|
|
55 |
FL_INDEX_SHIFT = SL_INDEX_COUNT_LOG2 + 2
|
|
|
56 |
FL_INDEX_COUNT = FL_INDEX_MAX - FL_INDEX_SHIFT + 1
|
|
|
57 |
SMALL_BLOCK_SIZE = 1 << FL_INDEX_SHIFT
|
|
|
58 |
|
|
|
59 |
|
|
|
60 |
class block_header_t(ExtendedCStruct):
|
|
|
61 |
_fields_ = [("prev_phys_block", c_uint32),
|
|
|
62 |
("size", c_uint32),
|
|
|
63 |
("next_free", c_uint32),
|
|
|
64 |
("prev_free", c_uint32)]
|
|
|
65 |
|
|
|
66 |
def is_last(self):
|
|
|
67 |
return self.get_size() == 0;
|
|
|
68 |
|
|
|
69 |
def is_null(self):
|
|
|
70 |
return self._address_ == self.next_free and self.size == 0 and self.prev_phys_block == 0
|
|
|
71 |
|
|
|
72 |
def is_free(self):
|
|
|
73 |
return (self.size & block_header_free_bit) != 0;
|
|
|
74 |
|
|
|
75 |
def is_prev_free(self):
|
|
|
76 |
return (self.size & block_header_prev_free_bit) != 0;
|
|
|
77 |
|
|
|
78 |
def get_address(self):
|
|
|
79 |
return self._address_
|
|
|
80 |
|
|
|
81 |
def get_size(self):
|
|
|
82 |
return self.size & 0xfffffffc
|
|
|
83 |
|
|
|
84 |
def get_prev_free(self):
|
|
|
85 |
if not self.is_free(): raise Exception("Trying to get previous free block of non-free block")
|
|
|
86 |
return block_header_t(self._data_, self._base_, self.prev_free)
|
|
|
87 |
|
|
|
88 |
def get_next_free(self):
|
|
|
89 |
if not self.is_free(): raise Exception("Trying to get next free block of non-free block")
|
|
|
90 |
return block_header_t(self._data_, self._base_, self.next_free)
|
|
|
91 |
|
|
|
92 |
def get_prev_phys(self):
|
|
|
93 |
if not self.is_prev_free(): raise Exception("Trying to get non-free previous physical block")
|
|
|
94 |
return block_header_t(self._data_, self._base_, self.prev_phys_block)
|
|
|
95 |
|
|
|
96 |
def get_next_phys(self):
|
|
|
97 |
return self.get_next_by_offset(self.get_size() + 4)
|
|
|
98 |
|
|
|
99 |
def get_next_by_offset(self, offset):
|
|
|
100 |
return block_header_t(self._data_, self._base_, self._address_ + offset)
|
|
|
101 |
|
|
|
102 |
|
|
|
103 |
class pool_t(ExtendedCStruct):
|
|
|
104 |
_fields_ = [("fl_bitmap", c_uint32),
|
|
|
105 |
("sl_bitmap", c_uint32 * FL_INDEX_COUNT),
|
|
|
106 |
("blocks", c_uint32 * SL_INDEX_COUNT * FL_INDEX_COUNT)]
|
|
|
107 |
|
|
|
108 |
|
|
|
109 |
block_header_free_bit = 1
|
|
|
110 |
block_header_prev_free_bit = 2
|
|
|
111 |
block_size_min = sizeof(block_header_t) - 4;
|
|
|
112 |
block_size_max = 1 << FL_INDEX_MAX
|
|
|
113 |
|
|
|
114 |
pool = pool_t(data, image_base, tlsf_pool)
|
|
|
115 |
block = block_header_t(data, image_base, tlsf_pool + sizeof(pool_t) - 4)
|
|
|
116 |
prev_free = False
|
|
|
117 |
prev_addr = 0
|
|
|
118 |
blocks = []
|
|
|
119 |
free_blocks = []
|
|
|
120 |
bytes_used = 0
|
|
|
121 |
bytes_free = 0
|
|
|
122 |
|
|
|
123 |
while True:
|
|
|
124 |
if block.is_prev_free() != prev_free:
|
|
|
125 |
print("Block %08X previous free indicator is %d, expected %d" % (block.get_address(), block.is_prev_free(), prev_free))
|
|
|
126 |
if prev_free and prev_addr != block.prev_phys_block:
|
|
|
127 |
print("Block %08X previous physical block address is wrong. Got %08X, should be %08X" % (block.get_address(), block.prev_phys_block, prev_addr))
|
|
|
128 |
prev_free = block.is_free()
|
|
|
129 |
prev_addr = block.get_address()
|
|
|
130 |
if block.is_last(): break
|
|
|
131 |
if blocks.count(prev_addr) > 0:
|
|
|
132 |
print("Block loop detected at %08X" % (prev_addr))
|
|
|
133 |
break
|
|
|
134 |
blocks.append(prev_addr)
|
|
|
135 |
if prev_free:
|
| 816 |
theseven |
136 |
try:
|
|
|
137 |
nfa = block.get_next_free().get_address()
|
|
|
138 |
if nfa >= prev_addr and nfa < prev_addr + block.get_size() + 4:
|
|
|
139 |
print("%08X: Next free block (%08X) lies within the block itself" % (prev_addr, nfa))
|
|
|
140 |
except: print("%08X: Invalid next free block pointer: %08X" % (prev_addr, block.next_free))
|
|
|
141 |
try:
|
|
|
142 |
pfa = block.get_prev_free().get_address()
|
|
|
143 |
if pfa >= prev_addr and pfa < prev_addr + block.get_size() + 4:
|
|
|
144 |
print("%08X: Previous free block (%08X) lies within the block itself" % (prev_addr, pfa))
|
|
|
145 |
except:
|
|
|
146 |
print("%08X: Invalid previous free block pointer: %08X" % (prev_addr, block.prev_free))
|
| 811 |
theseven |
147 |
print("%08X: %08X bytes free" % (prev_addr + 4, block.get_size() + 4))
|
|
|
148 |
free_blocks.append(prev_addr)
|
|
|
149 |
bytes_free = bytes_free + block.get_size() + 4
|
|
|
150 |
else:
|
| 816 |
theseven |
151 |
owner_address = prev_addr - image_base + block.get_size() + 4
|
| 811 |
theseven |
152 |
owner = struct.unpack("<I", data[owner_address : owner_address + 4])[0]
|
| 816 |
theseven |
153 |
if (owner & 3) == 0:
|
|
|
154 |
if (owner & 0xfffc0000) == 0x22000000: name = "<KERNEL_THREAD>"
|
|
|
155 |
else:
|
|
|
156 |
try:
|
|
|
157 |
thread = scheduler_thread(data, image_base, owner & ~3)
|
|
|
158 |
name = "Thread: " + string_from_image(data, image_base, thread.name, 128)
|
|
|
159 |
except: name = "<BAD_THREAD>"
|
|
|
160 |
elif (owner & 3) == 1:
|
|
|
161 |
try:
|
|
|
162 |
handle = struct.unpack("<I", data[(owner & ~3) - image_base + 4 : (owner & ~3) - image_base + 8])[0]
|
|
|
163 |
lib = struct.unpack("<4sI", data[(handle & ~3) - image_base + 4 : (handle & ~3) - image_base + 12])
|
|
|
164 |
name = "Library: " + lib[0].decode("latin_1") + "_v%d" % lib[1]
|
|
|
165 |
except: name = "<BAD_LIBRARY>"
|
|
|
166 |
elif (owner & 3) == 2:
|
|
|
167 |
if (owner >> 2) == 0: name = "<KERNEL_UNKNOWN>";
|
|
|
168 |
elif (owner >> 2) == 1: name = "<KERNEL_USB_MONITOR>";
|
|
|
169 |
elif (owner >> 2) == 2: name = "<KERNEL_FILE_HANDLE>";
|
|
|
170 |
elif (owner >> 2) == 3: name = "<KERNEL_DIR_HANDLE>";
|
|
|
171 |
elif (owner >> 2) == 4: name = "<KERNEL_ATA_BBT>";
|
|
|
172 |
else: name = "<KERNEL_UNKNOWN_TYPE>";
|
|
|
173 |
else: name = "<UNKNOWN_TYPE>"
|
|
|
174 |
print("%08X: %08X+8 bytes owned by %08X (%s)" % (prev_addr + 8, block.get_size() - 4, owner, name))
|
| 811 |
theseven |
175 |
bytes_used = bytes_used + block.get_size() + 4
|
|
|
176 |
try: block = block.get_next_phys()
|
|
|
177 |
except:
|
|
|
178 |
print("Block %08X has invalid size: %08X" % (prev_addr, block.get_size()))
|
|
|
179 |
print("Fatal error in block chain, continuing with map check")
|
|
|
180 |
break
|
|
|
181 |
|
|
|
182 |
handled_blocks = []
|
|
|
183 |
for i in range(FL_INDEX_COUNT):
|
|
|
184 |
fl_map = (pool.fl_bitmap >> i) & 1
|
|
|
185 |
sl_list = pool.sl_bitmap[i]
|
|
|
186 |
if fl_map == 0:
|
|
|
187 |
if sl_list != 0:
|
|
|
188 |
print("[%d:%d] Second-level map must be null, but isn't" % (i, j))
|
|
|
189 |
elif sl_list == 0:
|
|
|
190 |
print("[%d:%d] No free blocks in second-level map, but first-level map indicates there are some" % (i, j))
|
|
|
191 |
for j in range(SL_INDEX_COUNT):
|
|
|
192 |
sl_map = (sl_list >> j) & 1
|
|
|
193 |
ba = pool.blocks[i][j]
|
|
|
194 |
block = block_header_t(data, image_base, ba)
|
|
|
195 |
if sl_map == 0:
|
|
|
196 |
if not block.is_null():
|
|
|
197 |
print("[%d:%d:%08X] Block list must be null, but isn't" % (i, j, ba))
|
|
|
198 |
continue
|
|
|
199 |
elif block.is_null():
|
|
|
200 |
print("[%d:%d:%08X] Block list is null, but second-level map indicates there are free blocks" % (i, j, ba))
|
|
|
201 |
blocks = []
|
| 816 |
theseven |
202 |
prev = None
|
| 811 |
theseven |
203 |
while not block.is_null():
|
|
|
204 |
fatal = False
|
|
|
205 |
addr = block.get_address()
|
|
|
206 |
if blocks.count(addr) > 0:
|
|
|
207 |
print("[%d:%d:%08X] Detected block loop" % (i, j, addr))
|
|
|
208 |
break
|
|
|
209 |
blocks.append(addr)
|
| 816 |
theseven |
210 |
size = block.get_size()
|
|
|
211 |
print("[%d:%d] Block at %08X (%08X bytes)" % (i, j, addr, size + 4))
|
| 811 |
theseven |
212 |
if not block.is_free():
|
|
|
213 |
print("[%d:%d:%08X] Non-free block on free list" % (i, j, addr))
|
|
|
214 |
fatal = True
|
|
|
215 |
if block.is_prev_free():
|
|
|
216 |
print("[%d:%d:%08X] Block should have coalesced with previous one" % (i, j, addr))
|
|
|
217 |
try:
|
|
|
218 |
if block.get_next_phys().is_free():
|
|
|
219 |
print("[%d:%d:%08X] Block should have coalesced with next one" % (i, j, addr))
|
|
|
220 |
except:
|
|
|
221 |
print("Block %08X has invalid size: %08X" % (addr, block.get_size()))
|
|
|
222 |
if size < block_size_min:
|
|
|
223 |
print("[%d:%d:%08X] Block violates minimum size: %d (should be at least %d)" % (i, j, addr, size, block_size_min))
|
|
|
224 |
if size > block_size_max:
|
|
|
225 |
print("[%d:%d:%08X] Block violates maximum size: %d (should be at most %d)" % (i, j, addr, size, block_size_max))
|
|
|
226 |
if size < SMALL_BLOCK_SIZE:
|
|
|
227 |
fl = 0
|
|
|
228 |
sl = size / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT)
|
|
|
229 |
else:
|
|
|
230 |
fl = 32 - FL_INDEX_SHIFT;
|
|
|
231 |
if (size & 0xffff0000) == 0:
|
|
|
232 |
size = size << 16
|
|
|
233 |
fl = fl - 16
|
|
|
234 |
if (size & 0xff000000) == 0:
|
|
|
235 |
size = size << 8
|
|
|
236 |
fl = fl - 8
|
|
|
237 |
if (size & 0xf0000000) == 0:
|
|
|
238 |
size = size << 4
|
|
|
239 |
fl = fl - 4
|
|
|
240 |
if (size & 0xc0000000) == 0:
|
|
|
241 |
size = size << 2
|
|
|
242 |
fl = fl - 2
|
|
|
243 |
if (size & 0x80000000) == 0:
|
|
|
244 |
size = size << 1
|
|
|
245 |
fl = fl - 1
|
| 816 |
theseven |
246 |
size = block.get_size()
|
|
|
247 |
sl = (size >> (fl - SL_INDEX_COUNT_LOG2 + FL_INDEX_SHIFT - 1)) ^ (1 << SL_INDEX_COUNT_LOG2)
|
| 811 |
theseven |
248 |
if fl != i or sl != j:
|
|
|
249 |
print("Block %08X is in wrong free list: [%d:%d] (should be [%d:%d])" % (addr, i, j, fl, sl))
|
|
|
250 |
if free_blocks.count(addr) != 1:
|
|
|
251 |
print("[%d:%d:%08X] Block is in free list, but was not found in pool" % (i, j, addr))
|
|
|
252 |
if handled_blocks.count(addr) > 0:
|
|
|
253 |
print("[%d:%d:%08X] Block appears in multiple free lists" % (i, j, addr))
|
|
|
254 |
else: handled_blocks.append(addr)
|
| 816 |
theseven |
255 |
try:
|
|
|
256 |
nfa = block.get_next_free().get_address()
|
|
|
257 |
if nfa >= addr and nfa < addr + size + 4:
|
|
|
258 |
print("[%d:%d:%08X] Next free block (%08X) lies within the block itself" % (i, j, addr, nfa))
|
|
|
259 |
fatal = True
|
|
|
260 |
except:
|
|
|
261 |
print("[%d:%d:%08X] Invalid next free block pointer: %08X" % (i, j, addr, block.next_free))
|
|
|
262 |
fatal = True
|
|
|
263 |
try:
|
|
|
264 |
pfa = block.get_prev_free().get_address()
|
|
|
265 |
if pfa >= addr and pfa < addr + size + 4:
|
|
|
266 |
print("[%d:%d:%08X] Previous free block (%08X) lies within the block itself" % (i, j, addr, pfa))
|
|
|
267 |
if prev == None and not block.get_prev_free().is_null():
|
|
|
268 |
print("[%d:%d:%08X] Previous free block pointer is broken: %08X (should be NULL)" % (i, j, addr, pfa))
|
|
|
269 |
if prev != None and prev != pfa:
|
|
|
270 |
print("[%d:%d:%08X] Previous free block pointer is broken: %08X (should be %08X)" % (i, j, addr, pfa, prev))
|
|
|
271 |
except:
|
|
|
272 |
print("[%d:%d:%08X] Invalid previous free block pointer: %08X" % (i, j, addr, block.prev_free))
|
| 811 |
theseven |
273 |
if fatal:
|
|
|
274 |
print("Fatal error in block chain, continuing with next chain")
|
|
|
275 |
break
|
| 816 |
theseven |
276 |
prev = addr
|
| 811 |
theseven |
277 |
block = block.get_next_free()
|
|
|
278 |
|
|
|
279 |
for addr in free_blocks:
|
|
|
280 |
if handled_blocks.count(addr) != 1:
|
|
|
281 |
print("Free block %08X does not appear in any free list" % (addr))
|