diff --git a/assets/tests/cfa_tests.yml b/assets/tests/cfa_tests.yml new file mode 100644 index 0000000..e584c34 --- /dev/null +++ b/assets/tests/cfa_tests.yml @@ -0,0 +1,110 @@ +- test_id: 0 + function_start: 82000000 + function_bytes: 386000004e800020 + jump_table_start: 00000000 + jump_table_bytes: 00000000 +- test_id: 1 + function_start: 82086990 + function_bytes: fbe1fff8816300042b0b0000419a00b8e96300103be3000839000000f961ffe0e963000080c1ffe02f060000f961ffe8f961fff0409900908121ffe48061ffe880a1fff08149000480e90000554b00fe554a1f7e3d808208398c69fc5540103a7c0c002e7c0903a64e80042082086a4082086a0c82086a1482086a287d6b1a14480000302b1f0000419a0024815f00007d6a5a144800001c2b040000419a0010814400007d6a5a144800000839600000390800017d67292e392900087f0830004198ff84ebe1fff84e800020 + jump_table_start: 00000000 + jump_table_bytes: 00000000 +- test_id: 2 + function_start: 827f93f0 + function_bytes: 7d8802a69181fff8fbc1ffe8fbe1fff0dbe1ffe09421ff9080830a68ffe008907cbf2b782b040003419900683d808280398c94345480103a7c0c002e7c0903a64e800420827f94a0827f9444827f945c827f94a0c0030a5cff1f000040980054d01f00003860000148000050c0030a60ff1f00004099003c89630a6f2b0b0000409a0030d01f0000386000014800002c3d608203386bb6743bc3ffb449116efd7fc4f37838a0015f49116e4948e93ce5d3ff000038600000382100708181fff87d8803a6cbe1ffe0ebc1ffe8ebe1fff04e800020 + jump_table_start: 00000000 + jump_table_bytes: 00000000 +- test_id: 3 + function_start: 82fbb358 + function_bytes: fbe1fff83964ffffd021ffe039000010394bfffe3d208201554a103a3929c5b07d4a2a143861ffe0396b00013fe08212556b103a7c08544e2f0400017da0540e7d6b2a14118d0484100048c3116018c311205a8c110c04c4395fffa07ce85c4e7cc05c0e10863c8410a8480a114050c3118a54841164484a18112b50419800643d4082131120038c3d208338394a62f07c8b2378394a000439296e60110b000a80aa0000356bffff54a32036394a000410e348c311a0410a10ad522f10856b6e15ac2090146068901806039018614f1019861b90104b636e118261ea4082ffc03961ffe02b040003100058c3100004c4419900643d8082fc398cb4645480103a7c0c002e7c0903a64e80042082fbb4a082fbb49482fbb48482fbb4743960003011a3628c116658c3100b036e3960002011a2628c116658c3100b036e11a1628c116640c3100b036e11a0628c118030c3100c036e100039c3ebe1fff84e800020 + jump_table_start: 00000000 + jump_table_bytes: 00000000 +- test_id: 4 + function_start: 822c4618 + function_bytes: 7d8802a69181fff89421ffa03960000038e10052b161005238c0000138a100503880000e386000034869c6f52c0300004180016c896100502b0b006e41990160396bfffb2b0b0068419901543d808200398c0b187c0c58ae5400103a3d80822c60000000398c468c7d8c02147d8903a64e80042039600001480001283960000248000120396000034800011839600004480001103960000548000108396000064800010039600007480000f839600008480000f039600009480000e83960000a480000e03960000b480000d83960000c480000d03960000d480000c83960000e480000c03960000f480000b839600010480000b039600011480000a839600012480000a039600013480000983960001448000090396000154800008839600016480000803960001748000078396000184800007039600019480000683960001a480000603960001b4800005839600025480000503960001c480000483960001d480000403960001e480000383960001f480000303960002048000028396000214800002039600022480000183960002348000010396000244800000839600000556a063f4182000c2b0a0025409900404869c08d546bc63e2b0b0001409a001c3963feff7d6b0034556bdffe696b0001396b001448000018396bfffe7d6b0034556bdffe696b0001396b00235563063e382100608181fff87d8803a64e800020 + jump_table_start: 82000b18 + jump_table_bytes: 02004a044a4a4a4a064a4a084a420a0c0e4a1018124a4a4a4a4a3e144a16464a1a4a1c4a4a1e4a224a204a4a4a244a4a264a4a284a4a4a4a4a4a4a4a4a4a4a4a4a4a2a4a4a2c302e4a4a4a4a4a324a344a4a4a364a40384a3a4a4a4a4a4a4a4a444a484a4a4a4a4a3c000000 +- test_id: 5 + function_start: 824afd20 + function_bytes: 7d8802a69181fff8fbe1fff0dbe1ffe89421ff40386100507c9f237848c021853d60820338800000396bc418386100509161005048c02245817f0000814b00002b0a001b4199020c3d808203398cd8707c0c50ae5400103a3d80824b398cfd907d8c02147d8903a6600000004e800420386b000448c021953d6082037c641b7838abd9803861005848c04b717c641b783861005048c0227538610058480001b0386b000448c021653d6082017c641b7838abd5043861006048c04b417c641b783861005048c022453861006048000180386b000448c021353d6082017c641b7838ab0bc03861006848c04b117c641b783861005048c022153861006848000150386b000448c021053d6082017c641b7838ab0ba83861007048c04ae17c641b783861005048c021e53861007048000120386b000448c020d53d6082037c641b7838abd9743861007848c04ab17c641b783861005048c021b538610078480000f0386b000448c020a53d6082037c641b7838abd9683861008048c04a817c641b783861005048c0218538610080480000c0386b000448c020753d6082037c641b7838abd95c3861008848c04a517c641b783861005048c021553861008848000090386b000448c020453d6082037c641b7838abd94c3861009048c04a217c641b783861005048c021253861009048000060386b000448c020153d6082037c641b7838abd93c3861009848c049f17c641b783861005048c020f53861009848000030386b000448c01fe53d6082037c641b7838abd930386100a048c049c17c641b783861005048c020c5386100a048c01f6d3861005048c0200d28030000418200303861005048c01fa57c7f1b783861005048c01f9948c06f457fe3fb78ffe0089048c07359efff00724800000c3d608201c3ebd72c3861005048c01f21fc20f890382100c08181fff87d8803a6cbe1ffe8ebe1fff04e800020 + jump_table_start: 8202d870 + jump_table_bytes: 000000000c0c0c0c0c0c0c0c0c0c0c0c182400182400303c4854606c00000000 +- test_id: 6 + function_start: 8219b550 + function_bytes: 7d8802a6480dd5dd9421ff107c7f1b787dad6b78817f0018388000003bbf00343a2000009081005039c0000039e000002b0b0000419a02783fc0002a826100507d705b783d6082003d4082003d2082003d0082003ce082003cc082003ca082003c8082003c6082003fe0820063d223b93b6b48403b4a48303b2947f03b0847c43ae747983ac6476c3a85473c3b84471c3bc32ae03abf4700a17d0000280b0000418200107ea4ab787fc3f3784bfb0e35889d00092b04000a419901dc3d808200398c44f87c0c20ae5400103a3d80821a60000000398cb6347d8c02147d8903a64e800420897d000a280b0000418200107f84e3787fc3f3784bfb0de9817d000491610050480001987e83a3784bfb0dd54800018c897d000a280b0000418200107f84e3787fc3f3784bfb0db981dd00044800016c897d000a280b0000418200107f84e3787fc3f3784bfb0d9981fd00044800014c817d00047f0b9040419a00107ec4b3787fc3f3784bfb0d79897d000a280b0000418200107f84e3787fc3f3784bfb0d6162310010480001143d60002c815d0004616b83a47f0a5840419a00107ee4bb787fc3f3784bfb0d39897d000a280b0000418200107f84e3787fc3f3784bfb0d2162310020480000d43d600018815d0004616b28867f0a5840419a00107f04c3787fc3f3784bfb0cf9897d000a2b0b0001409900107f24cb787fc3f3784bfb0ce1897d000a280b00004082000c62310040480000886231008048000080897d000a3d40001a809d0004614a23a6396b00087f045040557f083c419a00447f049040419a00343d64ffd4356bdc5b418200202b0b5fff419a00107f43d3784bfb0c81480000203a600003480000183a600000480000103a600001480000083a6000027e6bf8307d6b8b783a2b01004800000c7f63db784bfb0c493610ffff3bbd000c4082fdf4808100503d60820039400000396b43e8812b00007f092040409a001c812b00047f097840409a0010812b00087f097040419a0038394a0010396b00102b0a01104198ffd03d6082007dc67378386b46887de57b784bfb0be57dce73787e238b78382100f0480dd324816b000c2b0b0000419affd47d718b784bffffe0 + jump_table_start: 820044f8 + jump_table_bytes: 000c141c2a4f6c6c6c093a0000000000 +- test_id: 7 + function_start: 821c3e88 + function_bytes: 7d8802a69181fff8fbc1ffe8fbe1fff09421ff907c7f1b787dad6b78817f0000556b02d7408200103d608201386b8d404bf885794bf7d2a52f030002409a00103d608271816b06404800000c3d608271816b064483cb0000817f0000556a073e394affff2b0a000b419903003d808201398c83e87c0c50ae5400103a3d80821c60000000398c3f1c7d8c02147d8903a64e800420556a0043408202d0556b00014182001c817f002c39401400813f001c7c8b53965523053e4bf76c91817f0020556b07ff418202a43d40827039600000916ae64448000294817f00082b0b0000419a00302b1e0000419a0028388000007fc3f3784bf9dd5538e000007fe6fb78809f000838a0000d7fc3f3784bf94f0d38a00001388000017fe3fb784bf89f9d817f0020815f00303880000355630026555e00264bf8a2557fc3f378388000034bf8a24948000224817f00082b0b0000419a00302b1e0000419a0028388000007fc3f3784bf9dce538e000007fe6fb78809f000838a000097fc3f3784bf94e9d817f00185563003a4bffffb4817f00082b0b0000419a00302b1e0000419a0028388000007fc3f3784bf9dca138a0000b38e00000809f00087fe6fb787fc3f3784bf94e59807f00184bffff74817f00082b0b0000419a00302b1e0000419a0028388000007fc3f3784bf9dc6138e000007fe6fb78809f000838a000077fc3f3784bf94e19807f00204bffff34817f00082b0b0000419affb02b1e0000419affa8388000007fc3f3784bf9dc2138a000084bffff80817f00082b0b0000419aff882b1e0000419aff80388000007fc3f3784bf9dbf938a000114bffff58817f00082b0b0000419a00302b1e0000419a0028388000007fc3f3784bf9dbd138e000007fe6fb78809f000838a0000f7fc3f3784bf94d897fe3fb784bf70b31480000cc817f00082b0b0000419a00302b1e0000419a0028388000007fc3f3784bf9db8d38e000007fe6fb78809f000838a000157fc3f3784bf94d457fe3fb784bf9511d48000088817f00082b0b0000419a00302b1e0000419a0028388000007fc3f3784bf9db4938e000007fe6fb78809f000838a000177fc3f3784bf94d017fe3fb784bf849f948000044817f00082b0b0000419a00302b1e0000419a0028388000007fc3f3784bf9db0538e000007fe6fb78809f000838a000197fc3f3784bf94cbd7fe3fb784bf752e53960000f7fe3fb78917f00004bf911e57dce7378382100708181fff87d8803a6ebc1ffe8ebe1fff04e800020 + jump_table_start: 820083e8 + jump_table_bytes: 2d3e1100b54e5e68728394a50000000000000000000000000000000000000000000000000000000000000000 +- test_id: 8 + function_start: 823178b0 + function_bytes: 7d8802a64830d269dbe1ffd83be1ff509421ff50816300547c7e1b783ba0ffff2b0b000b419903703d808204398c6ec87c0c58ae5400103a3d80823160000000398c79007d8c02147d8903a64e8004203fa082a4807d1fcc4bf3d4b1546b063f41820334807d1fcc4bf3ac79546b063f408203243ba0000348000304807e004c48057bc94bffffe8807e004c48057bbd546b063f408203003880ffff7fc3f3784bfffce93d608204387f0050388b7a90481fb0397c6b1b78387f0060808b00004bebc329817e000438c0000183bf0064387f00707fa5eb78816b00047d6bf214388b0004816b0004816b00187d6903a64e800421817f0074556b06f74182000c807f00704beb899539600001997e01207fa3eb784beb89854800027c807e004c48057b29546b063f4082026c387f0054809e01004bffe1253fa082a081430000809e0100817deaa87f0a5840419a001c387f00584bffe105816300003ba0000b917e007448000218387f005c4bffdfe581430000817deaa87f0a5840419a02183ba00004480001f8807e01044bffe3e5546b063f4182003cc01e005c807e0100c1be0108efe068284bffe1b9ff1f0800419900203d6082a438800000806b1fcc4bf3b3694bf6ce452f030064419801c8807e01044bffe4a5546b063f41820044397e005cc01e005cc1be010c917f0060ff00680040980008d01e010cc00b0000807e0100c1be010cefe068284bffe25dff1f0800419801803ba00006480001603ba0000748000158807e004c48057a1d546b063f408201603d608204387f0068388b7a7c481faea57c6b1b78387f0060808b00004bebc195817e000438c0000183bf0064387f00787fa5eb78816b00047d6bf214388b0004816b0004816b00187d6903a64e800421817f007c556b06f74182000c807f00784beb88017fa3eb784bfffe74807e004c480579a1546b063f408200e43d4082a43d6082a43bab58e4816a58ec556907ff40820038616b0001916a58ec3d608204387f006c388b7a68481fae097c6b1b787fa3eb78808b00004bebc0f93d60829d386b0eb84830d9dd817e000438c00001387f008080bd0004816b00047d6bf214388b0004816b0004816b00187d6903a64e800421817f0084556b06f74182000c807f00804beb875d3ba0000c480000387fc3f3784bffdad5546b063f4182004083be0058480000187fc3f3784bffdbd54bffffe87fc3f3784bffe4e12f1dffff419a001c809e00587fc3f3782f04ffff409a00087fa4eb784bfff9f9383f00b0cbe1ffd84830cf20 + jump_table_start: 82046ec8 + jump_table_bytes: 000b0e33d14e7695d1bec4c78215981c +- test_id: 9 + function_start: 823349f8 + function_bytes: 7d8802a69181fff8fbc1ffe8fbe1fff09421ff503d6082a07c7e1b787cdf33782b050001816beaa8916300004198025c419a009c2b0500034198007c419a00602b05000541980040419a00242b050007409802503d60820538610050388be6f4481ddf4181610050480002343d60820538610054388be6c8481ddf29816100544800021c3d60820538610058388be6a8481ddf1181610058480002043d6082053861005c388be68c481ddef98161005c480001ec3d60820538610060388be670481ddee181610060480001d42b1f0000409a001c3d60820538610064388be650481ddec181610064480001b4809f000481640000386b0010480708093963ffff2b0b00094199016c3d808205398ce5507c0c58ae5400103a3d80823360000000398c4b287d8c02147d8903a64e800420817f00047d645b78816b0000386b0010480707c13d60820538610068388be650481dde5181610068480001443d6082053861006c388be634481dde398161006c4800012c3d60820538610070388be618481dde218161007048000114809f000481640000386b0018480707692c030000418200b82f030002419a00b02f030003419a00902f030004419a0018817f00047d645b78816b0000386b0018480000b43d60820238610088388be3f0481dddc53d6082a48083000038a00001816b2830814b0000814a00047c6a5a144808194d38800000480706fd2c0300004182001c3d60820538610074388be5ec481ddd8581610074480000783d60820538610078388be5c0481ddd6d81610078480000603d6082053861007c388be59c481ddd558161007c480000483d60820538610080388be578481ddd3d8161008048000030817f00047d645b78816b0000386b0010480706814800001c3d60820538610084388be55c481ddd0d81610084917e00007fc3f378382100b08181fff87d8803a6ebc1ffe8ebe1fff04e800020 + jump_table_start: 8204e550 + jump_table_bytes: 00000b110000500050170000 +- test_id: 10 + function_start: 82592db8 + function_bytes: 7d8802a69181fff8fbe1fff09421f770816300047c7f1b782b0b0014419901503d80820c398c11b07c0c58ae5400103a3d80825960000000398c2e007d8c02147d8903a64e800420388000007fe3fb784be124f13d6082077c641b78386b10044be6430948000120388000007fe3fb784be125d13d60820c386b151c4be373e548000104388000007fe3fb784be11eed4be127313d6082017c641b78386bfe444be642c9480000e0388000007fe3fb784be11ec97c641b78386100544be2d28d7c6b1b783d408201386afe44808b00004be64299480000b07fe3fb784be1261d546b063f41820024388000007fe3fb784be125997c6b1b783d408201386afe44808b00184bffff9c3d608206388b7da04800006438a000007fe4fb78386100504be12449806300004800005c3d60820c388b1514480000403d60820c388b150848000034388000007fe3fb784be124b5480000343d60820c388b14fc480000183d60820c388b14f44800000c3d60820c388b14e8386100604bf827a9386100604bf82439382108908181fff87d8803a6ebe1fff04e800020 + jump_table_start: 820c11b0 + jump_table_bytes: 00080f18243349494949494949494949393c3f4346000000 +- test_id: 11 + function_start: 8222c138 + function_bytes: 7d8802a64825b1319421ff507c7f1b782b1f0000419a0394817f001c280b000041820388815f00002b0a0000419a037c3aa000002f0400043ba0fffb419a00087ebdab78814b00003bc0fffb2b0a000d419903583d6082023b80000d3b4b0f3c3d6082023b6000053b2b0f243d6082023ac0000c3b0b0f103d6082023aeb0ef43d808202398c0ec85540083c7c0c022e3d808223398cc1e07d8c02147d8903a6600000004e800420817f0004280b000041820304396bffff815f0000813f001c7fbeeb78917f0004817f0008396b0001917f0008896a000091690004815f0000817f001c394a0001812b0004915f0000552a073e2b0a0008419a0010938b000092ff00184800028c814b0004812b0010554ae13e394a00087f0a484040990010938b0000931f00184800026839400001914b0000815f0004280a000041820278392affff817f0000815f001c3900001f38eb00017fbeeb78913f0004813f000839290001913f0008896b000090ff0000812a00045529402e7d295a147d0943961d08001f7d28485141820010938a0000933f0018480001fc556b06b54082022039600007916a0000480001f0817f001c7fc5f3787fe4fb78806b0014480017ed7c7e1b782f1efffd409a0018817f001c938b0000817f001c92ab0004480001bc2f1e0000409a00087fbeeb782f1e0001409a01c4817f001c7fe4fb787fbeeb7838ab0004806b0014480015f1817f001c814b000c2f0a0000419a000c92cb00004800017839400008914b0000817f0004280b000041820180396bffff815f0000813f001c390000097fbeeb78917f0004817f0008396b0001917f0008896a0000556bc03e91690008817f0000815f001c396b0001917f0000910a0000815f0004280a000041820130394affff813f0000817f001c3900000a7fbeeb78915f0004815f0008394a0001915f000889490000812b0008554a803e7d4a4a14914b0008817f0000815f001c396b0001917f0000910a0000815f0004280a0000418200d8394affff813f0000817f001c3900000b7fbeeb78915f0004815f0008394a0001915f000889490000812b0008554a403e7d4a4a14914b0008817f0000815f001c396b0001917f0000910a0000815f0004280a000041820080394affff813f0000817f001c7fbeeb78915f0004815f0008394a0001915f000889490000812b00087d4a4a14914b0008815f0000817f001c394a0001915f0000814b0004812b00087f0a4840419a01c0938b0000935f0018817f001c936b0004817f001c814b00002b0a000d4099fcdc3860fffe382100b04825add47fc3f3784bfffff4817f001c39400002914b0000817f0004280b00004182ffe4396bffff815f0000813f001c390000037fbeeb78917f0004817f0008396b0001917f0008896a0000556bc03e91690008817f0000815f001c396b0001917f0000910a0000815f0004280a00004182ff94394affff813f0000817f001c390000047fbeeb78915f0004815f0008394a0001915f000889490000812b0008554a803e7d4a4a14914b0008817f0000815f001c396b0001917f0000910a0000815f0004280a00004182ff3c394affff813f0000817f001c7fbeeb78915f0004815f0008394a0001915f000889490000812b0008554a403e7d4a4a14914b0008817f0000815f001c396b0001917f0000936a0000815f0004280a00004182fee8394affff813f0000817f001c3900000638600002915f0004815f0008394a0001915f000889490000812b00087d4a4a14914b0008815f0000817f001c394a0001915f0000814b0008915f0030910b00004bfffe8c815f001c3d608202396b0ee4938a0000815f001c917f001892aa00044bfffe68817f001c92cb0000386000014bfffe5c3860fffd4bfffe54 + jump_table_start: 82020ec8 + jump_table_bytes: 0000008c0320037003c8041c047c0104018401d4022c028404a404ac +- test_id: 12 + function_start: 8229f7f0 + function_bytes: 7d8802a6481e7a919421ff407cbe2b787c7f1b787c9c23783ba000003b6000012b1e001040990008937f0044817f00442f0b0000409a04e4213e001057cb103a394100505525103a388000007c6b5214481e8691355effff4180004c554b103a392100507d2b4a14817f005c280b000041820064810b000c354affff80eb0008911f005c3900000090e900003929fffc910b0008811f0060910b000c917f00604080ffc82b1c002e419904403d808203398cbcf05780083c7c0c022e3d80822a398cf8c47d8c02147d8903a6600000004e8004203d60820338cbc0f438a00000389f0278387f0018480078a9937f0044480004288161005038a000017fe3fb78808b00184bfff1ed480003e0816100507fe3fb78808b00184bffe3c1480003cc81610050815f026c816b0018916a001c817f02782f0b000c419a03b0817f026c814b001c394affff914b001c4800039c8141005481610050812a0018816b0018815f026c916a001c817f02782f0b000c419a0014817f026c814b001c394affff914b001c2b090000419a0360817f026c912b0018480003547fe3fb784bfffb45480003487fe3fb784bffcce14800033c81610050808b00187fe3fb784bffe3cd480003288161005038c0000038a000007fe3fb78808b00184bffd5617c641b784bffffd88161005038c0000038a000007fe3fb78808b00184bffd5417c6b1b787d6b00345564dffe4bffffb0816100507fe3fb78808b00184bffcdd9480002cc7fe3fb784bffce8d480002c07fe3fb784bffe411480002b4388000017fe3fb784bffe34938800000807f026c48007d6548000298388000017fe3fb784bffcd954bffffe47fe3fb784bfff4094800027c83a100504800027483c10050396000027fe3fb787fddf378809e0018917e00104bffee79907e00184800025083c100503960000238c0000038a000007fe3fb787fddf378809e0018917e00104bffd47d907e0018937f00584800022083a10050817d00187d6b0034556bdffe917d00184800020883a10050817d00187d6b00d04bffffec83a1005081610054815d0018816b00187d6b51d64bffffd481610054814100507d5d5378816b0018280b000041820018812a00180ccb00007d695b96916a0018480001b43d60820338a005df38cbc0c8389f0278387f001848007645937f0044480001948161005081410054812b0018814a00187d4a4a147d7d5b78914b00184800017483a1005081610054815d0018816b00187d6b50504bffff5083a1005081610054815d0018816b00187d6b50107d6b5910556b07fe4bffff3083a1005081610054815d0018816b00187d6a58104bffffe08161005081410054812b0018814a00187d4950107d4a5110394a00014bffff888161005081410054812b0018814a00187d4a48104bffffe083a1005081610054815d0018816b00187d6a58504bfffec083a1005081610054815d0018816b00187d6a58507d6b0034556bdffe696b00014bfffea483a10050817d00182b0b0000419a001881610054816b00182b0b00007f6bdb78409afe80396000004bfffe7883a10050817d00182b0b0000409a001881610054816b00182b0b000039600000419afe547f6bdb784bfffe4c83a10050817d00182b0b000081610054409a000881610058816b00184bfffe2c38800010386000304802d5952803000041820014389f02784802da357c7d1b78480000083ba000007fa4eb787fe3fb784bffd3bd817f00442f0b0000409a0028807f006028030000418200248163000c917f006093a30008817f005c9163000c907f005c382100c0481e75c838800010386000144802d529280300004182001c3d60820380bf005c7fa4eb7838cbc0c04802d66d48000008386000002b030000409affc03d60820338cbc0a04bfffb7c + jump_table_start: 8202bcf0 + jump_table_bytes: 00200038004c007c00c400d000dc00f001100138014c0158016401800190019c019c01a401c8019c01f80210019c019c02200238019c028402a4019c02bc02dc02f40314019c032c0344019c0368019c0394019c03c0019c03e003e003e000 +- test_id: 13 + function_start: 822abac0 + function_bytes: 7d8802a6481db7c59421ff307c7f1b78817f00502f0b0000409a065c3b8000007f9ee3787ca903a62b050000419a003c54ab103a394100607d4b5214817f0034394afffc280b000041820054812b000c810b0008913f0034910a0000938b0008938b000c4200ffd83ba000012b04003f419905983d808203398cef685480083c7c0c022e3d80822b398cbb5c7d8c02147d8903a6600000004e8004203d608203807f000038a0000038cbc0f4389f00104bffb61139600001917f004c480005b883c10060480005448081006080610064480217a97c7e1b7848000530817f00742b0b0000409a001081610060816b0018917f00747f9ee37848000510808100607c9e23787fe3fb784bffeb59480004fc817f0038808100602f0b00067c9e2378419800142f0b00094199000c93a400544bffffd43d60820338a007eb38cbfa2038840010807f00004bffb57993bf004c480004b8817f003883c100642f0b00024198000c2f0b0005409900142f0b000c419800182f0b000f4199001081610060917e0040480004843d60820338a007ec38cbf9e0389e00104bffffac83c100602b05000193810060409900108161006493810064917e003c2b0500024099044c39610068393e00443945fffe810b0000354affff938b0000396b000491090000392900044082ffe84800042083c100602b050001938100604099041039610064393e00443945ffff810b0000354affff938b0000396b000491090000392900044082ffe8480003e483c10060817e001c2f0b0000419a002c3d608203807f000038a007e638cbf9b8389f00104bffb4753d60000f93bf004c917e0020480003ac816100647fe3fb78388b00104bffd095907e00204800039483c10060817e00142b0b0000419a00143d60820338a007e238cbf990480001083d600d00917e00144800036883c10060817e0014280b00004182007c3d4002007f0b5040419a00683d4004007f0b5040419a00543d4007007f0b5040419a00403d4009007f0b5040419a00243d400a007f0b5040419a00183d400b007f0b5040409a03103d600c004bffff9c3d60820338a007db38cbf9604800008c3d6008004bffff843d6005004bffff7c3d6003004bffff743d6001004bffff6c8161006083c10064816b00182b0b0001419a00143d60820338a007da38cbf94048000048817f00382f0b0006419800302f0b000941990028817e00142b0b0000419a00143d60820338a007dc38cbf910480000183d6006004bffff103d60820338a007ed38cbf8dc389f00104bfffda483c10060817e001c2f0b0000419a002c3d608203807f000038a007e638cbf8b8389f00104bffb2f93d6000e493bf004c917e002448000230816100647fe3fb78388b00104bffd029907e00244800021838a00000816100607fe3fb78388b00104bffdf75480001e880a100644bffffe883c1006081610064813e0018814b0018811e00287d4a4a142b080000915e0018409a0014814b0028915e0028938b0028480001c4816b00282b0b0000419a01b83d60820338a007d938cbf8684bffff3c388000103860002c4802131528030000418201848101006038c0000038e0000038a0000038800000480253cd48000160388000103860002c480212e52803000041820154816100603900000080cb00184bffffcc3880001038600030480212c12803000041820014389f0010480217617c7e1b78480000087f9ee3787fc4f3787fe3fb784bffcda13960000293be0018917e00104800011038800010386000304802127d2803000041820014389f00104802171d7c7e1b78480000087f9ee3787fc4f3787fe3fb784bffcd5d39600002939e0018917e0010480000cc83c10060817e00187d6b00d0917e0018480000b883c1006039600005917e0010817e0018f9610050c8010050fc00069cd81e00184800009483c1006039600005917e0010817e0018f9610050c8010050fc00069cfc0000504bffffd883c10060c81e00184bfffff03880001038600060480211d12803000041820040389f001080ff004880df004480bf00404802518d480000203880001038600030480211a52803000041820014389f0010480216457c7e1b78480000087f9ee3787fc4f3787fe3fb784bffcc85817f00502f0b0000409a0064388000103860001448021165280300004182001c3d60820380bf00347fc4f37838cbc0c0480212a9480000087f83e3782b030000409a00283d608203807f000038a0000038cbc0a0389f00104bffb06193bf005093bf004c48000008907f0034382100d0481db1a0 + jump_table_start: 8202ef68 + jump_table_bytes: 005800240024002c002400240058004000600074002400b8010001000100010001000100010001000100015001500024018c002401dc0208029800240308056c056c035803700024037803c803f8041c04600024002404a404b804b804dc002400240500050c050c050c050c050c050c050c050c050c050c050c053805380538 +- test_id: 14 + function_start: 823f7400 + function_bytes: 7d8802a648db0d5ddbe1ffc09421fe607c7e1b787c9b237848d5d1e93f8083267c7f1b7838610054809c7658489fc5dd7fe3fb783ba100544bf0dfb13d6082007c641b78386100a87fa5eb78c3eb08a4fc20f890487bb865806100ac2b030000419a00084bec942d38610054489fbfbd3fa0832638610054809d7c74489fc58d7fe3fb783b4100544bf0df617c641b7838610130fc20f8907f45d378487bb81d806101342b030000419a00084bec93e538610054489fbf757fe3fb784861c0dd7c641b78386100c048118001816300002b0b0000386bfffc409a000838600000481110497c6b1b7838610050808b000048a48a19386100c0489fa799817b00182b0b00074199077c3d808202398c31f05560083c7c0c022e3d80823f398c75307d8c02147d8903a6600000004e80042038610054809c7658489fc4d17fe3fb783be100544bf0dea57c641b7838610110fc20f8907fe5fb7838e00001487bb6c5806101142b030000419a00084bec932538610054480007083861007c809d7c74489fc4897fe3fb783be1007c4bf0de5d7c641b78386100d0fc20f8907fe5fb7838e00001487bb67d806100d42b030000419a00084bec92dd3861007c480006c0396000013fa08326997e0071387f02e8809d7c8c489fc2a53861009c809d7c8c489fc4297fe3fb783bc1009c4bf0ddfd7c641b7838610150fc20f8907fc5f37838e00001487bb61d806101542b030000419a00084bec927d3861009c489fbe0d3d60820238610064388b3334489fc3dd38c0000038a1006438810050386100e048a494297c7e1b787fe3fb784861bf4d815e0000396303c8389e0004386b0004914b00004beccdfd806100e42b030000419a00084bec921d38610064489fbdad3d60820238610084388b3318489fc37d38c0000038a10084388100503861012048a493c97c7e1b787fe3fb784861beed815e0000396303dc389e0004386b0004914b00004beccd9d806101242b030000419a00084bec91bd38610084480005a0396000013fa08326997e0071387f02e8809d7c78489fc1853861006c809d7c78489fc3097fe3fb783bc1006c4bf0dcdd7c641b78386100f0fc20f8907fc5f37838e00001487bb4fd806100f42b030000419a00084bec915d3861006c489fbced3d60820238610094388b32fc489fc2bd38c0000038a10094388100503861014048a493097c7e1b787fe3fb784861be2d815e0000396303c8389e0004386b0004914b00004becccdd806101442b030000419a00084bec90fd38610094489fbc8d3d60820238610074388b32e0489fc25d38c0000038a10074388100503861010048a492a97c7e1b787fe3fb784861bdcd815e0000396303dc389e0004386b0004914b00004beccc7d806101042b030000419a00084bec909d3861007448000480396000013fa08326997e0071387f02e8809d7c7c489fc0653861008c809d7c7c489fc1e97fe3fb783bc1008c4bf0dbbd7c641b78386100b0fc20f8907fc5f37838e00001487bb3dd806100b42b030000419a00084bec903d3861008c489fbbcd3d60820238610058388b32c4489fc19d38c0000038a1005838810050386100b848a491e97c7e1b787fe3fb784861bd0d815e0000396303c8389e0004386b0004914b00004beccbbd806100bc2b030000419a00084bec8fdd38610058489fbb6d3d6082023861005c388b32a8489fc13d38c0000038a1005c38810050386100c848a491897c7e1b787fe3fb784861bcad815e0000396303dc389e0004386b0004914b00004beccb5d806100cc2b030000419a00084bec8f7d3861005c48000360396000013fa08326997e0071387f02e8809d7c80489fbf4538610060809d7c80489fc0c97fe3fb783bc100604bf0da9d7c641b78386100d8fc20f8907fc5f37838e00001487bb2bd806100dc2b030000419a00084bec8f1d38610060489fbaad3d60820238610068388b328c489fc07d38c0000038a1006838810050386100e848a490c97c7e1b787fe3fb784861bbed815e0000396303c8389e0004386b0004914b00004becca9d806100ec2b030000419a00084bec8ebd38610068489fba4d3d60820238610070388b3270489fc01d38c0000038a1007038810050386100f848a490697c7e1b787fe3fb784861bb8d815e0000396303dc389e0004386b0004914b00004becca3d806100fc2b030000419a00084bec8e5d3861007048000240396000013fa08326997e0071387f02e8809d7c84489fbe2538610078809d7c84489fbfa97fe3fb783bc100784bf0d97d7c641b7838610108fc20f8907fc5f37838e00001487bb19d8061010c2b030000419a00084bec8dfd38610078489fb98d3d60820238610080388b3254489fbf5d38c0000038a10080388100503861011848a48fa97c7e1b787fe3fb784861bacd815e0000396303c8389e0004386b0004914b00004becc97d8061011c2b030000419a00084bec8d9d38610080489fb92d3d60820238610088388b3238489fbefd38c0000038a10088388100503861012848a48f497c7e1b787fe3fb784861ba6d815e0000396303dc389e0004386b0004914b00004becc91d8061012c2b030000419a00084bec8d3d3861008848000120396000013fa08326997e0071387f02e8809d7c88489fbd0538610090809d7c88489fbe897fe3fb783bc100904bf0d85d7c641b7838610138fc20f8907fc5f37838e00001487bb07d8061013c2b030000419a00084bec8cdd38610090489fb86d3d60820238610098388b321c489fbe3d38c0000038a10098388100503861014848a48e897c7e1b787fe3fb784861b9ad815e0000396303c8389e0004386b0004914b00004becc85d8061014c2b030000419a00084bec8c7d38610098489fb80d3d608202386100a0388b3200489fbddd38c0000038a100a0388100503861015848a48e297c7e1b787fe3fb784861b94d815e0000396303dc389e0004386b0004914b00004becc7fd8061015c2b030000419a00084bec8c1d386100a0489fb7ad38600001382101a0cbe1ffc048db0524 + jump_table_start: 820231f0 + jump_table_bytes: 00000048009001b002d003f005100630 +- test_id: 15 + function_start: 826a4e98 + function_bytes: 7d8802a648b032a99421fe603d6082027c761b783a6b4a987c9f2378386100507e649b784874eb4d7fe3fb7838a00000388100504befc6bd386100504874e5553d608204386100503b8b132c7f84e3784874eb217fe3fb7838a00000388100504befc691386100504874e5293d608204386100503b6b13107f64db784874eaf57fe3fb7838a00000388100504befc665386100504874e4fd3d608204386100503bcbfd087fc4f3784874eac97fe3fb7838a00000388100504befc639386100504874e4d13d608204386100503b0b43707f04c3784874ea9d7fe3fb7838a00000388100504befc60d386100504874e4a53d608205386100503b2bd2d07f24cb784874ea717fe3fb7838a00000388100504befc5e1386100504874e4793d608205386100503aeb11607ee4bb784874ea457fe3fb7838a00000388100504befc5b5386100504874e44d3d608205386100503b4b11547f44d3784874ea197fe3fb7838a00000388100504befc589386100504874e4213d608205386100503babd2e87fa4eb784874e9ed7fe3fb7838a00000388100504befc55d386100504874e3f53d608205386100503aab114c7ea4ab784874e9c17fe3fb7838a00000388100504befc531386100504874e3c93d608205386100503a8b113c7e84a3784874e9957fe3fb7838a00000388100504befc505386100504874e39d7e649b78386100504874e97138a00001388100507fe3fb784befc4e1386100504874e37981760064396bffff2b0b0009419907ac3d808205398c11185560083c7c0c022e3d80826a398c50ec7d8c02147d8903a6600000004e8004207f84e378386100504874e91538a00001388100507fe3fb784befc485386100504874e31d7f64db78386100bc4874e8f138a00001388100bc7fe3fb784befc461386100bc4874e2f97f04c3783861005c4874e8cd38a000013881005c7fe3fb784befc43d3861005c4874e2d57f24cb78386101144874e8a938a00001388101147fe3fb784befc419386101144874e2b17fa4eb78386100644874e88538a00001388100647fe3fb784befc3f5386100644874e28d7fc4f378386100c44874e86138a00001388100c47fe3fb784befc3d1386100c4480006a87f84e3783861006c4874e83d38a000013881006c7fe3fb784befc3ad3861006c4874e2457f64db78386100f44874e81938a00001388100f47fe3fb784befc389386100f44874e2217fc4f378386100744874e7f538a00001388100747fe3fb784befc365386100744874e1fd7f24cb78386100cc4874e7d138a00001388100cc7fe3fb784befc341386100cc4874e1d97f44d3783861007c4874e7ad38a000013881007c7fe3fb784befc31d3861007c4874e1b57fa4eb783861010c4874e78938a000013881010c7fe3fb784befc2f93861010c4874e1917fc4f378386100844874e76538a00001388100847fe3fb784befc2d538610084480005ac7f84e378386100d44874e74138a00001388100d47fe3fb784befc2b1386100d44874e1497f64db783861008c4874e71d38a000013881008c7fe3fb784befc28d3861008c4874e1257fc4f378386100fc4874e6f938a00001388100fc7fe3fb784befc269386100fc4874e1017f24cb78386100944874e6d538a00001388100947fe3fb784befc245386100944874e0dd7f44d378386100dc4874e6b138a00001388100dc7fe3fb784befc221386100dc4874e0b97fa4eb783861009c4874e68d38a000013881009c7fe3fb784befc1fd3861009c4874e0957fc4f3783861011c4874e66938a000013881011c7fe3fb784befc1d93861011c480004b07f84e378386100a44874e64538a00001388100a47fe3fb784befc1b5386100a44874e04d7f64db78386100e44874e62138a00001388100e47fe3fb784befc191386100e44874e0297fc4f378386100ac4874e5fd38a00001388100ac7fe3fb784befc16d386100ac4874e0057f04c378386101044874e5d938a00001388101047fe3fb784befc149386101044874dfe17fa4eb78386100b44874e5b538a00001388100b47fe3fb784befc125386100b44874dfbd7fc4f378386100ec4874e59138a00001388100ec7fe3fb784befc101386100ec480003d87f84e378386100544874e56d38a00001388100547fe3fb784befc0dd386100544874df757f64db78386100584874e54938a00001388100587fe3fb784befc0b9386100584874df517fc4f378386100604874e52538a00001388100607fe3fb784befc095386100604874df2d7f44d378386100684874e50138a00001388100687fe3fb784befc071386100684874df097fa4eb78386100704874e4dd38a00001388100707fe3fb784befc04d386100704874dee57fc4f378386100784874e4b938a00001388100787fe3fb784befc02938610078480003007f84e378386100804874e49538a00001388100807fe3fb784befc005386100804874de9d7f64db78386100884874e47138a00001388100887fe3fb784befbfe1386100884874de797fc4f378386100904874e44d38a00001388100907fe3fb784befbfbd386100904874de557f44d378386100984874e42938a00001388100987fe3fb784befbf99386100984874de317fa4eb78386100a04874e40538a00001388100a07fe3fb784befbf75386100a04874de0d7fc4f378386100a84874e3e138a00001388100a87fe3fb784befbf51386100a8480002287f04c378386100b04874e3bd38a00001388100b07fe3fb784befbf2d386100b04874ddc57ee4bb78386100b84874e39938a00001388100b87fe3fb784befbf09386100b84874dda17fa4eb78386100c04874e37538a00001388100c07fe3fb784befbee5386100c04874dd7d7fc4f378386100c84874e35138a00001388100c87fe3fb784befbec1386100c84874dd5989760084280b0000418201907f44d378386100d04874e32138a00001388100d07fe3fb784befbe91386100d0480001687f44d378386100d84874e2fd38a00001388100d87fe3fb784befbe6d386100d84874dd057ee4bb78386100e04874e2d938a00001388100e07fe3fb784befbe49386100e04874dce17fa4eb78386100e84874e2b538a00001388100e87fe3fb784befbe25386100e84874dcbd7fc4f378386100f04874e29138a00001388100f07fe3fb784befbe01386100f0480000d87f44d378386100f84874e26d38a00001388100f87fe3fb784befbddd386100f84874dc757ee4bb78386101004874e24938a00001388101007fe3fb784befbdb9386101004874dc517fa4eb78386101084874e22538a00001388101087fe3fb784befbd95386101084874dc2d7fc4f378386101104874e20138a00001388101107fe3fb784befbd7138610110480000487ea4ab78386101184874e1dd38a00001388101187fe3fb784befbd4d386101184874dbe57e84a378386101204874e1b938a00001388101207fe3fb784befbd29386101204874dbc1382101a048b02924 + jump_table_start: 82051118 + jump_table_bytes: 000000d801d402d003a804800558061806a8073800000000 +- test_id: 16 + function_start: 824ce490 + function_bytes: 7d8802a6481c8aa59421ff803d6082987c7f1b783b8bd2cc7c9e2378387c00087cbd2b78484927512f1f100041990364419a0344397fffff2b0b001b419904643d8082045560083c398cfd287c0c022e3d80824d398ce4f8600000007d8c02147d8903a64e800420817d00002b0b00044198001c3d608298396bd248816b004c3be00000917e00004800000c3fe0807a63ff100139600004917d00004800040c817d00002b0b00044198ffe43d608298396bd248816b00504bffffc8817d00002b0b00044198ffc83d608298396bd248816b00544bffffac817d00002b0b00044198ffac3d608298816bd2484bffff94817d00002b0b00044198ff943d608298396bd248816b00584bffff78817d00002b0b00044198ff783d608298396bd248816b005c4bffff5c817d00002b0b00044198ff5c3d608298396bd248816b00684bffff40817d00002b0b00044198ff403d608298396bd248816b006c4bffff24817d00002b0b00044198ff243d608298396bd248816b000c4bffff08817d00002b0b00044198ff083d608298396bd248a16b00744bfffeec817d00002b0b00044198feec3d608298396bd248a16b00764bfffed0817d00002b0b00044198fed03d608298396bd248a16b00784bfffeb4817d00002b0b00044198feb43d608298396bd248a16b007a4bfffe98817d00002b0b00044198fe983d608298396bd248816b00104bfffe7c817d00002b0b00044198fe7c3d608298396bd248816b00704bfffe60817d00002b0b00044198fe603d608298396bd248816b00184bfffe44817d00002b0b00044198fe443d608298396bd248816b001c4bfffe28817d00002b0b00044198fe283d608298396bd248816b00044bfffe0c817d00002b0b00044198fe0c3d608298396bd248816b00084bfffdf0817d00002b0b00044198fdf03d608298396bd248816b00204bfffdd4817d00002b0b00044198fdd43d608298396bd248816b007c4bfffdb8817d00002b0b00044198fdb83d608298396bd248816b00144bfffd9c817d00002b0b00044198fd9c3d608298396bd248816b00244bfffd80817d00002b0b00044198fd803d608298396bd248816b00284bfffd64817d00002b0b00044198fd643d608298396bd248816b002c4bfffd48817d00002b0b00044198fd483d608298396bd248816b00344bfffd2c817d00002b0b00044198fd2c3d608298396bd248816b00404bfffd10817d00002b0b00044198fd103d608298396bd248816b00604bfffcf4397feffd2b0b0007419901087d6903a62f0b0000405a0038405a0050405a0068405a0080405a0098405a00b0409a00c8817d00002b0b00044198fcc43d608298396bd248816b00304bfffca8817d00002b0b00044198fca83d608298396bd248a16b00384bfffc8c817d00002b0b00044198fc8c3d608298396bd248a16b003a4bfffc70817d00002b0b00044198fc703d608298396bd248a16b003c4bfffc54817d00002b0b00044198fc543d608298396bd248816b00644bfffc38817d00002b0b00044198fc383d608298396bd248816b00804bfffc1c817d00002b0b00044198fc1c3d608298396bd248816b00444bfffc00817d00002b0b00044198fc003d608298396bd248816b00484bfffbe43fe0807a63ff1009387c0008484922d97fe3fb7838210080481c8640 + jump_table_start: 8203fd28 + jump_table_bytes: 0000003800540070008800a400c000dc00f801140130014c0168018401a001bc01d801f40210022c024802640280029c02b8043802d402f0 +- test_id: 17 + function_start: 826478d8 + function_bytes: 7d8802a69181fff89421ffa02b03001d419906c83d80820a5460083c398c85307c0c022e3d808264398c7914600000007d8c02147d8903a64e8004203c80209c386000204bc7ca15280300004182068c3d20ff7f3d00820a6129ffff396000009123000439400014390885bc9123000839230004916300109103000091630014914300189143001c9163000c480006503c80209c386000204bc7c9c128030000418206383d20ff7f3d00820a6129ffff9123000491230008392885d039000001480005f03c80209c386000204bc7c98d28030000418206043d20ff7f3d00820a6129ffff9123000491230008392885e439000002480005bc3c80209c386000244bc7c95928030000418205d03d20ff7f3d00820a6129ffff9123000491230008392885f8390000033940001491430020480005843c80209c3860001c4bc7c91d28030000418205943d40ff7f3d20820a614affff3929860c914300043900000491430008396000003940001491630010916300149123000091430018480005503c80209c3860001c4bc7c8d128030000418205483d40ff7f3d20820a614affff392986209143000439000005914300084bffffb43c80209c3860001c4bc7c89d28030000418205143d40ff7f3d20820a614affff392986349143000439000018914300084bffff803c80209c3860001c4bc7c86928030000418204e03d40ff7f3d20820a614affff392986489143000439000019914300084bffff4c3c80209c3860001c4bc7c83528030000418204ac3d40ff7f3d20820a614affff3929865c914300043900001a914300084bffff183c80209c3860001c4bc7c80128030000418204783d40ff7f3d20820a614affff39298670914300043900001b914300084bfffee43c80209c3860001c4bc7c7cd28030000418204443d40ff7f3d20820a614affff39298684914300043900001c914300084bfffeb03c80209c3860001c4bc7c79928030000418204103d40ff7f3d20820a614affff392986989143000439000006914300084bfffe7c3c80209c3860001c4bc7c76528030000418203dc3d40ff7f3d20820a614affff392986ac9143000439000007914300084bfffe483c80209c3860001c4bc7c73128030000418203a83d40ff7f3d20820a614affff392986c09143000439000008914300084bfffe143c80209c3860001c4bc7c6fd28030000418203743d40ff7f3d20820a614affff392986d49143000439000015914300084bfffde03c80209c3860001c4bc7c6c928030000418203403d40ff7f3d20820a614affff392986e89143000439000016914300084bfffdac3c80209c3860001c4bc7c695280300004182030c3d40ff7f3d20820a614affff392986fc9143000439000017914300084bfffd783c80209c386000244bc7c66128030000418202d83d20ff7f3d00820a6129ffff912300049123000839288710390000094bfffd083c80209c386000244bc7c62d28030000418202a43d20ff7f3d00820a6129ffff9123000491230008392887243900000a4bfffcd43c80209c3860001c4bc7c5f928030000418202703d40ff7f3d20820a614affff39298738914300043900000b914300084bfffcdc3c80209c3860001c4bc7c5c5280300004182023c3d40ff7f3d20820a614affff3929874c914300043900000c914300084bfffca83c80209c3860001c4bc7c59128030000418202083d40ff7f3d20820a614affff39298760914300043900000d914300084bfffc743c80209c3860001c4bc7c55d28030000418201d43d40ff7f3d20820a614affff39298774914300043900000e914300084bfffc403c80209c3860001c4bc7c52928030000418201a03d40ff7f3d20820a614affff39298788914300043900000f914300084bfffc0c3c80209c3860001c4bc7c4f5280300004182016c3d40ff7f3d20820a614affff3929879c9143000439000010914300084bfffbd83c80209c3860001c4bc7c4c128030000418201383d40ff7f3d20820a614affff392987b09143000439000011914300084bfffba43c80209c386000204bc7c48d28030000418201043d20ff7f3d00820a6129ffff9123000491230008392887c439000012480000bc3c80209c386000204bc7c45928030000418200d03d20ff7f3d00820a6129ffff9123000491230008392887d839000013480000883c80209c386000204bc7c425280300004182009c3d20ff7f3d00820a6129ffff39600000912300043940001491230008392887ec91630010916300143963000491230000914300189143001c9143000c480000603c80209c386000204bc7c3d128030000418200483d20ff7f3d00820a6129ffff9123000491230008392888003900001d3940001439600000916300109163001491230000914300189143001c396300049103000c4800000838600000382100608181fff87d8803a64e800020 + jump_table_start: 82098530 + jump_table_bytes: 00000054008800bc00f80144027c02b002e403b403e8041c0450048404b804ec05200554058805bc05f00318034c0380017801ac01e0021402480644 +- test_id: 18 + function_start: 825ec718 + function_bytes: 7d8802a64bfa189d9421fef07c7f1b787c9e23787cb62b78835f0034817f003083be0000831e00047f1a5840837f0020839f001c409800107d7a58503aebffff4800000c817f002c7efa5850815f00002b0a0009419906e83d6082013aa000003a6b2c483d6082013a4000063a2b18c03a0000073a8000093d808201398c19105540083c7c0c022e3d80825f398cc7b87d8c02147d8903a6600000004e8004202b180000419a06d8897d00007eb6ab783b18ffff3bbd00017d6be0303b9c00087d7bdb782b1c00034198ffd8576b077e556af87e556b07fe2b0a0001917f001841980098419a00482b0a000341980030409a06403d608204929f0000576ae8fe396b3e7c393cfffd38a0fffd917e0018915f0020913f001c48000630577be8fe3b9cfffd39600003480000e07fc7f37838c1006838a1006c388100703861007448003e617fc7f37880c1006880a1006c80810070806100744800307928030000907f00044182062c577be8fe925f00003b9cfffd480005bc39400001397cfffd5769e8fe915f0000556a077e7f8a58507d3b54304800059c2b180000419a05e0897d00007eb6ab783b18ffff3bbd00017d6be0303b9c00087d7bdb782b1c00204198ffd87f6ad8f8576b043e554a843e7f0a5840409a05c4917f0004556b003e7ebcab787ebbab782b0b0000419a000c3960000248000014817f0018216b00007d6b5910556b077e917f0000480005242b180000419a05682b170000409a00bc813f002c7f1a4840409a0038817f0030815f00287f0b5040419a00287d5a53787f1a5840409800107d7a58503aebffff480000087efa48502b170000409a007c7ec5b378935f00347fc4f3787fe3fb7848003d61835f0034817f00307c651b787f1a5840409800107d5a58503aeaffff4800000c815f002c7efa5050813f002c7f1a4840409a002c815f00287f0b5040419a00207d5a53787f1a5840409800107d7a58503aebffff480000087efa48502b170000419a046c833f00047eb6ab787f19c040409900087f19c3787f19b840409900087ef9bb787f25cb787fa4eb787f43d3784bfa1675817f00047fb9ea147d7958517f19c0507f59d2147ef9b850917f00044082040c4bfffed02b180000419a044c897d00007eb6ab783b18ffff3bbd00017d6be0303b9c00087d7bdb782b1c000e4198ffd8576b04be556a06fe2b0a001d917f000441990448556bdefe2b0b001d4199043c7d6b5214807e0028815e002038a00004388b01027d4903a64e80042128030000907f000c418203f43960000492bf0008577b93be3b9cfff2917f0000480000602b180000419a03c0897d00007eb6ab783b18ffff3bbd00017d6be0303b9c00087d7bdb782b1c00034198ffd8817f00085769077e815f000c577be8fe556b103a3b9cfffd7d6b882e556b103a7d2b512e817f0008396b0001917f0008817f0004815f0008556bb2be396b00047f0a58404198ffb448000028817f0008815f000c556b103a7d6b882e556b103a7eab512e817f0008396b0001917f0008817f00082b0b00134198ffd4389f001080df00247fc7f378807f000c38bf0014921f0010480038897c791b794082033c3960000592bf0008917f000048000164817f0010480000282b180000419a02dc895d00007eb6ab783b18ffff3bbd00017d4ae0303b9c00087d5bdb787f1c58404198ffd8556b103a815f00147d6b982e7d6bd838556b18387d6b521480ab0004896b00012b0500104098002c815f00087f8be050813f000c7f7b5c30554a103a7caa492e815f0008394a0001915f0008480000e02b050012409a00107e09837838c0000b4800000c3925fff238c000037d495a14480000282b180000419a023c891d00007eb6ab783b18ffff3bbd00017d08e0303b9c00087d1bdb787f1c50404198ffd87d09e050552a103a7f8b40507f6b5c307d0a982e815f00047d0858385547defe554a06fe7d0832147d475214394a01027d7b4c30817f00087d285a147f0950404199023c2b050010409a00202b0b00014198022c813f000c556a103a7d4a4a14812afffc480000087ea9ab78556a103a80ff000c3508ffff396b00017d27512e394a00044082ffec917f0008817f0004813f0008556adefe556b06fe7d6a5a14396b01027f0958404198fe84817f0004392100783901007c815f00245564defe80bf000c556b06fe93c1005438e1006092bf001438c10064928100643884000192410060386b0101480037917c791b79408201ac7fc7f37880c1007880a1007c808100608061006448002b612803000041820118907f0004809f000c807e0028817e00247d6903a64e800421925f0000937f00207ec5b378939f001c7fc4f378817e00007fe3fb78815e00087d6be850931e000493be00007d6b5214917e0008935f003448002b657c651b787fc4f3782f050001409a0090807f00047eb6ab7848003189835f0034817f003083be0000831e00047f1a5840837f0020839f001c409800107d7a58503aebffff4800000c817f002c7efa5850817f00182b0b0000409a00e492bf0000815f00002b0a00094099f94038a0fffe937f0020939f001c931e0004817e00007fc4f378815e00087d6be85093be00007d6b5214917e0008935f00347fe3fb7848003861382101104bfa1178937f00207ec5b378939f001c92be00044bffffc038a0fffc4bffffac3d608204396b3e90929f0000917e001838a0fffd4bffff943d608204396b3eb04bffffe82f19fffd409a001c809f000c807e0028817e00247d6903a64e800421929f00007f25cb784bffff60809f000c807e0028817e00247d6903a64e8004213d608204396b3ed44bffffa02f19fffd4bffffb8921f00007ec5b378935f00347fc4f3787fe3fb78480037b1835f0034817f00307c651b787f0bd040409aff0c39600008917f000038a000014bfffefc + jump_table_start: 82011910 + jump_table_bytes: 0024011c017002b00374055405f8077007a00708 +- test_id: 19 + function_start: 82185b60 + function_bytes: 7d8802a69181fff89421ffa0906100749081007c90a1008480a100848081007c806100744bfc60dd808100848061007481610074394b00408121007c7d0a482e7d0903a64e80042148000b9880e1007c90e1005080c1005038a6ffd890a10050808100502b04016841990b74806100503d8082185460103a398c5be87c0c002e7c0903a64e8004208218618c8218673c8218673c8218673c8218619c8218673c8218673c8218673c821861ac8218673c8218673c8218673c821861bc8218673c8218673c8218673c821861cc8218673c8218673c8218673c821861dc8218673c8218673c8218673c821861ec8218673c8218673c8218673c821861fc8218673c8218673c8218673c8218620c8218673c8218673c8218673c8218621c8218673c8218673c8218673c8218622c8218673c8218673c8218673c8218623c8218673c8218673c8218673c8218624c8218673c8218673c8218673c8218625c8218673c8218673c8218673c8218626c8218673c8218673c8218673c8218627c8218673c8218673c8218673c8218628c8218673c8218673c8218673c8218629c8218673c8218673c8218673c821862ac8218673c8218673c8218673c821862bc8218673c8218673c8218673c821862cc8218673c8218673c8218673c821862dc8218673c8218673c8218673c821862ec8218673c8218673c8218673c821862fc8218673c8218673c8218673c8218630c8218673c8218673c8218673c8218631c8218673c8218673c8218673c8218632c8218673c8218673c8218673c8218633c8218673c8218673c8218673c8218634c8218673c8218673c8218673c8218635c8218673c8218673c8218673c8218636c8218673c8218673c8218673c8218637c8218673c8218673c8218673c8218638c8218673c8218673c8218673c8218639c8218673c8218673c8218673c821863ac8218673c8218673c8218673c821863bc8218673c8218673c8218673c821863cc8218673c8218673c8218673c821863dc8218673c8218673c8218673c821863ec8218673c8218673c8218673c821863fc8218673c8218673c8218673c8218640c8218673c8218673c8218673c8218641c8218673c8218673c8218673c8218642c8218673c8218673c8218673c8218643c8218673c8218673c8218673c8218644c8218673c8218673c8218673c8218645c8218673c8218673c8218673c8218646c8218673c8218673c8218673c8218647c8218673c8218673c8218673c8218648c8218673c8218673c8218673c8218649c8218673c8218673c8218673c821864ac8218673c8218673c8218673c821864bc8218673c8218673c8218673c821864cc8218673c8218673c8218673c821864dc8218673c8218673c8218673c821864ec8218673c8218673c8218673c821864fc8218673c8218673c8218673c8218650c8218673c8218673c8218673c8218651c8218673c8218673c8218673c8218652c8218673c8218673c8218673c8218653c8218673c8218673c8218673c8218654c8218673c8218673c8218673c8218655c8218673c8218673c8218673c8218656c8218673c8218673c8218673c8218657c8218673c8218673c8218673c8218658c8218673c8218673c8218673c8218659c8218673c8218673c8218673c821865ac8218673c8218673c8218673c821865bc8218673c8218673c8218673c821865cc8218673c8218673c8218673c821865dc8218673c8218673c8218673c821865ec8218673c8218673c8218673c821865fc8218673c8218673c8218673c8218660c8218673c8218673c8218673c8218661c8218673c8218673c8218673c8218662c8218673c8218673c8218673c8218663c8218673c8218673c8218673c8218664c8218673c8218673c8218673c8218665c8218673c8218673c8218673c8218666c8218673c8218673c8218673c8218667c8218673c8218673c8218673c8218668c8218673c8218673c8218673c8218669c8218673c8218673c8218673c821866ac8218673c8218673c8218673c821866bc8218673c8218673c8218673c821866cc8218673c8218673c8218673c821866dc8218673c8218673c8218673c821866ec8218673c8218673c8218673c821866fc8218673c8218673c8218673c8218670c8218673c8218673c8218673c8218671c8218673c8218673c8218673c8218672c80810084806100744bfced1d480005a880810084806100744bfbb75d4800059880810084806100744bfcbc1d4800058880810084806100744bfc4f0d4800057880810084806100744bfafb0d4800056880810084806100744bfb236d4800055880810084806100744bfab9fd4800054880810084806100744bfd110d4800053880810084806100744bfbce8d4800052880810084806100744bfacffd4800051880810084806100744bfcf28d4800050880810084806100744bfcfdbd480004f880810084806100744bfaadbd480004e880810084806100744bfbf95d480004d880810084806100744bfbd09d480004c880810084806100744bfaebad480004b880810084806100744bfdbbbd480004a880810084806100744bfdb9fd4800049880810084806100744bfd482d4800048880810084806100744bfb84ed4800047880810084806100744bfc132d4800046880810084806100744bfb854d4800045880810084806100744bfb809d4800044880810084806100744bfc5c9d4800043880810084806100744bfb855d4800042880810084806100744bfbdf3d4800041880810084806100744bfd691d4800040880810084806100744bfd82ed480003f880810084806100744bfd677d480003e880810084806100744bfda8dd480003d880810084806100744bfaf79d480003c880810084806100744bfd5d5d480003b880810084806100744bfd7a1d480003a880810084806100744bfbf47d4800039880810084806100744bfc13ed4800038880810084806100744bfb0b7d4800037880810084806100744bfc77fd4800036880810084806100744bfb0ead4800035880810084806100744bfb905d4800034880810084806100744bfd7bbd4800033880810084806100744bfb00bd4800032880810084806100744bfdac1d4800031880810084806100744bfc0a8d4800030880810084806100744bfc329d480002f880810084806100744bfcd13d480002e880810084806100744bfcd14d480002d880810084806100744bfcd16d480002c880810084806100744bfca5ad480002b880810084806100744bfd10cd480002a880810084806100744bfbbe3d4800029880810084806100744bfccfad4800028880810084806100744bfccfbd4800027880810084806100744bfccfcd4800026880810084806100744bfccfed4800025880810084806100744bfccfed4800024880810084806100744bfccfed4800023880810084806100744bfccffd4800022880810084806100744bfcd00d4800021880810084806100744bfcd00d4800020880810084806100744bfcd00d480001f880810084806100744bfdd85d480001e880810084806100744bfdd85d480001d880810084806100744bfdd85d480001c880810084806100744bfdd88d480001b880810084806100744bfdd88d480001a880810084806100744bfdd88d4800019880810084806100744bfc90ad4800018880810084806100744bfcb40d4800017880810084806100744bfb4d9d4800016880810084806100744bfb4dbd4800015880810084806100744bfb4dcd4800014880810084806100744bfb1afd4800013880810084806100744bfd5add4800012880810084806100744bfab37d4800011880810084806100744bfb8ddd4800010880810084806100744bfadcfd480000f880810084806100744bfd9b4d480000e880810084806100744bfd9b6d480000d880810084806100744bfaebcd480000c880810084806100744bfaebdd480000b880810084806100744bfd0b4d480000a880810084806100744bfba2dd4800009880810084806100744bfc92ad4800008880810084806100744bfab48d4800007880810084806100744bfca82d4800006880810084806100744bfcb12d4800005880810084806100744bfb9b2d4800004880810084806100744bfb355d4800003880810084806100744bfaf84d4800002880810084806100744bfaef5d4800001880810084806100744bfdd04d480000080fe00016382100608181fff87d8803a64e800020 + jump_table_start: 00000000 + jump_table_bytes: 00000000 +- test_id: 20 + function_start: 82588e08 + function_bytes: 7d8802a6480051e19421ff907c7e1b783bbe008083fe00804800002c809fffb883ff000089640035556b06f74182001838a000007fc3f3784bfff0992c030000418000107f1fe840409affd43860000038210070480051e07d8802a69181fff8fbc1ffe8fbe1fff09421ff907c6a1b787c8b23787ca42b782f0b000083ea0018419a00202f0b0001419a00182f0b0005419a0010807f00882c030000408201943bc000002b0b00084199017c3d808200398c2f107c0c58ae5400103a3d808259398c8edc7d8c02147d8903a6600000004e8004207fe3fb78481844b5480001507fe3fb78481844b94800014483df00882c1e0000408201387fe3fb784bffff057c7e1b794180012838a0ffff388000007fe3fb784bffcb2d7c7e1b78480001107fe3fb7883df00884bffa5a14800010083df00882c1e0000408200f4811f00a0395f0080813f00a480df004c80ff00c47d29421454c8a33e817f008054e7007e38c00000913f00a0911f00a890ff00c490df00a448000018812bffb8816b000089090035710800df990900357f0b5040409affe8895f00c8817f0030280a00004182008c396b0007892b0000394affff712900fb280a0000992b0000396b00084082ffe8480000683d60827d39400001396b8b5c38a0000038800001386b0010914b002048184749480000443d60827d7c832378396b8b5c38a00028396b00247d645b784800508548000024817f008c916400004800001881640000917f008c4800000c3fc0c00063de00107fc3f378382100708181fff87d8803a6ebc1ffe8ebe1fff04e800020 + jump_table_start: 82002f10 + jump_table_bytes: 00030613173d4e5146000000 +- test_id: 21 + function_start: 823349f8 + function_bytes: 7d8802a69181fff8fbc1ffe8fbe1fff09421ff503d6082a07c7e1b787cdf33782b050001816beaa8916300004198025c419a009c2b0500034198007c419a00602b05000541980040419a00242b050007409802503d60820538610050388be6f4481ddf4181610050480002343d60820538610054388be6c8481ddf29816100544800021c3d60820538610058388be6a8481ddf1181610058480002043d6082053861005c388be68c481ddef98161005c480001ec3d60820538610060388be670481ddee181610060480001d42b1f0000409a001c3d60820538610064388be650481ddec181610064480001b4809f000481640000386b0010480708093963ffff2b0b00094199016c3d808205398ce5507c0c58ae5400103a3d80823360000000398c4b287d8c02147d8903a64e800420817f00047d645b78816b0000386b0010480707c13d60820538610068388be650481dde5181610068480001443d6082053861006c388be634481dde398161006c4800012c3d60820538610070388be618481dde218161007048000114809f000481640000386b0018480707692c030000418200b82f030002419a00b02f030003419a00902f030004419a0018817f00047d645b78816b0000386b0018480000b43d60820238610088388be3f0481dddc53d6082a48083000038a00001816b2830814b0000814a00047c6a5a144808194d38800000480706fd2c0300004182001c3d60820538610074388be5ec481ddd8581610074480000783d60820538610078388be5c0481ddd6d81610078480000603d6082053861007c388be59c481ddd558161007c480000483d60820538610080388be578481ddd3d8161008048000030817f00047d645b78816b0000386b0010480706814800001c3d60820538610084388be55c481ddd0d81610084917e00007fc3f378382100b08181fff87d8803a6ebc1ffe8ebe1fff04e800020 + jump_table_start: 8204e550 + jump_table_bytes: 00000b110000500050170000 \ No newline at end of file diff --git a/assets/tests/relative_bytes_jump_table_snippets.txt b/assets/tests/relative_bytes_jump_table_snippets.txt new file mode 100644 index 0000000..8b65187 --- /dev/null +++ b/assets/tests/relative_bytes_jump_table_snippets.txt @@ -0,0 +1,87 @@ +-1- +822C465C: 2B0B0068 cmplwi r11, 0x68 +822C4660: 41990154 bgt 0x822C47B4 +822C4664: 3D808200 lis r12, 0x8200 +822C4668: 398C0B18 addi r12, r12, 0x0B18 +822C466C: 7C0C58AE lbzx r0, r12, r11 +822C4670: 5400103A slwi r0, r0, 0x2 +822C4674: 3D80822C lis r12, 0x822C +822C4678: 60000000 nop +822C467C: 398C468C addi r12, r12, 0x468C +822C4680: 7D8C0214 add r12, r12, r0 +822C4684: 7D8903A6 mtctr r12 +822C4688: 4E800420 bctr +822C468C: 39600001 li r11, 0x1 +... +822C47AC: 39600024 li r11, 0x24 + +-2- +824AFD60: 2B0A001B cmplwi r10, 0x1B +824AFD64: 4199020C bgt 0x824AFF70 +824AFD68: 3D808203 lis r12, 0x8203 +824AFD6C: 398CD870 addi r12, r12, -0x2790 +824AFD70: 7C0C50AE lbzx r0, r12, r10 +824AFD74: 5400103A slwi r0, r0, 0x2 +824AFD78: 3D80824B lis r12, 0x824B +824AFD7C: 398CFD90 addi r12, r12, -0x270 +824AFD80: 7D8C0214 add r12, r12, r0 +824AFD84: 7D8903A6 mtctr r12 +824AFD88: 60000000 nop +824AFD8C: 4E800420 bctr +824AFD90: 386B0004 addi r3, r11, 0x4 +... +824AFF70: 38610050 addi r3, r1, 0x50 + +-3- +8219B604: 2B04000A cmplwi r4, 0xA +8219B608: 419901DC bgt 0x8219B7E4 +8219B60C: 3D808200 lis r12, 0x8200 +8219B610: 398C44F8 addi r12, r12, 0x44F8 +8219B614: 7C0C20AE lbzx r0, r12, r4 +8219B618: 5400103A slwi r0, r0, 0x2 +8219B61C: 3D80821A lis r12, 0x821A +8219B620: 60000000 nop +8219B624: 398CB634 addi r12, r12, -0x49CC +8219B628: 7D8C0214 add r12, r12, r0 +8219B62C: 7D8903A6 mtctr r12 +8219B630: 4E800420 bctr +8219B634: 897D000A lbz r11, 0xa, r29 +... +8219B7E4: 7F63DB78 mr r3, r27 + +-4- +821C3EEC: 2B0A000B cmplwi r10, 0xB +821C3EF0: 41990300 bgt 0x821C41F0 +821C3EF4: 3D808201 lis r12, 0x8201 +821C3EF8: 398C83E8 addi r12, r12, -0x7C18 +821C3EFC: 7C0C50AE lbzx r0, r12, r10 +821C3F00: 5400103A slwi r0, r0, 0x2 +821C3F04: 3D80821C lis r12, 0x821C +821C3F08: 60000000 nop +821C3F0C: 398C3F1C addi r12, r12, 0x3F1C +821C3F10: 7D8C0214 add r12, r12, r0 +821C3F14: 7D8903A6 mtctr r12 +821C3F18: 4E800420 bctr +821C3F1C: 556A0043 rlwinm. r10, r11, 0x0, 0x1, 0x1 +... +821C41F0: 7DCE7378 mr r14, r14 + +-5- +823178D0: 2B0B000B cmplwi r11, 0xB +823178D4: 41990370 bgt 0x82317C44 +823178D8: 3D808204 lis r12, 0x8204 +823178DC: 398C6EC8 addi r12, r12, 0x6EC8 +823178E0: 7C0C58AE lbzx r0, r12, r11 +823178E4: 5400103A slwi r0, r0, 0x2 +823178E8: 3D808231 lis r12, 0x8231 +823178EC: 60000000 nop +823178F0: 398C7900 addi r12, r12, 0x7900 +823178F4: 7D8C0214 add r12, r12, r0 +823178F8: 7D8903A6 mtctr r12 +823178FC: 4E800420 bctr +82317900: 3FA082A4 lis r29, 0x82A4 +82317904: 807D1FCC lwz r3, 0x1FCC(r29) +... +82317C44: 7FC3F378 mr r3, r30 + +-6- \ No newline at end of file diff --git a/src/analysis/cfa.rs b/src/analysis/cfa.rs index 88dd139..8b87860 100644 --- a/src/analysis/cfa.rs +++ b/src/analysis/cfa.rs @@ -274,24 +274,9 @@ impl AnalyzerState { // Process known functions first for addr in self.functions.keys().cloned().collect_vec() { self.process_function_at(obj, addr)?; - - // some assertions, since we're working with known function boundaries - // if we got this from pdata or import data, there should be a known end - if let Some(value) = obj.known_functions.get(&addr) { - if let Some(func) = self.functions.get(&addr) { - if let Some(known_size) = value { - let known_end = addr + *known_size; - assert!(func.end.is_some(), "Function at {} has no detected end rather than known end {}. There must be an error in processing!", addr, known_end); - let func_end = func.end.unwrap(); - assert_eq!(func_end, known_end, - "Function at {} has known end addr {}, but during processing, ending was found to be {}!", - addr, known_end, func_end); - } - } else { - unreachable!(); - } - } - // assert something with slices? + // originally, I placed some assertions here to verify CFA reached the expected end + // what I failed to consider is that functions may need multiple passes to reach that end. + // so, some functions that had possible tail calls were ending CFA early on their first run, causing these to falsely fail. } // the rest... @@ -308,6 +293,7 @@ impl AnalyzerState { // Locate bounds for referenced functions until none are left self.process_functions(obj)?; // Final pass(es) + println!("Running final passes...\n"); while self.finalize_functions(obj, true)? { self.process_functions(obj)?; } @@ -558,3 +544,7 @@ impl AnalyzerState { Ok(found_new) } } + +#[cfg(test)] +#[path = "cfa_tests.rs"] +mod cfa_tests; diff --git a/src/analysis/cfa_tests.rs b/src/analysis/cfa_tests.rs new file mode 100644 index 0000000..d6b4a2e --- /dev/null +++ b/src/analysis/cfa_tests.rs @@ -0,0 +1,964 @@ +// use std::fs::File; +// +// use anyhow::Result; +// use serde::{de::Error, Deserialize, Deserializer}; +// +// use super::*; +// use crate::{ +// analysis::cfa::AnalyzerState, +// obj::{ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind}, +// }; +// +// fn bytestr_to_bytes<'de, D>(deserializer: D) -> Result, D::Error> +// where D: Deserializer<'de> { +// let hex_str = String::deserialize(deserializer)?; +// +// if hex_str.len() % 2 != 0 { +// return Err(D::Error::custom("hex string must have even length")); +// } +// +// let bytes = (0..hex_str.len()) +// .step_by(2) +// .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16)) +// .collect::, _>>() +// .map_err(D::Error::custom)?; +// +// Ok(bytes) +// } +// +// fn get_fn_start<'de, D>(deserializer: D) -> Result +// where D: Deserializer<'de> { +// let hex_str = String::deserialize(deserializer)?; +// if hex_str.len() != 8 { +// return Err(D::Error::custom(format!("expected 8 hex chars, got {}", hex_str.len()))); +// } +// let start = u32::from_str_radix(&*hex_str, 16).map_err(D::Error::custom)?; +// Ok(start) +// } +// +// #[derive(Debug, Deserialize)] +// struct TestConfig { +// test_id: u32, +// #[serde(deserialize_with = "get_fn_start")] +// function_start: u32, +// #[serde(deserialize_with = "bytestr_to_bytes")] +// function_bytes: Vec, +// #[serde(deserialize_with = "get_fn_start")] +// jump_table_start: u32, +// #[serde(deserialize_with = "bytestr_to_bytes")] +// jump_table_bytes: Vec, +// } +// +// // helper func to create an ObjInfo +// fn make_code_section(base_addr: u32, instructions: &[u8]) -> ObjSection { +// ObjSection { +// name: ".text".into(), +// kind: ObjSectionKind::Code, +// address: base_addr as u64, +// size: instructions.len() as u64, +// data: Vec::from(instructions), +// align: 0x10000, +// ..Default::default() +// } +// } +// +// fn make_data_section(base_addr: u32, instructions: &[u8]) -> ObjSection { +// ObjSection { +// name: ".rdata".into(), +// kind: ObjSectionKind::ReadOnlyData, +// address: base_addr as u64, +// size: instructions.len() as u64, +// data: Vec::from(instructions), +// align: 0x10000, +// ..Default::default() +// } +// } +// +// fn create_dummy_obj(code_section: ObjSection, rdata_section: Option) -> ObjInfo { +// let mut sections: Vec = vec![]; +// if let Some(rdata_section) = rdata_section { +// sections.push(rdata_section); +// } +// sections.push(code_section); +// ObjInfo::new(ObjKind::Executable, ObjArchitecture::PowerPc, "test.exe".into(), vec![], sections) +// } +// +// #[test] +// fn test_super_basic_cfa() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[0]; +// assert_eq!(cur_test.test_id, 0); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// None, +// ); +// let mut state = AnalyzerState::default(); +// let start_addr = SectionAddress::new(0, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // assert that we have slices +// assert!(func.slices.is_some()); +// let slices = func.slices.as_ref().unwrap(); +// // this func should only have 1 basic block +// assert_eq!(slices.blocks.len(), 1); +// Ok(()) +// } +// +// // Absolute general skeleton: +// // lis r12, +// // addi r12, r12, +// // rlwinm r0, rX, 0x2, 0x0, 0x1d // NOTE: rX likely has the table bounds, but because it's absolute, and because there are funny stack memes, we can ignore it +// // lwzx r0, r12, r0 +// // mtctr r0 +// // bctr +// // +// +// // Relative bytes (no rlwinm) general skeleton: +// // cmplwi crN, rX, +// // bgt crN, default +// // lis r12, +// // addi r12, r12, +// // lbzx r0, r12, rX +// // slwi r0, r0, 0x2 +// // lis r12, +// // nop +// // addi r12, r12, +// // add r12, r12, r0 +// // mtctr r12 +// // bctr +// // +// // ... +// // +// +// // Relative bytes (no rlwinm) alternate general skeleton: +// // cmplwi crN, rX, +// // bgt crN, default +// // lis r12, +// // addi r12, r12, +// // lbzx r0, r12, rX +// // lis r12, +// // addi r12, r12, +// // add r12, r12, r0 +// // mtctr r12 +// // nop +// // nop +// // bctr +// // +// // ... +// // +// +// // DDR Universe: FUN_822671c0 +// +// // Relative bytes (rlwinm after lbzx) general skeleton +// // (remember, the entries in the jump table need to be multiplied by 4): +// // cmplwi crN, rX, +// // bgt crN, default +// // lis r12, +// // addi r12, r12, +// // lbzx r0, r12, rX +// // rlwinm r0, r0, 0x2, 0x0, 0x1d +// // lis r12, +// // nop +// // addi r12, r12, +// // add r12, r12, r0 +// // mtctr r12 +// // bctr +// // +// // ... +// // +// +// // TBRB: FUN_823349f8 +// // halo 3: FUN_82588e08 +// +// // Relative shorts (no rlwinm) +// // for the life of me i can't find one +// +// // Relative shorts (rlwinm before lhzx) general skeleton +// // (remember, the entries in the jump table need to be multiplied by 2): // UPDATE: uhhh i guess maybe you don't need to multiply by 2 after all? +// // cmplwi crN, rX, +// // bgt crN, default +// // lis r12, +// // addi r12, r12, +// // rlwinm r0, rX, 0x1, 0x0, 0x1e +// // lhzx r0, r12, r0 +// // lis r12, +// // addi r12, r12, +// // add r12, r12, r0 +// // mtctr r12 +// // nop +// // bctr +// // +// // ... +// // +// +// #[test] +// fn test_jump_table_absolute_1() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[1]; +// assert_eq!(cur_test.test_id, 1); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// None, +// ); +// let mut state = AnalyzerState::default(); +// let start_addr = SectionAddress::new(0, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// // and there should be 4 entries in it +// let jump_table_entry = state.jump_tables.get(&SectionAddress::new(0, 0x820869fc)); +// assert!(jump_table_entry.is_some()); +// // 4 entries * 4 bytes per entry +// assert_eq!(*jump_table_entry.unwrap(), 4 * 4); +// // we should also have a lotta basic blocks +// assert!(func.slices.is_some()); +// let slices = func.slices.as_ref().unwrap(); +// assert!(slices.blocks.len() > 5); // idk the exact number but i know it's more than 5 +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_absolute_2() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[2]; +// assert_eq!(cur_test.test_id, 2); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// None, +// ); +// let mut state = AnalyzerState::default(); +// let start_addr = SectionAddress::new(0, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// // and there should be 4 entries in it +// let jump_table_entry = state.jump_tables.get(&SectionAddress::new(0, 0x827f9434)); +// assert!(jump_table_entry.is_some()); +// // 4 entries * 4 bytes per entry +// assert_eq!(*jump_table_entry.unwrap(), 4 * 4); +// // we should also have a lotta basic blocks +// assert!(func.slices.is_some()); +// let slices = func.slices.as_ref().unwrap(); +// assert!(slices.blocks.len() > 5); // idk the exact number but i know it's more than 5 +// Ok(()) +// } +// +// // this one's also got VMX! for added fun +// #[test] +// fn test_jump_table_absolute_3() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[3]; +// assert_eq!(cur_test.test_id, 3); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// None, +// ); +// let mut state = AnalyzerState::default(); +// let start_addr = SectionAddress::new(0, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// // and there should be 4 entries in it +// let jump_table_entry = state.jump_tables.get(&SectionAddress::new(0, 0x82fbb464)); +// assert!(jump_table_entry.is_some()); +// // 4 entries * 4 bytes per entry +// assert_eq!(*jump_table_entry.unwrap(), 4 * 4); +// // we should also have a lotta basic blocks +// assert!(func.slices.is_some()); +// let slices = func.slices.as_ref().unwrap(); +// assert!(slices.blocks.len() > 5); // idk the exact number but i know it's more than 5 +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_bytes_1() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[4]; +// assert_eq!(cur_test.test_id, 4); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 105); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_bytes_2() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[5]; +// assert_eq!(cur_test.test_id, 5); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 0x1c); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_bytes_3() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[6]; +// assert_eq!(cur_test.test_id, 6); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 11); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_bytes_4() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[7]; +// assert_eq!(cur_test.test_id, 7); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 12); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_bytes_5() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[8]; +// assert_eq!(cur_test.test_id, 8); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// // TODO: verify number of jump table entries and basic block count +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 12); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_bytes_6() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[9]; +// assert_eq!(cur_test.test_id, 9); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 10); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_bytes_7() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[10]; +// assert_eq!(cur_test.test_id, 10); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 0x15); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_shorts_1() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[11]; +// assert_eq!(cur_test.test_id, 11); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 14 * 2); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_shorts_2() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[12]; +// assert_eq!(cur_test.test_id, 12); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 47 * 2); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_shorts_3() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[13]; +// assert_eq!(cur_test.test_id, 13); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 64 * 2); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_shorts_4() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[14]; +// assert_eq!(cur_test.test_id, 14); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 8 * 2); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_shorts_5() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[15]; +// assert_eq!(cur_test.test_id, 15); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 10 * 2); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_shorts_6() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[16]; +// assert_eq!(cur_test.test_id, 16); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 28 * 2); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_shorts_7() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[17]; +// assert_eq!(cur_test.test_id, 17); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 30 * 2); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_shorts_8() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[18]; +// assert_eq!(cur_test.test_id, 18); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 10 * 2); +// // TODO: verify basic block count +// Ok(()) +// } +// +// // this one has an absolute jump table, +// // except different registers are used when rlwinm'ing - it stores R4 to 0x50(R1), and then loads from 0x50(R1) into R3, and R3 is then used to index. +// // to get this to pass, we need some sort of mechanism that keeps track of what's in the stack at any given time +// // or: since this is an absolute jump table, and i haven't seen this for any relative jump tables, +// // we can ignore the indexing and just iterate through the jump table normally (keep going until you find not-an-address) +// #[test] +// #[ignore] +// fn test_jump_table_absolute_stack_meme() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[19]; +// assert_eq!(cur_test.test_id, 19); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// None, +// ); +// let mut state = AnalyzerState::default(); +// let start_addr = SectionAddress::new(0, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// // NOTE: this func ends prematurely at a b it suspects is a tail call +// // because of this, we have to run finalize_functions...but even so, it's claiming the end of the function is at the end of the block +// // and not the actual end. furthermore, it doesn't seem to find a jump table...not sure what's going on in the context of this test. +// // This works in the real thing because this func is in pdata, so there's a known end. +// state.finalize_functions(&obj, true)?; +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// // and there should be 4 entries in it +// let jump_table_entry = state.jump_tables.get(&SectionAddress::new(0, 0x82185be8)); +// assert!(jump_table_entry.is_some()); +// // 0x169 entries * 4 bytes per entry +// assert_eq!(*jump_table_entry.unwrap(), 0x169 * 4); +// // we should also have a lotta basic blocks +// assert!(func.slices.is_some()); +// let slices = func.slices.as_ref().unwrap(); +// assert!(slices.blocks.len() > 5); // idk the exact number but i know it's more than 5 +// Ok(()) +// } +// +// // Fails because of premature b that it thinks is a tail call. +// #[test] +// #[ignore] +// fn test_jump_table_relative_bytes_with_rlwinm_1() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[20]; +// assert_eq!(cur_test.test_id, 20); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 10); +// // TODO: verify basic block count +// Ok(()) +// } +// +// #[test] +// fn test_jump_table_relative_bytes_with_rlwinm_2() -> Result<()> { +// let test_cfg: Vec = +// serde_yaml::from_reader(File::open("assets/tests/cfa_tests.yml")?)?; +// let cur_test = &test_cfg[21]; +// assert_eq!(cur_test.test_id, 21); +// let obj = create_dummy_obj( +// make_code_section(cur_test.function_start, &cur_test.function_bytes), +// Some(make_data_section(cur_test.jump_table_start, &cur_test.jump_table_bytes)), +// ); +// let mut state = AnalyzerState::default(); +// // section 1 is .text now that we have a relative jump table in .rdata +// let start_addr = SectionAddress::new(1, cur_test.function_start); +// // CFA completed with no errors +// let res = state.process_function_at(&obj, start_addr).unwrap_or_else(|e| panic!("{:?}", e)); +// // we have one more function +// assert!(res); +// assert_eq!(state.functions.len(), 1); +// let func = state.functions.get(&start_addr); +// assert!(func.is_some()); +// let func = func.unwrap(); +// assert!(func.is_function()); +// // does the detected function end match our expected end? +// assert_eq!(func.end, Some(start_addr + cur_test.function_bytes.len() as u32)); +// // for this func, we should have 1 jump table +// assert_eq!(state.jump_tables.is_empty(), false); +// assert_eq!(state.jump_tables.len(), 1); +// let jump_table_entry = +// state.jump_tables.get(&SectionAddress::new(0, cur_test.jump_table_start)); +// assert!(jump_table_entry.is_some()); +// assert_eq!(*jump_table_entry.unwrap(), 10); +// // TODO: verify basic block count +// Ok(()) +// } diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index f16a8c1..41bc3f8 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -6,7 +6,7 @@ use powerpc::{Extensions, Ins}; use crate::{ analysis::{cfa::SectionAddress, vm::JumpTableType}, array_ref, - obj::{ObjInfo, ObjKind, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbolKind}, + obj::{ObjInfo, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbolKind}, }; pub mod cfa; @@ -57,18 +57,11 @@ fn is_valid_jump_table_addr( jump_table_type: JumpTableType, ) -> bool { match jump_table_type { - // if absolute, jump table is in .text, in the middle of the func actually JumpTableType::Absolute => { let kind = obj.sections[addr.section].kind; kind == ObjSectionKind::Code && kind != ObjSectionKind::Bss } - // else, addr must not be in code or bss - JumpTableType::RelativeBytes(_) - | JumpTableType::RelativeBytesTimes4(_) - | JumpTableType::RelativeShorts(_) - | JumpTableType::RelativeShortsTimes2(_) => { - !matches!(obj.sections[addr.section].kind, ObjSectionKind::Code | ObjSectionKind::Bss) - } + _ => !matches!(obj.sections[addr.section].kind, ObjSectionKind::Code | ObjSectionKind::Bss), } } @@ -97,87 +90,32 @@ fn get_jump_table_entries( function_start: SectionAddress, function_end: Option, ) -> Result<(Vec, u32)> { - assert!(!matches!(jump_table_type, JumpTableType::RelativeShortsTimes2(_)), - "Somehow, we found a jump table type lhzx that needs its offsets multiplied at addr {} from {}", addr, from); - let section = &obj.sections[addr.section]; - // Check for an existing symbol with a known size, and use that if available. - // Allows overriding jump table size analysis. - let known_size = obj - .symbols - .kind_at_section_address(addr.section, addr.address, ObjSymbolKind::Object) - .ok() - .flatten() - .and_then(|(_, s)| if s.size_known { NonZeroU32::new(s.size as u32) } else { None }); - - if let Some(size) = known_size.or(size).map(|n| n.get()) { - let num_entries = match jump_table_type { - JumpTableType::Absolute => size / 4, - JumpTableType::RelativeBytes(_) | JumpTableType::RelativeBytesTimes4(_) => size, - JumpTableType::RelativeShorts(_) | JumpTableType::RelativeShortsTimes2(_) => size / 2, - }; - log::debug!( - "Located jump table @ {:#010X} with entry count {} (from {:#010X})", - addr, - num_entries, - from - ); - let mut entries = Vec::with_capacity(num_entries as usize); - let mut data = section.data_range(addr.address, addr.address + size)?; - let relative_addr = match jump_table_type { - JumpTableType::Absolute => None, - JumpTableType::RelativeBytes(addr) - | JumpTableType::RelativeBytesTimes4(addr) - | JumpTableType::RelativeShorts(addr) - | JumpTableType::RelativeShortsTimes2(addr) => { - match addr.context("No relative address to apply jump table offsets to!")? { - RelocationTarget::Address(addr) => Some(addr), - _ => bail!("No relative address to apply jump table offsets to! (RelocationTarget is type External)"), - } - } - }; - let mut cur_addr = addr; // cur_addr == the address of the current jump table entry we're analyzing - let increment = match jump_table_type { - JumpTableType::Absolute => 4, - JumpTableType::RelativeBytes(_) | JumpTableType::RelativeBytesTimes4(_) => 1, - JumpTableType::RelativeShorts(_) | JumpTableType::RelativeShortsTimes2(_) => 2, - }; - loop { - if data.is_empty() { - break; - } - let reloc_address = match jump_table_type { - JumpTableType::Absolute => cur_addr, - JumpTableType::RelativeBytes(_) => relative_addr.unwrap() + data[0] as u32, - JumpTableType::RelativeBytesTimes4(_) => { - relative_addr.unwrap() + (data[0] as u32 * 4) - } - JumpTableType::RelativeShorts(_) => { - relative_addr.unwrap() + u16::from_be_bytes(*array_ref!(data, 0, 2)) as u32 - } - JumpTableType::RelativeShortsTimes2(_) => { - relative_addr.unwrap() - + (u16::from_be_bytes(*array_ref!(data, 0, 2)) as u32 * 2) - } - }; - if let Some(target) = - relocation_target_for(obj, reloc_address, Some(ObjRelocKind::Absolute))? - { - match target { - RelocationTarget::Address(addr) => entries.push(addr), - RelocationTarget::External => { - bail!("Jump table entry at {:#010X} points to external symbol", cur_addr) - } - } - } else { - let entry_addr = match jump_table_type { - JumpTableType::Absolute => u32::from_be_bytes(*array_ref!(data, 0, 4)), - JumpTableType::RelativeBytes(_) - | JumpTableType::RelativeBytesTimes4(_) - | JumpTableType::RelativeShorts(_) - | JumpTableType::RelativeShortsTimes2(_) => reloc_address.address, - }; - if entry_addr > 0 { + match jump_table_type { + JumpTableType::Absolute => { + // the start of the jump table should be IMMEDIATELY after the bctr + assert_eq!( + from + 4, + addr, + "Absolute jump table did not start immediately after the bctr at {}!", + from + ); + assert!( + function_end.is_some(), + "Must know function end for absolute jump table, because pdata" + ); + let mut entries: Vec = Vec::new(); + // now, step through, line by line, until you find not-an-address + let mut data = + section.data_range(addr.address, (section.address + section.size) as u32)?; + let mut cur_addr = addr; + loop { + let entry_addr = u32::from_be_bytes(*array_ref!(data, 0, 4)); + // entry_addr must be within known function bounds + // if you have an absolute jump table, your func is in pdata - ergo, known bounds + if entry_addr >= function_start.address + && entry_addr < function_end.unwrap().address + { let (section_index, _) = obj.sections.at_address(entry_addr).with_context(|| { format!( @@ -185,51 +123,178 @@ fn get_jump_table_entries( ) })?; entries.push(SectionAddress::new(section_index, entry_addr)); + } else { + break; } + data = &data[4..]; + cur_addr += 4; } - data = &data[increment..]; - cur_addr += increment as u32; + let size = cur_addr.address - addr.address; + log::debug!( + "Inferred absolute jump table @ {:#010X} with entry count {} (from {:#010X})", + addr, + size / 4, + from + ); + Ok((entries, size)) } - Ok((entries, size)) - } - // FIXME: this guessing routine only works for absolute jump tables - // make one for relative jump tables! - else { - let mut entries = Vec::new(); - let mut cur_addr = addr; - loop { - let target = if let Some(target) = - relocation_target_for(obj, cur_addr, Some(ObjRelocKind::Absolute))? - { - match target { - RelocationTarget::Address(addr) => addr, - RelocationTarget::External => break, + JumpTableType::RelativeBytes { target, multiplier } => { + // Check for an existing symbol with a known size, and use that if available. + // Allows overriding jump table size analysis. + let known_size = obj + .symbols + .kind_at_section_address(addr.section, addr.address, ObjSymbolKind::Object) + .ok() + .flatten() + .and_then( + |(_, s)| if s.size_known { NonZeroU32::new(s.size as u32) } else { None }, + ); + if let Some(size) = known_size.or(size).map(|n| n.get()) { + log::trace!( + "Located jump table @ {:#010X} with entry count {} (from {:#010X})", + addr, + size, + from + ); + let mut entries = Vec::with_capacity(size as usize); + let mut data = section.data_range(addr.address, addr.address + size)?; + let mut cur_addr = addr; + loop { + if data.is_empty() { + break; + } + if let Some(target) = + relocation_target_for(obj, cur_addr, Some(ObjRelocKind::Absolute))? + { + match target { + RelocationTarget::Address(addr) => entries.push(addr), + RelocationTarget::External => { + bail!( + "Jump table entry at {:#010X} points to external symbol", + cur_addr + ) + } + } + } else { + assert!(target.is_some(), "We need a target address to apply offsets to!"); + let target = match target.unwrap() { + RelocationTarget::Address(addr) => addr, + _ => { + panic!("We need a target address to apply offsets to!") + } + }; + let entry_addr = target.address + + (u8::from_be_bytes(*array_ref!(data, 0, 1)) as usize * multiplier) + as u32; + if entry_addr > 0 { + let (section_index, _) = + obj.sections.at_address(entry_addr).with_context(|| { + format!( + "Invalid jump table entry {entry_addr:#010X} at {cur_addr:#010X}" + ) + })?; + entries.push(SectionAddress::new(section_index, entry_addr)); + } + } + data = &data[1..]; + cur_addr += 4; } - } else if obj.kind == ObjKind::Executable { - let Some(value) = read_u32(section, cur_addr.address) else { - break; - }; - let Ok((section_index, _)) = obj.sections.at_address(value) else { - break; - }; - SectionAddress::new(section_index, value) + Ok((entries, size)) + } else { + // TODO: this is a hack courtesy of jeff 0.1.0 + // Although the jump table code is a lot more robust, there are still cases where we don't definitively know the jump table size. + // When this happens, just set the jump table size to 0 and move on. + // While this does mean you miss out on CFA from the potential addresses in the jump table, + // the size will ultimately sort itself out when rebuilding relocs. + let entries: Vec = Vec::new(); + log::debug!( + "Guessed jump table @ {:#010X} with entry count {} (from {:#010X})", + addr, + 0, + from + ); + Ok((entries, 0)) + } + // target = the first addr immediately after the bctr + // multiplier = how much to multiply each entry in the jump table by + } + JumpTableType::RelativeShorts { target, multiplier: _ } => { + // Check for an existing symbol with a known size, and use that if available. + // Allows overriding jump table size analysis. + let known_size = obj + .symbols + .kind_at_section_address(addr.section, addr.address, ObjSymbolKind::Object) + .ok() + .flatten() + .and_then( + |(_, s)| if s.size_known { NonZeroU32::new(s.size as u32) } else { None }, + ); + if let Some(size) = known_size.or(size).map(|n| n.get()) { + log::trace!( + "Located jump table @ {:#010X} with entry count {} (from {:#010X})", + addr, + size / 2, + from + ); + let mut entries = Vec::with_capacity(size as usize / 2); + let mut data = section.data_range(addr.address, addr.address + size)?; + let mut cur_addr = addr; + loop { + if data.is_empty() { + break; + } + if let Some(target) = + relocation_target_for(obj, cur_addr, Some(ObjRelocKind::Absolute))? + { + match target { + RelocationTarget::Address(addr) => entries.push(addr), + RelocationTarget::External => { + bail!( + "Jump table entry at {:#010X} points to external symbol", + cur_addr + ) + } + } + } else { + assert!(target.is_some(), "We need a target address to apply offsets to!"); + let target = match target.unwrap() { + RelocationTarget::Address(addr) => addr, + _ => { + panic!("We need a target address to apply offsets to!") + } + }; + let entry_addr = + target.address + u16::from_be_bytes(*array_ref!(data, 0, 2)) as u32; + if entry_addr > 0 { + let (section_index, _) = + obj.sections.at_address(entry_addr).with_context(|| { + format!( + "Invalid jump table entry {entry_addr:#010X} at {cur_addr:#010X}" + ) + })?; + entries.push(SectionAddress::new(section_index, entry_addr)); + } + } + data = &data[2..]; + cur_addr += 4; + } + Ok((entries, size)) } else { - break; - }; - if target < function_start || matches!(function_end, Some(end) if target >= end) { - break; + // TODO: this is a hack courtesy of jeff 0.1.0 + // Although the jump table code is a lot more robust, there are still cases where we don't definitively know the jump table size. + // When this happens, just set the jump table size to 0 and move on. + // While this does mean you miss out on CFA from the potential addresses in the jump table, + // the size will ultimately sort itself out when rebuilding relocs. + let entries: Vec = Vec::new(); + log::debug!( + "Guessed jump table @ {:#010X} with entry count {} (from {:#010X})", + addr, + 0, + from + ); + Ok((entries, 0)) } - entries.push(target); - cur_addr += 4; } - let size = cur_addr.address - addr.address; - log::info!( - "Guessed jump table @ {:#010X} with entry count {} (from {:#010X})", - addr, - size / 4, - from - ); - Ok((entries, size)) } } diff --git a/src/analysis/slices.rs b/src/analysis/slices.rs index ae21f38..8f6567a 100644 --- a/src/analysis/slices.rs +++ b/src/analysis/slices.rs @@ -12,7 +12,7 @@ use crate::{ disassemble, executor::{ExecCbData, ExecCbResult, Executor}, uniq_jump_table_entries, - vm::{section_address_for, BranchTarget, JumpTableType, StepResult, VM}, + vm::{section_address_for, BranchTarget, StepResult, VM}, RelocationTarget, }, obj::{ObjInfo, ObjKind, ObjSection, ObjSymbolKind}, @@ -368,24 +368,11 @@ impl FunctionSlices { jump_table_address: RelocationTarget::Address(address), size, } => { - let next_addr_size = match jt { - JumpTableType::Absolute => match size { - Some(num) => num.get(), - None => 0, - }, - _ => 0, - }; - // End of block - let next_address = ins_addr + 4 + next_addr_size; + let next_address = ins_addr + 4; self.blocks.insert(block_start, Some(next_address)); - log::debug!( - "Fetching {} jump table entries @ {} with size {:?}", - if jt == JumpTableType::Absolute { "absolute" } else { "relative" }, - address, - size - ); + log::debug!("Fetching jump table entries @ {} with size {:?}", address, size); let (entries, size) = uniq_jump_table_entries( obj, address, diff --git a/src/analysis/vm.rs b/src/analysis/vm.rs index 6612bde..6bfa448 100644 --- a/src/analysis/vm.rs +++ b/src/analysis/vm.rs @@ -3,7 +3,7 @@ use std::num::NonZeroU32; use powerpc::{Argument, Ins, Opcode, GPR}; use crate::{ - analysis::{cfa::SectionAddress, disassemble, relocation_target_for, RelocationTarget}, + analysis::{cfa::SectionAddress, relocation_target_for, RelocationTarget}, obj::{ObjInfo, ObjKind}, }; @@ -11,43 +11,18 @@ use crate::{ pub enum JumpTableType { // the table came from an lwzx, contains absolute addresses Absolute, - // the table came from an lbzx, contains relative byte offsets (no rlwinm before the bctr) - RelativeBytes(Option), - // the table came from an lbzx, contains relative byte offsets that we must multiply by 4 - RelativeBytesTimes4(Option), - // the table came from an lhzx, contains relative short offsets (no rlwinm before the bctr) - RelativeShorts(Option), - // the table came from an lhzx, contains relative short offsets that we must multiply by 2 - RelativeShortsTimes2(Option), -} - -#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] -pub enum GprSourceLocation { - #[default] - Unknown, - Register(usize), - Stack(usize), - Memory(usize), - MemoryOffset { - address: usize, - offset_register: usize, - }, -} - -#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] -pub struct GprSource { - pub kind: GprSourceLocation, - pub version: usize, -} - -impl GprSource { - // fn from_register(source: &Gpr, regnum: usize) -> Self { - // Self { kind: GprSourceLocation::Register(regnum), version: source.version } - // } - - // from stack - - // TODO: write helper that checks if GprSource matches with a reg currently in the VM + // the table came from an lbzx, contains relative byte offsets + // if there is a rlwinm before the bctr, you must multiply the jump table entries by 4 + // otherwise, the multiple is 1 - no offset math needed + // target: the address immediately after the bctr + // multiplier: 1 or 4, depending on above + RelativeBytes { target: Option, multiplier: usize }, + // the table came from an lhzx, contains relative byte offsets + // if there is a rlwinm before the bctr, you must multiply the jump table entries by 4 + // otherwise, the multiple is 1 - no offset math needed + // target: the address immediately after the bctr + // multiplier: 1 or 2, depending on above + RelativeShorts { target: Option, multiplier: usize }, } #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] @@ -79,47 +54,25 @@ pub struct Gpr { pub hi_addr: Option, /// Address that loads the lo part of this GPR pub lo_addr: Option, - /// The source of this GPR's value - pub source: GprSource, - /// The revision of this GPR's value - pub version: usize, } impl Gpr { - fn set_direct(&mut self, value: GprValue, src_reg: Option) { + fn set_direct(&mut self, value: GprValue) { self.value = value; self.hi_addr = None; self.lo_addr = None; - self.set_source(src_reg); } - fn set_hi(&mut self, value: GprValue, addr: SectionAddress, src_reg: Option) { + fn set_hi(&mut self, value: GprValue, addr: SectionAddress) { self.value = value; self.hi_addr = Some(addr); self.lo_addr = None; - self.set_source(src_reg); } - fn set_lo(&mut self, value: GprValue, addr: SectionAddress, hi_gpr: Gpr, src_reg: Option) { + fn set_lo(&mut self, value: GprValue, addr: SectionAddress, hi_gpr: Gpr) { self.value = value; self.hi_addr = hi_gpr.hi_addr; self.lo_addr = Some(hi_gpr.lo_addr.unwrap_or(addr)); - self.set_source(src_reg); - } - - fn set_source(&mut self, src_reg: Option) { - match src_reg { - Some(reg_num) => { - self.source = GprSource { - kind: GprSourceLocation::Register(reg_num as usize), - version: self.version, - }; - } - None => { - self.source = GprSource { kind: GprSourceLocation::Unknown, version: self.version }; - } - } - self.version += 1; } fn address(&self, obj: &ObjInfo, ins_addr: SectionAddress) -> Option { @@ -151,8 +104,6 @@ pub struct VM { pub lr: GprValue, /// Count register pub ctr: GprValue, - /// The last modified CR - pub last_modified_cr: u8, } impl VM { @@ -270,117 +221,76 @@ impl VM { pub fn step(&mut self, obj: &ObjInfo, ins_addr: SectionAddress, ins: Ins) -> StepResult { match ins.op { Opcode::Illegal => { - println!("Warning! Illegal inst found at 0x{:X}", ins_addr.address); return StepResult::Illegal; } // add rD, rA, rB Opcode::Add => { let left = self.gpr[ins.field_ra() as usize].value; let right = self.gpr[ins.field_rb() as usize].value; - let value = match (left, right) { - (GprValue::Constant(left), GprValue::Constant(right)) => { - GprValue::Constant(left.wrapping_add(right)) - } - ( - GprValue::Address(RelocationTarget::Address(left)), - GprValue::Constant(right), - ) => GprValue::Address(RelocationTarget::Address( - left.wrapping_add(right as u32), - )), - ( - GprValue::Constant(left), - GprValue::Address(RelocationTarget::Address(right)), - ) => GprValue::Address(RelocationTarget::Address( - right.wrapping_add(left as u32), - )), - ( - GprValue::Constant(left), - GprValue::LoadIndexed { - jump_table_type: jt, - jump_table_address: ja, - max_offset: m, - }, - ) => { - match jt { - // if we reached this point, this should be a relative jump table - JumpTableType::Absolute => { - // this probably isn't a jump table anyway, so just keep the load indexed value - GprValue::LoadIndexed { - jump_table_type: jt, - jump_table_address: ja, - max_offset: m, - } - } - // anyways, mark down the relative address we should be adding offsets to - JumpTableType::RelativeBytes(addr) => { - assert!( - addr.is_none(), - "Relative addr should not be known at this point!" - ); - GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeBytes(Some( - RelocationTarget::Address(SectionAddress::new( - ins_addr.section, - left as u32, - )), - )), - jump_table_address: ja, - max_offset: m, - } - } - JumpTableType::RelativeBytesTimes4(addr) => { - assert!( - addr.is_none(), - "Relative addr should not be known at this point!" - ); - GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeBytesTimes4(Some( - RelocationTarget::Address(SectionAddress::new( - ins_addr.section, - left as u32, - )), - )), - jump_table_address: ja, - max_offset: m, - } - } - JumpTableType::RelativeShorts(addr) => { + let value = + match (left, right) { + (GprValue::Constant(left), GprValue::Constant(right)) => { + GprValue::Constant(left.wrapping_add(right)) + } + ( + GprValue::Address(RelocationTarget::Address(left)), + GprValue::Constant(right), + ) => GprValue::Address(RelocationTarget::Address( + left.wrapping_add(right as u32), + )), + ( + GprValue::Constant(left), + GprValue::Address(RelocationTarget::Address(right)), + ) => GprValue::Address(RelocationTarget::Address( + right.wrapping_add(left as u32), + )), + // if left == Constant and right == LoadIndexed { jump_table_type must be relativeshorts } + // left should also be R12, right should be R0 + ( + GprValue::Constant(left), + GprValue::LoadIndexed { + jump_table_type: jt, + jump_table_address: jt_addr, + max_offset: max, + }, + ) => match jt { + JumpTableType::RelativeShorts { target: addr, multiplier: mult } => { assert!( addr.is_none(), "Relative addr should not be known at this point!" ); GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeShorts(Some( - RelocationTarget::Address(SectionAddress::new( - ins_addr.section, - left as u32, + jump_table_type: JumpTableType::RelativeShorts { + target: Some(RelocationTarget::Address( + SectionAddress::new(ins_addr.section, left as u32), )), - )), - jump_table_address: ja, - max_offset: m, + multiplier: mult, + }, + jump_table_address: jt_addr, + max_offset: max, } } - JumpTableType::RelativeShortsTimes2(addr) => { + JumpTableType::RelativeBytes { target: addr, multiplier: mult } => { assert!( addr.is_none(), "Relative addr should not be known at this point!" ); GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeShortsTimes2(Some( - RelocationTarget::Address(SectionAddress::new( - ins_addr.section, - left as u32, + jump_table_type: JumpTableType::RelativeBytes { + target: Some(RelocationTarget::Address( + SectionAddress::new(ins_addr.section, left as u32), )), - )), - jump_table_address: ja, - max_offset: m, + multiplier: mult, + }, + jump_table_address: jt_addr, + max_offset: max, } } - } - } - _ => GprValue::Unknown, - }; - self.gpr[ins.field_rd() as usize].set_direct(value, None); + _ => GprValue::Unknown, + }, + _ => GprValue::Unknown, + }; + self.gpr[ins.field_rd() as usize].set_direct(value); } // addis rD, rA, SIMM Opcode::Addis => { @@ -388,11 +298,7 @@ impl VM { relocation_target_for(obj, ins_addr, None /* TODO */).ok().flatten() { debug_assert_eq!(ins.field_ra(), 0); - self.gpr[ins.field_rd() as usize].set_hi( - GprValue::Address(target), - ins_addr, - None, - ); + self.gpr[ins.field_rd() as usize].set_hi(GprValue::Address(target), ins_addr); } else { let left = if ins.field_ra() == 0 { GprValue::Constant(0) @@ -407,9 +313,9 @@ impl VM { }; if ins.field_ra() == 0 { // lis rD, SIMM - self.gpr[ins.field_rd() as usize].set_hi(value, ins_addr, None); + self.gpr[ins.field_rd() as usize].set_hi(value, ins_addr); } else { - self.gpr[ins.field_rd() as usize].set_direct(value, None); + self.gpr[ins.field_rd() as usize].set_direct(value); } } } @@ -424,7 +330,6 @@ impl VM { GprValue::Address(target), ins_addr, self.gpr[ins.field_ra() as usize], - None, ); } else { let load_zero = ins.field_ra() == 0 && ins.op == Opcode::Addi; @@ -444,13 +349,12 @@ impl VM { }; if load_zero { // li rD, SIMM - self.gpr[ins.field_rd() as usize].set_direct(value, None); + self.gpr[ins.field_rd() as usize].set_direct(value); } else { self.gpr[ins.field_rd() as usize].set_lo( value, ins_addr, self.gpr[ins.field_ra() as usize], - None, ); } } @@ -468,7 +372,6 @@ impl VM { } _ => GprValue::Unknown, }, - None, ); } // subfic rD, rA, SIMM @@ -480,7 +383,6 @@ impl VM { ), _ => GprValue::Unknown, }, - None, ); } // ori rA, rS, UIMM @@ -495,7 +397,6 @@ impl VM { GprValue::Address(target), ins_addr, self.gpr[ins.field_rs() as usize], - None, ); } else { let value = match self.gpr[ins.field_rs() as usize].value { @@ -508,7 +409,6 @@ impl VM { value, ins_addr, self.gpr[ins.field_rs() as usize], - None, ); } } @@ -517,7 +417,6 @@ impl VM { if ins.field_rs() == ins.field_rb() { // Register copy self.gpr[ins.field_ra() as usize] = self.gpr[ins.field_rs() as usize]; - self.gpr[ins.field_ra() as usize].set_source(Some(ins.field_rs())); } else { let left = self.gpr[ins.field_rs() as usize].value; let right = self.gpr[ins.field_rb() as usize].value; @@ -527,7 +426,7 @@ impl VM { } _ => GprValue::Unknown, }; - self.gpr[ins.field_ra() as usize].set_direct(value, None); + self.gpr[ins.field_ra() as usize].set_direct(value); } } // cmp [crfD], [L], rA, rB @@ -548,7 +447,6 @@ impl VM { let crf = ins.field_crfd(); self.cr[crf as usize] = Cr { signed, left, right }; self.gpr[left_reg].value = GprValue::ComparisonResult(crf); - self.last_modified_cr = crf; } } // rlwinm rA, rS, SH, MB, ME @@ -563,75 +461,26 @@ impl VM { _ => unreachable!(), } { let mask = mask_value(ins.field_mb() as u32, ins.field_me() as u32); - - // for jump table detection - check to see if rS has a source reg we can pull data from - if self.gpr[ins.field_rs() as usize].value == GprValue::Unknown { - // try to find source reg - let src = self.gpr[ins.field_rs() as usize].source; - if let GprSourceLocation::Register(r) = src.kind { - // check the src reg and the current src.version - // it MUST match src reg's current version in order to pull data from it - if self.gpr[r].version == src.version - && self.gpr[r].value != GprValue::Unknown - { - self.gpr[ins.field_rs() as usize].value = self.gpr[r].value; - } - } - } - match self.gpr[ins.field_rs() as usize].value { - // set everything as u32s before rotating - // because although regs are 64 bits on Xbox, 32-bit instructions run in 32-bit mode GprValue::Constant(value) => { GprValue::Constant(((value as u32).rotate_left(shift) & mask) as u64) } GprValue::Range { min, max, step } => GprValue::Range { min: ((min as u32).rotate_left(shift) & mask) as u64, max: ((max as u32).rotate_left(shift) & mask) as u64, - step: ((step as u32).rotate_left(shift)) as u64, + step: (step as u32).rotate_left(shift) as u64, }, - // if we've come across a rlwinm as a LoadIndexed... + // if we come across a RelativeBytes jump table here (because rlwinm can come after lbzx), + // it's still a RelativeBytes jump table, we just need the multiplier to be 4 now. GprValue::LoadIndexed { - jump_table_type: jt, - jump_table_address: ja, - max_offset: m, - } => { - let ret = match jt { - JumpTableType::Absolute => GprValue::LoadIndexed { - jump_table_type: jt, - jump_table_address: ja, - max_offset: m, - }, - // if the table type is currently relative, it means we need to multiply offsets by 4 - JumpTableType::RelativeBytes(addr) => GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeBytesTimes4(addr), - jump_table_address: ja, - max_offset: m, - }, - JumpTableType::RelativeBytesTimes4(addr) => { - log::warn!("Reached rlwinm with a JumpTableType of RelativeTimes4. Can we even reach this point? {}", ins_addr); - GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeBytesTimes4(addr), - jump_table_address: ja, - max_offset: m, - } - } - JumpTableType::RelativeShorts(addr) => GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeShortsTimes2(addr), - jump_table_address: ja, - max_offset: m, - }, - JumpTableType::RelativeShortsTimes2(addr) => { - log::warn!("Reached rlwinm with a JumpTableType of RelativeTimes2. Can we even reach this point? {}", ins_addr); - GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeShortsTimes2(addr), - jump_table_address: ja, - max_offset: m, - } - } - }; - ret - } + jump_table_type: JumpTableType::RelativeBytes { target, multiplier: _ }, + jump_table_address: addr, + max_offset: max, + } => GprValue::LoadIndexed { + jump_table_type: JumpTableType::RelativeBytes { target, multiplier: 4 }, + jump_table_address: addr, + max_offset: max, + }, _ => GprValue::Range { min: 0, max: mask as u64, @@ -641,7 +490,7 @@ impl VM { } else { GprValue::Unknown }; - self.gpr[ins.field_ra() as usize].set_direct(value, None); + self.gpr[ins.field_ra() as usize].set_direct(value); } // b[l][a] target_addr // b[c][l][a] BO, BI, target_addr @@ -663,19 +512,18 @@ impl VM { } else { BranchTarget::Unknown } - }, + } GprValue::Address(target) => BranchTarget::Address(target), GprValue::LoadIndexed { jump_table_type: jtype, jump_table_address: address, max_offset } // FIXME: avoids treating bctrl indirect calls as jump tables if !ins.field_lk() => { let add_increment = match jtype { JumpTableType::Absolute => 4, - JumpTableType::RelativeBytes(_) | JumpTableType::RelativeBytesTimes4(_) => 1, - JumpTableType::RelativeShorts(_) | JumpTableType::RelativeShortsTimes2(_) => 2, + JumpTableType::RelativeBytes { target: _, multiplier: _ } => 1, + JumpTableType::RelativeShorts { target: _, multiplier: _ } => 2, }; - BranchTarget::JumpTable { jump_table_type: jtype, jump_table_address: address, - size: max_offset.and_then(|n| n.checked_add( add_increment)) } - }, + BranchTarget::JumpTable { jump_table_type: jtype, jump_table_address: address, size: max_offset.and_then(|n| n.checked_add(add_increment)) } + } _ => BranchTarget::Unknown, } } @@ -742,19 +590,16 @@ impl VM { let right = self.gpr[ins.field_rb() as usize].value; let value = match (left, right) { (Some(address), GprValue::Range { min: _, max, .. }) - if /*min == 0 &&*/ max < u64::MAX - 4 && max & 3 == 0 => - { - // If the jump_table_address is within .text (supposed to be right after the bctr), this is a jump table - // else, this is a data table (i.e. an array of strings) - // but! since no bctr's come after data tables, these don't get treated like jump tables, soooooo I think this is fine? - GprValue::LoadIndexed { jump_table_type: JumpTableType::Absolute, jump_table_address: address, max_offset: NonZeroU32::new(max as u32) } - } + if /*min == 0 &&*/ max < u64::MAX - 4 && max & 3 == 0 => + { + GprValue::LoadIndexed { jump_table_type: JumpTableType::Absolute, jump_table_address: address, max_offset: NonZeroU32::new(max as u32) } + } (Some(address), _) => { GprValue::LoadIndexed { jump_table_type: JumpTableType::Absolute, jump_table_address: address, max_offset: None } } _ => GprValue::Unknown, }; - self.gpr[ins.field_rd() as usize].set_direct(value, None); + self.gpr[ins.field_rd() as usize].set_direct(value); } // lbzx rD, rA, rB Opcode::Lbzx => { @@ -762,24 +607,16 @@ impl VM { let right = self.gpr[ins.field_rb() as usize].value; let value = match (left, right) { (Some(address), GprValue::Range { min: _, max, .. }) - if /*min == 0 &&*/ max < u64::MAX - 4 => - { - // if we never encountered a bgt before this, we don't know the bounds for sure - let bounds_known: bool = match self.cr[self.last_modified_cr as usize].right { - GprValue::Constant(c) => { max == c }, - _ => false, - }; - GprValue::LoadIndexed { - jump_table_type: JumpTableType::RelativeBytes(None), - jump_table_address: address, - max_offset: if bounds_known { NonZeroU32::new(max as u32) } else { None } } - } + if /*min == 0 &&*/ max < u64::MAX - 4 => + { + GprValue::LoadIndexed { jump_table_type: JumpTableType::RelativeBytes { target: None, multiplier: 1 }, jump_table_address: address, max_offset: NonZeroU32::new(max as u32) } + } (Some(address), _) => { - GprValue::LoadIndexed { jump_table_type: JumpTableType::RelativeBytes(None), jump_table_address: address, max_offset: None } + GprValue::LoadIndexed { jump_table_type: JumpTableType::RelativeBytes { target: None, multiplier: 1 }, jump_table_address: address, max_offset: None } } _ => GprValue::Unknown, }; - self.gpr[ins.field_rd() as usize].set_direct(value, None); + self.gpr[ins.field_rd() as usize].set_direct(value); } // lhzx rD, rA, rB Opcode::Lhzx => { @@ -789,14 +626,14 @@ impl VM { (Some(address), GprValue::Range { min: _, max, .. }) if /*min == 0 &&*/ max < u64::MAX - 4 && max & 1 == 0 => { - GprValue::LoadIndexed { jump_table_type: JumpTableType::RelativeShorts(None), jump_table_address: address, max_offset: NonZeroU32::new(max as u32) } + GprValue::LoadIndexed { jump_table_type: JumpTableType::RelativeShorts { target: None, multiplier: 2 }, jump_table_address: address, max_offset: NonZeroU32::new(max as u32) } } (Some(address), _) => { - GprValue::LoadIndexed { jump_table_type: JumpTableType::RelativeShorts(None), jump_table_address: address, max_offset: None } + GprValue::LoadIndexed { jump_table_type: JumpTableType::RelativeShorts { target: None, multiplier: 1 }, jump_table_address: address, max_offset: None } } _ => GprValue::Unknown, }; - self.gpr[ins.field_rd() as usize].set_direct(value, None); + self.gpr[ins.field_rd() as usize].set_direct(value); } // mtspr SPR, rS Opcode::Mtspr => match ins.field_spr() { @@ -811,10 +648,10 @@ impl VM { 9 => self.ctr, _ => GprValue::Unknown, }; - self.gpr[ins.field_rd() as usize].set_direct(value, None); + self.gpr[ins.field_rd() as usize].set_direct(value); } // rfi - Opcode::Rfi | Opcode::Rfid => { + Opcode::Rfi => { return StepResult::Jump(BranchTarget::Unknown); } op if is_load_store_op(op) => { @@ -826,7 +663,6 @@ impl VM { GprValue::Address(target), ins_addr, self.gpr[source], - None, ); } result = StepResult::LoadStore { @@ -842,7 +678,6 @@ impl VM { GprValue::Address(target), ins_addr, self.gpr[source], - None, ); } result = StepResult::LoadStore { @@ -852,80 +687,17 @@ impl VM { }; } } else if is_update_op(op) { - self.gpr[source].set_direct(GprValue::Unknown, None); - } - if op == Opcode::Lwz { - // the following sequence checkers are terrible hacks - // the "proper" way to do it would be to track values of stack offsets/memory offsets as they're written to/read from, - // but for the life me i can't figure out how to do that - // so until that system gets implemented, these hacks will have to do - let section = obj.sections.at_address(ins_addr.address).expect("no section").1; - // check for the evil microsoft jump table bound sequence: lwz, cmplwi, bgt, lwz - // we're gonna check for the sequence from the second lwz - if ins_addr.address - section.address as u32 >= 12 { - if let (Some(first_lwz), Some(cmp), Some(bgt)) = ( - disassemble(section, ins_addr.address.wrapping_sub(12)), - disassemble(section, ins_addr.address.wrapping_sub(8)), - disassemble(section, ins_addr.address.wrapping_sub(4)), - ) { - let is_lwz = first_lwz.op == Opcode::Lwz - && first_lwz.field_ra() == ins.field_ra() - && first_lwz.field_offset() == ins.field_offset(); - let is_cmplwi = cmp.op == Opcode::Cmpli && cmp.field_l() == 0; - let is_bgt = bgt.op == Opcode::Bc - && (bgt.field_bo() & 30) == 12 - && (bgt.field_bi() & 3) == 1; - - // if we've found the sequence, retrieve the data - if is_lwz && is_cmplwi && is_bgt { - // println!("found sequence at {}!", ins_addr); - self.gpr[ins.field_rd() as usize].set_direct( - self.gpr[first_lwz.field_rd() as usize].value, - None, - ); - return result; - } - } - } - // check for the evil microsoft jump table bound sequence: lwz, cmplwi, ble, b, lwz - if ins_addr.address - section.address as u32 >= 16 { - if let (Some(first_lwz), Some(cmp), Some(ble), Some(branch)) = ( - disassemble(section, ins_addr.address.wrapping_sub(16)), - disassemble(section, ins_addr.address.wrapping_sub(12)), - disassemble(section, ins_addr.address.wrapping_sub(8)), - disassemble(section, ins_addr.address.wrapping_sub(4)), - ) { - let is_lwz = first_lwz.op == Opcode::Lwz - && first_lwz.field_ra() == ins.field_ra() - && first_lwz.field_offset() == ins.field_offset(); - let is_cmplwi = cmp.op == Opcode::Cmpli && cmp.field_l() == 0; - let is_ble = ble.op == Opcode::Bc - && (ble.field_bo() & 30) == 4 - && (ble.field_bi() & 3) == 1; - let is_branch = - branch.op == Opcode::B && !branch.field_aa() && !branch.field_lk(); - - // if we've found the sequence, retrieve the data - if is_lwz && is_cmplwi && is_ble && is_branch { - // println!("found sequence at {}!", ins_addr); - self.gpr[ins.field_rd() as usize].set_direct( - self.gpr[first_lwz.field_rd() as usize].value, - None, - ); - return result; - } - } - } + self.gpr[source].set_direct(GprValue::Unknown); } if is_load_op(op) { - self.gpr[ins.field_rd() as usize].set_direct(GprValue::Unknown, None); + self.gpr[ins.field_rd() as usize].set_direct(GprValue::Unknown); } return result; } _ => { for argument in ins.defs() { if let Argument::GPR(GPR(reg)) = argument { - self.gpr[reg as usize].set_direct(GprValue::Unknown, None); + self.gpr[reg as usize].set_direct(GprValue::Unknown); } } } @@ -1047,11 +819,8 @@ pub fn is_load_op(op: Opcode) -> bool { | Opcode::Lhz | Opcode::Lhzu | Opcode::Lmw - | Opcode::Lwa | Opcode::Lwz | Opcode::Lwzu - | Opcode::Ld - | Opcode::Ldu ) } @@ -1071,8 +840,6 @@ pub fn is_store_op(op: Opcode) -> bool { | Opcode::Stmw | Opcode::Stw | Opcode::Stwu - | Opcode::Std - | Opcode::Stdu ) } @@ -1092,8 +859,6 @@ pub fn is_update_op(op: Opcode) -> bool { op, Opcode::Lbzu | Opcode::Lbzux - | Opcode::Ldu - | Opcode::Ldux | Opcode::Lfdu | Opcode::Lfdux | Opcode::Lfsu @@ -1102,13 +867,10 @@ pub fn is_update_op(op: Opcode) -> bool { | Opcode::Lhaux | Opcode::Lhzu | Opcode::Lhzux - | Opcode::Lwaux | Opcode::Lwzu | Opcode::Lwzux | Opcode::Stbu | Opcode::Stbux - | Opcode::Stdu - | Opcode::Stdux | Opcode::Stfdu | Opcode::Stfdux | Opcode::Stfsu