1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
--[[
hwpatcher arm decoding/encoding library
]]--
arm = {}
-- determines whether an address is in Thumb code or not
function arm.is_thumb(addr)
return bit32.extract(addr.addr, 0) == 1
end
-- translate address to real address (ie without Thumb bit)
-- produces an error if address is not properly aligned in ARM mode
function arm.xlate_addr(addr)
local res = hwp.deepcopy(addr)
if arm.is_thumb(addr) then
res.addr = bit32.replace(addr.addr, 0, 0)
elseif bit32.extract(addr.addr, 0, 2) ~= 0 then
error("ARM address is not word-aligned")
end
return res
end
-- switch between arm and thumb
function arm.to_thumb(addr)
local res = hwp.deepcopy(addr)
res.addr = bit32.bor(addr.addr, 1)
return res
end
function arm.to_arm(addr)
return arm.xlate_addr(addr)
end
-- sign extend a value to 32-bits
-- only the lower 'bits' bits are considered, everything else is trashed
-- watch out arithmetic vs logical shift !
function arm.sign32(v)
if bit32.extract(v, 31) == 1 then
return -1 - bit32.bnot(v)
else
return v
end
end
function arm.sign_extend(val, bits)
return arm.sign32(bit32.arshift(bit32.lshift(val, 32 - bits), 32 - bits))
end
-- check that a signed value fits in some field
function arm.check_sign_truncation(val, bits)
return val == arm.sign_extend(val, bits)
end
-- create a branch description
function arm.make_branch(addr, link)
local t = {type = "branch", addr = addr, link = link}
local branch_to_string = function(self)
return string.format("branch(%s,%s)", self.addr, self.link)
end
setmetatable(t, {__tostring = branch_to_string})
return t
end
-- parse a jump and returns its description
function arm.parse_branch(fw, addr)
local opcode = hwp.read32(fw, arm.xlate_addr(addr))
if arm.is_thumb(addr) then
if bit32.band(opcode, 0xf800) ~= 0xf000 then
error("first instruction is not a bl(x) prefix")
end
local to_thumb = false
if bit32.band(opcode, 0xf8000000) == 0xf8000000 then
to_thumb = true
elseif bit32.band(opcode, 0xf8000000) ~= 0xe8000000 then
error("second instruction is not a bl(x) suffix")
end
local dest = hwp.make_addr(bit32.lshift(arm.sign_extend(opcode, 11), 12) +
arm.xlate_addr(addr).addr + 4 +
bit32.rshift(bit32.band(opcode, 0x7ff0000), 16) * 2, addr.section)
if to_thumb then
dest = arm.to_thumb(dest)
else
dest.addr = bit32.replace(dest.addr, 0, 0, 2)
end
return arm.make_branch(dest, true)
else
if bit32.band(opcode, 0xfe000000) == 0xfa000000 then -- BLX
local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 +
bit32.extract(opcode, 24) * 2 + arm.xlate_addr(addr).addr, addr.section)
return arm.make_branch(arm.to_thumb(dest), true)
elseif bit32.band(opcode, 0xfe000000) == 0xea000000 then -- B(L)
local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 +
arm.xlate_addr(addr).addr, addr.section)
return arm.make_branch(arm.to_arm(dest), bit32.extract(opcode, 24))
else
error("instruction is not a valid branch")
end
end
end
-- generate the encoding of a branch
-- if the branch cannot be encoded using an immediate branch, it is generated
-- with an indirect form like a ldr, using a pool
function arm.write_branch(fw, addr, dest, pool)
local offset = arm.xlate_addr(dest.addr).addr - arm.xlate_addr(addr).addr
local exchange = arm.is_thumb(addr) ~= arm.is_thumb(dest.addr)
local opcode = 0
if arm.is_thumb(addr) then
if not dest.link then
return arm.write_load_pc(fw, addr, dest, pool)
end
offset = offset - 4 -- in Thumb, PC+4 relative
-- NOTE: BLX is undefined if the resulting offset has bit 0 set, follow
-- the procedure from the manual to ensure correct operation
if bit32.extract(offset, 1) ~= 0 then
offset = offset + 2
end
offset = offset / 2
if not arm.check_sign_truncation(offset, 22) then
error("destination is too far for immediate branch from thumb")
end
opcode = 0xf000 + -- BL/BLX prefix
bit32.band(bit32.rshift(offset, 11), 0x7ff) + -- offset (high part)
bit32.lshift(exchange and 0xe800 or 0xf800,16) + -- BLX suffix
bit32.lshift(bit32.band(offset, 0x7ff), 16) -- offset (low part)
else
offset = offset - 8 -- in ARM, PC+8 relative
if exchange and not dest.link then
return arm.write_load_pc(fw, addr, dest, pool)
end
offset = offset / 4
if not arm.check_sign_truncation(offset, 24) then
if pool == nil then
error("destination is too far for immediate branch from arm (no pool available)")
else
return arm.write_load_pc(fw, addr, dest, pool)
end
end
opcode = bit32.lshift(exchange and 0xf or 0xe, 28) + -- BLX / AL cond
bit32.lshift(0xa, 24) + -- branch
bit32.lshift(exchange and bit32.extract(offset, 1) or dest.link and 1 or 0, 24) + -- link / bit1
bit32.band(offset, 0xffffff)
end
return hwp.write32(fw, arm.xlate_addr(addr), opcode)
end
function arm.write_load_pc(fw, addr, dest, pool)
-- write pool
hwp.write32(fw, pool, dest.addr.addr)
-- write "ldr pc, [pool]"
local opcode
if arm.is_thumb(addr) then
error("unsupported pc load in thumb mode")
else
local offset = pool.addr - arm.xlate_addr(addr).addr - 8 -- ARM is PC+8 relative
local add = offset >= 0 and 1 or 0
offset = math.abs(offset)
opcode = bit32.lshift(0xe, 28) + -- AL cond
bit32.lshift(1, 26) + -- ldr/str
bit32.lshift(1, 24) + -- P
bit32.lshift(add, 23) + -- U
bit32.lshift(1, 20) + -- ldr
bit32.lshift(15, 16) + -- Rn=PC
bit32.lshift(15, 12) + -- Rd=PC
bit32.band(offset, 0xfff)
end
return hwp.write32(fw, arm.xlate_addr(addr), opcode)
end
-- generate the encoding of a "bx lr"
function arm.write_return(fw, addr)
if arm.is_thumb(addr) then
error("unsupported return from thumb code")
end
local opcode = bit32.lshift(0xe, 28) + -- AL cond
bit32.lshift(0x12, 20) + -- BX
bit32.lshift(1, 4) + -- BX
bit32.lshift(0xfff, 8) + -- SBO
14 -- LR
hwp.write32(fw, arm.xlate_addr(addr), opcode)
end
function arm.write_xxx_regs(fw, addr, load)
if arm.is_thumb(addr) then
error("unsupported save/restore regs from thumb code")
end
-- STMFD sp!,{r0-r12, lr}
local opcode = bit32.lshift(0xe, 28) + -- AL cond
bit32.lshift(0x4, 25) + -- STM/LDM
bit32.lshift(load and 0 or 1,24) + -- P
bit32.lshift(load and 1 or 0, 23) + -- U
bit32.lshift(1, 21) +-- W
bit32.lshift(load and 1 or 0, 20) + -- L
bit32.lshift(13, 16) + -- base = SP
0x5fff -- R0-R12,LR
return hwp.write32(fw, addr, opcode)
end
function arm.write_save_regs(fw, addr)
return arm.write_xxx_regs(fw, addr, false)
end
function arm.write_restore_regs(fw, addr)
return arm.write_xxx_regs(fw, addr, true)
end
|