Index: emcore/trunk/tools/misc.py |
— | — | @@ -235,7 +235,6 @@ |
236 | 236 | else:
|
237 | 237 | return self.value != other
|
238 | 238 |
|
239 | | -
|
240 | 239 | class ExtendedCStruct(LittleEndianStructure):
|
241 | 240 | """
|
242 | 241 | This is a subclass of the LittleEndianStructure.
|
— | — | @@ -242,6 +241,19 @@ |
243 | 242 | It implements functions to easily convert
|
244 | 243 | structures to/from strings and Bunches.
|
245 | 244 | """
|
| 245 | + def __init__(self, data = None, base = 0, address = 0):
|
| 246 | + LittleEndianStructure.__init__(self)
|
| 247 | + if data != None:
|
| 248 | + self._data_ = data
|
| 249 | + self._base_ = base
|
| 250 | + self._address_ = address
|
| 251 | + if address < base or address + sizeof(self) > base + len(data):
|
| 252 | + raise Exception("Range 0x%08X+0x%X out of bounds [0x%08X:0x%08X]" % (address, sizeof(self), base, base + len(data)))
|
| 253 | + memmove(addressof(self), data[address - base : address - base + sizeof(self)], sizeof(self))
|
| 254 | +
|
| 255 | + def __int__(self):
|
| 256 | + return self._address_
|
| 257 | +
|
246 | 258 | def _from_bunch(self, bunch):
|
247 | 259 | for field, _ in self._fields_:
|
248 | 260 | if field in bunch:
|
Index: emcore/trunk/tools/tlsfanalyze.py |
— | — | @@ -0,0 +1,227 @@ |
| 2 | +#!/usr/bin/env python
|
| 3 | +#
|
| 4 | +#
|
| 5 | +# Copyright 2011 TheSeven
|
| 6 | +#
|
| 7 | +#
|
| 8 | +# This file is part of emCORE.
|
| 9 | +#
|
| 10 | +# emCORE is free software: you can redistribute it and/or
|
| 11 | +# modify it under the terms of the GNU General Public License as
|
| 12 | +# published by the Free Software Foundation, either version 2 of the
|
| 13 | +# License, or (at your option) any later version.
|
| 14 | +#
|
| 15 | +# emCORE is distributed in the hope that it will be useful,
|
| 16 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 17 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
| 18 | +# See the GNU General Public License for more details.
|
| 19 | +#
|
| 20 | +# You should have received a copy of the GNU General Public License
|
| 21 | +# along with emCORE. If not, see <http://www.gnu.org/licenses/>.
|
| 22 | +#
|
| 23 | +#
|
| 24 | +
|
| 25 | +
|
| 26 | +import sys
|
| 27 | +import struct
|
| 28 | +from misc import ExtendedCStruct
|
| 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:
|
| 136 | + print("%08X: %08X bytes free" % (prev_addr + 4, block.get_size() + 4))
|
| 137 | + free_blocks.append(prev_addr)
|
| 138 | + bytes_free = bytes_free + block.get_size() + 4
|
| 139 | + else:
|
| 140 | + owner_address = prev_addr - image_base + block.get_size() - 4
|
| 141 | + owner = struct.unpack("<I", data[owner_address : owner_address + 4])[0]
|
| 142 | + print("%08X: %08X+8 bytes owned by %08X" % (prev_addr + 8, block.get_size() - 4, owner))
|
| 143 | + bytes_used = bytes_used + block.get_size() + 4
|
| 144 | + try: block = block.get_next_phys()
|
| 145 | + except:
|
| 146 | + print("Block %08X has invalid size: %08X" % (prev_addr, block.get_size()))
|
| 147 | + print("Fatal error in block chain, continuing with map check")
|
| 148 | + break
|
| 149 | +
|
| 150 | +handled_blocks = []
|
| 151 | +for i in range(FL_INDEX_COUNT):
|
| 152 | + fl_map = (pool.fl_bitmap >> i) & 1
|
| 153 | + sl_list = pool.sl_bitmap[i]
|
| 154 | + if fl_map == 0:
|
| 155 | + if sl_list != 0:
|
| 156 | + print("[%d:%d] Second-level map must be null, but isn't" % (i, j))
|
| 157 | + elif sl_list == 0:
|
| 158 | + print("[%d:%d] No free blocks in second-level map, but first-level map indicates there are some" % (i, j))
|
| 159 | + for j in range(SL_INDEX_COUNT):
|
| 160 | + sl_map = (sl_list >> j) & 1
|
| 161 | + ba = pool.blocks[i][j]
|
| 162 | + block = block_header_t(data, image_base, ba)
|
| 163 | + if sl_map == 0:
|
| 164 | + if not block.is_null():
|
| 165 | + print("[%d:%d:%08X] Block list must be null, but isn't" % (i, j, ba))
|
| 166 | + continue
|
| 167 | + elif block.is_null():
|
| 168 | + print("[%d:%d:%08X] Block list is null, but second-level map indicates there are free blocks" % (i, j, ba))
|
| 169 | + blocks = []
|
| 170 | + while not block.is_null():
|
| 171 | + fatal = False
|
| 172 | + addr = block.get_address()
|
| 173 | + if blocks.count(addr) > 0:
|
| 174 | + print("[%d:%d:%08X] Detected block loop" % (i, j, addr))
|
| 175 | + break
|
| 176 | + blocks.append(addr)
|
| 177 | + if not block.is_free():
|
| 178 | + print("[%d:%d:%08X] Non-free block on free list" % (i, j, addr))
|
| 179 | + fatal = True
|
| 180 | + if block.is_prev_free():
|
| 181 | + print("[%d:%d:%08X] Block should have coalesced with previous one" % (i, j, addr))
|
| 182 | + try:
|
| 183 | + if block.get_next_phys().is_free():
|
| 184 | + print("[%d:%d:%08X] Block should have coalesced with next one" % (i, j, addr))
|
| 185 | + except:
|
| 186 | + print("Block %08X has invalid size: %08X" % (addr, block.get_size()))
|
| 187 | + fatal = True
|
| 188 | + size = block.get_size()
|
| 189 | + if size < block_size_min:
|
| 190 | + print("[%d:%d:%08X] Block violates minimum size: %d (should be at least %d)" % (i, j, addr, size, block_size_min))
|
| 191 | + if size > block_size_max:
|
| 192 | + print("[%d:%d:%08X] Block violates maximum size: %d (should be at most %d)" % (i, j, addr, size, block_size_max))
|
| 193 | + if size < SMALL_BLOCK_SIZE:
|
| 194 | + fl = 0
|
| 195 | + sl = size / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT)
|
| 196 | + else:
|
| 197 | + fl = 32 - FL_INDEX_SHIFT;
|
| 198 | + if (size & 0xffff0000) == 0:
|
| 199 | + size = size << 16
|
| 200 | + fl = fl - 16
|
| 201 | + if (size & 0xff000000) == 0:
|
| 202 | + size = size << 8
|
| 203 | + fl = fl - 8
|
| 204 | + if (size & 0xf0000000) == 0:
|
| 205 | + size = size << 4
|
| 206 | + fl = fl - 4
|
| 207 | + if (size & 0xc0000000) == 0:
|
| 208 | + size = size << 2
|
| 209 | + fl = fl - 2
|
| 210 | + if (size & 0x80000000) == 0:
|
| 211 | + size = size << 1
|
| 212 | + fl = fl - 1
|
| 213 | + sl = (block.get_size() >> (fl - SL_INDEX_COUNT_LOG2 + FL_INDEX_SHIFT - 1)) ^ (1 << SL_INDEX_COUNT_LOG2)
|
| 214 | + if fl != i or sl != j:
|
| 215 | + print("Block %08X is in wrong free list: [%d:%d] (should be [%d:%d])" % (addr, i, j, fl, sl))
|
| 216 | + if free_blocks.count(addr) != 1:
|
| 217 | + print("[%d:%d:%08X] Block is in free list, but was not found in pool" % (i, j, addr))
|
| 218 | + if handled_blocks.count(addr) > 0:
|
| 219 | + print("[%d:%d:%08X] Block appears in multiple free lists" % (i, j, addr))
|
| 220 | + else: handled_blocks.append(addr)
|
| 221 | + if fatal:
|
| 222 | + print("Fatal error in block chain, continuing with next chain")
|
| 223 | + break
|
| 224 | + block = block.get_next_free()
|
| 225 | +
|
| 226 | +for addr in free_blocks:
|
| 227 | + if handled_blocks.count(addr) != 1:
|
| 228 | + print("Free block %08X does not appear in any free list" % (addr))
|