| 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
|
|
|
27 |
from misc import ExtendedCStruct
|
|
|
28 |
from ctypes import *
|
|
|
29 |
|
|
|
30 |
|
|
|
31 |
def usage():
|
|
|
32 |
print("Usage: %s <image> [image_base] [tlsf_pool] [SL_INDEX_COUNT_LOG2] [FL_INDEX_MAX]" % (sys.argv[0]))
|
|
|
33 |
exit(2)
|
|
|
34 |
|
|
|
35 |
|
|
|
36 |
image_base = 0x08000000
|
|
|
37 |
tlsf_pool = 0x08000000
|
|
|
38 |
SL_INDEX_COUNT_LOG2 = 5
|
|
|
39 |
FL_INDEX_MAX = 30
|
|
|
40 |
|
|
|
41 |
if len(sys.argv) < 2: usage()
|
|
|
42 |
filename = sys.argv[1]
|
|
|
43 |
if len(sys.argv) > 2: image_base = int(sys.argv[2])
|
|
|
44 |
if len(sys.argv) > 3: tlsf_pool = int(sys.argv[3])
|
|
|
45 |
if len(sys.argv) > 4: SL_INDEX_COUNT_LOG2 = int(sys.argv[4])
|
|
|
46 |
if len(sys.argv) > 5: FL_INDEX_MAX = int(sys.argv[5])
|
|
|
47 |
if len(sys.argv) > 6: usage()
|
|
|
48 |
|
|
|
49 |
file = open(filename, "rb")
|
|
|
50 |
data = file.read()
|
|
|
51 |
file.close()
|
|
|
52 |
|
|
|
53 |
SL_INDEX_COUNT = 1 << SL_INDEX_COUNT_LOG2
|
|
|
54 |
FL_INDEX_SHIFT = SL_INDEX_COUNT_LOG2 + 2
|
|
|
55 |
FL_INDEX_COUNT = FL_INDEX_MAX - FL_INDEX_SHIFT + 1
|
|
|
56 |
SMALL_BLOCK_SIZE = 1 << FL_INDEX_SHIFT
|
|
|
57 |
|
|
|
58 |
|
|
|
59 |
class block_header_t(ExtendedCStruct):
|
|
|
60 |
_fields_ = [("prev_phys_block", c_uint32),
|
|
|
61 |
("size", c_uint32),
|
|
|
62 |
("next_free", c_uint32),
|
|
|
63 |
("prev_free", c_uint32)]
|
|
|
64 |
|
|
|
65 |
def is_last(self):
|
|
|
66 |
return self.get_size() == 0;
|
|
|
67 |
|
|
|
68 |
def is_null(self):
|
|
|
69 |
return self._address_ == self.next_free and self.size == 0 and self.prev_phys_block == 0
|
|
|
70 |
|
|
|
71 |
def is_free(self):
|
|
|
72 |
return (self.size & block_header_free_bit) != 0;
|
|
|
73 |
|
|
|
74 |
def is_prev_free(self):
|
|
|
75 |
return (self.size & block_header_prev_free_bit) != 0;
|
|
|
76 |
|
|
|
77 |
def get_address(self):
|
|
|
78 |
return self._address_
|
|
|
79 |
|
|
|
80 |
def get_size(self):
|
|
|
81 |
return self.size & 0xfffffffc
|
|
|
82 |
|
|
|
83 |
def get_prev_free(self):
|
|
|
84 |
if not self.is_free(): raise Exception("Trying to get previous free block of non-free block")
|
|
|
85 |
return block_header_t(self._data_, self._base_, self.prev_free)
|
|
|
86 |
|
|
|
87 |
def get_next_free(self):
|
|
|
88 |
if not self.is_free(): raise Exception("Trying to get next free block of non-free block")
|
|
|
89 |
return block_header_t(self._data_, self._base_, self.next_free)
|
|
|
90 |
|
|
|
91 |
def get_prev_phys(self):
|
|
|
92 |
if not self.is_prev_free(): raise Exception("Trying to get non-free previous physical block")
|
|
|
93 |
return block_header_t(self._data_, self._base_, self.prev_phys_block)
|
|
|
94 |
|
|
|
95 |
def get_next_phys(self):
|
|
|
96 |
return self.get_next_by_offset(self.get_size() + 4)
|
|
|
97 |
|
|
|
98 |
def get_next_by_offset(self, offset):
|
|
|
99 |
return block_header_t(self._data_, self._base_, self._address_ + offset)
|
|
|
100 |
|
|
|
101 |
|
|
|
102 |
class pool_t(ExtendedCStruct):
|
|
|
103 |
_fields_ = [("fl_bitmap", c_uint32),
|
|
|
104 |
("sl_bitmap", c_uint32 * FL_INDEX_COUNT),
|
|
|
105 |
("blocks", c_uint32 * SL_INDEX_COUNT * FL_INDEX_COUNT)]
|
|
|
106 |
|
|
|
107 |
|
|
|
108 |
block_header_free_bit = 1
|
|
|
109 |
block_header_prev_free_bit = 2
|
|
|
110 |
block_size_min = sizeof(block_header_t) - 4;
|
|
|
111 |
block_size_max = 1 << FL_INDEX_MAX
|
|
|
112 |
|
|
|
113 |
pool = pool_t(data, image_base, tlsf_pool)
|
|
|
114 |
block = block_header_t(data, image_base, tlsf_pool + sizeof(pool_t) - 4)
|
|
|
115 |
prev_free = False
|
|
|
116 |
prev_addr = 0
|
|
|
117 |
blocks = []
|
|
|
118 |
free_blocks = []
|
|
|
119 |
bytes_used = 0
|
|
|
120 |
bytes_free = 0
|
|
|
121 |
|
|
|
122 |
while True:
|
|
|
123 |
if block.is_prev_free() != prev_free:
|
|
|
124 |
print("Block %08X previous free indicator is %d, expected %d" % (block.get_address(), block.is_prev_free(), prev_free))
|
|
|
125 |
if prev_free and prev_addr != block.prev_phys_block:
|
|
|
126 |
print("Block %08X previous physical block address is wrong. Got %08X, should be %08X" % (block.get_address(), block.prev_phys_block, prev_addr))
|
|
|
127 |
prev_free = block.is_free()
|
|
|
128 |
prev_addr = block.get_address()
|
|
|
129 |
if block.is_last(): break
|
|
|
130 |
if blocks.count(prev_addr) > 0:
|
|
|
131 |
print("Block loop detected at %08X" % (prev_addr))
|
|
|
132 |
break
|
|
|
133 |
blocks.append(prev_addr)
|
|
|
134 |
if prev_free:
|
|
|
135 |
print("%08X: %08X bytes free" % (prev_addr + 4, block.get_size() + 4))
|
|
|
136 |
free_blocks.append(prev_addr)
|
|
|
137 |
bytes_free = bytes_free + block.get_size() + 4
|
|
|
138 |
else:
|
|
|
139 |
owner_address = prev_addr - image_base + block.get_size() - 4
|
|
|
140 |
owner = struct.unpack("<I", data[owner_address : owner_address + 4])[0]
|
|
|
141 |
print("%08X: %08X+8 bytes owned by %08X" % (prev_addr + 8, block.get_size() - 4, owner))
|
|
|
142 |
bytes_used = bytes_used + block.get_size() + 4
|
|
|
143 |
try: block = block.get_next_phys()
|
|
|
144 |
except:
|
|
|
145 |
print("Block %08X has invalid size: %08X" % (prev_addr, block.get_size()))
|
|
|
146 |
print("Fatal error in block chain, continuing with map check")
|
|
|
147 |
break
|
|
|
148 |
|
|
|
149 |
handled_blocks = []
|
|
|
150 |
for i in range(FL_INDEX_COUNT):
|
|
|
151 |
fl_map = (pool.fl_bitmap >> i) & 1
|
|
|
152 |
sl_list = pool.sl_bitmap[i]
|
|
|
153 |
if fl_map == 0:
|
|
|
154 |
if sl_list != 0:
|
|
|
155 |
print("[%d:%d] Second-level map must be null, but isn't" % (i, j))
|
|
|
156 |
elif sl_list == 0:
|
|
|
157 |
print("[%d:%d] No free blocks in second-level map, but first-level map indicates there are some" % (i, j))
|
|
|
158 |
for j in range(SL_INDEX_COUNT):
|
|
|
159 |
sl_map = (sl_list >> j) & 1
|
|
|
160 |
ba = pool.blocks[i][j]
|
|
|
161 |
block = block_header_t(data, image_base, ba)
|
|
|
162 |
if sl_map == 0:
|
|
|
163 |
if not block.is_null():
|
|
|
164 |
print("[%d:%d:%08X] Block list must be null, but isn't" % (i, j, ba))
|
|
|
165 |
continue
|
|
|
166 |
elif block.is_null():
|
|
|
167 |
print("[%d:%d:%08X] Block list is null, but second-level map indicates there are free blocks" % (i, j, ba))
|
|
|
168 |
blocks = []
|
|
|
169 |
while not block.is_null():
|
|
|
170 |
fatal = False
|
|
|
171 |
addr = block.get_address()
|
|
|
172 |
if blocks.count(addr) > 0:
|
|
|
173 |
print("[%d:%d:%08X] Detected block loop" % (i, j, addr))
|
|
|
174 |
break
|
|
|
175 |
blocks.append(addr)
|
|
|
176 |
if not block.is_free():
|
|
|
177 |
print("[%d:%d:%08X] Non-free block on free list" % (i, j, addr))
|
|
|
178 |
fatal = True
|
|
|
179 |
if block.is_prev_free():
|
|
|
180 |
print("[%d:%d:%08X] Block should have coalesced with previous one" % (i, j, addr))
|
|
|
181 |
try:
|
|
|
182 |
if block.get_next_phys().is_free():
|
|
|
183 |
print("[%d:%d:%08X] Block should have coalesced with next one" % (i, j, addr))
|
|
|
184 |
except:
|
|
|
185 |
print("Block %08X has invalid size: %08X" % (addr, block.get_size()))
|
|
|
186 |
fatal = True
|
|
|
187 |
size = block.get_size()
|
|
|
188 |
if size < block_size_min:
|
|
|
189 |
print("[%d:%d:%08X] Block violates minimum size: %d (should be at least %d)" % (i, j, addr, size, block_size_min))
|
|
|
190 |
if size > block_size_max:
|
|
|
191 |
print("[%d:%d:%08X] Block violates maximum size: %d (should be at most %d)" % (i, j, addr, size, block_size_max))
|
|
|
192 |
if size < SMALL_BLOCK_SIZE:
|
|
|
193 |
fl = 0
|
|
|
194 |
sl = size / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT)
|
|
|
195 |
else:
|
|
|
196 |
fl = 32 - FL_INDEX_SHIFT;
|
|
|
197 |
if (size & 0xffff0000) == 0:
|
|
|
198 |
size = size << 16
|
|
|
199 |
fl = fl - 16
|
|
|
200 |
if (size & 0xff000000) == 0:
|
|
|
201 |
size = size << 8
|
|
|
202 |
fl = fl - 8
|
|
|
203 |
if (size & 0xf0000000) == 0:
|
|
|
204 |
size = size << 4
|
|
|
205 |
fl = fl - 4
|
|
|
206 |
if (size & 0xc0000000) == 0:
|
|
|
207 |
size = size << 2
|
|
|
208 |
fl = fl - 2
|
|
|
209 |
if (size & 0x80000000) == 0:
|
|
|
210 |
size = size << 1
|
|
|
211 |
fl = fl - 1
|
|
|
212 |
sl = (block.get_size() >> (fl - SL_INDEX_COUNT_LOG2 + FL_INDEX_SHIFT - 1)) ^ (1 << SL_INDEX_COUNT_LOG2)
|
|
|
213 |
if fl != i or sl != j:
|
|
|
214 |
print("Block %08X is in wrong free list: [%d:%d] (should be [%d:%d])" % (addr, i, j, fl, sl))
|
|
|
215 |
if free_blocks.count(addr) != 1:
|
|
|
216 |
print("[%d:%d:%08X] Block is in free list, but was not found in pool" % (i, j, addr))
|
|
|
217 |
if handled_blocks.count(addr) > 0:
|
|
|
218 |
print("[%d:%d:%08X] Block appears in multiple free lists" % (i, j, addr))
|
|
|
219 |
else: handled_blocks.append(addr)
|
|
|
220 |
if fatal:
|
|
|
221 |
print("Fatal error in block chain, continuing with next chain")
|
|
|
222 |
break
|
|
|
223 |
block = block.get_next_free()
|
|
|
224 |
|
|
|
225 |
for addr in free_blocks:
|
|
|
226 |
if handled_blocks.count(addr) != 1:
|
|
|
227 |
print("Free block %08X does not appear in any free list" % (addr))
|