Skip to content

Commit d0cc1cd

Browse files
committed
CHB:ARM: add jumptable pattern found in clang-18.1.3 compiled binary
1 parent 8e6ad58 commit d0cc1cd

File tree

2 files changed

+175
-1
lines changed

2 files changed

+175
-1
lines changed

CodeHawk/CHB/bchlibarm32/bCHARMJumptable.ml

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,40 @@ let ldrtest
240240
| _ -> false
241241

242242

243+
let ldrs2test
244+
(tmpreg: arm_reg_t)
245+
(basereg: arm_reg_t)
246+
(indexreg: arm_reg_t)
247+
(instr: arm_assembly_instruction_int) =
248+
match instr#get_opcode with
249+
| LoadRegister (ACCAlways, rt, rn, rm, mem, false) ->
250+
let _ =
251+
chlog#add "ldrs2-test"
252+
(LBLOCK [instr#get_address#toPretty;
253+
STR ": ";
254+
STR (BCHARMOpcodeRecords.arm_opcode_to_string instr#get_opcode);
255+
STR " with ";
256+
STR (BCHCPURegisters.armreg_to_string tmpreg);
257+
STR ", ";
258+
STR (BCHCPURegisters.armreg_to_string basereg);
259+
STR ", ";
260+
STR (BCHCPURegisters.armreg_to_string indexreg)
261+
]) in
262+
rt#is_register
263+
&& rt#get_register = tmpreg
264+
&& rn#is_register
265+
&& rn#get_register = basereg
266+
&& rm#is_register
267+
&& rm#get_register = indexreg
268+
&& (match mem#get_kind with
269+
| ARMOffsetAddress (_, _, offset, _, _, _, _) ->
270+
(match offset with
271+
| ARMShiftedIndexOffset (_, ARMImmSRT (SRType_LSL, 2), _) -> true
272+
| _ -> false)
273+
| _ -> false)
274+
| _ -> false
275+
276+
243277
let ldrbtest
244278
(basereg: arm_reg_t)
245279
(indexreg: arm_reg_t)
@@ -899,6 +933,140 @@ let create_arm_add_pc_jumptable
899933
create_arm_add_pc_h_jumptable ch addpcinstr
900934

901935

936+
(* ADD-PC-LDR patterns (in ARM)
937+
938+
size is obtained from CMP instruction's immediate value
939+
table start address is obtained from the ADR instruction
940+
941+
[4 bytes] CMP indexreg, maxcase
942+
[4 bytes] BHI default address
943+
[4 bytes] ADR basereg, start_address
944+
[4 bytes] LDR scindexreg, [basereg, indexreg, LSL#2]
945+
[4 bytes] ADD PC, basereg, scindexreg
946+
947+
Notes: These instructions always appear in the same order, but they may
948+
be arbitrarily interleaved with other instructions, hence the large number
949+
of offsets in finding the various instructions below.
950+
951+
Notes: encountered in a binary compiled with clang 18.1.3 on an arm64 host,
952+
cross-compiled to arm32.
953+
*)
954+
let is_arm_add_pc_adr_jumptable
955+
(addpcinstr: arm_assembly_instruction_int):
956+
(arm_assembly_instruction_int (* CMP instr *)
957+
* arm_assembly_instruction_int (* BHI instr *)
958+
* arm_assembly_instruction_int (* ADR instr *)
959+
* arm_assembly_instruction_int) option = (* LDR instr *)
960+
match addpcinstr#get_opcode with
961+
| Add (_, ACCAlways, rd, baseregop, scindexregop, false)
962+
when rd#is_pc_register
963+
&& baseregop#is_register
964+
&& scindexregop#is_register ->
965+
let addr = addpcinstr#get_address in
966+
let basereg = baseregop#get_register in
967+
let scindexreg = scindexregop#get_register in (* scaled index register *)
968+
let cmptestf (instr: arm_assembly_instruction_int) =
969+
match instr#get_opcode with
970+
| Compare (_, rn, imm, _) -> rn#is_register && imm#is_immediate
971+
| _ -> false in
972+
let cmpinstr_o =
973+
find_instr cmptestf [(-16); (-20); (-24); (-28); (-32); (-36)] addr in
974+
(match cmpinstr_o with
975+
| Some cmpinstr ->
976+
(match cmpinstr#get_opcode with
977+
| Compare (_, indexregop, _imm, _) ->
978+
let indexreg = indexregop#get_register in
979+
let adrtestf = adr_reg_test basereg in
980+
let ldrs2testf = ldrs2test scindexreg basereg indexreg in
981+
let adrinstr_o = find_instr adrtestf [(-8); (-12)] addr in
982+
let bhiinstr_o = find_instr bhi_test [(-12); (-16)] addr in
983+
let ldrs2instr_o = find_instr ldrs2testf [(-4); (-8)] addr in
984+
let _ =
985+
if Option.is_none adrinstr_o then
986+
chlog#add "adr-jump table failure"
987+
(LBLOCK [addpcinstr#get_address#toPretty; STR ": adrinstr"]) in
988+
let _ =
989+
if Option.is_none bhiinstr_o then
990+
chlog#add "adr-jump table failure"
991+
(LBLOCK [addpcinstr#get_address#toPretty; STR ": bhiinstr"]) in
992+
let _ =
993+
if Option.is_none ldrs2instr_o then
994+
chlog#add "adr-jump table failure"
995+
(LBLOCK [addpcinstr#get_address#toPretty; STR ": ldrs2instr"]) in
996+
(match (bhiinstr_o, adrinstr_o, ldrs2instr_o) with
997+
| (Some bhiinstr, Some adrinstr, Some ldrs2instr) ->
998+
Some (cmpinstr, bhiinstr, adrinstr, ldrs2instr)
999+
| _ -> None)
1000+
| _ ->
1001+
let _ =
1002+
chlog#add "arm-jumptable cmpinstr failure"
1003+
(LBLOCK [addpcinstr#get_address#toPretty]) in
1004+
None)
1005+
| _ ->
1006+
let _ =
1007+
chlog#add "arm-jumptable cmpinstr failure (not found)"
1008+
(LBLOCK [addpcinstr#get_address#toPretty]) in
1009+
None)
1010+
| _ -> None
1011+
1012+
1013+
let create_arm_add_pc_adr_jumptable
1014+
(ch: pushback_stream_int)
1015+
(addpcinstr: arm_assembly_instruction_int):
1016+
(arm_assembly_instruction_int list * arm_jumptable_int) option =
1017+
match is_arm_add_pc_adr_jumptable addpcinstr with
1018+
| None ->
1019+
begin
1020+
chlog#add "arm-jumptable: add-pc-addr (None)"
1021+
(LBLOCK [addpcinstr#get_address#toPretty]);
1022+
None
1023+
end
1024+
| Some (cmpinstr, bhiinstr, adrinstr, ldrinstr) ->
1025+
match (cmpinstr#get_opcode,
1026+
bhiinstr#get_opcode,
1027+
adrinstr#get_opcode,
1028+
ldrinstr#get_opcode) with
1029+
| (Compare (_, _, imm, _),
1030+
Branch (_, tgtop, _),
1031+
Adr (_, _, adrop),
1032+
LoadRegister (_, dstregop, _, _, _, _))
1033+
when tgtop#is_absolute_address && adrop#is_absolute_address ->
1034+
let iaddr = addpcinstr#get_address in
1035+
let defaulttgt = tgtop#get_absolute_address in
1036+
let size = imm#to_numerical#toInt in
1037+
let jtaddr = adrop#get_absolute_address in
1038+
let targets = ref [] in
1039+
let _ =
1040+
for i = 0 to size do
1041+
let offset = ch#read_num_signed_doubleword in
1042+
targets := (iaddr#add_int (4 + offset#toInt), i) :: !targets
1043+
done in
1044+
let endaddr = jtaddr#add_int (4 + (4 * size)) in
1045+
let jt:arm_jumptable_int =
1046+
make_arm_jumptable
1047+
~end_address:endaddr
1048+
~start_address:jtaddr
1049+
~default_target:defaulttgt
1050+
~targets:(List.rev !targets)
1051+
~index_operand:dstregop
1052+
() in
1053+
let instrs =
1054+
[cmpinstr; bhiinstr; adrinstr; ldrinstr; addpcinstr] in
1055+
begin
1056+
chlog#add "arm-jumptable: add_pc_addr"
1057+
(LBLOCK [addpcinstr#get_address#toPretty;
1058+
STR ": of size: ";
1059+
INT size]);
1060+
Some (instrs, jt)
1061+
end
1062+
| _ ->
1063+
begin
1064+
chlog#add "arm-jumptable: add_pc_addr"
1065+
(LBLOCK [addpcinstr#get_address#toPretty;
1066+
STR ": unsuccesful"]);
1067+
None
1068+
end
1069+
9021070
(* format of BX-based jumptable (in Thumb-22):
9031071
9041072
Type 1

CodeHawk/CHB/bchlibarm32/bCHARMJumptable.mli

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
------------------------------------------------------------------------------
55
The MIT License (MIT)
66
7-
Copyright (c) 2022-2024 Aarno Labs LLC
7+
Copyright (c) 2022-2025 Aarno Labs LLC
88
99
Permission is hereby granted, free of charge, to any person obtaining a copy
1010
of this software and associated documentation files (the "Software"), to deal
@@ -80,6 +80,12 @@ val create_arm_add_pc_jumptable:
8080
-> (arm_assembly_instruction_int list * arm_jumptable_int) option
8181

8282

83+
val create_arm_add_pc_adr_jumptable:
84+
pushback_stream_int
85+
-> arm_assembly_instruction_int
86+
-> (arm_assembly_instruction_int list * arm_jumptable_int) option
87+
88+
8389
(** [create_bx_jumptable ch instr] creates a jump table targeted by a BX
8490
instruction [instr] by processing the associated list of addresses from
8591
stream [ch].*)

0 commit comments

Comments
 (0)