refactored hunk scanner

This commit is contained in:
Christian Vogelgsang 2011-03-17 18:27:30 +01:00
parent dab43e3723
commit 256b9e7c78
4 changed files with 178 additions and 126 deletions

View File

@ -5,23 +5,28 @@ from pyadf import Adf, AdfIOException
ST_DIR = 2
ST_FILE = -3
def scan_dir(file_name, visit_func, dir_path, adf):
entries = adf.ls_dir(dir_path)
for entry in entries:
name = entry.fname
if dir_path == "":
path = name
else:
path = dir_path + "/" + name
if entry.ftype == ST_DIR:
scan_dir(file_name, visit_func, path, adf)
elif entry.ftype == ST_FILE:
try:
data = adf.get_file(path)
visit_func(file_name, path, data)
except AdfIOException, info:
pass
class ADFScanner:
def scan_adf(file_name, visit_func):
adf = Adf(file_name, mode='r')
scan_dir(file_name, visit_func, "", adf)
def __init__(self, handler):
self.handler = handler
def scan_dir(self, file_name, dir_path, adf):
entries = adf.ls_dir(dir_path)
for entry in entries:
name = entry.fname
if dir_path == "":
path = name
else:
path = dir_path + "/" + name
if entry.ftype == ST_DIR:
self.scan_dir(file_name, path, adf)
elif entry.ftype == ST_FILE:
try:
data = adf.get_file(path)
self.handler(file_name, path, data)
except AdfIOException, info:
pass
def scan_adf(self, file_name):
adf = Adf(file_name, mode='r')
self.scan_dir(file_name, "", adf)

View File

