Skip to content

Commit 1483c71

Browse files
committed
Add MCU Loader
1 parent ab51ee6 commit 1483c71

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed

qiling/loader/mcu.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4+
# Built on top of Unicorn emulator (www.unicorn-engine.org)
5+
6+
7+
import struct
8+
from elftools.elf.elffile import ELFFile
9+
10+
from qiling.const import *
11+
from qiling.core import Qiling
12+
13+
from .loader import QlLoader
14+
15+
16+
class IhexParser:
17+
def __init__(self, path):
18+
self.pc = None
19+
self.base = None
20+
self.mem = []
21+
self.segments = []
22+
23+
with open(path, 'r') as f:
24+
for line in f.read().splitlines():
25+
self.parse_line(line.strip())
26+
27+
begin, end, bindata = 0, 0, b''
28+
for addr, data in self.mem:
29+
if addr != end:
30+
self.add_segment(begin, end, bindata)
31+
begin, end, bindata = addr, addr + len(data), data
32+
else:
33+
bindata += data
34+
end += len(data)
35+
self.add_segment(begin, end, bindata)
36+
37+
def add_segment(self, begin, end, bindata):
38+
if len(bindata) > 0 and end - begin == len(bindata):
39+
self.segments.append((begin, end, bindata))
40+
41+
def parse_line(self, line):
42+
if len(line) < 9:
43+
return
44+
45+
size = int(line[1: 3], 16)
46+
type = line[7: 9]
47+
48+
addr = bytes.fromhex(line[3: 7])
49+
data = bytes.fromhex(line[9: 9 + size * 2])
50+
51+
if type == '04':
52+
self.base = struct.unpack('>I', data + b'\x00\x00')[0]
53+
elif type == '05':
54+
self.pc = struct.unpack('>I', data)[0]
55+
elif type == '00':
56+
offset = struct.unpack('>I', b'\x00\x00' + addr)[0]
57+
self.mem.append((self.base + offset, data))
58+
59+
class QlLoaderMCU(QlLoader):
60+
def __init__(self, ql:Qiling):
61+
super(QlLoaderMCU, self).__init__(ql)
62+
63+
self.load_address = 0
64+
self.path = self.argv[0]
65+
self.filetype = self.guess_filetype()
66+
67+
if self.filetype == 'elf':
68+
self.elf = ELFFile(open(self.path, 'rb'))
69+
70+
elif self.filetype == 'bin':
71+
self.map_address = self.argv[1]
72+
73+
else: # self.filetype == 'hex':
74+
self.ihex = IhexParser(self.path)
75+
76+
def guess_filetype(self):
77+
if self.path.endswith('.elf'):
78+
return 'elf'
79+
80+
if self.path.endswith('.bin'):
81+
return 'bin'
82+
83+
if self.path.endswith('.hex'):
84+
return 'hex'
85+
86+
return 'elf'
87+
88+
def reset(self):
89+
if self.filetype == 'elf':
90+
for segment in self.elf.iter_segments():
91+
if segment['p_type'] != 'PT_LOAD':
92+
continue
93+
94+
for section in self.elf.iter_sections():
95+
if segment.section_in_segment(section):
96+
self.ql.mem.write(section.header['sh_addr'], section.data())
97+
98+
# TODO: load symbol table
99+
100+
elif self.filetype == 'bin':
101+
with open(self.path, 'rb') as f:
102+
self.ql.mem.write(self.map_address, f.read())
103+
104+
else: # self.filetype == 'hex':
105+
for begin, _, data in self.ihex.segments:
106+
self.ql.mem.write(begin, data)
107+
108+
self.ql.reg.write('lr', 0xffffffff)
109+
self.ql.reg.write('msp', self.ql.mem.read_ptr(0x0))
110+
self.ql.reg.write('pc' , self.entry_point)
111+
112+
def run(self):
113+
## Load memory / mmio / peripheral from profile
114+
for section_name in self.ql.profile.sections():
115+
section = self.ql.profile[section_name]
116+
if section['type'] == 'memory':
117+
size = eval(section['size'])
118+
base = eval(section['base'])
119+
self.ql.mem.map(base, size, info=f'[{section_name}]')
120+
if section_name == 'FLASH':
121+
self.ql.hw.setup_remap(0, base, size, info=f'[CODE]')
122+
123+
if section['type'] == 'bitband':
124+
size = eval(section['size']) * 32
125+
base = eval(section['base'])
126+
alias = eval(section['alias'])
127+
self.ql.hw.setup_bitband(base, alias, size, info=f'[{section_name}]')
128+
129+
if section['type'] == 'mmio':
130+
size = eval(section['size'])
131+
base = eval(section['base'])
132+
self.ql.hw.setup_mmio(base, size, info=f'[{section_name}]')
133+
134+
if section['type'] == 'core periperal':
135+
self.ql.hw.create(section_name.lower())
136+
137+
## Handle interrupt from instruction execution
138+
self.ql.hook_intr(self.ql.arch.intr_cb)
139+
140+
self.reset()
141+
142+
@property
143+
def entry_point(self):
144+
return self.ql.mem.read_ptr(0x4)

0 commit comments

Comments
 (0)