├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── macros ├── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs └── src ├── guest.rs ├── guest ├── arm64.rs └── arm64 │ ├── b_exc_sys.rs │ ├── data_proc_imm.rs │ ├── data_proc_reg.rs │ ├── data_proc_simd_fp.rs │ ├── facility.rs │ ├── ldst.rs │ ├── sve.rs │ └── system.rs ├── host.rs ├── host ├── dump_ir.rs ├── llvm.rs └── llvm │ └── codegen.rs ├── ir.rs ├── ir ├── op.rs ├── op │ ├── meta.rs │ └── opt.rs └── storage.rs ├── lib.rs ├── main.rs ├── runtime.rs ├── runtime └── loader.rs └── util.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | # used for testing 4 | hello 5 | hello_dyn 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.10" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.14" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "autocfg" 23 | version = "1.0.0" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "1.2.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | 31 | [[package]] 32 | name = "cc" 33 | version = "1.0.54" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | 36 | [[package]] 37 | name = "cfg-if" 38 | version = "0.1.10" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | 41 | [[package]] 42 | name = "cloudabi" 43 | version = "0.0.3" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | dependencies = [ 46 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 47 | ] 48 | 49 | [[package]] 50 | name = "either" 51 | version = "1.5.3" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | 54 | [[package]] 55 | name = "env_logger" 56 | version = "0.7.1" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | dependencies = [ 59 | "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 61 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 62 | "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 64 | ] 65 | 66 | [[package]] 67 | name = "goblin" 68 | version = "0.2.3" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | dependencies = [ 71 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 74 | ] 75 | 76 | [[package]] 77 | name = "hermit-abi" 78 | version = "0.1.13" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", 82 | ] 83 | 84 | [[package]] 85 | name = "humantime" 86 | version = "1.3.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | dependencies = [ 89 | "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 90 | ] 91 | 92 | [[package]] 93 | name = "inkwell" 94 | version = "0.1.0" 95 | source = "git+https://github.com/TheDan64/inkwell?branch=llvm10-0#f211c50f020a83fe3fb35bea3ea8cec50b700033" 96 | dependencies = [ 97 | "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", 98 | "inkwell_internals 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm10-0)", 99 | "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "llvm-sys 100.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 101 | "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 104 | ] 105 | 106 | [[package]] 107 | name = "inkwell_internals" 108 | version = "0.1.0" 109 | source = "git+https://github.com/TheDan64/inkwell?branch=llvm10-0#f211c50f020a83fe3fb35bea3ea8cec50b700033" 110 | dependencies = [ 111 | "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 112 | "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "syn 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", 114 | ] 115 | 116 | [[package]] 117 | name = "khemu" 118 | version = "0.1.0" 119 | dependencies = [ 120 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 121 | "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 122 | "goblin 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 123 | "inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm10-0)", 124 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 125 | "macros 0.0.0", 126 | "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 127 | "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 128 | "paste 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 129 | ] 130 | 131 | [[package]] 132 | name = "lazy_static" 133 | version = "1.4.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | 136 | [[package]] 137 | name = "libc" 138 | version = "0.2.70" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | 141 | [[package]] 142 | name = "llvm-sys" 143 | version = "100.0.1" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | dependencies = [ 146 | "cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)", 147 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 148 | "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", 149 | "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 150 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 151 | ] 152 | 153 | [[package]] 154 | name = "lock_api" 155 | version = "0.3.4" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | dependencies = [ 158 | "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 159 | ] 160 | 161 | [[package]] 162 | name = "log" 163 | version = "0.4.8" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | dependencies = [ 166 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 167 | ] 168 | 169 | [[package]] 170 | name = "macros" 171 | version = "0.0.0" 172 | dependencies = [ 173 | "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 175 | "syn 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", 176 | ] 177 | 178 | [[package]] 179 | name = "memchr" 180 | version = "2.3.3" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | 183 | [[package]] 184 | name = "memmap" 185 | version = "0.7.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | dependencies = [ 188 | "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", 189 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 190 | ] 191 | 192 | [[package]] 193 | name = "num-traits" 194 | version = "0.2.11" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | dependencies = [ 197 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 198 | ] 199 | 200 | [[package]] 201 | name = "once_cell" 202 | version = "1.4.0" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | 205 | [[package]] 206 | name = "parking_lot" 207 | version = "0.10.2" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | dependencies = [ 210 | "lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 211 | "parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 212 | ] 213 | 214 | [[package]] 215 | name = "parking_lot_core" 216 | version = "0.7.2" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | dependencies = [ 219 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 220 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", 222 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 223 | "smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 224 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 225 | ] 226 | 227 | [[package]] 228 | name = "paste" 229 | version = "0.1.13" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | dependencies = [ 232 | "paste-impl 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 233 | "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", 234 | ] 235 | 236 | [[package]] 237 | name = "paste-impl" 238 | version = "0.1.13" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | dependencies = [ 241 | "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", 242 | "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 243 | "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 244 | "syn 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", 245 | ] 246 | 247 | [[package]] 248 | name = "plain" 249 | version = "0.2.3" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | 252 | [[package]] 253 | name = "proc-macro-hack" 254 | version = "0.5.16" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | 257 | [[package]] 258 | name = "proc-macro2" 259 | version = "1.0.17" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | dependencies = [ 262 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 263 | ] 264 | 265 | [[package]] 266 | name = "quick-error" 267 | version = "1.2.3" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | 270 | [[package]] 271 | name = "quote" 272 | version = "1.0.6" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | dependencies = [ 275 | "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 276 | ] 277 | 278 | [[package]] 279 | name = "redox_syscall" 280 | version = "0.1.56" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | 283 | [[package]] 284 | name = "regex" 285 | version = "1.3.7" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | dependencies = [ 288 | "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", 289 | "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 290 | "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", 291 | "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 292 | ] 293 | 294 | [[package]] 295 | name = "regex-syntax" 296 | version = "0.6.17" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | 299 | [[package]] 300 | name = "scopeguard" 301 | version = "1.1.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | 304 | [[package]] 305 | name = "scroll" 306 | version = "0.10.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | dependencies = [ 309 | "scroll_derive 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 310 | ] 311 | 312 | [[package]] 313 | name = "scroll_derive" 314 | version = "0.10.2" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | dependencies = [ 317 | "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 318 | "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 319 | "syn 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", 320 | ] 321 | 322 | [[package]] 323 | name = "semver" 324 | version = "0.9.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | dependencies = [ 327 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 328 | ] 329 | 330 | [[package]] 331 | name = "semver-parser" 332 | version = "0.7.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | 335 | [[package]] 336 | name = "smallvec" 337 | version = "1.4.0" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | 340 | [[package]] 341 | name = "syn" 342 | version = "1.0.24" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | dependencies = [ 345 | "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 346 | "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 347 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 348 | ] 349 | 350 | [[package]] 351 | name = "termcolor" 352 | version = "1.1.0" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | dependencies = [ 355 | "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 356 | ] 357 | 358 | [[package]] 359 | name = "thread_local" 360 | version = "1.0.1" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | dependencies = [ 363 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 364 | ] 365 | 366 | [[package]] 367 | name = "unicode-xid" 368 | version = "0.2.0" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | 371 | [[package]] 372 | name = "winapi" 373 | version = "0.3.8" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | dependencies = [ 376 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 377 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 378 | ] 379 | 380 | [[package]] 381 | name = "winapi-i686-pc-windows-gnu" 382 | version = "0.4.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | 385 | [[package]] 386 | name = "winapi-util" 387 | version = "0.1.5" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | dependencies = [ 390 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 391 | ] 392 | 393 | [[package]] 394 | name = "winapi-x86_64-pc-windows-gnu" 395 | version = "0.4.0" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | 398 | [metadata] 399 | "checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" 400 | "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 401 | "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 402 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 403 | "checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" 404 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 405 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 406 | "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" 407 | "checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 408 | "checksum goblin 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d20fd25aa456527ce4f544271ae4fea65d2eda4a6561ea56f39fb3ee4f7e3884" 409 | "checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" 410 | "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 411 | "checksum inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm10-0)" = "" 412 | "checksum inkwell_internals 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm10-0)" = "" 413 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 414 | "checksum libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" 415 | "checksum llvm-sys 100.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df29b4f2f4e8a5f7871ccad859aa17a171ba251da96bfd55224d53f48a96d2a2" 416 | "checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 417 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 418 | "checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 419 | "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" 420 | "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 421 | "checksum once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" 422 | "checksum parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" 423 | "checksum parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" 424 | "checksum paste 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "678f27e19361472a23717f11d229a7522ef64605baf0715c896a94b8b6b13a06" 425 | "checksum paste-impl 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "149089128a45d8e377677b08873b4bad2a56618f80e4f28a83862ba250994a30" 426 | "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 427 | "checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" 428 | "checksum proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" 429 | "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 430 | "checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" 431 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 432 | "checksum regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" 433 | "checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 434 | "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 435 | "checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" 436 | "checksum scroll_derive 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9" 437 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 438 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 439 | "checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" 440 | "checksum syn 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f87bc5b2815ebb664de0392fdf1b95b6d10e160f86d9f64ff65e5679841ca06a" 441 | "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 442 | "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 443 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 444 | "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 445 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 446 | "checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 447 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 448 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "khemu" 3 | version = "0.1.0" 4 | authors = ["KireinaHoro "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | macros = { path = "macros" } 11 | num-traits = "0.2" 12 | paste = "0.1" 13 | bitflags = "1.2" 14 | goblin = "0.2" 15 | log = "0.4" 16 | env_logger = "0.7" 17 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm10-0" } 18 | memmap = "0.7" 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Pengcheng Xu . All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 26 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KHEmu: Binary Translation in Rust 2 | 3 | KHEmu is an emulator that works like a compiler. Started originally as the term project for the course "Compiler Lab (Honor Track)" at Peking University, KHEmu aims to help improve the performance of foreign-architecture code translation. Quoting from the term project report, 4 | 5 | > QEMU's userspace emulation comes with a limitation though; during the lifetime of a guest program, only the parts in the kernel are executed natively. This is not ideal for computation-heavy activities such as graphics rendering or scientific computing. In this work, we introduce the idea of dynamic linking to userspace emulation to foster faster emulation by executing more of the computation natively. The prototype implementation for the idea, KHEmu, is written in the Rust programming language and is under active development. 6 | 7 | The full report at submission state is hosted on [Google Docs](https://docs.google.com/document/d/1E8nGi2ca_9TGM_ECLdPZfSFOVbdYuNBzf2mjqYu95wI/edit?usp=sharing) and will not be updated any further. The following milestone list is updated in real time. 8 | 9 | - [x] IR generation via macro 10 | - [x] ARMv8 aarch64 frontend (partial) 11 | - [x] Speculative disassembly 12 | - [x] DumpIR dummy backend for IR printout 13 | - [x] LLVM backend (partial) 14 | - [x] Static ELF loading and guest mapping 15 | - [x] Guest code execution, guest register dump 16 | - [x] LOOKUP_TB trap handling 17 | - [ ] LLVM branch generation 18 | - [ ] Guest stack setup, syscall proxy 19 | - [ ] Block chaining 20 | - [ ] Dynamic ELF loading 21 | - [ ] Parse and load dependent libraries 22 | - [ ] Host-side stub generation, DYNAMIC trap handling 23 | - [ ] Chaining 24 | - [ ] Frontend support for FP & vector 25 | 26 | ## How to test 27 | 28 | To run the project, you'll need Rust Nightly (as of June 2020). The regular compiler can be simply retrieved with [Rustup](https://www.rust-lang.org/tools/install), while [enabling nightly](https://github.com/rust-lang/rustup/blob/master/README.md#working-with-nightly-rust) may require a bit more work. Make sure you have LLVM 10 installed. The code is only tested to work on Linux. At present, you also need a statically-linked test executable for AArch64; a "Hello, world!" ELF is included in the submission archive (not committed to GitHub). To run a simple test: 29 | 30 | ```bash 31 | # clone the repo 32 | git clone https://github.com/KireinaHoro/khemu && cd khemu 33 | # setup LLVM for Rust 34 | export LLVM_SYS_100_STRICT_VERSIONING=1 35 | export LLVM_SYS_100_PREFIX= 36 | # use a higher log level to see diagnosis information 37 | export RUST_LOG=debug 38 | # assume that `hello` is the test executable 39 | cargo run hello 40 | ``` 41 | 42 | The test is expected to fail, likely panicking with the following message. The failing point at submission is `host::llvm::make_label`, which is part of the LLVM branch generation milestone. 43 | 44 | ```text 45 | thread 'main' panicked at 'not implemented', src/host/llvm.rs:313:9 46 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 47 | ``` 48 | 49 | ## Documentation 50 | 51 | The project is documented via `rustdoc`. To generate documentation locally, 52 | 53 | ```bash 54 | # setup LLVM for Rust 55 | export LLVM_SYS_100_STRICT_VERSIONING=1 56 | export LLVM_SYS_100_PREFIX= 57 | # assume that `hello` is the test executable 58 | cargo doc 59 | ``` 60 | 61 | The documentation will be generated in `target/doc/khemu/index.html`. A generated copy is included in the submitted archive (not committed to GitHub). 62 | 63 | ## License 64 | 65 | This project is licensed under the 3-Clause BSD License. See the `LICENSE` file for more information. 66 | -------------------------------------------------------------------------------- /macros/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "glob" 5 | version = "0.3.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 8 | 9 | [[package]] 10 | name = "itoa" 11 | version = "0.4.5" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 14 | 15 | [[package]] 16 | name = "lazy_static" 17 | version = "1.4.0" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 20 | 21 | [[package]] 22 | name = "macros" 23 | version = "0.0.0" 24 | dependencies = [ 25 | "proc-macro2", 26 | "quote", 27 | "syn", 28 | "trybuild", 29 | ] 30 | 31 | [[package]] 32 | name = "proc-macro2" 33 | version = "1.0.10" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" 36 | dependencies = [ 37 | "unicode-xid", 38 | ] 39 | 40 | [[package]] 41 | name = "quote" 42 | version = "1.0.3" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 45 | dependencies = [ 46 | "proc-macro2", 47 | ] 48 | 49 | [[package]] 50 | name = "ryu" 51 | version = "1.0.3" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" 54 | 55 | [[package]] 56 | name = "serde" 57 | version = "1.0.106" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" 60 | dependencies = [ 61 | "serde_derive", 62 | ] 63 | 64 | [[package]] 65 | name = "serde_derive" 66 | version = "1.0.106" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" 69 | dependencies = [ 70 | "proc-macro2", 71 | "quote", 72 | "syn", 73 | ] 74 | 75 | [[package]] 76 | name = "serde_json" 77 | version = "1.0.51" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" 80 | dependencies = [ 81 | "itoa", 82 | "ryu", 83 | "serde", 84 | ] 85 | 86 | [[package]] 87 | name = "syn" 88 | version = "1.0.17" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" 91 | dependencies = [ 92 | "proc-macro2", 93 | "quote", 94 | "unicode-xid", 95 | ] 96 | 97 | [[package]] 98 | name = "termcolor" 99 | version = "1.1.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 102 | dependencies = [ 103 | "winapi-util", 104 | ] 105 | 106 | [[package]] 107 | name = "toml" 108 | version = "0.5.6" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" 111 | dependencies = [ 112 | "serde", 113 | ] 114 | 115 | [[package]] 116 | name = "trybuild" 117 | version = "1.0.25" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "459186ab1afd6d93bd23c2269125f4f7694f8771fe0e64434b4bdc212b94034d" 120 | dependencies = [ 121 | "glob", 122 | "lazy_static", 123 | "serde", 124 | "serde_json", 125 | "termcolor", 126 | "toml", 127 | ] 128 | 129 | [[package]] 130 | name = "unicode-xid" 131 | version = "0.2.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 134 | 135 | [[package]] 136 | name = "winapi" 137 | version = "0.3.8" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 140 | dependencies = [ 141 | "winapi-i686-pc-windows-gnu", 142 | "winapi-x86_64-pc-windows-gnu", 143 | ] 144 | 145 | [[package]] 146 | name = "winapi-i686-pc-windows-gnu" 147 | version = "0.4.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 150 | 151 | [[package]] 152 | name = "winapi-util" 153 | version = "0.1.4" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" 156 | dependencies = [ 157 | "winapi", 158 | ] 159 | 160 | [[package]] 161 | name = "winapi-x86_64-pc-windows-gnu" 162 | version = "0.4.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 165 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macros" 3 | version = "0.0.0" 4 | authors = ["KireinaHoro "] 5 | edition = "2018" 6 | autotests = false 7 | publish = false 8 | 9 | [lib] 10 | proc-macro = true 11 | 12 | [dev-dependencies] 13 | trybuild = "1.0" 14 | 15 | [dependencies] 16 | syn = { version = "1.0", features = ["visit", "parsing"] } 17 | quote = "1.0" 18 | proc-macro2 = "1.0" 19 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | #![feature(proc_macro_diagnostic)] 6 | 7 | extern crate proc_macro; 8 | 9 | use proc_macro::TokenStream; 10 | use quote::{format_ident, quote}; 11 | use std::collections::{HashMap, HashSet}; 12 | use std::iter::repeat; 13 | use syn::parse::{Parse, ParseStream}; 14 | use syn::punctuated::Punctuated; 15 | use syn::{braced, parse_macro_input, token, Attribute, Ident, Path, Result, Token}; 16 | 17 | struct GenOpSingle { 18 | reg_type: Path, 19 | _brace: token::Brace, 20 | rules: Punctuated, 21 | } 22 | 23 | impl Parse for GenOpSingle { 24 | fn parse(input: ParseStream) -> Result { 25 | let content; 26 | Ok(Self { 27 | reg_type: input.call(Path::parse_mod_style)?, 28 | _brace: braced!(content in input), 29 | rules: content.parse_terminated(OpRule::parse)?, 30 | }) 31 | } 32 | } 33 | 34 | struct GenOps { 35 | types: Punctuated, 36 | } 37 | 38 | impl Parse for GenOps { 39 | fn parse(input: ParseStream) -> Result { 40 | let parser = Punctuated::::parse_terminated; 41 | Ok(Self { 42 | types: input.call(parser)?, 43 | }) 44 | } 45 | } 46 | 47 | struct OpRule { 48 | doc: Vec, 49 | rule_type: Ident, 50 | defs: Punctuated, 51 | } 52 | 53 | impl Parse for OpRule { 54 | fn parse(input: ParseStream) -> Result { 55 | Ok(Self { 56 | doc: input.call(Attribute::parse_outer)?, 57 | rule_type: input.parse()?, 58 | defs: { 59 | let _ = input.parse::()?; 60 | input.call(Punctuated::::parse_separated_nonempty)? 61 | }, 62 | }) 63 | } 64 | } 65 | 66 | #[proc_macro] 67 | pub fn gen_ops(input: TokenStream) -> TokenStream { 68 | let GenOps { types } = parse_macro_input!(input as GenOps); 69 | 70 | let mut unary = HashMap::new(); 71 | let mut convert = HashMap::new(); 72 | let mut binary = HashMap::new(); 73 | let mut custom = HashMap::new(); 74 | let mut override_maker = HashSet::new(); 75 | 76 | for GenOpSingle { 77 | reg_type, 78 | _brace: _, 79 | rules, 80 | } in types.into_iter() 81 | { 82 | for rule in rules.into_iter() { 83 | let ru = &rule.rule_type; 84 | match rule.rule_type.to_string().as_ref() { 85 | "unary" => { 86 | unary.extend(rule.defs.into_iter().zip(repeat((rule.doc.clone(), reg_type.clone())))); 87 | } 88 | "convert" => { 89 | convert.extend(rule.defs.into_iter().zip(repeat((rule.doc.clone(), reg_type.clone())))); 90 | } 91 | "binary" => { 92 | binary.extend(rule.defs.into_iter().zip(repeat((rule.doc.clone(), reg_type.clone())))); 93 | } 94 | "override_maker" => { 95 | override_maker.extend(rule.defs.into_iter()); 96 | } 97 | "custom" => { 98 | let def: Vec<_> = rule.defs.into_iter().collect(); 99 | if def.len() < 1 { 100 | ru.span() 101 | .unwrap() 102 | .error( 103 | "not enough operands for custom rule: [, ,...]", 104 | ) 105 | .emit(); 106 | return TokenStream::new(); 107 | } 108 | custom.insert(def, (rule.doc.clone(), reg_type.clone())); 109 | } 110 | _ => { 111 | ru.span() 112 | .unwrap() 113 | .error("unknown rule when defining op") 114 | .emit(); 115 | return TokenStream::new(); 116 | } 117 | } 118 | } 119 | } 120 | 121 | let customs = custom 122 | .iter() 123 | .map(|(v, (d, _))| { 124 | let mnemonic = &v[0]; 125 | let iter = v.iter().skip(1); 126 | quote! { 127 | #( #d )* 128 | #mnemonic { #( #iter: ::std::rc::Rc> ),* }, 129 | } 130 | }) 131 | .into_iter(); 132 | let custom_makers = custom 133 | .iter() 134 | .map(|(v, (_, t))| { 135 | let mnemonic = &v[0]; 136 | let lower = mnemonic.to_string().to_lowercase(); 137 | let fn_name = if !override_maker.contains(mnemonic) { 138 | format_ident!("push_{}", lower) 139 | } else { 140 | format_ident!("_push_{}", lower) 141 | }; 142 | let params = v.iter().skip(1); 143 | let aa = params.clone(); 144 | let bb = params.clone(); 145 | quote! { 146 | impl Op { 147 | pub fn #fn_name( 148 | ctx: &mut impl crate::guest::DisasContext, 149 | #( #params: &::std::rc::Rc> ),*) { 150 | // we enforce all arguments to be of the declared type 151 | #( assert_eq!(#aa.ty, #t); )* 152 | ctx.push_op(Self::#mnemonic { #( #bb: ::std::rc::Rc::clone(#bb) ),* }) 153 | } 154 | } 155 | } 156 | }) 157 | .into_iter(); 158 | let custom_display = custom 159 | .iter() 160 | .map(|(v, _)| { 161 | let mnemonic = &v[0]; 162 | let _lower = mnemonic.to_string().to_lowercase(); 163 | let params = v.iter().skip(1); 164 | let args = params.clone(); 165 | let placeholder = params.clone().map(|_| "{}").collect::>().join(", "); 166 | quote! { 167 | Self::#mnemonic { #( #params ),* } => { 168 | write!(f, "{}\t", #_lower)?; 169 | write!(f, #placeholder, #( #args ),*)?; 170 | Ok(()) 171 | }, 172 | } 173 | }) 174 | .into_iter(); 175 | let custom_codegen = custom 176 | .iter() 177 | .map(|(v, _)| { 178 | let mnemonic = &v[0]; 179 | let fn_name = format_ident!("gen_{}", mnemonic.to_string().to_lowercase()); 180 | let params = v.iter().skip(1); 181 | quote! { 182 | fn #fn_name(&mut self, 183 | #( #params: ::std::rc::Rc> ),*) { 184 | unimplemented!(stringify!(#fn_name)) 185 | } 186 | } 187 | }) 188 | .into_iter(); 189 | let custom_dispatch = custom 190 | .iter() 191 | .map(|(v, _)| { 192 | let mnemonic = &v[0]; 193 | let fn_name = format_ident!("gen_{}", mnemonic.to_string().to_lowercase()); 194 | let params = v.iter().skip(1); 195 | let args = params.clone(); 196 | quote! { 197 | Op::#mnemonic { #( #params ),* } => self.#fn_name(#( #args ),*), 198 | } 199 | }) 200 | .into_iter(); 201 | 202 | let unaries = unary 203 | .iter() 204 | .map(|(m, (d, _))| { 205 | quote! { 206 | #( #d )* 207 | #m { 208 | rd: ::std::rc::Rc>, 209 | rs1: ::std::rc::Rc>, 210 | }, 211 | } 212 | }) 213 | .into_iter(); 214 | let unary_makers = unary 215 | .iter() 216 | .map(|(m, (_, t))| { 217 | let lower = m.to_string().to_lowercase(); 218 | let fn_name = if !override_maker.contains(m) { 219 | format_ident!("push_{}", lower) 220 | } else { 221 | format_ident!("_push_{}", lower) 222 | }; 223 | quote! { 224 | impl Op { 225 | pub fn #fn_name( 226 | ctx: &mut impl crate::guest::DisasContext, 227 | rd: &::std::rc::Rc>, 228 | rs1: &::std::rc::Rc>) { 229 | // we enforce all arguments to be of the declared type 230 | assert_eq!(rd.ty, #t); 231 | assert_eq!(rs1.ty, #t); 232 | ctx.push_op(Self::#m { 233 | rd: ::std::rc::Rc::clone(rd), 234 | rs1: ::std::rc::Rc::clone(rs1), 235 | }) 236 | } 237 | } 238 | } 239 | }) 240 | .into_iter(); 241 | let unary_display = unary 242 | .iter() 243 | .map(|(m, _)| { 244 | let _lower = m.to_string().to_lowercase(); 245 | quote! { 246 | Self::#m { rd, rs1 } => { 247 | write!(f, "{}\t{}, {}", #_lower, rd, rs1)?; 248 | Ok(()) 249 | }, 250 | } 251 | }) 252 | .into_iter(); 253 | let unary_codegen = unary 254 | .iter() 255 | .map(|(m, _)| { 256 | let fn_name = format_ident!("gen_{}", m.to_string().to_lowercase()); 257 | quote! { 258 | fn #fn_name(&mut self, 259 | rd: ::std::rc::Rc>, 260 | rs1: ::std::rc::Rc>) { 261 | unimplemented!(stringify!(#fn_name)) 262 | } 263 | } 264 | }) 265 | .into_iter(); 266 | let unary_dispatch = unary 267 | .iter() 268 | .map(|(m, _)| { 269 | let fn_name = format_ident!("gen_{}", m.to_string().to_lowercase()); 270 | quote! { 271 | Op::#m { rd, rs1 } => self.#fn_name(rd, rs1), 272 | } 273 | }) 274 | .into_iter(); 275 | 276 | let converts = convert 277 | .iter() 278 | .map(|(m, (d, _))| { 279 | quote! { 280 | #( #d )* 281 | #m { 282 | rd: ::std::rc::Rc>, 283 | rs: ::std::rc::Rc>, 284 | }, 285 | } 286 | }) 287 | .into_iter(); 288 | let convert_makers = convert 289 | .iter() 290 | .map(|(m, (_, t))| { 291 | let lower = m.to_string().to_lowercase(); 292 | let fn_name = if !override_maker.contains(m) { 293 | format_ident!("push_{}", lower) 294 | } else { 295 | format_ident!("_push_{}", lower) 296 | }; 297 | quote! { 298 | impl Op { 299 | pub fn #fn_name( 300 | ctx: &mut impl crate::guest::DisasContext, 301 | rd: &::std::rc::Rc>, 302 | rs: &::std::rc::Rc>) { 303 | // we enforce rd to be the type declared 304 | assert_eq!(rd.ty, #t); 305 | ctx.push_op(Self::#m { 306 | rd: ::std::rc::Rc::clone(rd), 307 | rs: ::std::rc::Rc::clone(rs), 308 | }) 309 | } 310 | } 311 | } 312 | }) 313 | .into_iter(); 314 | let convert_display = convert 315 | .iter() 316 | .map(|(m, _)| { 317 | let _lower = m.to_string().to_lowercase(); 318 | quote! { 319 | Self::#m { rd, rs } => { 320 | write!(f, "{}\t{}, {}", #_lower, rd, rs)?; 321 | Ok(()) 322 | }, 323 | } 324 | }) 325 | .into_iter(); 326 | let convert_codegen = convert 327 | .iter() 328 | .map(|(m, _)| { 329 | let fn_name = format_ident!("gen_{}", m.to_string().to_lowercase()); 330 | quote! { 331 | fn #fn_name(&mut self, 332 | rd: ::std::rc::Rc>, 333 | rs: ::std::rc::Rc>) { 334 | unimplemented!(stringify!(#fn_name)) 335 | } 336 | } 337 | }) 338 | .into_iter(); 339 | let convert_dispatch = convert 340 | .iter() 341 | .map(|(m, _)| { 342 | let fn_name = format_ident!("gen_{}", m.to_string().to_lowercase()); 343 | quote! { 344 | Op::#m { rd, rs } => self.#fn_name(rd, rs), 345 | } 346 | }) 347 | .into_iter(); 348 | 349 | let binaries = binary 350 | .iter() 351 | .map(|(m, (d, _))| { 352 | quote! { 353 | #( #d )* 354 | #m { 355 | rd: ::std::rc::Rc>, 356 | rs1: ::std::rc::Rc>, 357 | rs2: ::std::rc::Rc>, 358 | }, 359 | } 360 | }) 361 | .into_iter(); 362 | let binary_makers = binary 363 | .iter() 364 | .map(|(m, (_, t))| { 365 | let lower = m.to_string().to_lowercase(); 366 | let fn_name = if !override_maker.contains(m) { 367 | format_ident!("push_{}", lower) 368 | } else { 369 | format_ident!("_push_{}", lower) 370 | }; 371 | quote! { 372 | impl Op { 373 | pub fn #fn_name( 374 | ctx: &mut impl crate::guest::DisasContext, 375 | rd: &::std::rc::Rc>, 376 | rs1: &::std::rc::Rc>, 377 | rs2: &::std::rc::Rc>) { 378 | // we enforce all arguments to be of the declared type 379 | assert_eq!(rd.ty, #t); 380 | assert_eq!(rs1.ty, #t); 381 | assert_eq!(rs2.ty, #t); 382 | ctx.push_op(Self::#m { 383 | rd: ::std::rc::Rc::clone(rd), 384 | rs1: ::std::rc::Rc::clone(rs1), 385 | rs2: ::std::rc::Rc::clone(rs2), 386 | }) 387 | } 388 | } 389 | } 390 | }) 391 | .into_iter(); 392 | let binary_display = binary 393 | .iter() 394 | .map(|(m, _)| { 395 | let _lower = m.to_string().to_lowercase(); 396 | quote! { 397 | Self::#m { rd, rs1, rs2 } => { 398 | write!(f, "{}\t{}, {}, {}", #_lower, rd, rs1, rs2)?; 399 | Ok(()) 400 | }, 401 | } 402 | }) 403 | .into_iter(); 404 | let binary_codegen = binary 405 | .iter() 406 | .map(|(m, _)| { 407 | let fn_name = format_ident!("gen_{}", m.to_string().to_lowercase()); 408 | quote! { 409 | fn #fn_name(&mut self, 410 | rd: ::std::rc::Rc>, 411 | rs1: ::std::rc::Rc>, 412 | rs2: ::std::rc::Rc>) { 413 | unimplemented!(stringify!(#fn_name)) 414 | } 415 | } 416 | }) 417 | .into_iter(); 418 | let binary_dispatch = binary 419 | .iter() 420 | .map(|(m, _)| { 421 | let fn_name = format_ident!("gen_{}", m.to_string().to_lowercase()); 422 | quote! { 423 | Op::#m { rd, rs1, rs2 } => self.#fn_name(rd, rs1, rs2), 424 | } 425 | }) 426 | .into_iter(); 427 | 428 | let expanded = quote! { 429 | #[derive(Debug)] 430 | /// The IR operators definition. 431 | /// 432 | /// Use the `push_OP` maker functions to generate a specific OP. To override a default maker, 433 | /// specify the op in `override_maker`. The original generated maker is still available as `_push_OP`. 434 | pub enum Op { 435 | #( #unaries )* 436 | #( #converts )* 437 | #( #binaries )* 438 | #( #customs )* 439 | } 440 | 441 | impl ::std::fmt::Display for Op { 442 | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> { 443 | match self { 444 | #( #unary_display )* 445 | #( #convert_display )* 446 | #( #binary_display )* 447 | #( #custom_display )* 448 | } 449 | } 450 | } 451 | 452 | /// Methods that the backend needs to implement to emit the IR operators. 453 | /// 454 | /// Default implementations that invokes `unimplemented!` are provided; backend implementations 455 | /// should override them. 456 | pub trait CodeGen { 457 | fn dispatch(&mut self, op: Op) { 458 | match op { 459 | #( #unary_dispatch )* 460 | #( #convert_dispatch )* 461 | #( #binary_dispatch )* 462 | #( #custom_dispatch )* 463 | } 464 | } 465 | 466 | #( #unary_codegen )* 467 | #( #convert_codegen )* 468 | #( #binary_codegen )* 469 | #( #custom_codegen )* 470 | } 471 | 472 | #( #unary_makers )* 473 | #( #convert_makers )* 474 | #( #binary_makers )* 475 | #( #custom_makers )* 476 | }; 477 | 478 | TokenStream::from(expanded) 479 | } 480 | -------------------------------------------------------------------------------- /src/guest.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | /// The ARM64 frontend. 6 | pub mod arm64; 7 | 8 | use crate::host::HostContext; 9 | use crate::ir::op::Op; 10 | use crate::ir::storage::*; 11 | use bitflags::_core::fmt::{Error, Formatter}; 12 | use std::fmt::Display; 13 | use std::rc::{Rc, Weak}; 14 | 15 | /// Reason that the current translation block terminated. 16 | pub enum DisasException { 17 | /// Direct continue to the next instruction. 18 | /// 19 | /// Possible cases: 20 | /// - jump target (forcing start of a new block) 21 | /// - translation block size limit exceeded 22 | /// 23 | /// Fields: 24 | /// - `0`: the next PC 25 | Continue(usize), 26 | /// Branch, both conditional or unconditional. 27 | /// 28 | /// Possible cases: 29 | /// - direct branch (and link) 30 | /// - conditional branch 31 | /// 32 | /// Fields (`None` denotes unknown target at compile time) 33 | /// - `0`: PC for branch taken 34 | /// - `1`: PC for branch not taken 35 | Branch(Option, Option), 36 | /// Unexpected error. 37 | /// 38 | /// Possible cases: 39 | /// - unimplemented function 40 | /// 41 | /// Fields: 42 | /// - `0`: the error message 43 | Unexpected(String), 44 | } 45 | 46 | impl Display for DisasException { 47 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 48 | match self { 49 | DisasException::Continue(d) => write!(f, "direct continue, next instr: {:#x}", d), 50 | DisasException::Branch(d, a) => { 51 | let df = if let Some(a) = d { 52 | format!("{:#x}", a) 53 | } else { 54 | "unknown".to_owned() 55 | }; 56 | let af = if let Some(a) = a { 57 | format!("{:#x}", a) 58 | } else { 59 | "unknown".to_owned() 60 | }; 61 | write!(f, "branch: direct {}; aux {}", df, af) 62 | } 63 | DisasException::Unexpected(s) => write!(f, "unexpected: {}", s), 64 | } 65 | } 66 | } 67 | 68 | /// Denotes a disassembled, but not yet emitted, IR block. 69 | pub struct TranslationBlock { 70 | /// Start address in guest view. 71 | pub start_pc: usize, 72 | /// Generated IR operations. 73 | pub ops: Vec>, 74 | direct_chain_idx: Option, // taken branch 75 | aux_chain_idx: Option, // not taken branch 76 | } 77 | 78 | /// Disassembler functions to be invoked from the runtime. 79 | pub trait Disassembler { 80 | /// Run disassembly loop to generate a translation block. 81 | fn disas_block(&mut self, start_pos: usize, tb_size: usize) -> DisasException; 82 | 83 | /// Retrieve the newly-generated translation block. 84 | fn get_tb(&mut self) -> TranslationBlock; 85 | 86 | // get tracking weak pointers of allocated KHVals 87 | #[doc(hidden)] 88 | fn get_tracking(&self) -> &[Weak>]; 89 | // run housekeeping on tracking 90 | #[doc(hidden)] 91 | fn clean_tracking(&mut self); 92 | } 93 | 94 | /// Disassembler functions to be invoked from within the frontend. 95 | pub trait DisasContext: Disassembler 96 | where 97 | Self: Sized, 98 | { 99 | /// PC of last fetched instruction. 100 | fn curr_pc(&self) -> usize; 101 | /// PC of upcoming instruction. 102 | fn next_pc(&self) -> usize; 103 | 104 | /// Allocate a new unassigned [KHVal](../ir/storage/struct.KHVal.html). 105 | fn alloc_val(&mut self, ty: ValueType) -> Rc>; 106 | /// Allocate a new label. 107 | fn alloc_label(&mut self) -> Rc> { 108 | let ret = self.alloc_val(ValueType::Label); 109 | *ret.storage.borrow_mut() = R::HostContext::get().make_label(); 110 | ret 111 | } 112 | /// Allocate `u32` immediate value. 113 | fn alloc_u32(&mut self, v: u32) -> Rc> { 114 | let ret = self.alloc_val(ValueType::U32); 115 | *ret.storage.borrow_mut() = R::HostContext::get().make_u32(v); 116 | ret 117 | } 118 | /// Allocate `u64` immediate value. 119 | fn alloc_u64(&mut self, v: u64) -> Rc> { 120 | let ret = self.alloc_val(ValueType::U64); 121 | *ret.storage.borrow_mut() = R::HostContext::get().make_u64(v); 122 | ret 123 | } 124 | /// Allocate `f64` immediate value. 125 | fn alloc_f64(&mut self, v: f64) -> Rc> { 126 | let ret = self.alloc_val(ValueType::F64); 127 | *ret.storage.borrow_mut() = R::HostContext::get().make_f64(v); 128 | ret 129 | } 130 | 131 | /// Push an Op into the current translation block. 132 | /// 133 | /// This should queue the Op pending for next translation block whenever there is an exception. 134 | fn push_op(&mut self, op: Op); 135 | } 136 | -------------------------------------------------------------------------------- /src/guest/arm64.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | extern crate paste; 6 | 7 | use crate::guest::*; 8 | use crate::ir::op::*; 9 | use crate::ir::storage::*; 10 | use crate::runtime::*; 11 | use crate::util::*; 12 | use std::collections::HashMap; 13 | use std::iter::*; 14 | use std::rc::{Rc, Weak}; 15 | 16 | type InsnType = u32; 17 | 18 | /// Disassembler context for the ARM64 frontend. 19 | pub struct Arm64GuestContext { 20 | map: GuestMap, 21 | disas_pos: Option, // addr for next instruction to be disassembled 22 | // 32 general-purpose registers 23 | xreg: Vec>>, 24 | // Negative, Zero, Carry, Overflow 25 | nf: Rc>, 26 | zf: Rc>, 27 | cf: Rc>, 28 | vf: Rc>, 29 | // emulated PC 30 | pc: Rc>, 31 | // TB book-keeping 32 | start_pc: Option, 33 | // emitted IR operations in current TB 34 | ops: Vec>, 35 | // jump targets discovered statically 36 | targets: Vec, 37 | // chaining points 38 | direct_chain_idx: Option, 39 | aux_chain_idx: Option, 40 | // tracking Weak for allocated values 41 | tracking: Vec>>, 42 | u32_cache: HashMap>>, 43 | u64_cache: HashMap>>, 44 | } 45 | 46 | impl Arm64GuestContext { 47 | /// Create a new ARM64 disassembler context. 48 | /// 49 | /// Note that this will create fixed registers (x0-x31, nzcv) for the disassembler, so make sure 50 | /// that the host context has been [initialized](../../host/trait.HostContext.html#tymethod.init) 51 | /// before calling this method, or the host storage creation for registers will fail. 52 | pub fn new(map: GuestMap) -> Self { 53 | Self { 54 | map, 55 | disas_pos: None, 56 | xreg: (0..32) 57 | .map(|i| { 58 | Rc::new(KHVal::named( 59 | if i == 31 { 60 | "sp".to_owned() 61 | } else { 62 | format!("x{:02}", i) 63 | }, 64 | ValueType::U64, 65 | )) 66 | }) 67 | .collect(), 68 | // use 32bit to simplify calculation when reading NZCV as a whole 69 | nf: Rc::new(KHVal::named("nf".to_owned(), ValueType::U32)), 70 | zf: Rc::new(KHVal::named("zf".to_owned(), ValueType::U32)), 71 | cf: Rc::new(KHVal::named("cf".to_owned(), ValueType::U32)), 72 | vf: Rc::new(KHVal::named("vf".to_owned(), ValueType::U32)), 73 | // 64bit simulated PC 74 | pc: Rc::new(KHVal::named("pc".to_owned(), ValueType::U64)), 75 | start_pc: None, 76 | ops: Vec::new(), 77 | targets: Vec::new(), 78 | direct_chain_idx: None, 79 | aux_chain_idx: None, 80 | tracking: Vec::new(), 81 | u32_cache: HashMap::new(), 82 | u64_cache: HashMap::new(), 83 | } 84 | } 85 | 86 | /// Fetch the next instruction from the guest address space. 87 | /// 88 | /// For ARM64 this is bound to return a `u32` for 4-byte instructions. 89 | pub fn next_insn(&mut self) -> InsnType { 90 | let addr = self.disas_pos.unwrap(); 91 | let insn_u8 = self.map.borrow().index(addr..addr + 4).try_into().unwrap(); 92 | self.disas_pos = Some(addr + 4); 93 | 94 | u32::from_le_bytes(insn_u8) 95 | } 96 | 97 | /// Fetch a fixed register, excluding the SP register. 98 | /// 99 | /// The SP register encoding per ARM64 specification is the same as hardwired zero in some 100 | /// contexts. 101 | pub fn reg(&mut self, r: usize) -> Rc> { 102 | assert!(r < 32); 103 | if r == 31 { 104 | self.alloc_u64(0) 105 | } else { 106 | Rc::clone(&self.xreg[r]) 107 | } 108 | } 109 | 110 | /// Fetch a fixed register, including the SP register. 111 | pub fn reg_sp(&self, r: usize) -> Rc> { 112 | assert!(r < 32); 113 | Rc::clone(&self.xreg[r]) 114 | } 115 | 116 | fn set_direct_chain(&mut self) { 117 | if let Some(_) = self.direct_chain_idx { 118 | panic!("direct chain set twice in a single translation block") 119 | } 120 | self.direct_chain_idx = Some(self.ops.len() - 1); 121 | } 122 | 123 | fn set_aux_chain(&mut self) { 124 | if let Some(_) = self.aux_chain_idx { 125 | panic!("aux chain set twice in a single translation block") 126 | } 127 | self.aux_chain_idx = Some(self.ops.len() - 1); 128 | } 129 | 130 | fn clean_state(&mut self) { 131 | self.disas_pos = None; 132 | self.start_pc = None; 133 | self.direct_chain_idx = None; 134 | self.aux_chain_idx = None; 135 | } 136 | } 137 | 138 | impl DisasContext for Arm64GuestContext { 139 | fn curr_pc(&self) -> usize { 140 | self.disas_pos.unwrap() - 4 141 | } 142 | 143 | fn next_pc(&self) -> usize { 144 | self.disas_pos.unwrap() 145 | } 146 | 147 | fn alloc_val(&mut self, ty: ValueType) -> Rc> { 148 | let ret = Rc::new(KHVal::new(ty)); 149 | self.tracking.push(Rc::downgrade(&ret)); 150 | ret 151 | } 152 | 153 | // override the default implementation to cache smaller immediate values 154 | fn alloc_u32(&mut self, v: u32) -> Rc> { 155 | match self.u32_cache.get(&v) { 156 | None => { 157 | let ret = Rc::new(KHVal::u32(v)); 158 | self.tracking.push(Rc::downgrade(&ret)); 159 | self.u32_cache.insert(v, Rc::clone(&ret)); 160 | ret 161 | } 162 | Some(r) => Rc::clone(r), 163 | } 164 | } 165 | 166 | // override the default implementation to cache smaller immediate values 167 | fn alloc_u64(&mut self, v: u64) -> Rc> { 168 | match self.u64_cache.get(&v) { 169 | None => { 170 | let ret = Rc::new(KHVal::u64(v)); 171 | self.tracking.push(Rc::downgrade(&ret)); 172 | self.u64_cache.insert(v, Rc::clone(&ret)); 173 | ret 174 | } 175 | Some(r) => Rc::clone(r), 176 | } 177 | } 178 | 179 | // override the default implementation to reduce substitution overhead 180 | fn alloc_f64(&mut self, v: f64) -> Rc> { 181 | let ret = Rc::new(KHVal::f64(v)); 182 | self.tracking.push(Rc::downgrade(&ret)); 183 | ret 184 | } 185 | 186 | fn push_op(&mut self, op: Op) { 187 | self.ops.push(op) 188 | } 189 | } 190 | 191 | impl Disassembler for Arm64GuestContext { 192 | fn disas_block(&mut self, start_pos: usize, tb_size: usize) -> DisasException { 193 | self.start_pc = Some(start_pos); 194 | self.disas_pos = Some(start_pos); 195 | loop { 196 | let pc = self.next_pc(); 197 | if self.ops.len() >= tb_size { 198 | // TB size exceeded limit, starting new one 199 | let next = self.alloc_u64(pc as u64); 200 | Op::push_trap(self, TrapOp::LOOKUP_TB, &next); 201 | return DisasException::Continue(pc); 202 | } else { 203 | // check if instruction is start of other TB 204 | if pc != start_pos && self.targets.contains(&pc) { 205 | // jump target of some other TBs, terminate this here 206 | return DisasException::Continue(pc); 207 | } 208 | let insn = self.next_insn(); 209 | if let Err(e) = disas_single(self, insn) { 210 | // record the branch targets to break TBs 211 | if let DisasException::Branch(direct, aux) = e { 212 | if let Some(direct) = direct { 213 | self.targets.push(direct); 214 | } 215 | if let Some(aux) = aux { 216 | self.targets.push(aux); 217 | } 218 | } 219 | return e; 220 | } 221 | } 222 | } 223 | } 224 | 225 | fn get_tb(&mut self) -> TranslationBlock { 226 | let mut ret = Vec::new(); 227 | std::mem::swap(&mut ret, &mut self.ops); 228 | 229 | let ret = TranslationBlock { 230 | start_pc: self.start_pc.unwrap(), 231 | ops: ret, 232 | direct_chain_idx: self.direct_chain_idx, 233 | aux_chain_idx: self.aux_chain_idx, 234 | }; 235 | self.clean_state(); 236 | 237 | ret 238 | } 239 | 240 | fn get_tracking(&self) -> &[Weak>] { 241 | self.tracking.as_slice() 242 | } 243 | 244 | fn clean_tracking(&mut self) { 245 | self.tracking.retain(|x| x.weak_count() > 0); 246 | } 247 | } 248 | 249 | // AArch64 Opcodes 250 | fn unallocated( 251 | ctx: &mut Arm64GuestContext, 252 | insn: InsnType, 253 | ) -> Result<(), DisasException> { 254 | // Emit trap to runtime with UNDEF cause and emulated PC value 255 | Op::push_trap(ctx, TrapOp::UNDEF_OPCODE, &Rc::clone(&ctx.pc)); 256 | 257 | Ok(()) 258 | } 259 | 260 | macro_rules! disas_category { 261 | ( $name:ident, $start:expr, $len:expr, $( [ $($opc:expr),* ], $handler:ident ),* ) => { 262 | paste::item! { 263 | fn [< disas_ $name >](ctx: &mut Arm64GuestContext, insn: InsnType) -> Result<(), DisasException> { 264 | (match extract(insn, $start, $len) { 265 | $( 266 | $($opc)|* => paste::expr! { [< disas_ $handler >] }, 267 | )* 268 | _ => unallocated 269 | })(ctx, insn) 270 | } 271 | } 272 | }; 273 | } 274 | 275 | macro_rules! disas_subcategory { 276 | ( $name:ident, $start:expr, $len:expr, $( [ $($opc:expr),* ], $handler:ident ),* ) => { 277 | paste::item! { 278 | fn [< disas_ $name >](ctx: &mut Arm64GuestContext, insn: InsnType) -> Result<(), DisasException> { 279 | (match extract(insn, $start, $len) { 280 | $( 281 | $($opc)|* => paste::expr! { $name :: [< disas_ $handler >] }, 282 | )* 283 | _ => unallocated 284 | })(ctx, insn) 285 | } 286 | } 287 | }; 288 | } 289 | 290 | macro_rules! disas_stub { 291 | ( $($handler:ident),* ) => { 292 | $( 293 | paste::item! { 294 | /// Stub for disassembling instructions of a specific opcode that are not yet 295 | /// implemented. 296 | pub fn [< disas_ $handler >](ctx: &mut Arm64GuestContext, insn: InsnType) -> Result<(), DisasException> { 297 | Err(DisasException::Unexpected(format!("insn 0x{:0x}: {} not implemented", insn, stringify!($handler)))) 298 | } 299 | } 300 | )* 301 | }; 302 | } 303 | 304 | #[rustfmt::skip] 305 | disas_category!(single, 25, 4, 306 | [0x2], sve, 307 | [0x8, 0x9], data_proc_imm, 308 | [0x5, 0xd], data_proc_reg, 309 | [0x7, 0xf], data_proc_simd_fp, 310 | [0xa, 0xb], b_exc_sys, 311 | [0x4, 0x6, 0xc, 0xe], ldst 312 | ); 313 | disas_stub![sve]; 314 | 315 | #[rustfmt::skip] 316 | disas_subcategory!(data_proc_imm, 23, 6, 317 | [0x20, 0x21], pc_rel_addr, 318 | [0x22, 0x23], add_sub_imm, 319 | [0x24], logic_imm, 320 | [0x25], movw_imm, 321 | [0x26], bitfield, 322 | [0x27], extract 323 | ); 324 | 325 | use data_proc_reg::disas_data_proc_reg; 326 | 327 | #[rustfmt::skip] 328 | disas_subcategory!(ldst, 24, 6, 329 | [0x08], ldst_excl, 330 | [0x18], ld_lit, 331 | [0x28, 0x29, 0x2c, 0x2d], ldst_pair, 332 | [0x38, 0x39, 0x3c, 0x3d], ldst_reg, 333 | [0x0c], ldst_multiple_struct, 334 | [0x0d], ldst_single_struct, 335 | [0x19], ldst_ldapr_stlr 336 | ); 337 | 338 | use data_proc_simd_fp::disas_data_proc_simd_fp; 339 | use memmap::{Mmap, MmapMut}; 340 | use std::cell::RefCell; 341 | use std::convert::TryInto; 342 | use std::ops::Index; 343 | 344 | #[rustfmt::skip] 345 | disas_subcategory!(b_exc_sys, 25, 7, 346 | [0x0a, 0x0b, 0x4a, 0x4b], uncond_b_imm, 347 | [0x1a, 0x5a], comp_b_imm, 348 | [0x1b, 0x5b], test_b_imm, 349 | [0x2a], cond_b_imm, 350 | [0x6a], exc_sys, 351 | [0x6b], uncond_b_reg 352 | ); 353 | 354 | // declare the submodules 355 | // IntelliJ Rust will not recognize if these are in macro definition 356 | mod b_exc_sys; 357 | mod data_proc_imm; 358 | mod data_proc_reg; 359 | mod data_proc_simd_fp; 360 | mod facility; 361 | mod ldst; 362 | mod sve; 363 | mod system; 364 | -------------------------------------------------------------------------------- /src/guest/arm64/b_exc_sys.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | use crate::guest::arm64::facility::*; 7 | 8 | pub fn disas_exc_sys( 9 | ctx: &mut Arm64GuestContext, 10 | insn: InsnType, 11 | ) -> Result<(), DisasException> { 12 | (if extract(insn, 24, 1) == 1 { 13 | if extract(insn, 22, 2) == 0 { 14 | super::system::disas_system 15 | } else { 16 | super::unallocated 17 | } 18 | } else { 19 | disas_exc 20 | })(ctx, insn) 21 | } 22 | 23 | pub fn disas_cond_b_imm( 24 | ctx: &mut Arm64GuestContext, 25 | insn: InsnType, 26 | ) -> Result<(), DisasException> { 27 | if extract(insn, 4, 1) == 1 || extract(insn, 24, 1) == 1 { 28 | return unallocated(ctx, insn); 29 | } 30 | 31 | let addr = (ctx.curr_pc() as i64 + sextract(insn as i64, 5, 19) * 4) as usize; 32 | let cond = extract(insn, 0, 4); 33 | let addr_val = ctx.alloc_u64(addr as u64); 34 | 35 | if cond < 0xe { 36 | // genuine conditional branches 37 | let label = ctx.alloc_label(); 38 | do_test_jump_cc(ctx, cond, &label); 39 | let next_pc = ctx.alloc_u64(ctx.next_pc() as u64); 40 | do_end_tb_to_addr(ctx, &next_pc, true); // branch not taken 41 | Op::push_setlbl(ctx, &label); 42 | do_end_tb_to_addr(ctx, &addr_val, false); // branch taken 43 | 44 | Err(DisasException::Branch(Some(addr), Some(ctx.next_pc()))) 45 | } else { 46 | // 0xe and 0xf are "always" conditions 47 | do_end_tb_to_addr(ctx, &addr_val, false); 48 | 49 | // not returning 50 | Err(DisasException::Branch(Some(addr), None)) 51 | } 52 | } 53 | 54 | pub fn disas_uncond_b_imm( 55 | ctx: &mut Arm64GuestContext, 56 | insn: InsnType, 57 | ) -> Result<(), DisasException> { 58 | let addr = (ctx.curr_pc() as i64 + sextract(insn as i64, 0, 26) * 4) as usize; 59 | let addr_val = ctx.alloc_u64(addr as u64); 60 | 61 | if insn & (1 << 31) != 0 { 62 | // BL: branch with link 63 | let next_pc = ctx.alloc_u64(ctx.next_pc() as u64); 64 | let reg = ctx.reg(30); 65 | Op::push_mov(ctx, ®, &next_pc); 66 | } 67 | 68 | do_end_tb_to_addr(ctx, &addr_val, false); 69 | Err(DisasException::Branch(Some(addr), None)) 70 | } 71 | 72 | pub fn disas_comp_b_imm( 73 | ctx: &mut Arm64GuestContext, 74 | insn: InsnType, 75 | ) -> Result<(), DisasException> { 76 | let sf = extract(insn, 31, 1) == 1; 77 | let op = extract(insn, 24, 1) == 1; 78 | let rt = extract(insn, 0, 5); 79 | let addr = (ctx.curr_pc() as i64 + sextract(insn as i64, 5, 19) * 4) as usize; 80 | 81 | let next_pc = ctx.alloc_u64(ctx.next_pc() as u64); 82 | let addr_val = ctx.alloc_u64(addr as u64); 83 | 84 | let cmp = read_cpu_reg(ctx, rt as usize, sf); 85 | let label_match = ctx.alloc_label(); 86 | let zero = ctx.alloc_u64(0); 87 | 88 | Op::push_brc( 89 | ctx, 90 | &label_match, 91 | &cmp, 92 | &zero, 93 | if op { CondOp::NE } else { CondOp::EQ }, 94 | ); 95 | do_end_tb_to_addr(ctx, &next_pc, true); // branch not taken 96 | 97 | Op::push_setlbl(ctx, &label_match); 98 | do_end_tb_to_addr(ctx, &addr_val, false); // branch taken 99 | 100 | Err(DisasException::Branch(Some(addr), Some(ctx.next_pc()))) 101 | } 102 | 103 | pub fn disas_uncond_b_reg( 104 | ctx: &mut Arm64GuestContext, 105 | insn: InsnType, 106 | ) -> Result<(), DisasException> { 107 | let opc = extract(insn, 21, 4); 108 | let op2 = extract(insn, 16, 5); 109 | let op3 = extract(insn, 10, 6); 110 | let rn = extract(insn, 5, 5) as usize; 111 | let op4 = extract(insn, 0, 5); 112 | 113 | let dst; 114 | 115 | if op2 != 0x1f { 116 | return unallocated(ctx, insn); 117 | } 118 | 119 | match opc { 120 | 0 | 1 | 2 => { 121 | // br, blr, ret 122 | match op3 { 123 | 0 => { 124 | // br, blr, ret 125 | if op4 != 0 { 126 | return unallocated(ctx, insn); 127 | } 128 | dst = ctx.reg(rn); 129 | } 130 | _ => return unallocated(ctx, insn), // pauth unimplemented 131 | } 132 | if opc == 1 { 133 | // blr: load return address 134 | let ret_addr = ctx.alloc_u64(ctx.next_pc() as u64); 135 | let x30 = ctx.reg(30); 136 | Op::push_mov(ctx, &x30, &ret_addr); 137 | } 138 | do_end_tb_to_addr(ctx, &dst, false); 139 | } 140 | 8 | 9 => return unallocated(ctx, insn), // pauth unimplemented 141 | 4 => return unallocated(ctx, insn), // eret impossible in EL0 142 | 5 => { 143 | // drps 144 | if op3 != 0 || op4 != 0 || rn != 0x1f { 145 | return unallocated(ctx, insn); 146 | } 147 | unreachable!("DRPS not supported"); 148 | } 149 | _ => return unallocated(ctx, insn), 150 | } 151 | 152 | Err(DisasException::Branch(None, None)) 153 | } 154 | 155 | disas_stub![test_b_imm, exc]; 156 | -------------------------------------------------------------------------------- /src/guest/arm64/data_proc_imm.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | use crate::guest::arm64::facility::*; 7 | 8 | pub fn disas_add_sub_imm( 9 | ctx: &mut Arm64GuestContext, 10 | insn: InsnType, 11 | ) -> Result<(), DisasException> { 12 | let rd = extract(insn, 0, 5) as usize; 13 | let rn = extract(insn, 5, 5) as usize; 14 | let shift = extract(insn, 22, 2); 15 | let setflags = extract(insn, 29, 1) == 1; 16 | let sub_op = extract(insn, 30, 1) == 1; 17 | let is_64bit = extract(insn, 31, 1) == 1; 18 | let mut imm = extract(insn, 10, 12) as u64; 19 | 20 | let rn = ctx.reg_sp(rn); 21 | let rd = if setflags { 22 | ctx.reg(rd) 23 | } else { 24 | ctx.reg_sp(rd) 25 | }; 26 | let result = ctx.alloc_val(ValueType::U64); 27 | 28 | if shift == 0x1 { 29 | imm <<= 12; 30 | } else if shift != 0x0 { 31 | return unallocated(ctx, insn); 32 | } 33 | 34 | let imm = ctx.alloc_u64(imm); 35 | if !setflags { 36 | (if sub_op { Op::push_sub } else { Op::push_add })(ctx, &result, &rn, &imm); 37 | } else { 38 | // update condition codes 39 | if sub_op { 40 | do_sub_cc(ctx, is_64bit, &result, &rn, &imm); 41 | } else { 42 | do_add_cc(ctx, is_64bit, &result, &rn, &imm); 43 | } 44 | } 45 | 46 | (if is_64bit { 47 | Op::push_mov 48 | } else { 49 | Op::push_extulq 50 | })(ctx, &rd, &result); 51 | 52 | Ok(()) 53 | } 54 | 55 | pub fn disas_movw_imm( 56 | ctx: &mut Arm64GuestContext, 57 | insn: InsnType, 58 | ) -> Result<(), DisasException> { 59 | let rd = extract(insn, 0, 5) as usize; 60 | let sf = extract(insn, 31, 1) == 1; 61 | let opc = extract(insn, 29, 2); 62 | let pos = extract(insn, 21, 2) << 4; 63 | let mut imm = extract(insn, 5, 16) as u64; 64 | 65 | let rd = ctx.reg(rd); 66 | 67 | if !sf && pos >= 32 { 68 | return unallocated(ctx, insn); 69 | } 70 | 71 | match opc { 72 | 0 | 2 => { 73 | // movn / movz 74 | imm <<= pos as u64; 75 | if opc == 0 { 76 | imm = !imm; 77 | } 78 | if !sf { 79 | imm &= 0xffffffffu64; 80 | } 81 | let imm = ctx.alloc_u64(imm); 82 | Op::push_mov(ctx, &rd, &imm); 83 | } 84 | 3 => { 85 | // movk 86 | let imm = ctx.alloc_u64(imm); 87 | Op::push_depos(ctx, &rd, &rd, &imm, pos as u64, 16); 88 | if !sf { 89 | Op::push_extulq(ctx, &rd, &rd); 90 | } 91 | } 92 | _ => return unallocated(ctx, insn), 93 | } 94 | 95 | Ok(()) 96 | } 97 | 98 | pub fn disas_pc_rel_addr( 99 | ctx: &mut Arm64GuestContext, 100 | insn: InsnType, 101 | ) -> Result<(), DisasException> { 102 | let page = extract(insn, 31, 1); 103 | let rd = extract(insn, 0, 5); 104 | let rd = ctx.reg(rd as usize); 105 | 106 | let offset = sextract(insn as i64, 5, 19); // immhi 107 | let mut offset = offset << 2 | extract(insn, 29, 2) as i64; // immlo 108 | let mut base = ctx.curr_pc(); 109 | 110 | if page == 1 { 111 | // ADRP: page based 112 | base &= !0xfff; 113 | offset <<= 12; 114 | } 115 | 116 | let val = ctx.alloc_u64(base as u64 + offset as u64); 117 | Op::push_mov(ctx, &rd, &val); 118 | 119 | Ok(()) 120 | } 121 | 122 | pub fn disas_logic_imm( 123 | ctx: &mut Arm64GuestContext, 124 | insn: InsnType, 125 | ) -> Result<(), DisasException> { 126 | let sf = extract(insn, 31, 1) == 1; 127 | let opc = extract(insn, 29, 2); 128 | let is_n = extract(insn, 22, 1) == 1; 129 | let immr = extract(insn, 16, 6); 130 | let imms = extract(insn, 10, 6); 131 | let rn = extract(insn, 5, 5) as usize; 132 | let rd = extract(insn, 0, 5) as usize; 133 | 134 | if !sf && is_n { 135 | return unallocated(ctx, insn); 136 | } 137 | 138 | let rd_val; 139 | 140 | if opc == 3 { 141 | rd_val = ctx.reg(rd); 142 | } else { 143 | rd_val = ctx.reg_sp(rd); 144 | } 145 | let rn_val = ctx.reg(rn); 146 | 147 | match logic_imm_decode_wmask(is_n as u32, imms, immr) { 148 | None => return unallocated(ctx, insn), 149 | Some(mut wmask) => { 150 | if !sf { 151 | wmask &= 0xffffffff; 152 | } 153 | 154 | let wmask = ctx.alloc_u64(wmask); 155 | let mut is_and = false; 156 | (match opc { 157 | 3 | 0 => { 158 | // ands, and 159 | is_and = true; 160 | Op::push_add 161 | } 162 | 1 => Op::push_or, // orr 163 | 2 => Op::push_xor, // eor 164 | _ => unreachable!(), 165 | })(ctx, &rd_val, &rn_val, &wmask); 166 | 167 | if !sf && !is_and { 168 | Op::push_extulq(ctx, &rd_val, &rd_val); 169 | } 170 | 171 | if opc == 3 { 172 | // ands 173 | do_logic_cc(ctx, sf, &rd_val); 174 | } 175 | 176 | Ok(()) 177 | } 178 | } 179 | } 180 | 181 | disas_stub![bitfield, extract]; 182 | -------------------------------------------------------------------------------- /src/guest/arm64/data_proc_reg.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | use crate::guest::arm64::facility::*; 7 | 8 | pub fn disas_data_proc_reg( 9 | ctx: &mut Arm64GuestContext, 10 | insn: InsnType, 11 | ) -> Result<(), DisasException> { 12 | let op0 = extract(insn, 30, 1); 13 | let op1 = extract(insn, 28, 1); 14 | let op2 = extract(insn, 21, 4); 15 | let op3 = extract(insn, 10, 6); 16 | 17 | if op1 == 0 { 18 | return (if op2 & 8 != 0 { 19 | if op2 & 1 != 0 { 20 | disas_add_sub_ext_reg 21 | } else { 22 | disas_add_sub_reg 23 | } 24 | } else { 25 | disas_logic_reg 26 | })(ctx, insn); 27 | } 28 | (match op2 { 29 | 0x0 => match op3 { 30 | 0x0 => disas_adc_sbc, 31 | 0x1 | 0x21 => disas_rotate_right_into_flags, 32 | 0x2 | 0x12 | 0x22 | 0x32 => disas_evaluate_into_flags, 33 | _ => super::unallocated, 34 | }, 35 | 0x2 => disas_cc, 36 | 0x4 => disas_cond_select, 37 | 0x6 => { 38 | if op0 != 0 { 39 | disas_data_proc_1src 40 | } else { 41 | disas_data_proc_2src 42 | } 43 | } 44 | 0x8..=0xf => disas_data_proc_3src, 45 | _ => super::unallocated, 46 | })(ctx, insn) 47 | } 48 | 49 | pub fn disas_logic_reg( 50 | ctx: &mut Arm64GuestContext, 51 | insn: InsnType, 52 | ) -> Result<(), DisasException> { 53 | let sf = extract(insn, 31, 1) == 1; 54 | let opc = extract(insn, 29, 2); 55 | let shift_type = extract(insn, 22, 2); 56 | let invert = extract(insn, 21, 1); 57 | let rm = extract(insn, 16, 5) as usize; 58 | let shift_amount = extract(insn, 10, 6); 59 | let rn = extract(insn, 5, 5) as usize; 60 | let rd = extract(insn, 0, 5) as usize; 61 | 62 | if !sf && shift_amount & (1 << 5) != 0 { 63 | return unallocated(ctx, insn); 64 | } 65 | 66 | let rd = ctx.reg(rd); 67 | 68 | if opc == 1 && shift_amount == 0 && shift_type == 0 && rn == 31 { 69 | // unshifted ORR and ORN with WZR/XZR is the standard encoding for register-register MOV and MVN 70 | let rm = ctx.reg(rm); 71 | if invert == 1 { 72 | Op::push_not(ctx, &rd, &rm); 73 | if !sf { 74 | Op::push_extulq(ctx, &rd, &rd); 75 | } 76 | } else { 77 | if sf { 78 | Op::push_mov(ctx, &rd, &rm); 79 | } else { 80 | Op::push_extulq(ctx, &rd, &rm); 81 | } 82 | } 83 | return Ok(()); 84 | } 85 | 86 | let rm = read_cpu_reg(ctx, rm, sf); 87 | 88 | let shift_type = match A64Shift::from_bits(shift_type) { 89 | Some(s) => s, 90 | None => { 91 | return Err(DisasException::Unexpected( 92 | "unknown shift_type in logic_reg".to_owned(), 93 | )) 94 | } 95 | }; 96 | if shift_amount != 0 { 97 | do_shift_imm(ctx, &rm, &rm, sf, shift_type, shift_amount); 98 | } 99 | 100 | let rn = ctx.reg(rn); 101 | 102 | match opc | (invert << 2) { 103 | 0 | 3 => { 104 | // and / ands 105 | Op::push_and(ctx, &rd, &rn, &rm); 106 | } 107 | 1 => { 108 | // orr 109 | Op::push_or(ctx, &rd, &rn, &rm); 110 | } 111 | 2 => { 112 | // eor 113 | Op::push_xor(ctx, &rd, &rn, &rm); 114 | } 115 | 4 | 7 => { 116 | // bic / bics 117 | Op::push_andc(ctx, &rd, &rn, &rm); 118 | } 119 | 5 => { 120 | // orn 121 | Op::push_orc(ctx, &rd, &rn, &rm); 122 | } 123 | 6 => { 124 | // eon 125 | Op::push_eqv(ctx, &rd, &rn, &rm); 126 | } 127 | _ => { 128 | return Err(DisasException::Unexpected( 129 | "unknown opc in logic_reg".to_owned(), 130 | )) 131 | } 132 | } 133 | 134 | if !sf { 135 | Op::push_extulq(ctx, &rd, &rd); 136 | } 137 | 138 | if opc == 3 { 139 | do_logic_cc(ctx, sf, &rd); 140 | } 141 | 142 | Ok(()) 143 | } 144 | 145 | pub fn disas_add_sub_ext_reg( 146 | ctx: &mut Arm64GuestContext, 147 | insn: InsnType, 148 | ) -> Result<(), DisasException> { 149 | let rd = extract(insn, 0, 5) as usize; 150 | let rn = extract(insn, 5, 5) as usize; 151 | let imm3 = extract(insn, 10, 3); 152 | let option = extract(insn, 13, 3); 153 | let rm = extract(insn, 16, 5) as usize; 154 | let opt = extract(insn, 22, 2); 155 | let setflags = extract(insn, 29, 1) == 1; 156 | let sub_op = extract(insn, 30, 1) == 1; 157 | let sf = extract(insn, 31, 1) == 1; 158 | 159 | if imm3 > 4 || opt != 0 { 160 | return unallocated(ctx, insn); 161 | } 162 | 163 | // non-flag version may use SP 164 | let rd = if setflags { 165 | ctx.reg(rd) 166 | } else { 167 | ctx.reg_sp(rd) 168 | }; 169 | let rn = read_cpu_reg_sp(ctx, rn, sf); 170 | let rm = read_cpu_reg(ctx, rm, sf); 171 | do_ext_and_shift_reg(ctx, &rm, &rm, option, imm3); 172 | 173 | let result = ctx.alloc_val(ValueType::U64); 174 | 175 | if !setflags { 176 | (if sub_op { Op::push_sub } else { Op::push_add })(ctx, &result, &rn, &rm); 177 | } else { 178 | // update condition codes 179 | if sub_op { 180 | do_sub_cc(ctx, sf, &result, &rn, &rm); 181 | } else { 182 | do_add_cc(ctx, sf, &result, &rn, &rm); 183 | } 184 | } 185 | 186 | (if sf { Op::push_mov } else { Op::push_extulq })(ctx, &rd, &result); 187 | 188 | Ok(()) 189 | } 190 | 191 | pub fn disas_cond_select( 192 | ctx: &mut Arm64GuestContext, 193 | insn: InsnType, 194 | ) -> Result<(), DisasException> { 195 | if extract(insn, 29, 1) == 1 || extract(insn, 11, 1) == 1 { 196 | return unallocated(ctx, insn); 197 | } 198 | 199 | let sf = extract(insn, 31, 1) == 1; 200 | let else_inv = extract(insn, 30, 1) == 1; 201 | let rm = extract(insn, 16, 5) as usize; 202 | let cond = extract(insn, 12, 4); 203 | let else_inc = extract(insn, 10, 1) == 1; 204 | let rn = extract(insn, 5, 5) as usize; 205 | let rd = extract(insn, 0, 5) as usize; 206 | 207 | let rd = ctx.reg(rd); 208 | let Arm64CC { 209 | mut cond, 210 | value: val32, 211 | } = test_cc(ctx, cond); 212 | let value = ctx.alloc_val(ValueType::U64); 213 | Op::push_extslq(ctx, &value, &val32); 214 | 215 | let one = ctx.alloc_u64(1); 216 | 217 | let zero = ctx.alloc_u64(0); 218 | 219 | if rn == 31 && rm == 31 && (else_inc ^ else_inv) { 220 | // cset & csetm 221 | cond.invert(); 222 | 223 | Op::push_setc(ctx, &rd, &value, &zero, cond); 224 | 225 | if else_inv { 226 | Op::push_neg(ctx, &rd, &rd); 227 | } 228 | } else { 229 | let t_true = ctx.reg(rn); 230 | let t_false = read_cpu_reg(ctx, rm, true); 231 | if else_inc && else_inc { 232 | Op::push_neg(ctx, &t_false, &t_false); 233 | } else if else_inv { 234 | Op::push_not(ctx, &t_false, &t_false); 235 | } else if else_inc { 236 | Op::push_add(ctx, &t_false, &t_false, &one); 237 | } 238 | 239 | Op::push_movc(ctx, &rd, &value, &zero, &t_true, &t_false, cond); 240 | } 241 | 242 | if !sf { 243 | Op::push_extulq(ctx, &rd, &rd); 244 | } 245 | 246 | Ok(()) 247 | } 248 | 249 | disas_stub![ 250 | add_sub_reg, 251 | adc_sbc, 252 | rotate_right_into_flags, 253 | evaluate_into_flags, 254 | cc, 255 | data_proc_1src, 256 | data_proc_2src, 257 | data_proc_3src 258 | ]; 259 | -------------------------------------------------------------------------------- /src/guest/arm64/data_proc_simd_fp.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | 7 | pub fn disas_data_proc_simd_fp( 8 | ctx: &mut Arm64GuestContext, 9 | insn: InsnType, 10 | ) -> Result<(), DisasException> { 11 | (if extract(insn, 28, 1) == 1 && extract(insn, 30, 1) == 0 { 12 | disas_data_proc_fp 13 | } else { 14 | disas_data_proc_simd 15 | })(ctx, insn) 16 | } 17 | 18 | disas_stub![data_proc_fp, data_proc_simd]; 19 | -------------------------------------------------------------------------------- /src/guest/arm64/facility.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | use super::{CondOp, MemOp}; 7 | 8 | // read CPU register. 9 | // the SP encoding is used to represent XZR (hardwired zero) in some contexts. 10 | // If SP is needed, use `read_cpu_reg_sp`. 11 | pub fn read_cpu_reg( 12 | ctx: &mut Arm64GuestContext, 13 | reg: usize, 14 | sf: bool, 15 | ) -> Rc> { 16 | let v = ctx.alloc_val(ValueType::U64); 17 | let src = ctx.reg(reg); 18 | (if sf { Op::push_mov } else { Op::push_extulq })(ctx, &v, &src); 19 | v 20 | } 21 | 22 | // read CPU register, containing SP. 23 | pub fn read_cpu_reg_sp( 24 | ctx: &mut Arm64GuestContext, 25 | reg: usize, 26 | sf: bool, 27 | ) -> Rc> { 28 | let v = ctx.alloc_val(ValueType::U64); 29 | let src = ctx.reg_sp(reg); 30 | (if sf { Op::push_mov } else { Op::push_extulq })(ctx, &v, &src); 31 | v 32 | } 33 | 34 | pub fn top_byte_ignore( 35 | ctx: &mut Arm64GuestContext, 36 | dst: &Rc>, 37 | src: &Rc>, 38 | tbi: u32, 39 | ) { 40 | // note that we've ignored the case of EL2 and EL3 in which the address 41 | // does not have two ranges, thus the tag bytes will be forced to be zero. 42 | // We're performing system emulation, so we assume that we always run in EL0. 43 | if tbi == 0 { 44 | Op::push_mov(ctx, dst, src); 45 | } else { 46 | Op::push_extrs(ctx, dst, src, 0, 56); 47 | } 48 | } 49 | 50 | pub fn clean_data_tbi( 51 | ctx: &mut Arm64GuestContext, 52 | addr: &Rc>, 53 | ) -> Rc> { 54 | let ret = ctx.alloc_val(ValueType::U64); 55 | // Linux aarch64 enables TBI for EL0: https://www.kernel.org/doc/Documentation/arm64/tagged-pointers.txt 56 | // we assume TBI always happens 57 | top_byte_ignore(ctx, &ret, addr, 1); 58 | ret 59 | } 60 | 61 | // generate load / store with proper memory operation 62 | pub fn do_ldst( 63 | ctx: &mut Arm64GuestContext, 64 | is_load: bool, 65 | sign: bool, 66 | extend: bool, 67 | size: u64, 68 | reg: &Rc>, 69 | addr: &Rc>, 70 | ) { 71 | (if is_load { 72 | Op::push_load 73 | } else { 74 | Op::push_store 75 | })( 76 | ctx, 77 | reg, 78 | addr, 79 | MemOp::from_sign(sign) | MemOp::from_size(size) | MemOp::GUEST_LE, 80 | ); 81 | 82 | if is_load && extend && sign { 83 | assert!(size < 8); 84 | Op::push_extulq(ctx, reg, reg); 85 | } 86 | } 87 | 88 | fn get_flags( 89 | ctx: &mut Arm64GuestContext, 90 | ) -> (Rc>, Rc>, Rc>, Rc>) { 91 | ( 92 | Rc::clone(&ctx.nf), 93 | Rc::clone(&ctx.zf), 94 | Rc::clone(&ctx.cf), 95 | Rc::clone(&ctx.vf), 96 | ) 97 | } 98 | 99 | fn set_nz64(ctx: &mut Arm64GuestContext, v: &Rc>) { 100 | let (nf, zf, _, _) = get_flags(ctx); 101 | assert_eq!(v.ty, ValueType::U64); 102 | Op::push_extr(ctx, &zf, &nf, v); 103 | Op::push_orl(ctx, &zf, &zf, &nf); 104 | } 105 | 106 | // generate add with condition code modification 107 | pub fn do_add_cc( 108 | ctx: &mut Arm64GuestContext, 109 | sf: bool, 110 | dest: &Rc>, 111 | t0: &Rc>, 112 | t1: &Rc>, 113 | ) { 114 | let (nf, zf, cf, vf) = get_flags(ctx); 115 | if sf { 116 | let flag = ctx.alloc_val(ValueType::U64); 117 | let tmp = ctx.alloc_val(ValueType::U64); 118 | 119 | let zero = ctx.alloc_u64(0); 120 | // capture carry in flag 121 | Op::push_add2(ctx, dest, &flag, t0, &zero, t1, &zero); 122 | Op::push_extrl(ctx, &cf, &flag); 123 | set_nz64(ctx, dest); 124 | // calculate vf 125 | Op::push_xor(ctx, &flag, dest, t0); 126 | Op::push_xor(ctx, &tmp, t0, t1); 127 | Op::push_andc(ctx, &flag, &flag, &tmp); 128 | Op::push_extrh(ctx, &vf, &flag); 129 | } else { 130 | let t0_32 = ctx.alloc_val(ValueType::U32); 131 | let t1_32 = ctx.alloc_val(ValueType::U32); 132 | let tmp = ctx.alloc_val(ValueType::U32); 133 | 134 | let zero = ctx.alloc_u32(0); 135 | Op::push_extrl(ctx, &t0_32, t0); 136 | Op::push_extrl(ctx, &t1_32, t1); 137 | Op::push_add2l(ctx, &nf, &cf, &t0_32, &zero, &t1_32, &zero); 138 | Op::push_mov(ctx, &zf, &nf); 139 | Op::push_xorl(ctx, &vf, &nf, &t0_32); 140 | Op::push_xorl(ctx, &tmp, &t0_32, &t1_32); 141 | Op::push_andcl(ctx, &vf, &vf, &tmp); 142 | Op::push_extulq(ctx, dest, &nf); 143 | } 144 | } 145 | 146 | // generate sub with condition code modification 147 | pub fn do_sub_cc( 148 | ctx: &mut Arm64GuestContext, 149 | sf: bool, 150 | dest: &Rc>, 151 | t0: &Rc>, 152 | t1: &Rc>, 153 | ) { 154 | let (nf, zf, cf, vf) = get_flags(ctx); 155 | if sf { 156 | let flag = ctx.alloc_val(ValueType::U64); 157 | let tmp = ctx.alloc_val(ValueType::U64); 158 | 159 | Op::push_sub(ctx, dest, t0, t1); 160 | set_nz64(ctx, dest); 161 | // calculate cf 162 | Op::push_setc(ctx, &flag, t0, t1, CondOp::GEU); 163 | Op::push_extrl(ctx, &cf, &flag); 164 | // calculate vf 165 | Op::push_xor(ctx, &flag, &dest, t0); 166 | Op::push_xor(ctx, &tmp, t0, t1); 167 | Op::push_andc(ctx, &flag, &flag, &tmp); 168 | Op::push_extrh(ctx, &vf, &flag); 169 | } else { 170 | let t0_32 = ctx.alloc_val(ValueType::U32); 171 | let t1_32 = ctx.alloc_val(ValueType::U32); 172 | let tmp = ctx.alloc_val(ValueType::U32); 173 | 174 | Op::push_extrl(ctx, &t0_32, t0); 175 | Op::push_extrl(ctx, &t1_32, t1); 176 | Op::push_subl(ctx, &nf, &t0_32, &t1_32); 177 | Op::push_mov(ctx, &zf, &nf); 178 | Op::push_setc(ctx, &cf, &t0_32, &t1_32, CondOp::GEU); 179 | Op::push_xorl(ctx, &vf, &nf, &t0_32); 180 | Op::push_xorl(ctx, &tmp, &t0_32, &t1_32); 181 | Op::push_andcl(ctx, &vf, &vf, &tmp); 182 | Op::push_extulq(ctx, dest, &nf); 183 | } 184 | } 185 | 186 | pub struct Arm64CC { 187 | pub cond: CondOp, 188 | pub value: Rc>, 189 | } 190 | 191 | pub fn test_cc(ctx: &mut Arm64GuestContext, cc: u32) -> Arm64CC { 192 | let mut cond: CondOp; 193 | let value: Rc>; 194 | let (nf, zf, cf, vf) = get_flags(ctx); 195 | 196 | match cc { 197 | 0 | 1 => { 198 | // eq: Z; ne: !Z 199 | cond = CondOp::EQ; 200 | value = Rc::clone(&zf); 201 | } 202 | 2 | 3 => { 203 | // cs: C; cc: !C 204 | cond = CondOp::NE; 205 | value = Rc::clone(&cf); 206 | } 207 | 4 | 5 => { 208 | // mi: N; pl: !N 209 | cond = CondOp::LT; 210 | value = Rc::clone(&nf); 211 | } 212 | 6 | 7 => { 213 | // vs: V; vc: !V 214 | cond = CondOp::LT; 215 | value = Rc::clone(&vf); 216 | } 217 | 8 | 9 => { 218 | // hi: C && !Z; ls: !(C && !Z) 219 | cond = CondOp::NE; 220 | value = ctx.alloc_val(ValueType::U32); 221 | Op::push_negl(ctx, &value, &cf); 222 | Op::push_andl(ctx, &value, &value, &zf); 223 | } 224 | 10 | 11 => { 225 | // ge: N ^ V == 0; lt: N ^ V != 0 226 | cond = CondOp::GE; 227 | value = ctx.alloc_val(ValueType::U32); 228 | Op::push_xorl(ctx, &value, &vf, &nf); 229 | } 230 | 12 | 13 => { 231 | // gt: !Z && N == V; Z || N != V 232 | cond = CondOp::NE; 233 | value = ctx.alloc_val(ValueType::U32); 234 | let shift = ctx.alloc_u32(31); 235 | Op::push_xorl(ctx, &value, &vf, &nf); 236 | Op::push_sarl(ctx, &value, &value, &shift); 237 | Op::push_andcl(ctx, &value, &zf, &value); 238 | } 239 | 14 | 15 => { 240 | // always 241 | cond = CondOp::ALWAYS; 242 | value = Rc::clone(&zf); 243 | } 244 | _ => unreachable!("bad condition code {:#x}", cc), 245 | } 246 | 247 | if cc & 1 == 1 && cc != 14 && cc != 15 { 248 | cond.invert(); 249 | } 250 | 251 | Arm64CC { cond, value } 252 | } 253 | 254 | pub fn do_test_jump_cc( 255 | ctx: &mut Arm64GuestContext, 256 | cc: u32, 257 | label: &Rc>, 258 | ) { 259 | assert_eq!(label.ty, ValueType::Label); 260 | let Arm64CC { cond, value } = test_cc(ctx, cc); 261 | let zero = ctx.alloc_u32(0); 262 | Op::push_brc(ctx, label, &value, &zero, cond); 263 | } 264 | 265 | // set PC and return to runtime to find out next TB 266 | pub fn do_end_tb_to_addr( 267 | ctx: &mut Arm64GuestContext, 268 | dest: &Rc>, 269 | is_aux: bool, 270 | ) { 271 | let pc = Rc::clone(&ctx.pc); 272 | Op::push_mov(ctx, &pc, dest); 273 | Op::push_trap(ctx, TrapOp::LOOKUP_TB, dest); 274 | if is_aux { 275 | ctx.set_aux_chain(); 276 | } else { 277 | ctx.set_direct_chain(); 278 | } 279 | } 280 | 281 | bitflags! { 282 | pub struct A64Shift: u32 { 283 | const LSL = 0; 284 | const LSR = 1; 285 | const ASR = 2; 286 | const ROR = 3; 287 | } 288 | } 289 | 290 | // shift KHVal by KHVal according to ARM shifting types 291 | // caller needs to ensure shift is not out of range and provide semantics for out of 292 | // range shifts per ARM mandated 293 | pub fn do_shift( 294 | ctx: &mut Arm64GuestContext, 295 | dest: &Rc>, 296 | src: &Rc>, 297 | sf: bool, 298 | shift_type: A64Shift, 299 | shift_amount: &Rc>, 300 | ) { 301 | match shift_type { 302 | A64Shift::LSL => Op::push_shl(ctx, dest, src, shift_amount), 303 | A64Shift::LSR => Op::push_shr(ctx, dest, src, shift_amount), 304 | A64Shift::ASR => { 305 | if !sf { 306 | Op::push_extslq(ctx, dest, src); 307 | } 308 | Op::push_sar(ctx, dest, if sf { src } else { dest }, shift_amount) 309 | } 310 | A64Shift::ROR => { 311 | if sf { 312 | Op::push_rotr(ctx, dest, src, shift_amount); 313 | } else { 314 | let t0 = ctx.alloc_val(ValueType::U32); 315 | let t1 = ctx.alloc_val(ValueType::U32); 316 | Op::push_extrl(ctx, &t0, src); 317 | Op::push_extrl(ctx, &t1, shift_amount); 318 | Op::push_rotrl(ctx, &t0, &t0, &t1); 319 | Op::push_extulq(ctx, dest, &t0); 320 | } 321 | } 322 | _ => unreachable!(), 323 | } 324 | 325 | if !sf { 326 | Op::push_extulq(ctx, dest, dest); 327 | } 328 | } 329 | 330 | pub fn do_shift_imm( 331 | ctx: &mut Arm64GuestContext, 332 | dest: &Rc>, 333 | src: &Rc>, 334 | sf: bool, 335 | shift_type: A64Shift, 336 | shift_i: u32, 337 | ) { 338 | if sf { 339 | assert!(shift_i < 64); 340 | } else { 341 | assert!(shift_i < 32); 342 | } 343 | 344 | if shift_i == 0 { 345 | Op::push_mov(ctx, dest, src); 346 | } else { 347 | let shift_i = ctx.alloc_u64(shift_i as u64); 348 | do_shift(ctx, dest, src, sf, shift_type, &shift_i); 349 | } 350 | } 351 | 352 | pub fn do_ext_and_shift_reg( 353 | ctx: &mut Arm64GuestContext, 354 | dest: &Rc>, 355 | src: &Rc>, 356 | option: u32, 357 | shift: u32, 358 | ) { 359 | let extsize = extract(option, 0, 2); 360 | let is_signed = extract(option, 2, 1) == 1; 361 | 362 | (if is_signed { 363 | match extsize { 364 | 0 => Op::push_extsbq, 365 | 1 => Op::push_extslq, 366 | 2 => Op::push_extslq, 367 | 3 => Op::push_mov, 368 | _ => unreachable!(), 369 | } 370 | } else { 371 | match extsize { 372 | 0 => Op::push_extubq, 373 | 1 => Op::push_extulq, 374 | 2 => Op::push_extulq, 375 | 3 => Op::push_mov, 376 | _ => unreachable!(), 377 | } 378 | })(ctx, dest, src); 379 | 380 | if shift != 0 { 381 | let shift = ctx.alloc_u64(shift as u64); 382 | Op::push_shl(ctx, dest, dest, &shift); 383 | } 384 | } 385 | 386 | pub fn do_logic_cc( 387 | ctx: &mut Arm64GuestContext, 388 | sf: bool, 389 | result: &Rc>, 390 | ) { 391 | let (nf, zf, cf, vf) = get_flags(ctx); 392 | if sf { 393 | set_nz64(ctx, result); 394 | } else { 395 | Op::push_extrl(ctx, &zf, result); 396 | Op::push_mov(ctx, &nf, &zf); 397 | } 398 | let zero = ctx.alloc_u32(0); 399 | Op::push_mov(ctx, &cf, &zero); 400 | Op::push_mov(ctx, &vf, &zero); 401 | } 402 | 403 | fn bitfield_replicate(mut mask: u64, mut e: u32) -> u64 { 404 | assert_ne!(e, 0); 405 | 406 | while e < 64 { 407 | mask |= mask << e; 408 | e *= 2; 409 | } 410 | 411 | mask 412 | } 413 | 414 | pub fn logic_imm_decode_wmask(immn: u32, imms: u32, immr: u32) -> Option { 415 | assert!(immn < 2); 416 | assert!(imms < 64); 417 | assert!(immr < 64); 418 | 419 | /* The bit patterns we create here are 64 bit patterns which 420 | * are vectors of identical elements of size e = 2, 4, 8, 16, 32 or 421 | * 64 bits each. Each element contains the same value: a run 422 | * of between 1 and e-1 non-zero bits, rotated within the 423 | * element by between 0 and e-1 bits. 424 | * 425 | * The element size and run length are encoded into immn (1 bit) 426 | * and imms (6 bits) as follows: 427 | * 64 bit elements: immn = 1, imms = 428 | * 32 bit elements: immn = 0, imms = 0 : 429 | * 16 bit elements: immn = 0, imms = 10 : 430 | * 8 bit elements: immn = 0, imms = 110 : 431 | * 4 bit elements: immn = 0, imms = 1110 : 432 | * 2 bit elements: immn = 0, imms = 11110 : 433 | * Notice that immn = 0, imms = 11111x is the only combination 434 | * not covered by one of the above options; this is reserved. 435 | * Further, all-ones is a reserved pattern. 436 | * 437 | * In all cases the rotation is by immr % e (and immr is 6 bits). 438 | */ 439 | 440 | let len = 31 - (immn << 6 | !imms & 0x3f).leading_zeros(); 441 | if len < 1 { 442 | return None; 443 | } 444 | 445 | let e = 1 << len; 446 | let levels = e - 1; 447 | let s = imms & levels; 448 | let r = immr & levels; 449 | 450 | if s == levels { 451 | // mustn't be all-ones 452 | return None; 453 | } 454 | 455 | fn bitmask64(len: u32) -> u64 { 456 | assert!(len > 0 && len <= 64); 457 | !0u64 >> (64 - len as u64) 458 | } 459 | 460 | let mut mask = bitmask64(s + 1); 461 | if r != 0 { 462 | mask = (mask >> r as u64) | (mask << (e - r) as u64); 463 | mask &= bitmask64(e); 464 | } 465 | 466 | mask = bitfield_replicate(mask, e); 467 | 468 | Some(mask) 469 | } 470 | 471 | // check that FP/neon is enabled 472 | // if not enabled, the caller should not emit any code for the instruction 473 | pub fn fp_access_check(ctx: &mut Arm64GuestContext) -> bool { 474 | // FP not enabled yet, always disabled 475 | Op::push_trap(ctx, TrapOp::UNDEF_OPCODE, &Rc::clone(&ctx.pc)); 476 | 477 | false 478 | } 479 | -------------------------------------------------------------------------------- /src/guest/arm64/ldst.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::facility::*; 6 | use super::*; 7 | 8 | use log::*; 9 | use std::convert::TryInto; 10 | 11 | pub fn disas_ldst_pair( 12 | ctx: &mut Arm64GuestContext, 13 | insn: InsnType, 14 | ) -> Result<(), DisasException> { 15 | trace!("ldst_pair"); 16 | let rt = extract(insn, 0, 5) as usize; 17 | let rn = extract(insn, 5, 5) as usize; 18 | let rt2 = extract(insn, 10, 5) as usize; 19 | let index = extract(insn, 23, 2); 20 | let is_vector = extract(insn, 26, 1) == 1; 21 | let is_load = extract(insn, 22, 1) == 1; 22 | let opc = extract(insn, 30, 2); 23 | let size: u32; 24 | let postindex; 25 | let wback; 26 | 27 | let mut is_signed = false; 28 | if opc == 3 { 29 | return unallocated(ctx, insn); 30 | } 31 | 32 | if is_vector { 33 | size = 2 + opc; 34 | } else { 35 | size = 2 + extract(opc, 1, 1); 36 | is_signed = extract(opc, 0, 1) == 1; 37 | if !is_load && is_signed { 38 | return unallocated(ctx, insn); 39 | } 40 | } 41 | let offset = sextract(insn as i64, 15, 7) << (size as i64); 42 | 43 | match index { 44 | 0 => { 45 | // signed offset with non-temporal hint 46 | if is_signed { 47 | // no non-temporal hint version of LDPSW 48 | return unallocated(ctx, insn); 49 | } 50 | postindex = false; 51 | wback = false; 52 | } 53 | 1 => { 54 | // post-index 55 | postindex = true; 56 | wback = true; 57 | } 58 | 2 => { 59 | // signed offset, no wback 60 | postindex = false; 61 | wback = false; 62 | } 63 | 3 => { 64 | // pre-index 65 | postindex = false; 66 | wback = true; 67 | } 68 | _ => return Err(DisasException::Unexpected("unmatched index".to_owned())), 69 | } 70 | 71 | if rn == 31 { 72 | // sp 73 | check_sp_alignment(ctx); 74 | } 75 | 76 | let dirty_addr = read_cpu_reg_sp(ctx, rn, true); 77 | let offset_val = ctx.alloc_u64(offset.abs().try_into().unwrap()); 78 | let size = 1 << size as u64; 79 | let size_val = ctx.alloc_u64(size); 80 | if !postindex { 81 | (if offset >= 0 { 82 | Op::push_add 83 | } else { 84 | Op::push_sub 85 | })(ctx, &dirty_addr, &dirty_addr, &offset_val); 86 | } 87 | let clean_addr = clean_data_tbi(ctx, &dirty_addr); 88 | 89 | if is_vector { 90 | // TODO(jsteward) support for vector / fp 91 | return unallocated(ctx, insn); 92 | } else { 93 | let rt = ctx.reg(rt); 94 | let rt2 = ctx.reg(rt2); 95 | 96 | if is_load { 97 | // do not modify rt before recognizing any exception from the second load 98 | let tmp = ctx.alloc_val(ValueType::U64); 99 | do_ldst(ctx, is_load, is_signed, false, size, &tmp, &clean_addr); 100 | Op::push_add(ctx, &clean_addr, &clean_addr, &size_val); 101 | do_ldst(ctx, is_load, is_signed, false, size, &rt2, &clean_addr); 102 | Op::push_mov(ctx, &rt, &tmp); 103 | } else { 104 | do_ldst(ctx, is_load, is_signed, false, size, &rt, &clean_addr); 105 | Op::push_add(ctx, &clean_addr, &clean_addr, &size_val); 106 | do_ldst(ctx, is_load, is_signed, false, size, &rt2, &clean_addr); 107 | } 108 | } 109 | 110 | if wback { 111 | if postindex { 112 | (if offset >= 0 { 113 | Op::push_add 114 | } else { 115 | Op::push_sub 116 | })(ctx, &dirty_addr, &dirty_addr, &offset_val); 117 | } 118 | Op::push_mov(ctx, &ctx.reg_sp(rn), &dirty_addr); 119 | } 120 | 121 | Ok(()) 122 | } 123 | 124 | #[allow(unused)] 125 | fn check_sp_alignment(ctx: &mut Arm64GuestContext) { 126 | /* sp alignment check as specified in AArch64 omitted */ 127 | } 128 | 129 | pub fn disas_ldst_reg_imm9( 130 | ctx: &mut Arm64GuestContext, 131 | insn: InsnType, 132 | opc: u32, 133 | mut size: u32, 134 | rt: usize, 135 | is_vector: bool, 136 | ) -> Result<(), DisasException> { 137 | trace!("ldst_reg_imm9"); 138 | let rn = extract(insn, 5, 5) as usize; 139 | let idx = extract(insn, 10, 2); 140 | let imm9 = sextract(insn as i64, 12, 9); 141 | 142 | let is_unpriv = idx == 2; 143 | let mut is_signed = false; 144 | let mut is_extended = false; 145 | 146 | let is_store; 147 | let post_index; 148 | let writeback; 149 | 150 | if is_vector { 151 | size |= (opc & 2) << 1; 152 | if size > 4 || is_unpriv { 153 | return unallocated(ctx, insn); 154 | } 155 | is_store = (opc & 1) == 0; 156 | if !fp_access_check(ctx) { 157 | return Ok(()); 158 | } 159 | } else { 160 | if size == 3 && opc == 2 { 161 | // prfm - prefetch 162 | if idx != 0 { 163 | return unallocated(ctx, insn); 164 | } 165 | return Ok(()); 166 | } 167 | if opc == 3 && size > 1 { 168 | return unallocated(ctx, insn); 169 | } 170 | is_store = opc == 0; 171 | is_signed = extract(opc, 1, 1) == 1; 172 | is_extended = size < 3 && extract(opc, 0, 1) == 1; 173 | } 174 | 175 | match idx { 176 | 0 | 2 => { 177 | post_index = false; 178 | writeback = false; 179 | } 180 | 1 => { 181 | post_index = true; 182 | writeback = true; 183 | } 184 | 3 => { 185 | post_index = false; 186 | writeback = true; 187 | } 188 | _ => unreachable!(), 189 | } 190 | 191 | if rn == 31 { 192 | check_sp_alignment(ctx); 193 | } 194 | 195 | let dirty_addr = read_cpu_reg_sp(ctx, rn, true); 196 | let imm_val = ctx.alloc_u64(imm9.abs().try_into().unwrap()); 197 | if !post_index { 198 | (if imm9 >= 0 { 199 | Op::push_add 200 | } else { 201 | Op::push_sub 202 | })(ctx, &dirty_addr, &dirty_addr, &imm_val); 203 | } 204 | let clean_addr = clean_data_tbi(ctx, &dirty_addr); 205 | 206 | let size = 1 << size as u64; // our ld / st accepts bytes 207 | 208 | if is_vector { 209 | // TODO(jsteward) implement vector/SIMD 210 | return unallocated(ctx, insn); 211 | } else { 212 | let rt = ctx.reg(rt); 213 | 214 | do_ldst( 215 | ctx, 216 | !is_store, 217 | is_signed, 218 | is_extended, 219 | size, 220 | &rt, 221 | &clean_addr, 222 | ); 223 | } 224 | 225 | if writeback { 226 | if post_index { 227 | (if imm9 >= 0 { 228 | Op::push_add 229 | } else { 230 | Op::push_sub 231 | })(ctx, &dirty_addr, &dirty_addr, &imm_val); 232 | } 233 | Op::push_mov(ctx, &ctx.reg_sp(rn), &dirty_addr); 234 | } 235 | 236 | Ok(()) 237 | } 238 | 239 | pub fn disas_ldst_atomic( 240 | ctx: &mut Arm64GuestContext, 241 | insn: InsnType, 242 | size: u32, 243 | rt: usize, 244 | is_vector: bool, 245 | ) -> Result<(), DisasException> { 246 | Err(DisasException::Unexpected( 247 | "ldst_atomic work in progress".to_owned(), 248 | )) 249 | } 250 | 251 | pub fn disas_ldst_reg_offset( 252 | ctx: &mut Arm64GuestContext, 253 | insn: InsnType, 254 | opc: u32, 255 | size: u32, 256 | rt: usize, 257 | is_vector: bool, 258 | ) -> Result<(), DisasException> { 259 | Err(DisasException::Unexpected( 260 | "ldst_reg_offset work in progress".to_owned(), 261 | )) 262 | } 263 | 264 | pub fn disas_ldst_pac( 265 | ctx: &mut Arm64GuestContext, 266 | insn: InsnType, 267 | size: u32, 268 | rt: usize, 269 | is_vector: bool, 270 | ) -> Result<(), DisasException> { 271 | Err(DisasException::Unexpected( 272 | "ldst_pac work in progress".to_owned(), 273 | )) 274 | } 275 | 276 | pub fn disas_ldst_reg_unsigned_imm( 277 | ctx: &mut Arm64GuestContext, 278 | insn: InsnType, 279 | opc: u32, 280 | size: u32, 281 | rt: usize, 282 | is_vector: bool, 283 | ) -> Result<(), DisasException> { 284 | trace!("ldst_reg_unsigned_imm"); 285 | let rn = extract(insn, 5, 5); 286 | let imm12 = extract(insn, 10, 12); 287 | 288 | let is_store; 289 | let is_signed; 290 | let mut is_extended = false; 291 | 292 | let mut size = size; 293 | 294 | if is_vector { 295 | is_signed = false; 296 | size |= (opc & 2) << 1; 297 | if size > 4 { 298 | return unallocated(ctx, insn); 299 | } 300 | is_store = extract(opc, 0, 1) == 0; 301 | if !fp_access_check(ctx) { 302 | return Ok(()); 303 | } 304 | } else { 305 | if size == 3 && opc == 2 { 306 | // prfm - prefetch, ignore 307 | return Ok(()); 308 | } 309 | if opc == 3 && size > 1 { 310 | return unallocated(ctx, insn); 311 | } 312 | is_store = opc == 0; 313 | is_signed = extract(opc, 1, 1) == 1; 314 | is_extended = size < 3 && extract(opc, 0, 1) == 1; 315 | } 316 | 317 | if rn == 31 { 318 | check_sp_alignment(ctx); 319 | } 320 | trace!("Reading SP"); 321 | let dirty_addr = read_cpu_reg_sp(ctx, rn as usize, true); 322 | let offset = ctx.alloc_u64((imm12 << size) as u64); 323 | trace!("Calculating offset"); 324 | Op::push_add(ctx, &dirty_addr, &dirty_addr, &offset); 325 | trace!("Cleaning TBI"); 326 | let clean_addr = clean_data_tbi(ctx, &dirty_addr); 327 | 328 | let size = 1 << size as u64; 329 | 330 | if is_vector { 331 | // TODO(jsteward) implement vector/SIMD 332 | return unallocated(ctx, insn); 333 | } else { 334 | let rt = ctx.reg(rt as usize); 335 | // FIXME we skipped the ISS (Instruction-Specific Syndrome) calculation 336 | trace!("Performing ldst"); 337 | do_ldst( 338 | ctx, 339 | !is_store, 340 | is_signed, 341 | is_extended, 342 | size, 343 | &rt, 344 | &clean_addr, 345 | ); 346 | } 347 | 348 | Ok(()) 349 | } 350 | 351 | pub fn disas_ldst_reg( 352 | ctx: &mut Arm64GuestContext, 353 | insn: InsnType, 354 | ) -> Result<(), DisasException> { 355 | trace!("ldst_reg"); 356 | let rt = extract(insn, 0, 5) as usize; 357 | let opc = extract(insn, 22, 2); 358 | let is_vector = extract(insn, 26, 1) == 1; 359 | let size = extract(insn, 30, 2); 360 | 361 | match extract(insn, 24, 2) { 362 | 0 => { 363 | if extract(insn, 21, 1) == 0 { 364 | disas_ldst_reg_imm9(ctx, insn, opc, size, rt, is_vector) 365 | } else { 366 | match extract(insn, 10, 2) { 367 | 0 => disas_ldst_atomic(ctx, insn, size, rt, is_vector), 368 | 2 => disas_ldst_reg_offset(ctx, insn, opc, size, rt, is_vector), 369 | _ => disas_ldst_pac(ctx, insn, size, rt, is_vector), 370 | } 371 | } 372 | } 373 | 1 => disas_ldst_reg_unsigned_imm(ctx, insn, opc, size, rt, is_vector), 374 | _ => unallocated(ctx, insn), 375 | } 376 | } 377 | 378 | disas_stub![ 379 | ldst_excl, 380 | ld_lit, 381 | ldst_multiple_struct, 382 | ldst_single_struct, 383 | ldst_ldapr_stlr 384 | ]; 385 | -------------------------------------------------------------------------------- /src/guest/arm64/sve.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | -------------------------------------------------------------------------------- /src/guest/arm64/system.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | 7 | pub fn disas_system( 8 | ctx: &mut Arm64GuestContext, 9 | insn: InsnType, 10 | ) -> Result<(), DisasException> { 11 | let l = extract(insn, 21, 1) == 1; 12 | let op0 = extract(insn, 19, 2); 13 | let op1 = extract(insn, 16, 3); 14 | let crn = extract(insn, 12, 4); 15 | let crm = extract(insn, 8, 4); 16 | let op2 = extract(insn, 5, 3); 17 | let rt = extract(insn, 0, 5) as usize; 18 | 19 | if op0 == 0 { 20 | if l || rt != 31 { 21 | return unallocated(ctx, insn); 22 | } 23 | (match crn { 24 | 2 => handle_hint, 25 | 3 => handle_sync, 26 | 4 => handle_msr_i, 27 | _ => return unallocated(ctx, insn), 28 | })(ctx, insn, op1, op2, crm) 29 | } else { 30 | handle_sys(ctx, insn, l, op0, op1, op2, crn, crm, rt) 31 | } 32 | } 33 | 34 | fn handle_hint( 35 | ctx: &mut Arm64GuestContext, 36 | insn: InsnType, 37 | op1: u32, 38 | op2: u32, 39 | crm: u32, 40 | ) -> Result<(), DisasException> { 41 | let selector = crm << 3 | op2; 42 | 43 | if op1 != 3 { 44 | return unallocated(ctx, insn); 45 | } 46 | 47 | // TODO(jsteward) figure out proper behaviors for these instructions in EL0 48 | match selector { 49 | 0 => {} // nop 50 | 3 => {} // wfi 51 | 1 => {} // yield 52 | 2 => {} // wfe 53 | 4 | 5 => {} // sev / sevl 54 | _ => {} // pauth, etc. specified as nop-equivalent 55 | } 56 | 57 | Ok(()) 58 | } 59 | 60 | fn handle_sync( 61 | ctx: &mut Arm64GuestContext, 62 | insn: InsnType, 63 | op1: u32, 64 | op2: u32, 65 | crm: u32, 66 | ) -> Result<(), DisasException> { 67 | Err(DisasException::Unexpected( 68 | "handle_sync work in progress".to_owned(), 69 | )) 70 | } 71 | 72 | fn handle_msr_i( 73 | ctx: &mut Arm64GuestContext, 74 | insn: InsnType, 75 | op1: u32, 76 | op2: u32, 77 | crm: u32, 78 | ) -> Result<(), DisasException> { 79 | Err(DisasException::Unexpected( 80 | "handle_msr_i work in progress".to_owned(), 81 | )) 82 | } 83 | 84 | fn handle_sys( 85 | ctx: &mut Arm64GuestContext, 86 | insn: InsnType, 87 | isread: bool, 88 | op0: u32, 89 | op1: u32, 90 | op2: u32, 91 | crn: u32, 92 | crm: u32, 93 | rt: usize, 94 | ) -> Result<(), DisasException> { 95 | Err(DisasException::Unexpected( 96 | "handle_sys work in progress".to_owned(), 97 | )) 98 | } 99 | -------------------------------------------------------------------------------- /src/host.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::ir::storage::*; 6 | use crate::guest::{DisasException, TranslationBlock}; 7 | use crate::runtime::{GuestMap, TrapHandler}; 8 | use std::rc::Weak; 9 | 10 | /// The DumpIR dummy backend for IR printout. 11 | pub mod dump_ir; 12 | /// The LLVM backend. 13 | pub mod llvm; 14 | 15 | /// An emitted block that can be executed on the host. 16 | pub trait HostBlock { 17 | /// Run from start of block. 18 | unsafe fn execute(&self); 19 | } 20 | 21 | /// Code generator functions to be invoked from the runtime. 22 | pub trait HostContext { 23 | /// Storage type for the IR registers. 24 | type StorageType: HostStorage; 25 | /// Emitted block type. 26 | type BlockType: HostBlock; 27 | /// Emit host code block for a [TranslationBlock](../guest/struct.TranslationBlock.html). 28 | /// 29 | /// Parameters: 30 | /// - `tb`: translation block to emit. 31 | /// - `name`: name of the emitted block. 32 | /// - `exception`: reason of translation termination from frontend. 33 | fn emit_block( 34 | &mut self, 35 | tb: TranslationBlock, 36 | name: &str, 37 | tracking: &[Weak>], 38 | exception: Option, 39 | ) -> Self::BlockType; 40 | 41 | /// Initialize the global backend context. 42 | fn init(guest_vm: GuestMap, handler: TrapHandler); 43 | /// Retrieve the global backend context. 44 | fn get() -> &'static mut Self; 45 | 46 | /// Allocate new block in backend for code generation. 47 | /// 48 | /// Must be called before any of the following for an emitted block. 49 | fn push_block(&mut self, name: &str, create_func: bool); 50 | 51 | /// Create a label value. 52 | fn make_label(&self) -> Self::StorageType; 53 | /// Create a `u32` value. Backends may implement caching to avoid allocating duplicate values. 54 | fn make_u32(&self, v: u32) -> Self::StorageType; 55 | /// Create a `u64` value. Backends may implement caching to avoid allocating duplicate values. 56 | fn make_u64(&self, v: u64) -> Self::StorageType; 57 | /// Create a `f64` value. Backends may implement caching to avoid allocating duplicate values. 58 | fn make_f64(&self, v: f64) -> Self::StorageType; 59 | /// Create a named value for fixed registers. 60 | fn make_named(&self, name: String, ty: ValueType) -> Self::StorageType; 61 | 62 | /// Backend-specific routine for handling traps. 63 | /// 64 | /// Backend-irrelevant parts should go into `runtime::trap_handler`. 65 | fn handle_trap(&mut self); 66 | } 67 | -------------------------------------------------------------------------------- /src/host/dump_ir.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | // This is a dummy host that simply dumps the IR without executing them 6 | 7 | use crate::guest::{DisasException, TranslationBlock}; 8 | use crate::host::*; 9 | use crate::ir::storage::*; 10 | use crate::runtime::{GuestMap, TrapHandler}; 11 | use std::cell::RefCell; 12 | use std::fmt::{Display, Error, Formatter}; 13 | use std::rc::Weak; 14 | 15 | /// Dummy context for IR printout. 16 | pub struct DumpIRHostContext { 17 | label_counter: RefCell, 18 | } 19 | 20 | #[derive(PartialEq)] 21 | /// Dummy storage that only notes input constants for IR printing purposes. 22 | /// 23 | /// No actual memory allocation for intermediate results will happen; they are denoted with the 24 | /// `Unassigned` variant. 25 | pub enum DumpIRHostStorage { 26 | Label(u64), 27 | ImmU32(u32), 28 | ImmU64(u64), 29 | ImmF64(f64), 30 | Named(String), 31 | Unassigned, 32 | } 33 | 34 | impl Default for DumpIRHostStorage { 35 | fn default() -> Self { 36 | DumpIRHostStorage::Unassigned 37 | } 38 | } 39 | 40 | impl Display for DumpIRHostStorage { 41 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 42 | match self { 43 | DumpIRHostStorage::Label(n) => write!(f, "L{}", n), 44 | DumpIRHostStorage::Named(name) => write!(f, "${}", name), 45 | DumpIRHostStorage::ImmF64(v) => write!(f, "#{}", v), 46 | DumpIRHostStorage::ImmU64(v) => write!(f, "#{:#x}", v), 47 | DumpIRHostStorage::ImmU32(v) => write!(f, "#{:#x}", v), 48 | // temporaries should use the value hash directly 49 | DumpIRHostStorage::Unassigned => Err(Error), 50 | } 51 | } 52 | } 53 | 54 | impl HostStorage for DumpIRHostStorage { 55 | type HostContext = DumpIRHostContext; 56 | 57 | fn try_as_u32(&self) -> Option { 58 | if let &DumpIRHostStorage::ImmU32(v) = self { 59 | Some(v) 60 | } else { 61 | None 62 | } 63 | } 64 | 65 | fn try_as_u64(&self) -> Option { 66 | if let &DumpIRHostStorage::ImmU64(v) = self { 67 | Some(v) 68 | } else { 69 | None 70 | } 71 | } 72 | 73 | fn try_as_f64(&self) -> Option { 74 | if let &DumpIRHostStorage::ImmF64(v) = self { 75 | Some(v) 76 | } else { 77 | None 78 | } 79 | } 80 | } 81 | 82 | impl HostBlock for String { 83 | /// Dump the string contents of the dummy generated block. 84 | unsafe fn execute(&self) { 85 | print!("{}", self); 86 | } 87 | } 88 | 89 | static mut DUMP_IR_CTX: Option = None; 90 | 91 | impl HostContext for DumpIRHostContext { 92 | /// Use dummy storage for IR registers. 93 | type StorageType = DumpIRHostStorage; 94 | /// Use strings that hold IR printout as emitted blocks. 95 | type BlockType = String; 96 | 97 | fn emit_block( 98 | &mut self, 99 | tb: TranslationBlock, 100 | _name: &str, 101 | _tracking: &[Weak>], 102 | _exception: Option, 103 | ) -> Self::BlockType { 104 | let mut ret = String::new(); 105 | for op in tb.ops.into_iter() { 106 | ret += &format!("{}\n", op); 107 | } 108 | ret 109 | } 110 | 111 | fn init(_: GuestMap, _: TrapHandler) { 112 | unsafe { 113 | DUMP_IR_CTX = Some(Self { 114 | label_counter: RefCell::new(0), 115 | }); 116 | } 117 | } 118 | 119 | fn get() -> &'static mut Self { 120 | unsafe { DUMP_IR_CTX.as_mut().unwrap() } 121 | } 122 | 123 | fn push_block(&mut self, name: &str, create_func: bool) { 124 | unimplemented!() 125 | } 126 | 127 | fn make_label(&self) -> Self::StorageType { 128 | let ret = DumpIRHostStorage::Label(*self.label_counter.borrow()); 129 | *self.label_counter.borrow_mut() += 1; 130 | ret 131 | } 132 | 133 | fn make_u32(&self, v: u32) -> Self::StorageType { 134 | DumpIRHostStorage::ImmU32(v) 135 | } 136 | 137 | fn make_u64(&self, v: u64) -> Self::StorageType { 138 | DumpIRHostStorage::ImmU64(v) 139 | } 140 | 141 | fn make_f64(&self, v: f64) -> Self::StorageType { 142 | DumpIRHostStorage::ImmF64(v) 143 | } 144 | 145 | fn make_named(&self, name: String, ty: ValueType) -> Self::StorageType { 146 | DumpIRHostStorage::Named(name) 147 | } 148 | 149 | fn handle_trap(&mut self) { 150 | unimplemented!() 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/host/llvm.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use inkwell::builder::Builder; 6 | use inkwell::context::Context; 7 | use inkwell::execution_engine::{ExecutionEngine, JitFunction}; 8 | use inkwell::module::{Linkage, Module}; 9 | use inkwell::targets::{InitializationConfig, Target}; 10 | use inkwell::types::{FloatType, FunctionType, IntType}; 11 | use inkwell::values::{BasicValue, BasicValueEnum, FloatValue, GlobalValue, IntValue}; 12 | use inkwell::{AddressSpace, OptimizationLevel}; 13 | 14 | use log::*; 15 | use std::cell::RefCell; 16 | use std::collections::{HashMap, VecDeque}; 17 | use std::fmt::{Display, Error, Formatter}; 18 | use std::rc::{Rc, Weak}; 19 | 20 | use crate::guest::*; 21 | use crate::host::*; 22 | use crate::ir::op::*; 23 | use crate::ir::storage::*; 24 | use crate::runtime::*; 25 | use bitflags::_core::cell::RefMut; 26 | 27 | type GuestFunc = unsafe extern "C" fn(); 28 | 29 | /// Code generator context for the LLVM backend. 30 | pub struct LLVMHostContext<'ctx> { 31 | context: &'ctx Context, 32 | // FIXME(jsteward): how to recycle these? 33 | modules: Vec>, 34 | builder: Builder<'ctx>, 35 | execution_engine: Option>, 36 | fn_type: Option>, 37 | i32_type: Option>, 38 | i64_type: Option>, 39 | f64_type: Option>, 40 | handler_type: Option>, 41 | guest_vm: GuestMap, 42 | handler: TrapHandler, 43 | global_map: RefCell, Option>>>, 44 | dump_reg_func: Option>, 45 | } 46 | 47 | #[derive(Debug, PartialEq)] 48 | /// LLVM Storage for IR registers. 49 | /// 50 | /// Largely resembles the LLVM value categories. Per the SSA semantics, assigned values cannot be 51 | /// reassigned; a new one needs to be created instead. 52 | pub enum LLVMHostStorage<'ctx> { 53 | /// A not-yet used register. 54 | Empty, 55 | /// A fixed register. 56 | Global(GlobalValue<'ctx>), 57 | /// Assigned int temporary value. 58 | IntV(IntValue<'ctx>), 59 | /// Assigned float temporary value. 60 | FloatV(FloatValue<'ctx>), 61 | } 62 | 63 | impl Default for LLVMHostStorage<'_> { 64 | fn default() -> Self { 65 | // default unallocated storage for temporaries 66 | LLVMHostStorage::Empty 67 | } 68 | } 69 | 70 | impl Display for LLVMHostStorage<'_> { 71 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 72 | match self { 73 | LLVMHostStorage::Empty => write!(f, "_"), 74 | LLVMHostStorage::Global(v) => write!(f, "{}", v.get_name().to_str().unwrap()), 75 | LLVMHostStorage::IntV(v) => write!(f, "{}", v.print_to_string()), 76 | LLVMHostStorage::FloatV(v) => write!(f, "{}", v.print_to_string()), 77 | } 78 | } 79 | } 80 | 81 | impl HostStorage for LLVMHostStorage<'static> { 82 | type HostContext = LLVMHostContext<'static>; 83 | 84 | fn try_as_u32(&self) -> Option { 85 | if let LLVMHostStorage::IntV(lv) = self { 86 | lv.get_zero_extended_constant().map(|x| x as u32) 87 | } else { 88 | None 89 | } 90 | } 91 | 92 | fn try_as_u64(&self) -> Option { 93 | if let LLVMHostStorage::IntV(lv) = self { 94 | lv.get_zero_extended_constant() 95 | } else { 96 | None 97 | } 98 | } 99 | 100 | fn try_as_f64(&self) -> Option { 101 | if let LLVMHostStorage::FloatV(lv) = self { 102 | lv.get_constant().map(|x| x.0) 103 | } else { 104 | None 105 | } 106 | } 107 | } 108 | 109 | impl HostBlock for JitFunction<'_, GuestFunc> { 110 | /// Execute the generated LLVM JIT function. 111 | unsafe fn execute(&self) { 112 | self.call() 113 | } 114 | } 115 | 116 | mod codegen; 117 | 118 | static mut LLVM_CTX: Option = None; 119 | static REG_INIT: u64 = 0; 120 | static REG_INIT_FP: f64 = 0.0; 121 | 122 | impl LLVMHostContext<'static> { 123 | fn store_context(&mut self) { 124 | // store cached values back into global 125 | for (k, v) in self.global_map.borrow_mut().iter_mut() { 126 | if let Some(iv) = v { 127 | debug!( 128 | "Emitting store {} -> {}", 129 | iv.print_to_string(), 130 | k.get_name().to_str().unwrap() 131 | ); 132 | self.builder.build_store(k.as_pointer_value(), *iv); 133 | *v = None; 134 | } 135 | } 136 | } 137 | 138 | fn dump_modules(&self) { 139 | for x in &self.modules { 140 | x.print_to_stderr(); 141 | } 142 | } 143 | 144 | fn dump_reg(&mut self) { 145 | // check that all values are stored back into global 146 | for (k, v) in self.global_map.borrow().iter() { 147 | assert_eq!(*v, None, "found leaked value for global {:?}", k); 148 | } 149 | 150 | if let None = self.dump_reg_func { 151 | // build func 152 | let name = "trap_handler"; 153 | self.push_block(name, true); 154 | 155 | let module = self.modules.last().unwrap(); 156 | 157 | let printf = module.get_function("printf").unwrap_or_else(|| { 158 | let char_ptr_type = self.context.i8_type().ptr_type(AddressSpace::Generic); 159 | let func_type = self 160 | .context 161 | .i32_type() 162 | .fn_type(&[char_ptr_type.into()], true); 163 | module.add_function("printf", func_type, Some(Linkage::External)) 164 | }); 165 | 166 | let mut format_string = String::new(); 167 | let mut tagged_args = Vec::new(); 168 | for (k, _) in self.global_map.borrow().iter() { 169 | let name = String::from(k.get_name().to_str().unwrap()); 170 | let val = self.builder.build_load(k.as_pointer_value(), ""); 171 | 172 | tagged_args.push((name, val)); 173 | } 174 | tagged_args.sort_by_key(|(n, _)| n.to_owned()); 175 | let (names, mut args): (Vec<_>, VecDeque<_>) = tagged_args.into_iter().unzip(); 176 | let names = names 177 | .iter() 178 | .map(|n| format!("{}=0x%016lx", n)) 179 | .collect::>(); 180 | let mut format_string: String = names 181 | .chunks(4) 182 | .map(|c| c.join("\t")) 183 | .collect::>() 184 | .join("\n"); 185 | 186 | format_string.push('\n'); 187 | 188 | args.push_front(BasicValueEnum::from( 189 | self.builder 190 | .build_global_string_ptr(&format_string, "") 191 | .as_pointer_value(), 192 | )); 193 | let args = args.into_iter().collect::>(); 194 | 195 | self.builder.build_call(printf, args.as_slice(), ""); 196 | self.builder.build_return(None); 197 | 198 | unsafe { 199 | let f: JitFunction = self 200 | .execution_engine 201 | .as_ref() 202 | .unwrap() 203 | .get_function(name) 204 | .unwrap(); 205 | self.dump_reg_func = Some(f); 206 | } 207 | } 208 | 209 | unsafe { self.dump_reg_func.as_ref().unwrap().call() } 210 | } 211 | } 212 | 213 | impl HostContext for LLVMHostContext<'static> { 214 | /// Use LLVM values for IR register storage. 215 | type StorageType = LLVMHostStorage<'static>; 216 | /// Use LLVM JIT function as emitted block. 217 | type BlockType = JitFunction<'static, GuestFunc>; 218 | 219 | fn emit_block( 220 | &mut self, 221 | tb: TranslationBlock, 222 | name: &str, 223 | tracking: &[Weak>], 224 | exception: Option, 225 | ) -> Self::BlockType { 226 | // consume TB 227 | for op in tb.ops.into_iter() { 228 | debug!("Emitting {}", op); 229 | self.dispatch(op); 230 | } 231 | 232 | // end block, insert return 233 | self.builder.build_return(None); 234 | 235 | unsafe { 236 | self.execution_engine 237 | .as_ref() 238 | .unwrap() 239 | .get_function(name) 240 | .expect("failed to get function from JIT engine") 241 | } 242 | } 243 | 244 | fn init(guest_vm: GuestMap, handler: TrapHandler) { 245 | // FIXME(jsteward): there should be a better way to do this (without leaking) 246 | let context = Box::new(Context::create()); 247 | let context = Box::leak(context); 248 | 249 | unsafe { 250 | LLVM_CTX = Some(Self { 251 | context, 252 | modules: Vec::new(), 253 | builder: context.create_builder(), 254 | execution_engine: None, 255 | fn_type: None, 256 | i32_type: None, 257 | i64_type: None, 258 | f64_type: None, 259 | handler_type: None, 260 | guest_vm, 261 | handler, 262 | global_map: Default::default(), 263 | dump_reg_func: None, 264 | }); 265 | 266 | LLVM_CTX.as_mut().unwrap().fn_type = Some( 267 | LLVM_CTX 268 | .as_mut() 269 | .unwrap() 270 | .context 271 | .void_type() 272 | .fn_type(&[], false), 273 | ); 274 | 275 | LLVM_CTX.as_mut().unwrap().i32_type = 276 | Some(LLVM_CTX.as_mut().unwrap().context.i32_type()); 277 | LLVM_CTX.as_mut().unwrap().i64_type = 278 | Some(LLVM_CTX.as_mut().unwrap().context.i64_type()); 279 | 280 | let i64_type = LLVM_CTX.as_mut().unwrap().i64_type.unwrap(); 281 | 282 | LLVM_CTX.as_mut().unwrap().f64_type = 283 | Some(LLVM_CTX.as_mut().unwrap().context.f64_type()); 284 | LLVM_CTX.as_mut().unwrap().handler_type = Some( 285 | LLVM_CTX 286 | .as_mut() 287 | .unwrap() 288 | .context 289 | .void_type() 290 | .fn_type(&[i64_type.into(), i64_type.into()], false), 291 | ); 292 | 293 | // create default module for guest fixed register initializer 294 | LLVM_CTX.as_mut().unwrap().push_block("default", false); 295 | } 296 | } 297 | 298 | fn get() -> &'static mut Self { 299 | unsafe { LLVM_CTX.as_mut().unwrap() } 300 | } 301 | 302 | fn push_block(&mut self, name: &str, create_func: bool) { 303 | self.modules.push(self.context.create_module(name)); 304 | let module = self.modules.last().expect("failed to create module"); 305 | 306 | if let None = self.execution_engine { 307 | self.execution_engine = Some( 308 | module 309 | .create_jit_execution_engine(OptimizationLevel::None) 310 | .expect("failed to create JIT engine"), 311 | ); 312 | } else { 313 | self.execution_engine 314 | .as_ref() 315 | .unwrap() 316 | .add_module(module) 317 | .expect("failed to add new module to existing engine"); 318 | } 319 | 320 | if create_func { 321 | let func = module.add_function(name, self.fn_type.unwrap(), None); 322 | 323 | let basic_block = self.context.append_basic_block(func, "entry"); 324 | self.builder.position_at_end(basic_block); 325 | } 326 | } 327 | 328 | fn make_label(&self) -> Self::StorageType { 329 | unimplemented!() 330 | } 331 | 332 | fn make_u32(&self, v: u32) -> Self::StorageType { 333 | unimplemented!() 334 | } 335 | 336 | fn make_u64(&self, v: u64) -> Self::StorageType { 337 | LLVMHostStorage::IntV(self.i64_type.unwrap().const_int(v, false)) 338 | } 339 | 340 | fn make_f64(&self, v: f64) -> Self::StorageType { 341 | unimplemented!() 342 | } 343 | 344 | fn make_named(&self, name: String, ty: ValueType) -> Self::StorageType { 345 | let module = self.modules.last().expect("failed to get current module"); 346 | let glb = match ty { 347 | ValueType::U32 => { 348 | let g = module.add_global(self.i32_type.unwrap(), None, name.as_ref()); 349 | g.set_initializer(&self.i32_type.unwrap().const_int(REG_INIT, false)); 350 | g 351 | } 352 | ValueType::U64 => { 353 | let g = module.add_global(self.i64_type.unwrap(), None, name.as_ref()); 354 | g.set_initializer(&self.i64_type.unwrap().const_int(REG_INIT, false)); 355 | g 356 | } 357 | ValueType::F64 => { 358 | let g = module.add_global(self.f64_type.unwrap(), None, name.as_ref()); 359 | g.set_initializer(&self.f64_type.unwrap().const_float(REG_INIT_FP)); 360 | g 361 | } 362 | _ => unreachable!(), 363 | }; 364 | // record global 365 | self.global_map.borrow_mut().insert(glb, None); 366 | LLVMHostStorage::Global(glb) 367 | } 368 | 369 | fn handle_trap(&mut self) { 370 | info!("Dumping registers"); 371 | self.dump_reg(); 372 | 373 | info!("Dumping modules generated so far"); 374 | self.dump_modules(); 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/host/llvm/codegen.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | use std::ops::Index; 7 | 8 | type Reg = Rc>>; 9 | 10 | macro_rules! read_value { 11 | ($self:expr, $rs:expr) => { 12 | match *$rs.storage.borrow() { 13 | LLVMHostStorage::Empty => panic!("trying to use empty value"), 14 | LLVMHostStorage::Global(v) => { 15 | // check if value has previously been read 16 | match $self 17 | .global_map 18 | .borrow_mut() 19 | .get_mut(&v) 20 | .expect("untracked global") 21 | { 22 | Some(cv) => *cv, 23 | o @ None => { 24 | let temp = $self 25 | .builder 26 | .build_load(v.as_pointer_value(), "") 27 | .into_int_value(); 28 | *o = Some(temp); 29 | temp 30 | } 31 | } 32 | } 33 | LLVMHostStorage::IntV(v) => v, 34 | _ => panic!("not implemented"), 35 | } 36 | }; 37 | } 38 | 39 | macro_rules! store_result { 40 | ($self:expr, $rd:expr, $result:expr) => { 41 | let mut rd_storage = $rd.storage.borrow_mut(); 42 | match *rd_storage { 43 | LLVMHostStorage::Empty => *rd_storage = LLVMHostStorage::IntV($result), 44 | LLVMHostStorage::Global(v) => { 45 | // store into cache area in global_map 46 | *$self 47 | .global_map 48 | .borrow_mut() 49 | .get_mut(&v) 50 | .expect("untracked global") = Some($result); 51 | } 52 | _ => panic!("ssa violation: trying to write to to initialized value"), 53 | } 54 | }; 55 | } 56 | 57 | impl CodeGen> for LLVMHostContext<'static> { 58 | fn gen_mov(&mut self, rd: Reg, rs1: Reg) { 59 | let result = read_value!(self, rs1); 60 | store_result!(self, rd, result); 61 | } 62 | 63 | fn gen_extrs(&mut self, rd: Reg, rs: Reg, ofs: Reg, len: Reg) { 64 | let i64_type = self.i64_type.unwrap(); 65 | let rs = read_value!(self, rs); 66 | 67 | let ofs = ofs.storage.borrow().try_as_u64().unwrap(); 68 | let len = len.storage.borrow().try_as_u64().unwrap(); 69 | let left_shift = i64_type.const_int(64 - len - ofs, false); 70 | let right_shift = i64_type.const_int(64 - len, false); 71 | 72 | let chop_high = self.builder.build_left_shift(rs, left_shift, ""); 73 | let result = self 74 | .builder 75 | .build_right_shift(chop_high, right_shift, true, ""); 76 | store_result!(self, rd, result); 77 | } 78 | 79 | fn gen_load(&mut self, rd: Reg, rs1: Reg, rs2: Reg) { 80 | let i64_type = self.i64_type.unwrap(); 81 | 82 | let rs1 = read_value!(self, rs1); 83 | let mem_op = rs2.storage.borrow().try_as_u64().unwrap(); 84 | let mem_op = MemOp::from_bits(mem_op).unwrap(); 85 | let size: u64 = mem_op.get_size(); 86 | let sign: bool = mem_op.get_sign(); 87 | 88 | // calculate real address = offset + guest 89 | let offset = self.guest_vm.borrow().as_ptr() as u64; 90 | let offset = i64_type.const_int(offset, false); 91 | let addr = self.builder.build_int_add(rs1, offset, ""); 92 | 93 | let ptr_type = match size { 94 | 1 => self.context.i8_type(), 95 | 2 => self.context.i16_type(), 96 | 4 => self.context.i32_type(), 97 | 8 => self.context.i64_type(), 98 | _ => unreachable!(), 99 | } 100 | .ptr_type(AddressSpace::Generic); 101 | 102 | let addr_ptr = self.builder.build_int_to_ptr(addr, ptr_type, ""); 103 | let word = self.builder.build_load(addr_ptr, "").into_int_value(); 104 | 105 | let result = if sign { 106 | self.builder.build_int_s_extend(word, i64_type, "") 107 | } else { 108 | self.builder.build_int_z_extend(word, i64_type, "") 109 | }; 110 | 111 | store_result!(self, rd, result); 112 | } 113 | 114 | fn gen_add(&mut self, rd: Reg, rs1: Reg, rs2: Reg) { 115 | let rs1 = read_value!(self, rs1); 116 | let rs2 = read_value!(self, rs2); 117 | 118 | let result = self.builder.build_int_add(rs1, rs2, ""); 119 | store_result!(self, rd, result); 120 | } 121 | 122 | fn gen_trap(&mut self, cause: Reg, val: Reg) { 123 | // store context before calling trap handler 124 | self.store_context(); 125 | 126 | let cause = read_value!(self, cause); 127 | let val = read_value!(self, val); 128 | 129 | let i64_type = self.i64_type.unwrap(); 130 | let handler_type = self.handler_type.unwrap(); 131 | let handler_ptr_type = handler_type.ptr_type(AddressSpace::Generic); 132 | let handler = i64_type 133 | .const_int(self.handler as u64, false) 134 | .const_to_pointer(handler_ptr_type); 135 | 136 | self.builder 137 | .build_call(handler, &[cause.into(), val.into()], ""); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/ir.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | extern crate paste; 6 | 7 | /// The IR operator definition. 8 | pub mod op; 9 | /// The IR register storage. 10 | pub mod storage; 11 | -------------------------------------------------------------------------------- /src/ir/op.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use crate::guest::DisasContext; 6 | use crate::ir::storage::{HostStorage, KHVal, MemOp, ValueType}; 7 | use macros::gen_ops; 8 | use std::fmt::{Display, Error, Formatter}; 9 | use std::rc::Rc; 10 | 11 | #[rustfmt::skip] 12 | // all types except for convert will be enforced to take arguments of the declared type 13 | // type mnemonic (q, w, d, ..) can be omitted if there is no ambiguity 14 | gen_ops! { 15 | ValueType::Label { 16 | /// Set `label` to current insertion point in translated block. 17 | custom: Setlbl, label; 18 | /// Conditional branch to label `dest`. 19 | /// 20 | /// Branches if `c1` and `c2` satisfies `cc`, or `c1 _cc_ c2`. 21 | custom: Brc, dest, c1, c2, cc; 22 | override_maker: Brc; 23 | }, 24 | ValueType::U64 { 25 | /// Basic unary operators for `U64` IR registers. 26 | /// 27 | /// Notable ones: 28 | /// - Mov: duplicate register (as in SSA paradigm) 29 | /// - Bswap: byte swap 30 | unary: Neg, Not, Mov, Bswap; 31 | /// Extend lower 32 bit (long) to full word (quad), unsigned (zero extension) or signed 32 | /// (sign extension). 33 | convert: ExtUlq, ExtSlq; 34 | /// Extend lower 16 bit (word) to full word (quad), unsigned (zero extension) or signed 35 | /// (sign extension). 36 | convert: ExtUwq, ExtSwq; 37 | /// Extend lower 8 bit (byte) to full word (quad), unsigned (zero extension) or signed 38 | /// (sign extension). 39 | convert: ExtUbq, ExtSbq; 40 | /// Basic binary arithmetic operators for `U64` IR registers. 41 | /// 42 | /// Notable ones: 43 | /// - Rem: take remainder, signed 44 | /// - Remu: take remainder, unsigned 45 | binary: Add, Sub, Mul, Div, Rem, Remu; 46 | /// Basic logical (bitwise) arithmetic operators for `U64` IR registers. 47 | /// 48 | /// Notable ones: 49 | /// - Andc: `!(a & b)` 50 | /// - Orc: `!(a | b)` 51 | /// - Clz: count leading zeroes 52 | /// - Ctz: count tail zeroes 53 | binary: And, Or, Xor, Andc, Eqv, Nand, Nor, Orc, Clz, Ctz; 54 | /// Bit shift/rotations for `U64` IR registers. 55 | /// 56 | /// Notable ones: 57 | /// - Shr: logical right shift (zero extension) 58 | /// - Sar: arithmetic right shift (sign extension) 59 | binary: Shl, Shr, Sar, Rotl, Rotr; 60 | /// Load and store for `U64` IR registers. 61 | /// 62 | /// Instruction format: 63 | /// - `rd`: register 64 | /// - `rs1`: memory access target 65 | /// - `rs2`: memory operation mode (see [`MemOp`](../storage/struct.MemOp.html)) 66 | binary: Load, Store; 67 | /// Trap to runtime. Refer to [`TrapOP`](struct.TrapOp.html) for trap cause and value 68 | /// definitions. 69 | custom: Trap, cause, val; 70 | /// Signed bitfield extraction. 71 | /// 72 | /// Instruction format: 73 | /// - `rd`: destination register 74 | /// - `rs`: source register 75 | /// - `ofs`: offset from LSB 76 | /// - `len`: length of extracted bitfield 77 | custom: ExtrU, rd, rs, ofs, len; 78 | /// Unsigned bitfield extraction. 79 | /// 80 | /// Instruction format: 81 | /// - `rd`: destination register 82 | /// - `rs`: source register 83 | /// - `ofs`: offset from LSB 84 | /// - `len`: length of extracted bitfield 85 | custom: ExtrS, rd, rs, ofs, len; 86 | /// Bitfield deposit. Overrides the destination bitfield inside `rs1` with contents from 87 | /// `rs2` while leaving the rest bits intact. 88 | /// 89 | /// Instruction format: 90 | /// - `rd`: destination register 91 | /// - `rs1`: source of bits outside bitfield 92 | /// - `rs2`: source of bits in bitfield 93 | /// - `ofs`: offset from LSB 94 | /// - `len`: length of bitfield 95 | custom: Depos, rd, rs1, rs2, ofs, len; 96 | /// Conditional set. 97 | /// 98 | /// Sets the LSB to 1 in `rd` if `c1` and `c2` satisfies `cc`, or `c1 _cc_ c2`, 0 otherwise. 99 | custom: Setc, rd, c1, c2, cc; 100 | /// Conditional move. 101 | /// 102 | /// Moves `rs1` into `rd` if `c1` and `c2` satisfies `cc`, or `c1 _cc_ c2`, `rs2` otherwise. 103 | custom: Movc, rd, rs1, rs2, c1, c2, cc; // rd = if c1 `cc` c2 then rs1 else rs2 104 | /// Double add fused into a single instruction for `U64` IR registers. 105 | /// 106 | /// `[rh:rl] = [ah:al] + [bh:bl]` 107 | custom: Add2, rl, rh, al, ah, bl, bh; 108 | override_maker: Mov; 109 | override_maker: Load, Store; // to accept MemOp 110 | override_maker: Setc, Movc; // to accept CondOp and to allow multiple types 111 | override_maker: Add, Sub, ExtUlq; // simple optimizations 112 | override_maker: Trap; // argument form, inject TB end 113 | override_maker: ExtrU, ExtrS, Depos; // to accept immediate value for ofs len 114 | }, 115 | ValueType::U32 { 116 | /// Basic unary operators for `U32` IR registers (`l` suffix). 117 | /// 118 | /// Notable ones: 119 | /// - Movl: duplicate register (as in SSA paradigm) 120 | unary: Negl, Movl; 121 | /// Extract lower and higher 32 bits into 64 bit results. 122 | convert: Extrl, Extrh; 123 | /// Basic binary arithmetic operators for `U32` IR registers (`l` suffix). 124 | binary: Subl; 125 | /// Basic logical (bitwise) arithmetic operators for `U32` IR registers (`l` suffix). 126 | binary: Andl, Orl, Xorl, Andcl; 127 | /// Bit shift/rotations for `U32` IR registers (`l` suffix). 128 | binary: Sarl, Rotrl; 129 | /// Double add fused into a single instruction for `U32` IR registers. 130 | /// 131 | /// `[rh:rl] = [ah:al] + [bh:bl]` 132 | custom: Add2l, rl, rh, al, ah, bl, bh; 133 | override_maker: Movl; 134 | }, 135 | ValueType::F64 { 136 | /// Basic unary operators for `F64` IR registers (`d` suffix). 137 | /// 138 | /// Notable ones: 139 | /// - Movd: duplicate register (as in SSA paradigm) 140 | unary: Movd; 141 | /// Basic binary arithmetic operators for `F64` IR registers (`d` suffix). 142 | binary: Addd, Subd, Muld, Divd; 143 | override_maker: Movd; 144 | } 145 | } 146 | 147 | // the bitfield is designed to support inverting condition or allowing equality 148 | // with only a single bit toggle. 149 | bitflags! { 150 | /// Condition codes for use in conditional operators. 151 | pub struct CondOp: u64 { 152 | // sign-irrelevant 153 | const NEVER = 0b0000; 154 | const ALWAYS = 0b0001; 155 | const EQ = 0b1000; 156 | const NE = 0b1001; 157 | // signed 158 | const LT = 0b0010; 159 | const GE = 0b0011; 160 | const LE = 0b1010; 161 | const GT = 0b1011; 162 | // unsigned 163 | const LTU = 0b0100; 164 | const GEU = 0b0101; 165 | const LEU = 0b1100; 166 | const GTU = 0b1101; 167 | } 168 | } 169 | 170 | impl CondOp { 171 | pub fn invert(&mut self) { 172 | self.bits = self.bits ^ 1; 173 | } 174 | } 175 | 176 | bitflags! { 177 | /// Encoding for different trap causes. 178 | pub struct TrapOp: u64 { 179 | /// The next block to execute is not known and requires lookup by the runtime. 180 | /// 181 | /// Value meaning: start address of the next block to be executed. 182 | const LOOKUP_TB = 0; 183 | /// The corresponding guest instruction is an undefined instruction. 184 | /// 185 | /// Value meaning: guest PC of the faulty instruction. 186 | const UNDEF_OPCODE = 1; 187 | /// The guest is attempting to perform an impossible memory access. 188 | /// 189 | /// Note that this only captures faulty addresses during the disassembly phase, e.g. 190 | /// destinations that are outside of the guest virtual memory space (see [`GUEST_SIZE`]()). 191 | /// 192 | /// Value meaning: guest address of the faulty memory access. 193 | const ACCESS_FAULT = 2; 194 | /// The guest is attempting to perform a system call. 195 | /// 196 | /// Value meaning: host pointer to the syscall data block. 197 | const SYSCALL = 3; 198 | /// The guest is attempting to perform a dynamically-linked function call. 199 | /// 200 | /// Value meaning: guest address of the dynamic call site. 201 | const DYNAMIC = 4; 202 | } 203 | } 204 | 205 | impl Display for TrapOp { 206 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 207 | let s = match *self { 208 | TrapOp::LOOKUP_TB => "lookup_tb", 209 | TrapOp::UNDEF_OPCODE => "undef_opcode", 210 | TrapOp::ACCESS_FAULT => "access_fault", 211 | TrapOp::SYSCALL => "syscall", 212 | TrapOp::DYNAMIC => "dynamic", 213 | _ => unreachable!(), 214 | }; 215 | 216 | write!(f, "{}", s) 217 | } 218 | } 219 | 220 | // optimizations when creating Op 221 | mod opt; 222 | // fused Ops or those with a different interface 223 | mod meta; 224 | -------------------------------------------------------------------------------- /src/ir/op/meta.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | 7 | use log::*; 8 | 9 | impl Op { 10 | pub fn push_load( 11 | ctx: &mut impl DisasContext, 12 | rd: &Rc>, 13 | addr: &Rc>, 14 | mem_op: MemOp, 15 | ) { 16 | assert_eq!(rd.ty, ValueType::U64); 17 | assert_eq!(addr.ty, ValueType::U64); 18 | let mem_op = ctx.alloc_u64(mem_op.bits()); 19 | Op::_push_load(ctx, rd, addr, &mem_op); 20 | } 21 | 22 | pub fn push_store( 23 | ctx: &mut impl DisasContext, 24 | rd: &Rc>, 25 | addr: &Rc>, 26 | mem_op: MemOp, 27 | ) { 28 | assert_eq!(rd.ty, ValueType::U64); 29 | assert_eq!(addr.ty, ValueType::U64); 30 | let mem_op = ctx.alloc_u64(mem_op.bits()); 31 | Op::_push_store(ctx, rd, addr, &mem_op); 32 | } 33 | 34 | pub fn push_setc( 35 | ctx: &mut impl DisasContext, 36 | rd: &Rc>, 37 | c1: &Rc>, 38 | c2: &Rc>, 39 | cc: CondOp, 40 | ) { 41 | let cc = ctx.alloc_u64(cc.bits()); 42 | assert_eq!(c1.ty, c2.ty); 43 | // we can't use the default impl due to type violations 44 | ctx.push_op(match rd.ty { 45 | ValueType::U64 | ValueType::U32 => Op::Setc { 46 | rd: Rc::clone(rd), 47 | c1: Rc::clone(c1), 48 | c2: Rc::clone(c2), 49 | cc, 50 | }, 51 | _ => unreachable!("setc only accepts u32 and u64 destination"), 52 | }); 53 | } 54 | 55 | pub fn push_movc( 56 | ctx: &mut impl DisasContext, 57 | rd: &Rc>, 58 | rs1: &Rc>, 59 | rs2: &Rc>, 60 | c1: &Rc>, 61 | c2: &Rc>, 62 | cc: CondOp, 63 | ) { 64 | trace!("push_movc"); 65 | let cc = ctx.alloc_u64(cc.bits()); 66 | assert_eq!(c1.ty, c2.ty); 67 | assert_eq!(rd.ty, rs1.ty); 68 | assert_eq!(rd.ty, rs2.ty); 69 | // we can't use the default impl due to type violations 70 | ctx.push_op(match rd.ty { 71 | ValueType::U64 | ValueType::U32 => Op::Movc { 72 | rd: Rc::clone(rd), 73 | rs1: Rc::clone(rs1), 74 | rs2: Rc::clone(rs2), 75 | c1: Rc::clone(c1), 76 | c2: Rc::clone(c2), 77 | cc, 78 | }, 79 | _ => unreachable!("movc only accepts u32 and u64 destination"), 80 | }); 81 | } 82 | 83 | pub fn push_brc( 84 | ctx: &mut impl DisasContext, 85 | dest: &Rc>, 86 | c1: &Rc>, 87 | c2: &Rc>, 88 | cc: CondOp, 89 | ) { 90 | trace!("push_brc"); 91 | let cc = ctx.alloc_u64(cc.bits()); 92 | assert_eq!(c1.ty, c2.ty); 93 | assert_eq!(dest.ty, ValueType::Label); 94 | // we can't use the default impl due to type violations 95 | ctx.push_op(Op::Brc { 96 | dest: Rc::clone(dest), 97 | c1: Rc::clone(c1), 98 | c2: Rc::clone(c2), 99 | cc, 100 | }); 101 | } 102 | 103 | pub fn push_extr( 104 | ctx: &mut impl DisasContext, 105 | lo: &Rc>, 106 | hi: &Rc>, 107 | arg: &Rc>, 108 | ) { 109 | trace!("push_extr"); 110 | Op::push_extrl(ctx, lo, arg); 111 | Op::push_extrh(ctx, hi, arg); 112 | } 113 | 114 | pub fn push_extru( 115 | ctx: &mut impl DisasContext, 116 | rd: &Rc>, 117 | rs: &Rc>, 118 | ofs: u64, 119 | len: u64, 120 | ) { 121 | trace!("push_extru"); 122 | let ofs = ctx.alloc_u64(ofs); 123 | let len = ctx.alloc_u64(len); 124 | Op::_push_extru(ctx, rd, rs, &ofs, &len); 125 | } 126 | 127 | pub fn push_extrs( 128 | ctx: &mut impl DisasContext, 129 | rd: &Rc>, 130 | rs: &Rc>, 131 | ofs: u64, 132 | len: u64, 133 | ) { 134 | trace!("push_extrs"); 135 | let ofs = ctx.alloc_u64(ofs); 136 | let len = ctx.alloc_u64(len); 137 | Op::_push_extrs(ctx, rd, rs, &ofs, &len); 138 | } 139 | 140 | pub fn push_depos( 141 | ctx: &mut impl DisasContext, 142 | rd: &Rc>, 143 | rs1: &Rc>, 144 | rs2: &Rc>, 145 | ofs: u64, 146 | len: u64, 147 | ) { 148 | trace!("push_depos"); 149 | let ofs = ctx.alloc_u64(ofs); 150 | let len = ctx.alloc_u64(len); 151 | Op::_push_depos(ctx, rd, rs1, rs2, &ofs, &len); 152 | } 153 | 154 | pub fn push_mov(ctx: &mut impl DisasContext, rd: &Rc>, rs: &Rc>) { 155 | assert_eq!(rd.ty, rs.ty); 156 | trace!("push_mov"); 157 | if rd.storage == rs.storage { 158 | trace!("rd == rs, do nothing"); 159 | return; 160 | } 161 | match rd.ty { 162 | ValueType::U64 => Op::_push_mov(ctx, rd, rs), 163 | ValueType::U32 => Op::_push_movl(ctx, rd, rs), 164 | ValueType::F64 => Op::_push_movd(ctx, rd, rs), 165 | _ => unreachable!(), 166 | } 167 | } 168 | 169 | pub fn push_trap(ctx: &mut impl DisasContext, cause: TrapOp, val: &Rc>) { 170 | trace!("push_trap"); 171 | let cause = ctx.alloc_u64(cause.bits); 172 | let new_val = ctx.alloc_val(val.ty); 173 | Op::push_mov(ctx, &new_val, val); 174 | Op::_push_trap(ctx, &cause, &new_val); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/ir/op/opt.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use super::*; 6 | 7 | use log::*; 8 | 9 | impl Op { 10 | pub fn push_add( 11 | ctx: &mut impl DisasContext, 12 | rd: &Rc>, 13 | rs1: &Rc>, 14 | rs2: &Rc>, 15 | ) { 16 | trace!("push_add"); 17 | if let Some(0) = rd.storage.borrow().try_as_u64() { 18 | trace!("rd is 0, do nothing"); 19 | return; 20 | } 21 | if let Some(0) = rs1.storage.borrow().try_as_u64() { 22 | trace!("rs1 is 0, rd = rs2"); 23 | Op::push_mov(ctx, rd, rs2); 24 | return; 25 | } 26 | if let Some(0) = rs2.storage.borrow().try_as_u64() { 27 | trace!("rs2 is 0, rd = rs1"); 28 | Op::push_mov(ctx, rd, rs1); 29 | return; 30 | } 31 | Op::_push_add(ctx, rd, rs1, rs2); 32 | } 33 | 34 | pub fn push_sub( 35 | ctx: &mut impl DisasContext, 36 | rd: &Rc>, 37 | rs1: &Rc>, 38 | rs2: &Rc>, 39 | ) { 40 | if let Some(0) = rd.storage.borrow().try_as_u64() { 41 | return; 42 | } 43 | if let Some(0) = rs2.storage.borrow().try_as_u64() { 44 | Op::push_mov(ctx, rd, rs1); 45 | return; 46 | } 47 | Op::_push_sub(ctx, rd, rs1, rs2); 48 | } 49 | 50 | pub fn push_extulq(ctx: &mut impl DisasContext, rd: &Rc>, rs1: &Rc>) { 51 | if let Some(0) = rd.storage.borrow().try_as_u64() { 52 | return; 53 | } 54 | Op::_push_extulq(ctx, rd, rs1); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/ir/storage.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | extern crate num_traits; 6 | 7 | use crate::host::HostContext; 8 | use std::cell::RefCell; 9 | use std::collections::hash_map::DefaultHasher; 10 | use std::fmt::{Debug, Display, Error, Formatter}; 11 | use std::hash::{Hash, Hasher}; 12 | 13 | /// Underlying storage for IR registers. 14 | pub trait HostStorage: Default + Display + PartialEq { 15 | /// Type of the corresponding host context. 16 | type HostContext: HostContext + 'static; 17 | 18 | /// Attempt to cast the storage to constant `u32` for constant propagation. 19 | fn try_as_u32(&self) -> Option; 20 | /// Attempt to cast the storage to constant `u64` for constant propagation. 21 | fn try_as_u64(&self) -> Option; 22 | /// Attempt to cast the storage to constant `f64` for constant propagation. 23 | fn try_as_f64(&self) -> Option; 24 | } 25 | 26 | /// Valid value types for an IR register. 27 | #[derive(Debug, PartialEq, Clone, Copy)] 28 | pub enum ValueType { 29 | /// Fake value useful for dealing with jump targets. 30 | Label, 31 | /// 32bit word (`l` suffix in operators) 32 | U32, 33 | /// 64bit word (no suffix in operators) 34 | U64, 35 | /// Double word (`d` suffix in operators) 36 | F64, 37 | } 38 | 39 | impl Display for ValueType { 40 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 41 | Debug::fmt(self, f) 42 | } 43 | } 44 | 45 | /// IR register in SSA form. 46 | #[derive(Debug)] 47 | pub struct KHVal { 48 | /// Data type of the register. 49 | pub ty: ValueType, 50 | /// Backend storage of the register. 51 | pub storage: RefCell, 52 | } 53 | 54 | impl KHVal { 55 | /// Allocate new unassigned register from the frontend. 56 | pub fn new(ty: ValueType) -> Self { 57 | Self { 58 | ty, 59 | storage: RefCell::new(Default::default()), 60 | } 61 | } 62 | 63 | /// Allocate new named register from the frontend. 64 | pub fn named(name: String, ty: ValueType) -> Self { 65 | Self { 66 | ty, 67 | storage: RefCell::new(R::HostContext::get().make_named(name, ty)), 68 | } 69 | } 70 | 71 | /// Allocate new label from the frontend. 72 | pub fn label() -> Self { 73 | Self { 74 | ty: ValueType::Label, 75 | storage: RefCell::new(R::HostContext::get().make_label()), 76 | } 77 | } 78 | 79 | /// Allocate `U32` immediate value from the frontend. 80 | pub fn u32(v: u32) -> Self { 81 | Self { 82 | ty: ValueType::U32, 83 | storage: RefCell::new(R::HostContext::get().make_u32(v)), 84 | } 85 | } 86 | 87 | /// Allocate `U64` immediate value from the frontend. 88 | pub fn u64(v: u64) -> Self { 89 | Self { 90 | ty: ValueType::U64, 91 | storage: RefCell::new(R::HostContext::get().make_u64(v)), 92 | } 93 | } 94 | 95 | /// Allocate `F64` immediate value from the frontend. 96 | pub fn f64(v: f64) -> Self { 97 | Self { 98 | ty: ValueType::F64, 99 | storage: RefCell::new(R::HostContext::get().make_f64(v)), 100 | } 101 | } 102 | } 103 | 104 | impl Display for KHVal { 105 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 106 | let mut s = DefaultHasher::new(); 107 | (self as *const Self as u64).hash(&mut s); 108 | // we hope that 5 digits are enough for display purposes 109 | if let Err(_) = write!(f, "{}", self.storage.borrow()) { 110 | write!(f, "%{:05x}", s.finish() % 0x100000) 111 | } else { 112 | Ok(()) 113 | } 114 | } 115 | } 116 | 117 | bitflags! { 118 | /// Used in Load and Store ops to denote the exact memory operation. 119 | pub struct MemOp: u64 { 120 | const SIZE_8 = 0b00; 121 | const SIZE_16 = 0b01; 122 | const SIZE_32 = 0b10; 123 | const SIZE_64 = 0b11; 124 | const SIZE_MASK = 0b11; 125 | 126 | const UNSIGNED = 0; 127 | const SIGN_EXTEND = 0b1 << 2; 128 | const BYTE_SWAP = 0b1 << 3; 129 | 130 | // no align 0b000 << 4; 131 | const ALIGN_2 = 0b001 << 4; 132 | const ALIGN_4 = 0b010 << 4; 133 | const ALIGN_8 = 0b011 << 4; 134 | const ALIGN_16 = 0b100 << 4; 135 | const ALIGN_32 = 0b101 << 4; 136 | const ALIGN_64 = 0b110 << 4; 137 | const ALIGN_MASK = 0b111 << 4; 138 | 139 | // aliases for operand types 140 | const UB = Self::SIZE_8.bits; 141 | const UW = Self::SIZE_16.bits; 142 | const UL = Self::SIZE_32.bits; 143 | const SB = Self::SIZE_8.bits | Self::SIGN_EXTEND.bits; 144 | const SW = Self::SIZE_16.bits | Self::SIGN_EXTEND.bits; 145 | const SL = Self::SIZE_32.bits | Self::SIGN_EXTEND.bits; 146 | const Q = Self::SIZE_64.bits; 147 | // aliases assuming a little-endian host 148 | const GUEST_LE = 0; // no need for byte swap 149 | const GUEST_BE = Self::BYTE_SWAP.bits; 150 | } 151 | } 152 | 153 | impl MemOp { 154 | /// Construct `MemOp` from memory size. 155 | /// 156 | /// Only access sizes of 1, 2, 4, and 8 are supported. 157 | pub fn from_size(bytes: u64) -> Self { 158 | match bytes { 159 | 1 => Self::SIZE_8, 160 | 2 => Self::SIZE_16, 161 | 4 => Self::SIZE_32, 162 | 8 => Self::SIZE_64, 163 | _ => unreachable!("size of {} bytes not supported", bytes), 164 | } 165 | } 166 | 167 | /// Construct `MemOp` from signedness. 168 | pub fn from_sign(sign: bool) -> Self { 169 | if sign { 170 | Self::SIGN_EXTEND 171 | } else { 172 | Self::UNSIGNED 173 | } 174 | } 175 | 176 | /// Retrieve the access size from `MemOp`. 177 | pub fn get_size(&self) -> u64 { 178 | match *self & Self::SIZE_MASK { 179 | Self::SIZE_8 => 1, 180 | Self::SIZE_16 => 2, 181 | Self::SIZE_32 => 4, 182 | Self::SIZE_64 => 8, 183 | _ => unreachable!(), 184 | } 185 | } 186 | 187 | /// Retrieve the signedness from `MemOp`. 188 | pub fn get_sign(&self) -> bool { 189 | (*self & Self::SIGN_EXTEND).bits != 0 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | #![feature(vec_leak)] 6 | #![feature(external_doc)] 7 | #![allow(dead_code, unused)] 8 | #![doc(include = "../README.md")] 9 | 10 | #[macro_use] 11 | extern crate bitflags; 12 | 13 | /// The frontend. Disassembles guest code, producing [translation blocks](struct.TranslationBlock.html) (of IR operators). 14 | pub mod guest; 15 | 16 | /// The backend. Accepts [translation blocks](../guest/struct.TranslationBlock.html) and emits [host blocks](trait.HostBlock.html). 17 | pub mod host; 18 | 19 | /// IR definition and manipulation. 20 | pub mod ir; 21 | 22 | /// The runtime environment. 23 | pub mod runtime; 24 | 25 | /// Various utilities. 26 | pub mod util; 27 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use khemu::*; 6 | 7 | use crate::runtime::*; 8 | use khemu::host::llvm::LLVMHostContext; 9 | 10 | fn main() -> Result<(), String> { 11 | env_logger::init(); 12 | 13 | do_work::() 14 | } 15 | -------------------------------------------------------------------------------- /src/runtime.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | extern crate log; 6 | 7 | use crate::guest::*; 8 | use crate::host::{HostBlock, HostContext}; 9 | use crate::ir::op::TrapOp; 10 | 11 | use log::*; 12 | use memmap::{MmapMut, MmapOptions}; 13 | 14 | use std::cell::{Ref, RefCell}; 15 | use std::collections::{BTreeMap, HashMap, VecDeque}; 16 | use std::path::Path; 17 | use std::rc::Rc; 18 | use std::{env, fs}; 19 | 20 | /// Number of IR operations to generate before interrupting the translation flow and starting a new 21 | /// translation block. 22 | /// 23 | /// Note that the resulting translation block may have slightly more than `DEFAULT_TB_SIZE` ops; 24 | /// the translation block will always split on guest instruction boundaries. 25 | pub const DEFAULT_TB_SIZE: usize = 4096; 26 | 27 | /// Read ELF into byte vector. The ELF file is specified via commandline argument. 28 | pub fn read_elf() -> Result, String> { 29 | let args: Vec<_> = env::args().collect(); 30 | let prog_path; 31 | match args.len() { 32 | 2 => prog_path = Path::new(args[1].as_str()), 33 | _ => return Err(format!("usage: {} ", args[0])), 34 | }; 35 | 36 | match fs::read(prog_path) { 37 | Ok(b) => Ok(b), 38 | Err(e) => Err(format!("failed to read {}: {}", prog_path.display(), e)), 39 | } 40 | } 41 | 42 | /// Guest address space size. Default to 512MB. 43 | pub const GUEST_SIZE: usize = 0x2000_0000; 44 | 45 | /// Type of guest virtual address space. 46 | pub type GuestMap = Rc>; 47 | 48 | /// Map guest virtual memory. 49 | pub fn map_virtual() -> Result { 50 | MmapOptions::new() 51 | .len(GUEST_SIZE) 52 | .map_anon() 53 | .map(|x| Rc::new(RefCell::new(x))) 54 | .map_err(|e| format!("failed to map guest virtual space: {}", e)) 55 | } 56 | 57 | /// Routine to parse and load an ELF program. 58 | pub mod loader; 59 | 60 | /// Type of a guest trap handler. 61 | /// 62 | /// The guest trap handler accepts a trap cause `ir::op::TrapOp` and a per-trap-defined value. 63 | pub type TrapHandler = fn(u64, u64); 64 | 65 | static mut START_POSITIONS: Option> = None; 66 | 67 | fn trap_handler(cause: u64, val: u64) { 68 | let trap_op = TrapOp::from_bits(cause).unwrap(); 69 | 70 | C::get().handle_trap(); 71 | 72 | match trap_op { 73 | TrapOp::LOOKUP_TB => { 74 | info!("Lookup TB: continuing at {:#x}", val); 75 | // insert target right after pending 76 | unsafe { 77 | let waiting = START_POSITIONS.as_mut().unwrap().pop_front().unwrap(); 78 | START_POSITIONS.as_mut().unwrap().push_front(val as usize); 79 | START_POSITIONS.as_mut().unwrap().push_front(waiting); 80 | } 81 | } 82 | _ => unimplemented!(), 83 | } 84 | } 85 | 86 | /// The main "disassemble-emit-execute" loop. 87 | pub fn do_work() -> Result<(), String> { 88 | let elf = read_elf()?; 89 | 90 | let (mut disassembler, entry_point) = loader::load_program(elf, trap_handler::)?; 91 | let mut blk_cache: HashMap<_, C::BlockType> = HashMap::new(); 92 | 93 | unsafe { 94 | START_POSITIONS = Some(VecDeque::new()); 95 | START_POSITIONS 96 | .as_mut() 97 | .unwrap() 98 | .push_back(entry_point as usize); 99 | } 100 | 101 | let mut ret = None; 102 | unsafe { 103 | while let Some(&start_pos) = START_POSITIONS.as_mut().unwrap().front() { 104 | match blk_cache.get(&start_pos) { 105 | // found block, execute 106 | Some(blk) => { 107 | info!("Executing host block for guest {:#x}", start_pos); 108 | unsafe { 109 | blk.execute(); 110 | } 111 | START_POSITIONS.as_mut().unwrap().pop_front(); 112 | } 113 | // not found, translate and insert 114 | None => { 115 | let name = format!("func_{}", start_pos); 116 | C::get().push_block(&name, true); 117 | 118 | let result = disassembler.disas_block(start_pos, DEFAULT_TB_SIZE); 119 | let tb = disassembler.get_tb(); 120 | match result { 121 | DisasException::Unexpected(s) => { 122 | error!("Ending TB @ {:#x} with error: {}", tb.start_pc, s); 123 | ret = Some(s); 124 | break; 125 | } 126 | e => { 127 | info!("Ending TB @ {:#x} with reason: {}", tb.start_pc, e); 128 | // find blocks that can be found statically 129 | match e { 130 | DisasException::Continue(dest) => { 131 | // Size exceeded or unconditional jump 132 | // insert target right after pending 133 | let waiting = 134 | START_POSITIONS.as_mut().unwrap().pop_front().unwrap(); 135 | START_POSITIONS.as_mut().unwrap().push_front(dest); 136 | START_POSITIONS.as_mut().unwrap().push_front(waiting); 137 | } 138 | DisasException::Branch(Some(taken), Some(not_taken)) => { 139 | // both destinations are known 140 | // TODO(jsteward) modify to fit proper translation branch prediction 141 | START_POSITIONS.as_mut().unwrap().push_back(taken); 142 | START_POSITIONS.as_mut().unwrap().push_back(not_taken); 143 | } 144 | DisasException::Branch(Some(dest), None) 145 | | DisasException::Branch(None, Some(dest)) => { 146 | // only one destination is known 147 | // TODO(jsteward) modify to fit proper translation branch prediction 148 | START_POSITIONS.as_mut().unwrap().push_back(dest); 149 | } 150 | _ => { 151 | // none of the jump targets are known 152 | // bail out, wait for actual LOOKUP trap 153 | } 154 | } 155 | 156 | // emit backend instructions 157 | let blk = C::get().emit_block( 158 | tb, 159 | &name, 160 | disassembler.get_tracking(), 161 | Some(e), 162 | ); 163 | 164 | // record in cache, run it next round 165 | blk_cache.insert(start_pos, blk); 166 | continue; 167 | } 168 | } 169 | } 170 | } 171 | } 172 | } 173 | 174 | match ret { 175 | Some(r) => Err(r), 176 | None => Ok(()), 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/runtime/loader.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | use crate::guest::arm64::Arm64GuestContext; 6 | use crate::guest::Disassembler; 7 | use crate::ir::storage::HostStorage; 8 | use crate::runtime::*; 9 | use log::info; 10 | 11 | use goblin::elf; 12 | use goblin::elf::header::*; 13 | use goblin::elf::program_header::pt_to_str; 14 | use std::rc::Rc; 15 | 16 | use std::cell::RefCell; 17 | use std::ops::IndexMut; 18 | 19 | /// Loads a guest ELF and creates the frontend context, also known as the disassembler. 20 | pub fn load_program( 21 | buffer: Vec, 22 | handler: TrapHandler, 23 | ) -> Result<(impl Disassembler, u64), String> { 24 | let binary: elf::Elf = match elf::Elf::parse(&buffer) { 25 | Ok(b) => b, 26 | Err(e) => return Err(format!("failed to parse ELF: {}", e)), 27 | }; 28 | 29 | match binary.header.e_machine { 30 | EM_AARCH64 => { 31 | if binary.header.e_type != ET_EXEC && binary.header.e_type != ET_DYN { 32 | return Err(format!( 33 | "requested to load executable (EXEC or DYN) but ELF type is {}", 34 | et_to_str(binary.header.e_type) 35 | )); 36 | } 37 | 38 | if let Some(_) = binary.dynamic { 39 | return Err("dynamically linked executable not supported yet".to_owned()); 40 | } 41 | 42 | // mmap for guest virtual 43 | let guest_map = map_virtual()?; 44 | info!( 45 | "Created guest address space at {:#x}", 46 | guest_map.as_ptr() as usize 47 | ); 48 | 49 | for ph in &binary.program_headers { 50 | if ph.p_type == elf::program_header::PT_LOAD { 51 | let len = ph.p_filesz as usize; 52 | let file_off = ph.p_offset as usize; 53 | let virt = ph.p_vaddr as usize; 54 | 55 | info!( 56 | "{}: reading {:#x} bytes for {:#x}", 57 | pt_to_str(ph.p_type), 58 | len, 59 | virt 60 | ); 61 | 62 | // memsz may be larger than filesz, in which case the rest is zero-filled 63 | let data = &buffer[file_off..file_off + len]; 64 | guest_map 65 | .borrow_mut() 66 | .index_mut(virt..virt + len) 67 | .copy_from_slice(data); 68 | } 69 | } 70 | 71 | info!("Entry point: {:#x}", binary.entry); 72 | 73 | R::HostContext::init(Rc::clone(&guest_map), handler); 74 | 75 | Ok((Arm64GuestContext::::new(guest_map), binary.entry)) 76 | } 77 | _ => Err(format!( 78 | "unsupported architecture {}", 79 | elf::header::machine_to_str(binary.header.e_machine) 80 | )), 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Pengcheng Xu 2 | // 3 | // SPDX-License-Identifier: BSD-3-Clause 4 | 5 | extern crate num_traits; 6 | 7 | use num_traits::int::PrimInt; 8 | use num_traits::sign::{Signed, Unsigned}; 9 | use std::mem; 10 | 11 | // shifts rely on the type being correct to enforce sign extension behavior 12 | 13 | #[inline] 14 | pub fn extract(val: T, start: usize, len: usize) -> T 15 | where 16 | T: Unsigned + PrimInt, 17 | { 18 | let word_bits = 8 * mem::size_of::(); 19 | (val >> start) & (!T::zero() >> (word_bits - len)) 20 | } 21 | 22 | #[inline] 23 | pub fn sextract(val: T, start: usize, len: usize) -> T 24 | where 25 | T: Signed + PrimInt, 26 | { 27 | let word_bits = 8 * mem::size_of::(); 28 | ((val << (word_bits - len - start)) as T) >> (word_bits - len) 29 | } 30 | --------------------------------------------------------------------------------