@ -31,32 +31,36 @@ HUNK_RELOC32SHORT = 1020
HUNK_RELRELOC32 = 1021
HUNK_ABSRELOC16 = 1022
hunk_names = [
"HUNK_UNIT",
"HUNK_NAME",
"HUNK_CODE",
"HUNK_DATA",
"HUNK_BSS",
"HUNK_ABSRELOC32",
"HUNK_RELRELOC16",
"HUNK_RELRELOC8",
"HUNK_EXT",
"HUNK_SYMBOL",
"HUNK_DEBUG",
"HUNK_END",
"HUNK_HEADER",
"",
"HUNK_OVERLAY",
"HUNK_BREAK",
"HUNK_DREL32",
"HUNK_DREL16",
"HUNK_DREL8",
"HUNK_LIB",
"HUNK_INDEX",
"HUNK_RELOC32SHORT",
"HUNK_RELRELOC32",
"HUNK_ABSRELOC16"
]
HUNK_PPC_CODE = 1257
HUNK_RELRELOC26 = 1260
hunk_names = {
HUNK_UNIT : "HUNK_UNIT",
HUNK_NAME : "HUNK_NAME",
HUNK_CODE : "HUNK_CODE",
HUNK_DATA : "HUNK_DATA",
HUNK_BSS : "HUNK_BSS",
HUNK_ABSRELOC32 : "HUNK_ABSRELOC32",
HUNK_RELRELOC16 : "HUNK_RELRELOC16",
HUNK_RELRELOC8 : "HUNK_RELRELOC8",
HUNK_EXT : "HUNK_EXT",
HUNK_SYMBOL : "HUNK_SYMBOL",
HUNK_DEBUG : "HUNK_DEBUG",
HUNK_END : "HUNK_END",
HUNK_HEADER : "HUNK_HEADER",
HUNK_OVERLAY : "HUNK_OVERLAY",
HUNK_BREAK : "HUNK_BREAK",
HUNK_DREL32 : "HUNK_DREL32",
HUNK_DREL16 : "HUNK_DREL16",
HUNK_DREL8 : "HUNK_DREL8",
HUNK_LIB : "HUNK_LIB",
HUNK_INDEX : "HUNK_INDEX",
HUNK_RELOC32SHORT : "HUNK_RELOC32SHORT",
HUNK_RELRELOC32 : "HUNK_RELRELOC32",
HUNK_ABSRELOC16 : "HUNK_ABSRELOC16",
HUNK_PPC_CODE : "HUNK_PPC_CODE",
HUNK_RELRELOC26 : "HUNK_RELRELOC26"
}
EXT_SYMB = 0
EXT_DEF = 1
@ -100,6 +104,13 @@ RESULT_NO_HUNK_FILE = 1
RESULT_INVALID_HUNK_FILE = 2
RESULT_UNSUPPORTED_HUNKS = 3
result_names = {
RESULT_OK : "RESULT_OK",
RESULT_NO_HUNK_FILE : "RESULT_NO_HUNK_FILE",
RESULT_INVALID_HUNK_FILE : "RESULT_INVALID_HUNK_FILE",
RESULT_UNSUPPORTED_HUNKS : "RESULT_UNSUPPORTED_HUNKS"
}
HUNKF_ADVISORY = 1<<29
HUNKF_CHIP = 1<<30
HUNKF_FAST = 1<<31
@ -114,7 +125,6 @@ class HunkFile:
def __init__(self):
self.hunks = []
self.error_string = None
self.v37_compat = True
def read_long(self, f):
data = f.read(4)
@ -160,8 +170,7 @@ class HunkFile:
return strtab[offset:end]
def is_valid_first_hunk_type(self, hunk_type):
return hunk_type == HUNK_HEADER or hunk_type == HUNK_LIB or hunk_type == HUNK_UNIT \
or hunk_type == HUNK_CODE # strange?
return hunk_type == HUNK_HEADER or hunk_type == HUNK_LIB or hunk_type == HUNK_UNIT
def parse_header(self, f, hunk):
names = []
@ -417,7 +426,7 @@ class HunkFile:
total_size -= 6
name = self.get_index_name(strtab, name_offset)
d = { 'name':name, 'value':def_value,'type':def_type}
set_mem_flags(d,def_flags,14)
self.set_mem_flags(d,def_flags,14)
defs.append(d)
# align hunk
@ -499,19 +508,21 @@ class HunkFile:
"""Read a hunk file and build internal hunk structure
Return status and set self.error_string on failure
"""
def read_file(self, hfile):
def read_file(self, hfile, v37_compat=None):
with open(hfile) as f:
return self.read_file_obj(hfile, f)
return self.read_file_obj(hfile, f, v37_compat)
"""Read a hunk from memory"""
def read_mem(self, name, data):
def read_mem(self, name, data, v37_compat=None):
fobj = StringIO.StringIO(data)
return self.read_file_obj(name, fobj)
return self.read_file_obj(name, fobj, v37_compat)
def read_file_obj(self, hfile, f):
def read_file_obj(self, hfile, f, v37_compat):
self.hunks = []
is_first_hunk = True
was_end = False
was_potentail_v37_hunk = False
self.error_string = None
while True:
# read hunk type
@ -531,40 +542,51 @@ class HunkFile:
hunk_flags = hunk_raw_type & HUNK_FLAGS_MASK
# check range of hunk type
if hunk_type < HUNK_MIN or hunk_type > HUNK_MAX:
if not hunk_names.has_key(hunk_type):
# no hunk file?
if is_first_hunk:
self.error_string = "No valid hunk file: '%s' type was %d" % (hfile, hunk_type)
self.error_string = "No hunk file: '%s' type was %d" % (hfile, hunk_type)
return RESULT_NO_HUNK_FILE
elif was_end:
# garbage after an end tag is ignored
return RESULT_OK
elif was_potentail_v37_hunk:
# auto fix v37 -> reread whole file
f.seek(0)
return self.read_file_obj(hfile, f, True)
else:
self.error_string = "Invalid hunk type %d found at @%08x" % (hunk_type,f.tell())
self.error_string = "Invalid hunk type %d/%x found at @%08x" % (hunk_type,hunk_type,f.tell())
return RESULT_INVALID_HUNK_FILE
else:
# check for valid first hunk type
if is_first_hunk and not self.is_valid_first_hunk_type(hunk_type):
self.error_string = "No valid hunk file: '%s' first hunk type was %d" % (hfile, hunk_type)
return RESULT_INVALID_HUNK_FILE
self.error_string = "No hunk file: '%s' first hunk type was %d" % (hfile, hunk_type)
return RESULT_NO_HUNK_FILE
is_first_hunk = False
was_end = False
was_potentail_v37_hunk = False
hunk = { 'type' : hunk_type }
self.hunks.append(hunk)
hunk['type_name'] = hunk_names[hunk_type - HUNK_MIN]
hunk['type_name'] = hunk_names[hunk_type]
self.set_mem_flags(hunk, hunk_flags, 30)
# V37 fix
if self.v37_compat and hunk_type == HUNK_DREL32:
hunk_type = HUNK_RELOC32SHORT
# V37 fix?
if hunk_type == HUNK_DREL32:
# try to fix automatically...
if v37_compat == None:
was_potentail_v37_hunk = True
# fix was forced
elif v37_compat:
hunk_type = HUNK_RELOC32SHORT
hunk['fixes'] = 'v37'
# ----- HUNK_HEADER -----
if hunk_type == HUNK_HEADER:
result = self.parse_header(f,hunk)
# ----- HUNK_CODE/HUNK_DATA ------
elif hunk_type == HUNK_CODE or hunk_type == HUNK_DATA:
elif hunk_type == HUNK_CODE or hunk_type == HUNK_DATA or hunk_type == HUNK_PPC_CODE:
result = self.parse_code_or_data(f,hunk)
# ---- HUNK_BSS ----
elif hunk_type == HUNK_BSS:
@ -574,6 +596,10 @@ class HunkFile:
or hunk_type == HUNK_RELRELOC8 or hunk_type == HUNK_RELRELOC16 or hunk_type == HUNK_ABSRELOC32 \
or hunk_type ==HUNK_DREL32 or hunk_type == HUNK_DREL16 or hunk_type == HUNK_DREL8:
result = self.parse_reloc(f,hunk)
# auto fix v37 bug?
if hunk_type == HUNK_DREL32 and result != RESULT_OK and v37_compat == None:
f.seek(0)
return self.read_file_obj(hfile, f, True)
# ---- HUNK_<reloc short> -----
elif hunk_type == HUNK_RELOC32SHORT:
result = self.parse_reloc_short(f,hunk)
@ -614,5 +640,5 @@ class HunkFile:
if result != RESULT_OK:
return result
return RESULT_OK
return RESULT_OK

