summaryrefslogtreecommitdiff
path: root/utils/hwpatcher/arm.lua
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2014-06-24 18:04:17 +0200
committerAmaury Pouly <amaury.pouly@gmail.com>2014-06-24 18:07:56 +0200
commitc9a028cc183d638c16ca9a8858b783b1830be16f (patch)
tree1bd29843ec0b8d3eb80a6209d1c4e32144175dfe /utils/hwpatcher/arm.lua
parent761f59c9e3be0ffd77d2dc1b8095a3b877badeda (diff)
downloadrockbox-c9a028cc183d638c16ca9a8858b783b1830be16f.tar.gz
rockbox-c9a028cc183d638c16ca9a8858b783b1830be16f.zip
Introduce hwpatcher, a tool to patch binaries
This tool is a scriptable (lua) tool to patch binaries, it supports: - raw binary - ELF - SB(v1/v2) It also contains some basic routines to parse and generate useful arm/thumb code like jump or register load/store. This is very useful to take a firmware and patch an interrupt vector or some code to jump to an extra payload added to the binary. Examples are provided for several STMP based target which the payload is expected to be hwstub, and also for the Sansa View. A typical patcher usually requires three elements: - the lua patcher itself - the payload (hwstub for example) - (optional) a small stub either to jump properly to the payload or determine under which circumstance to do the jump (hold a key for example) Change-Id: I6d36020a3bc9e636615ac8221b7591ade5f251e3
Diffstat (limited to 'utils/hwpatcher/arm.lua')
-rw-r--r--utils/hwpatcher/arm.lua205
1 files changed, 205 insertions, 0 deletions
diff --git a/utils/hwpatcher/arm.lua b/utils/hwpatcher/arm.lua
new file mode 100644
index 0000000000..0211244d11
--- /dev/null
+++ b/utils/hwpatcher/arm.lua
@@ -0,0 +1,205 @@
1--[[
2hwpatcher arm decoding/encoding library
3
4]]--
5arm = {}
6
7-- determines whether an address is in Thumb code or not
8function arm.is_thumb(addr)
9 return bit32.extract(addr.addr, 0) == 1
10end
11
12-- translate address to real address (ie without Thumb bit)
13-- produces an error if address is not properly aligned in ARM mode
14function arm.xlate_addr(addr)
15 local res = hwp.deepcopy(addr)
16 if arm.is_thumb(addr) then
17 res.addr = bit32.replace(addr.addr, 0, 0)
18 elseif bit32.extract(addr.addr, 0, 2) ~= 0 then
19 error("ARM address is not word-aligned")
20 end
21 return res
22end
23
24-- switch between arm and thumb
25function arm.to_thumb(addr)
26 local res = hwp.deepcopy(addr)
27 res.addr = bit32.bor(addr.addr, 1)
28 return res
29end
30
31function arm.to_arm(addr)
32 return arm.xlate_addr(addr)
33end
34
35-- sign extend a value to 32-bits
36-- only the lower 'bits' bits are considered, everything else is trashed
37-- watch out arithmetic vs logical shift !
38function arm.sign32(v)
39 if bit32.extract(v, 31) == 1 then
40 return -1 - bit32.bnot(v)
41 else
42 return v
43 end
44end
45function arm.sign_extend(val, bits)
46 return arm.sign32(bit32.arshift(bit32.lshift(val, 32 - bits), 32 - bits))
47end
48
49-- check that a signed value fits in some field
50function arm.check_sign_truncation(val, bits)
51 return val == arm.sign_extend(val, bits)
52end
53
54-- create a branch description
55function arm.make_branch(addr, link)
56 local t = {type = "branch", addr = addr, link = link}
57 local branch_to_string = function(self)
58 return string.format("branch(%s,%s)", self.addr, self.link)
59 end
60 setmetatable(t, {__tostring = branch_to_string})
61 return t
62end
63
64-- parse a jump and returns its description
65function arm.parse_branch(fw, addr)
66 local opcode = hwp.read32(fw, arm.xlate_addr(addr))
67 if arm.is_thumb(addr) then
68 if bit32.band(opcode, 0xf800) ~= 0xf000 then
69 error("first instruction is not a bl(x) prefix")
70 end
71 local to_thumb = false
72 if bit32.band(opcode, 0xf8000000) == 0xf8000000 then
73 to_thumb = true
74 elseif bit32.band(opcode, 0xf8000000) ~= 0xe8000000 then
75 error("second instruction is not a bl(x) suffix")
76 end
77 local dest = hwp.make_addr(bit32.lshift(arm.sign_extend(opcode, 11), 12) +
78 arm.xlate_addr(addr).addr + 4 +
79 bit32.rshift(bit32.band(opcode, 0x7ff0000), 16) * 2, addr.section)
80 if to_thumb then
81 dest = arm.to_thumb(dest)
82 else
83 dest.addr = bit32.replace(dest.addr, 0, 0, 2)
84 end
85 return arm.make_branch(dest, true)
86 else
87 if bit32.band(opcode, 0xfe000000) == 0xfa000000 then -- BLX
88 local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 +
89 bit32.extract(opcode, 24) * 2 + arm.xlate_addr(addr).addr, addr.section)
90 return arm.make_branch(arm.to_thumb(dest), true)
91 elseif bit32.band(opcode, 0xfe000000) == 0xea000000 then -- B(L)
92 local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 +
93 arm.xlate_addr(addr).addr, addr.section)
94 return arm.make_branch(arm.to_arm(dest), bit32.extract(opcode, 24))
95 else
96 error("instruction is not a valid branch")
97 end
98 end
99end
100
101-- generate the encoding of a branch
102-- if the branch cannot be encoded using an immediate branch, it is generated
103-- with an indirect form like a ldr, using a pool
104function arm.write_branch(fw, addr, dest, pool)
105 local offset = arm.xlate_addr(dest.addr).addr - arm.xlate_addr(addr).addr
106 local exchange = arm.is_thumb(addr) ~= arm.is_thumb(dest.addr)
107 local opcode = 0
108 if arm.is_thumb(addr) then
109 if not dest.link then
110 return arm.write_load_pc(fw, addr, dest, pool)
111 end
112 offset = offset - 4 -- in Thumb, PC+4 relative
113 -- NOTE: BLX is undefined if the resulting offset has bit 0 set, follow
114 -- the procedure from the manual to ensure correct operation
115 if bit32.extract(offset, 1) ~= 0 then
116 offset = offset + 2
117 end
118 offset = offset / 2
119 if not arm.check_sign_truncation(offset, 22) then
120 error("destination is too far for immediate branch from thumb")
121 end
122 opcode = 0xf000 + -- BL/BLX prefix
123 bit32.band(bit32.rshift(offset, 11), 0x7ff) + -- offset (high part)
124 bit32.lshift(exchange and 0xe800 or 0xf800,16) + -- BLX suffix
125 bit32.lshift(bit32.band(offset, 0x7ff), 16) -- offset (low part)
126 else
127 offset = offset - 8 -- in ARM, PC+8 relative
128 if exchange and not dest.link then
129 return arm.write_load_pc(fw, addr, dest, pool)
130 end
131 offset = offset / 4
132 if not arm.check_sign_truncation(offset, 24) then
133 if pool == nil then
134 error("destination is too far for immediate branch from arm (no pool available)")
135 else
136 return arm.write_load_pc(fw, addr, dest, pool)
137 end
138 end
139 opcode = bit32.lshift(exchange and 0xf or 0xe, 28) + -- BLX / AL cond
140 bit32.lshift(0xa, 24) + -- branch
141 bit32.lshift(exchange and bit32.extract(offset, 1) or dest.link and 1 or 0, 24) + -- link / bit1
142 bit32.band(offset, 0xffffff)
143 end
144 return hwp.write32(fw, arm.xlate_addr(addr), opcode)
145end
146
147function arm.write_load_pc(fw, addr, dest, pool)
148 -- write pool
149 hwp.write32(fw, pool, dest.addr.addr)
150 -- write "ldr pc, [pool]"
151 local opcode
152 if arm.is_thumb(addr) then
153 error("unsupported pc load in thumb mode")
154 else
155 local offset = pool.addr - arm.xlate_addr(addr).addr - 8 -- ARM is PC+8 relative
156 local add = offset >= 0 and 1 or 0
157 offset = math.abs(offset)
158 opcode = bit32.lshift(0xe, 28) + -- AL cond
159 bit32.lshift(1, 26) + -- ldr/str
160 bit32.lshift(1, 24) + -- P
161 bit32.lshift(add, 23) + -- U
162 bit32.lshift(1, 20) + -- ldr
163 bit32.lshift(15, 16) + -- Rn=PC
164 bit32.lshift(15, 12) + -- Rd=PC
165 bit32.band(offset, 0xfff)
166 end
167 return hwp.write32(fw, arm.xlate_addr(addr), opcode)
168end
169
170-- generate the encoding of a "bx lr"
171function arm.write_return(fw, addr)
172 if arm.is_thumb(addr) then
173 error("unsupported return from thumb code")
174 end
175 local opcode = bit32.lshift(0xe, 28) + -- AL cond
176 bit32.lshift(0x12, 20) + -- BX
177 bit32.lshift(1, 4) + -- BX
178 bit32.lshift(0xfff, 8) + -- SBO
179 14 -- LR
180 hwp.write32(fw, arm.xlate_addr(addr), opcode)
181end
182
183function arm.write_xxx_regs(fw, addr, load)
184 if arm.is_thumb(addr) then
185 error("unsupported save/restore regs from thumb code")
186 end
187 -- STMFD sp!,{r0-r12, lr}
188 local opcode = bit32.lshift(0xe, 28) + -- AL cond
189 bit32.lshift(0x4, 25) + -- STM/LDM
190 bit32.lshift(load and 0 or 1,24) + -- P
191 bit32.lshift(load and 1 or 0, 23) + -- U
192 bit32.lshift(1, 21) +-- W
193 bit32.lshift(load and 1 or 0, 20) + -- L
194 bit32.lshift(13, 16) + -- base = SP
195 0x5fff -- R0-R12,LR
196 return hwp.write32(fw, addr, opcode)
197end
198
199function arm.write_save_regs(fw, addr)
200 return arm.write_xxx_regs(fw, addr, false)
201end
202
203function arm.write_restore_regs(fw, addr)
204 return arm.write_xxx_regs(fw, addr, true)
205end \ No newline at end of file