53
amitools/HunkScanner.py Normal file
View File

@ -0,0 +1,53 @@
# scan a set of hunks
import os
import Hunk
class HunkScanner:
def __init__(self, handler, use_adf = False, ignore_no_hunk = True):
self.ignore_no_hunk = ignore_no_hunk
self.handler = handler
if use_adf:
import ADFScanner
self.adf_scanner = ADFScanner.ADFScanner(lambda a,b,c: self.handle_adf_file(a,b,c))
else:
self.adf_scanner = None
def call_handler(self, path, hunk_file, return_code):
if return_code == Hunk.RESULT_NO_HUNK_FILE and self.ignore_no_hunk:
return
self.handler(path, hunk_file, return_code)
def handle_adf_file(self, img_path, file_path, data):
vpath = img_path + ":" + file_path
hf = Hunk.HunkFile()
result = hf.read_mem(vpath, data)
self.call_handler(vpath, hf, result)
def handle_adf(self, path):
if self.adf_scanner != None:
self.adf_scanner.scan_adf(path)
def handle_file(self, path):
if path.lower().endswith(".adf"):
self.handle_adf(path)
return
hf = Hunk.HunkFile()
result = hf.read_file(path)
self.call_handler(path, hf, result)
def handle_dir(self, path):
for root, dirs, files in os.walk(path):
for name in files:
self.handle_file(os.path.join(root,name))
for name in dirs:
self.handle_dir(os.path.join(root,name))
def handle_path(self, path):
if os.path.isdir(path):
self.handle_dir(path)
elif os.path.isfile(path):
self.handle_file(path)

View File

@ -6,81 +6,49 @@
#
# written by Christian Vogelgsang (chris@vogelgsang.org)
import os, sys
import sys
import argparse
import pprint
from amitools.HunkScanner import HunkScanner
from amitools import Hunk
from amitools import ADFScanner
def show_hunks_brief(hunks):
for hunk in hunks:
print hunk['name']
def dump_hunks(hunks):
def print_pretty(data):
pp = pprint.PrettyPrinter(indent=2)
pp.pprint(hunks)
pp.pprint(data)
# ----- handle paths -----
# ----- commands -----
def check_file(path, hunk_file, result):
if result == Hunk.RESULT_OK:
print path,"OK"
class Validator:
def __init__(self, args):
self.counts = {}
self.args = args
def handle_file(self, path, hunk_file, error_code):
if not self.counts.has_key(error_code):
self.counts[error_code] = 0
self.counts[error_code] += 1
print path, error_code, hunk_file.error_string
if args.dump:
dump_hunks(hunk_file.hunks)
elif result == Hunk.RESULT_NO_HUNK_FILE:
#print "No:",path,hunk_file.error_string
pass
elif result == Hunk.RESULT_INVALID_HUNK_FILE:
print path,"Invalid:",hunk_file.error_string
if args.dump:
dump_hunks(hunk_file.hunks)
sys.exit(1)
elif result == Hunk.RESULT_UNSUPPORTED_HUNKS:
print path,"Unsupported:",hunk_file.error_string
if args.dump:
dump_hunks(hunk_file.hunks)
sys.exit(1)
def handle_adf_file(img_path,file_path,data):
vpath = img_path + ":" + file_path
hf = Hunk.HunkFile()
result = hf.read_mem(vpath, data)
check_file(vpath, hf, result)
def handle_adf(path):
ADFScanner.scan_adf(path, handle_adf_file)
def handle_file(path):
global args
print_pretty(hunk_file.hunks)
if path.endswith(".adf"):
handle_adf(path)
return
hf = Hunk.HunkFile()
result = hf.read_file(path)
check_file(path, hf, result)
def handle_dir(path):
for root, dirs, files in os.walk(path):
for name in files:
handle_file(os.path.join(root,name))
for name in dirs:
handle_dir(os.path.join(root,name))
def handle_path(path):
if os.path.isdir(path):
handle_dir(path)
elif os.path.isfile(path):
handle_file(path)
def result(self):
for code in self.counts.keys():
print Hunk.result_names[code],":",self.counts[code]
return 0
# ----- main -----
parser = argparse.ArgumentParser()
#parser.add_argument('command')
parser.add_argument('hunkfiles', nargs='+')
parser.add_argument('-d', '--dump', action='store_true', default=False, help="dump the hunk structure")
parser.add_argument('-a', '--adf', action='store_true', default=False, help="enable adf scanner (requires adflib)")
args = parser.parse_args()
for p in args.hunkfiles:
handle_path(p)
print "done"
# call scanner and process all files with selected command
cmd = Validator(args)
scanner = HunkScanner(cmd.handle_file, use_adf=args.adf)
for path in args.hunkfiles:
scanner.handle_path(path)
res = cmd.result()
sys.exit(res)