smallworld-re 1.0.3__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. smallworld/analyses/__init__.py +8 -0
  2. smallworld/analyses/analysis.py +8 -67
  3. smallworld/analyses/code_coverage.py +1 -2
  4. smallworld/analyses/colorizer.py +301 -534
  5. smallworld/analyses/colorizer_def_use.py +217 -0
  6. smallworld/analyses/colorizer_summary.py +173 -83
  7. smallworld/analyses/field_detection/field_analysis.py +7 -8
  8. smallworld/analyses/field_detection/hints.py +1 -1
  9. smallworld/analyses/field_detection/malloc.py +2 -2
  10. smallworld/analyses/trace_execution.py +160 -0
  11. smallworld/analyses/trace_execution_types.py +42 -0
  12. smallworld/analyses/unstable/angr/divergence.py +1 -2
  13. smallworld/analyses/unstable/angr/model.py +5 -6
  14. smallworld/analyses/unstable/angr_nwbt.py +3 -4
  15. smallworld/analyses/unstable/code_coverage.py +2 -3
  16. smallworld/analyses/unstable/code_reachable.py +2 -3
  17. smallworld/analyses/unstable/control_flow_tracer.py +2 -3
  18. smallworld/analyses/unstable/pointer_finder.py +2 -3
  19. smallworld/analyses/unstable/utils/tui.py +71 -0
  20. smallworld/emulators/__init__.py +3 -1
  21. smallworld/emulators/angr/angr.py +30 -9
  22. smallworld/emulators/angr/machdefs/__init__.py +2 -0
  23. smallworld/emulators/angr/machdefs/aarch64.py +1 -1
  24. smallworld/emulators/angr/machdefs/amd64.py +0 -4
  25. smallworld/emulators/angr/machdefs/arm.py +0 -2
  26. smallworld/emulators/angr/machdefs/i386.py +0 -2
  27. smallworld/emulators/angr/machdefs/loongarch.py +340 -0
  28. smallworld/emulators/angr/machdefs/machdef.py +1 -8
  29. smallworld/emulators/angr/machdefs/mips.py +0 -2
  30. smallworld/emulators/angr/machdefs/mips64.py +0 -2
  31. smallworld/emulators/angr/machdefs/ppc.py +1 -2
  32. smallworld/emulators/angr/machdefs/riscv.py +8 -10
  33. smallworld/emulators/angr/machdefs/xtensa.py +7 -4
  34. smallworld/emulators/emulator.py +22 -0
  35. smallworld/emulators/ghidra/__init__.py +37 -0
  36. smallworld/emulators/ghidra/ghidra.py +513 -0
  37. smallworld/emulators/ghidra/machdefs/__init__.py +31 -0
  38. smallworld/emulators/ghidra/machdefs/aarch64.py +289 -0
  39. smallworld/emulators/ghidra/machdefs/amd64.py +185 -0
  40. smallworld/emulators/ghidra/machdefs/arm.py +370 -0
  41. smallworld/emulators/ghidra/machdefs/i386.py +109 -0
  42. smallworld/emulators/ghidra/machdefs/loongarch.py +162 -0
  43. smallworld/emulators/ghidra/machdefs/machdef.py +81 -0
  44. smallworld/emulators/ghidra/machdefs/mips.py +163 -0
  45. smallworld/emulators/ghidra/machdefs/mips64.py +186 -0
  46. smallworld/emulators/ghidra/machdefs/ppc.py +98 -0
  47. smallworld/emulators/ghidra/machdefs/riscv.py +208 -0
  48. smallworld/emulators/ghidra/machdefs/xtensa.py +21 -0
  49. smallworld/emulators/ghidra/typing.py +28 -0
  50. smallworld/emulators/hookable.py +18 -4
  51. smallworld/emulators/panda/machdefs/__init__.py +2 -2
  52. smallworld/emulators/panda/machdefs/aarch64.py +186 -11
  53. smallworld/emulators/panda/machdefs/amd64.py +103 -11
  54. smallworld/emulators/panda/machdefs/arm.py +216 -20
  55. smallworld/emulators/panda/machdefs/i386.py +30 -7
  56. smallworld/emulators/panda/machdefs/machdef.py +9 -16
  57. smallworld/emulators/panda/machdefs/mips.py +49 -5
  58. smallworld/emulators/panda/machdefs/mips64.py +57 -5
  59. smallworld/emulators/panda/machdefs/ppc.py +38 -13
  60. smallworld/emulators/panda/panda.py +146 -44
  61. smallworld/emulators/unicorn/__init__.py +2 -0
  62. smallworld/emulators/unicorn/machdefs/aarch64.py +253 -264
  63. smallworld/emulators/unicorn/machdefs/amd64.py +254 -259
  64. smallworld/emulators/unicorn/machdefs/arm.py +200 -212
  65. smallworld/emulators/unicorn/machdefs/i386.py +84 -90
  66. smallworld/emulators/unicorn/machdefs/machdef.py +2 -23
  67. smallworld/emulators/unicorn/machdefs/mips.py +127 -135
  68. smallworld/emulators/unicorn/unicorn.py +52 -13
  69. smallworld/helpers.py +4 -19
  70. smallworld/hinting/hinting.py +22 -192
  71. smallworld/hinting/hints.py +50 -18
  72. smallworld/instructions/bsid.py +8 -8
  73. smallworld/logging.py +4 -2
  74. smallworld/platforms/__init__.py +12 -0
  75. smallworld/platforms/defs/__init__.py +36 -0
  76. smallworld/platforms/defs/aarch64.py +450 -0
  77. smallworld/platforms/defs/amd64.py +463 -0
  78. smallworld/platforms/defs/arm.py +519 -0
  79. smallworld/platforms/defs/i386.py +258 -0
  80. smallworld/platforms/defs/loongarch.py +270 -0
  81. smallworld/platforms/defs/mips.py +321 -0
  82. smallworld/platforms/defs/mips64.py +313 -0
  83. smallworld/platforms/defs/platformdef.py +97 -0
  84. smallworld/platforms/defs/powerpc.py +259 -0
  85. smallworld/platforms/defs/riscv.py +257 -0
  86. smallworld/platforms/defs/xtensa.py +96 -0
  87. smallworld/{platforms.py → platforms/platforms.py} +3 -0
  88. smallworld/state/cpus/__init__.py +2 -0
  89. smallworld/state/cpus/aarch64.py +0 -9
  90. smallworld/state/cpus/amd64.py +6 -28
  91. smallworld/state/cpus/arm.py +0 -11
  92. smallworld/state/cpus/cpu.py +0 -11
  93. smallworld/state/cpus/i386.py +0 -7
  94. smallworld/state/cpus/loongarch.py +299 -0
  95. smallworld/state/cpus/mips.py +4 -47
  96. smallworld/state/cpus/mips64.py +18 -58
  97. smallworld/state/cpus/powerpc.py +2 -9
  98. smallworld/state/cpus/riscv.py +1 -11
  99. smallworld/state/cpus/xtensa.py +0 -5
  100. smallworld/state/memory/code.py +38 -2
  101. smallworld/state/memory/elf/__init__.py +5 -1
  102. smallworld/state/memory/elf/coredump/__init__.py +3 -0
  103. smallworld/state/memory/elf/coredump/coredump.py +46 -0
  104. smallworld/state/memory/elf/coredump/prstatus/__init__.py +27 -0
  105. smallworld/state/memory/elf/coredump/prstatus/aarch64.py +46 -0
  106. smallworld/state/memory/elf/coredump/prstatus/amd64.py +40 -0
  107. smallworld/state/memory/elf/coredump/prstatus/arm.py +53 -0
  108. smallworld/state/memory/elf/coredump/prstatus/i386.py +30 -0
  109. smallworld/state/memory/elf/coredump/prstatus/mips.py +55 -0
  110. smallworld/state/memory/elf/coredump/prstatus/mips64.py +57 -0
  111. smallworld/state/memory/elf/coredump/prstatus/ppc.py +82 -0
  112. smallworld/state/memory/elf/coredump/prstatus/prstatus.py +129 -0
  113. smallworld/state/memory/elf/elf.py +211 -57
  114. smallworld/state/memory/elf/register_state.py +36 -0
  115. smallworld/state/memory/elf/rela/__init__.py +2 -0
  116. smallworld/state/memory/elf/rela/aarch64.py +3 -1
  117. smallworld/state/memory/elf/rela/amd64.py +4 -2
  118. smallworld/state/memory/elf/rela/arm.py +4 -2
  119. smallworld/state/memory/elf/rela/i386.py +4 -2
  120. smallworld/state/memory/elf/rela/loongarch.py +32 -0
  121. smallworld/state/memory/elf/rela/mips.py +39 -18
  122. smallworld/state/memory/elf/rela/ppc.py +31 -14
  123. smallworld/state/memory/elf/structs.py +3 -0
  124. smallworld/state/memory/heap.py +2 -2
  125. smallworld/state/memory/memory.py +18 -0
  126. smallworld/state/memory/pe/__init__.py +3 -0
  127. smallworld/state/memory/pe/pe.py +361 -0
  128. smallworld/state/memory/pe/structs.py +60 -0
  129. smallworld/state/memory/stack/__init__.py +2 -0
  130. smallworld/state/memory/stack/loongarch.py +26 -0
  131. smallworld/state/models/__init__.py +29 -2
  132. smallworld/state/models/aarch64/__init__.py +1 -0
  133. smallworld/state/models/aarch64/systemv/__init__.py +6 -0
  134. smallworld/state/models/aarch64/systemv/c99/__init__.py +12 -0
  135. smallworld/state/models/aarch64/systemv/c99/signal.py +16 -0
  136. smallworld/state/models/aarch64/systemv/c99/stdio.py +265 -0
  137. smallworld/state/models/aarch64/systemv/c99/stdlib.py +169 -0
  138. smallworld/state/models/aarch64/systemv/c99/string.py +139 -0
  139. smallworld/state/models/aarch64/systemv/c99/time.py +61 -0
  140. smallworld/state/models/aarch64/systemv/posix/__init__.py +6 -0
  141. smallworld/state/models/aarch64/systemv/posix/libgen.py +16 -0
  142. smallworld/state/models/aarch64/systemv/posix/signal.py +157 -0
  143. smallworld/state/models/aarch64/systemv/systemv.py +80 -0
  144. smallworld/state/models/amd64/__init__.py +1 -0
  145. smallworld/state/models/amd64/systemv/__init__.py +6 -0
  146. smallworld/state/models/amd64/systemv/c99/__init__.py +12 -0
  147. smallworld/state/models/amd64/systemv/c99/signal.py +16 -0
  148. smallworld/state/models/amd64/systemv/c99/stdio.py +265 -0
  149. smallworld/state/models/amd64/systemv/c99/stdlib.py +169 -0
  150. smallworld/state/models/amd64/systemv/c99/string.py +139 -0
  151. smallworld/state/models/amd64/systemv/c99/time.py +61 -0
  152. smallworld/state/models/amd64/systemv/posix/__init__.py +6 -0
  153. smallworld/state/models/amd64/systemv/posix/libgen.py +16 -0
  154. smallworld/state/models/amd64/systemv/posix/signal.py +157 -0
  155. smallworld/state/models/amd64/systemv/systemv.py +78 -0
  156. smallworld/state/models/armel/__init__.py +1 -0
  157. smallworld/state/models/armel/systemv/__init__.py +6 -0
  158. smallworld/state/models/armel/systemv/c99/__init__.py +12 -0
  159. smallworld/state/models/armel/systemv/c99/signal.py +16 -0
  160. smallworld/state/models/armel/systemv/c99/stdio.py +265 -0
  161. smallworld/state/models/armel/systemv/c99/stdlib.py +169 -0
  162. smallworld/state/models/armel/systemv/c99/string.py +139 -0
  163. smallworld/state/models/armel/systemv/c99/time.py +61 -0
  164. smallworld/state/models/armel/systemv/posix/__init__.py +6 -0
  165. smallworld/state/models/armel/systemv/posix/libgen.py +16 -0
  166. smallworld/state/models/armel/systemv/posix/signal.py +157 -0
  167. smallworld/state/models/armel/systemv/systemv.py +82 -0
  168. smallworld/state/models/armhf/__init__.py +1 -0
  169. smallworld/state/models/armhf/systemv/__init__.py +6 -0
  170. smallworld/state/models/armhf/systemv/c99/__init__.py +12 -0
  171. smallworld/state/models/armhf/systemv/c99/signal.py +16 -0
  172. smallworld/state/models/armhf/systemv/c99/stdio.py +265 -0
  173. smallworld/state/models/armhf/systemv/c99/stdlib.py +169 -0
  174. smallworld/state/models/armhf/systemv/c99/string.py +139 -0
  175. smallworld/state/models/armhf/systemv/c99/time.py +61 -0
  176. smallworld/state/models/armhf/systemv/posix/__init__.py +6 -0
  177. smallworld/state/models/armhf/systemv/posix/libgen.py +16 -0
  178. smallworld/state/models/armhf/systemv/posix/signal.py +157 -0
  179. smallworld/state/models/armhf/systemv/systemv.py +77 -0
  180. smallworld/state/models/c99/__init__.py +12 -0
  181. smallworld/state/models/c99/fmt_print.py +915 -0
  182. smallworld/state/models/c99/fmt_scan.py +864 -0
  183. smallworld/state/models/c99/math.py +362 -0
  184. smallworld/state/models/c99/signal.py +71 -0
  185. smallworld/state/models/c99/stdio.py +1305 -0
  186. smallworld/state/models/c99/stdlib.py +595 -0
  187. smallworld/state/models/c99/string.py +674 -0
  188. smallworld/state/models/c99/time.py +340 -0
  189. smallworld/state/models/c99/utils.py +89 -0
  190. smallworld/state/models/cstd.py +759 -0
  191. smallworld/state/models/errno.py +581 -0
  192. smallworld/state/models/filedesc.py +515 -0
  193. smallworld/state/models/i386/__init__.py +1 -0
  194. smallworld/state/models/i386/systemv/__init__.py +6 -0
  195. smallworld/state/models/i386/systemv/c99/__init__.py +12 -0
  196. smallworld/state/models/i386/systemv/c99/signal.py +16 -0
  197. smallworld/state/models/i386/systemv/c99/stdio.py +265 -0
  198. smallworld/state/models/i386/systemv/c99/stdlib.py +169 -0
  199. smallworld/state/models/i386/systemv/c99/string.py +139 -0
  200. smallworld/state/models/i386/systemv/c99/time.py +61 -0
  201. smallworld/state/models/i386/systemv/posix/__init__.py +6 -0
  202. smallworld/state/models/i386/systemv/posix/libgen.py +16 -0
  203. smallworld/state/models/i386/systemv/posix/signal.py +157 -0
  204. smallworld/state/models/i386/systemv/systemv.py +71 -0
  205. smallworld/state/models/loongarch64/__init__.py +1 -0
  206. smallworld/state/models/loongarch64/systemv/__init__.py +6 -0
  207. smallworld/state/models/loongarch64/systemv/c99/__init__.py +12 -0
  208. smallworld/state/models/loongarch64/systemv/c99/signal.py +16 -0
  209. smallworld/state/models/loongarch64/systemv/c99/stdio.py +265 -0
  210. smallworld/state/models/loongarch64/systemv/c99/stdlib.py +169 -0
  211. smallworld/state/models/loongarch64/systemv/c99/string.py +139 -0
  212. smallworld/state/models/loongarch64/systemv/c99/time.py +61 -0
  213. smallworld/state/models/loongarch64/systemv/posix/__init__.py +6 -0
  214. smallworld/state/models/loongarch64/systemv/posix/libgen.py +16 -0
  215. smallworld/state/models/loongarch64/systemv/posix/signal.py +157 -0
  216. smallworld/state/models/loongarch64/systemv/systemv.py +83 -0
  217. smallworld/state/models/mips/__init__.py +1 -0
  218. smallworld/state/models/mips/systemv/__init__.py +6 -0
  219. smallworld/state/models/mips/systemv/c99/__init__.py +12 -0
  220. smallworld/state/models/mips/systemv/c99/signal.py +16 -0
  221. smallworld/state/models/mips/systemv/c99/stdio.py +265 -0
  222. smallworld/state/models/mips/systemv/c99/stdlib.py +169 -0
  223. smallworld/state/models/mips/systemv/c99/string.py +139 -0
  224. smallworld/state/models/mips/systemv/c99/time.py +61 -0
  225. smallworld/state/models/mips/systemv/posix/__init__.py +6 -0
  226. smallworld/state/models/mips/systemv/posix/libgen.py +16 -0
  227. smallworld/state/models/mips/systemv/posix/signal.py +157 -0
  228. smallworld/state/models/mips/systemv/systemv.py +78 -0
  229. smallworld/state/models/mips64/__init__.py +1 -0
  230. smallworld/state/models/mips64/systemv/__init__.py +6 -0
  231. smallworld/state/models/mips64/systemv/c99/__init__.py +12 -0
  232. smallworld/state/models/mips64/systemv/c99/signal.py +16 -0
  233. smallworld/state/models/mips64/systemv/c99/stdio.py +265 -0
  234. smallworld/state/models/mips64/systemv/c99/stdlib.py +169 -0
  235. smallworld/state/models/mips64/systemv/c99/string.py +139 -0
  236. smallworld/state/models/mips64/systemv/c99/time.py +61 -0
  237. smallworld/state/models/mips64/systemv/posix/__init__.py +6 -0
  238. smallworld/state/models/mips64/systemv/posix/libgen.py +16 -0
  239. smallworld/state/models/mips64/systemv/posix/signal.py +157 -0
  240. smallworld/state/models/mips64/systemv/systemv.py +98 -0
  241. smallworld/state/models/mips64el/__init__.py +1 -0
  242. smallworld/state/models/mips64el/systemv/__init__.py +6 -0
  243. smallworld/state/models/mips64el/systemv/c99/__init__.py +12 -0
  244. smallworld/state/models/mips64el/systemv/c99/signal.py +16 -0
  245. smallworld/state/models/mips64el/systemv/c99/stdio.py +265 -0
  246. smallworld/state/models/mips64el/systemv/c99/stdlib.py +169 -0
  247. smallworld/state/models/mips64el/systemv/c99/string.py +139 -0
  248. smallworld/state/models/mips64el/systemv/c99/time.py +61 -0
  249. smallworld/state/models/mips64el/systemv/posix/__init__.py +6 -0
  250. smallworld/state/models/mips64el/systemv/posix/libgen.py +16 -0
  251. smallworld/state/models/mips64el/systemv/posix/signal.py +157 -0
  252. smallworld/state/models/mips64el/systemv/systemv.py +96 -0
  253. smallworld/state/models/mipsel/__init__.py +1 -0
  254. smallworld/state/models/mipsel/systemv/__init__.py +6 -0
  255. smallworld/state/models/mipsel/systemv/c99/__init__.py +12 -0
  256. smallworld/state/models/mipsel/systemv/c99/signal.py +16 -0
  257. smallworld/state/models/mipsel/systemv/c99/stdio.py +265 -0
  258. smallworld/state/models/mipsel/systemv/c99/stdlib.py +169 -0
  259. smallworld/state/models/mipsel/systemv/c99/string.py +139 -0
  260. smallworld/state/models/mipsel/systemv/c99/time.py +61 -0
  261. smallworld/state/models/mipsel/systemv/posix/__init__.py +6 -0
  262. smallworld/state/models/mipsel/systemv/posix/libgen.py +16 -0
  263. smallworld/state/models/mipsel/systemv/posix/signal.py +157 -0
  264. smallworld/state/models/mipsel/systemv/systemv.py +78 -0
  265. smallworld/state/models/model.py +27 -2
  266. smallworld/state/models/posix/__init__.py +6 -0
  267. smallworld/state/models/posix/libgen.py +123 -0
  268. smallworld/state/models/posix/signal.py +690 -0
  269. smallworld/state/models/powerpc/__init__.py +1 -0
  270. smallworld/state/models/powerpc/systemv/__init__.py +6 -0
  271. smallworld/state/models/powerpc/systemv/c99/__init__.py +12 -0
  272. smallworld/state/models/powerpc/systemv/c99/signal.py +16 -0
  273. smallworld/state/models/powerpc/systemv/c99/stdio.py +265 -0
  274. smallworld/state/models/powerpc/systemv/c99/stdlib.py +169 -0
  275. smallworld/state/models/powerpc/systemv/c99/string.py +139 -0
  276. smallworld/state/models/powerpc/systemv/c99/time.py +61 -0
  277. smallworld/state/models/powerpc/systemv/posix/__init__.py +6 -0
  278. smallworld/state/models/powerpc/systemv/posix/libgen.py +16 -0
  279. smallworld/state/models/powerpc/systemv/posix/signal.py +157 -0
  280. smallworld/state/models/powerpc/systemv/systemv.py +93 -0
  281. smallworld/state/models/riscv64/__init__.py +1 -0
  282. smallworld/state/models/riscv64/systemv/__init__.py +6 -0
  283. smallworld/state/models/riscv64/systemv/c99/__init__.py +12 -0
  284. smallworld/state/models/riscv64/systemv/c99/signal.py +16 -0
  285. smallworld/state/models/riscv64/systemv/c99/stdio.py +265 -0
  286. smallworld/state/models/riscv64/systemv/c99/stdlib.py +169 -0
  287. smallworld/state/models/riscv64/systemv/c99/string.py +139 -0
  288. smallworld/state/models/riscv64/systemv/c99/time.py +61 -0
  289. smallworld/state/models/riscv64/systemv/posix/__init__.py +6 -0
  290. smallworld/state/models/riscv64/systemv/posix/libgen.py +16 -0
  291. smallworld/state/models/riscv64/systemv/posix/signal.py +157 -0
  292. smallworld/state/models/riscv64/systemv/systemv.py +85 -0
  293. smallworld/state/state.py +65 -24
  294. smallworld/state/unstable/elf.py +16 -31
  295. smallworld/utils.py +6 -1
  296. {smallworld_re-1.0.3.dist-info → smallworld_re-2.0.0.dist-info}/METADATA +74 -42
  297. smallworld_re-2.0.0.dist-info/RECORD +374 -0
  298. {smallworld_re-1.0.3.dist-info → smallworld_re-2.0.0.dist-info}/WHEEL +1 -1
  299. smallworld/state/models/x86/__init__.py +0 -2
  300. smallworld/state/models/x86/microsoftcdecl.py +0 -35
  301. smallworld/state/models/x86/systemv.py +0 -240
  302. smallworld_re-1.0.3.dist-info/RECORD +0 -166
  303. /smallworld/state/models/{posix.py → _posix.py} +0 -0
  304. {smallworld_re-1.0.3.dist-info → smallworld_re-2.0.0.dist-info}/entry_points.txt +0 -0
  305. {smallworld_re-1.0.3.dist-info → smallworld_re-2.0.0.dist-info}/licenses/LICENSE.txt +0 -0
  306. {smallworld_re-1.0.3.dist-info → smallworld_re-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,915 @@
1
+ import re
2
+ import struct
3
+ import typing
4
+
5
+ from ....emulators import Emulator
6
+ from ....platforms import Byteorder
7
+ from ..cstd import ArgumentType, CStdModel, VariadicContext
8
+ from .utils import _emu_strlen
9
+
10
+ # Conversion specifiers:
11
+ #
12
+ # Group 1: Zero or more flags
13
+ #
14
+ # Group 2: Zero or one field width specifiers
15
+ # - '[0-9]+': Explicit width
16
+ # - '*': Next argument is an int storing the width
17
+ #
18
+ # Group 3: Zero or one precision specifiers
19
+ # - '.': Zero precision
20
+ # - '.[0-9]+': Explicit precision
21
+ # - '.*': Next argument is an int storing the width
22
+ # - NOTE: Default precision for floats is six.
23
+ #
24
+ # Group 4: Unused; part of the precision pattern
25
+ #
26
+ # Group 5: Zero or one length specifiers
27
+ #
28
+ # Group 6: Conversion ID
29
+
30
+ # Signed decimal int conversion
31
+ #
32
+ # Allowed flags:
33
+ # - '0': Justify the field with zeros
34
+ # - ' ': Add a space before a positive number
35
+ # - '-': Left-justify the field. Default is right-justify
36
+ # - '+': Always include a sign symbol
37
+ #
38
+ # Allowed lengths:
39
+ # - 'hh': char
40
+ # - 'h': short
41
+ # - '': int
42
+ # - 'l': long
43
+ # - 'll', 'q': long long
44
+ # - 'z', 'Z': ssize_t
45
+ # - 'j': intmax_t
46
+ # - 't': ptrdiff_t
47
+ #
48
+ # Allowed conversions:
49
+ # - d, i: signed decimal integer
50
+ sdecint_re = re.compile(
51
+ "%([0 \\-+]*)([0-9]*|[*])((\\.[0-9]*|\\.[*])?)(hh|h|l|ll|q|z|Z|j|t|)(d|i)"
52
+ )
53
+
54
+ # Unsigned decimal int conversion
55
+ #
56
+ # Allowed flags:
57
+ # - '0': Justify the field with zeros
58
+ # - '-': Left-justify the field. Default is right-justify
59
+ #
60
+ # Allowed lengths:
61
+ # - 'hh': char
62
+ # - 'h': short
63
+ # - '': int
64
+ # - 'l': long
65
+ # - 'll', 'q': long long
66
+ # - 'z', 'Z': size_t
67
+ # - 'j': intmax_t
68
+ # - 't': ptrdiff_t
69
+ #
70
+ # Allowed conversions:
71
+ # - u: unsigned decimal integer
72
+ udecint_re = re.compile(
73
+ "%([0\\-]*)([0-9]*|[*])((\\.[0-9]*|\\.[*])?)(hh|h|l|ll|q|z|Z|j|t|)(u)"
74
+ )
75
+
76
+ # Unsigned non-decimal int conversion
77
+ #
78
+ # Allowed flags:
79
+ # - '#': Add '0o' or '0x' prefix, as appropriate
80
+ # - '0': Justify the field with zeros
81
+ # - '-': Left-justify the field. Default is right-justify
82
+ #
83
+ # Allowed lengths:
84
+ # - 'hh': char
85
+ # - 'h': short
86
+ # - '': int
87
+ # - 'l': long
88
+ # - 'll', 'q': long long
89
+ # - 'z', 'Z': size_t
90
+ # - 'j': intmax_t
91
+ # - 't': ptrdiff_t
92
+ #
93
+ # Allowed conversions:
94
+ # - o: unsigned octal integer
95
+ # - x: unsigned hexadecimal integer, lowercase
96
+ # - X: unsigned hexadecimal integer, uppercase
97
+ uint_re = re.compile(
98
+ "%([#0\\-]*)([0-9]*|[*])((\\.[0-9]*|\\.[*])?)(hh|h|l|ll|q|z|Z|j|t|)(o|x|X)"
99
+ )
100
+
101
+ # Scientific notation conversion
102
+ #
103
+ # D.DDeDD
104
+ #
105
+ # Allowed flags:
106
+ # - '#': Include decimal and fractional component if zero
107
+ # - '0': Justify the field with zeros
108
+ # - ' ': Add a space before a positive number
109
+ # - '-': Left-justify the field. Default is right-justify
110
+ # - '+': Always include a sign symbol
111
+ #
112
+ # Allowed lengths
113
+ # - '': double (the compiler better damned well have gotten this right).
114
+ # - 'L': long double
115
+ #
116
+ # Allowed conversions:
117
+ # - 'e': Scientific notation, lowercase
118
+ # - 'E': Scientific notation, uppercase
119
+ sci_re = re.compile("%([#0 \\-+]*)([0-9]*|[*])((\\.[0-9]*|\\.[*])?)(L|)(e|E)")
120
+
121
+ # Floating point conversion
122
+ #
123
+ # DD.DD
124
+ #
125
+ # Allowed flags:
126
+ # - '#': Include decimal and fractional component if zero
127
+ # - '0': Justify the field with zeros
128
+ # - ' ': Add a space before a positive number
129
+ # - '-': Left-justify the field. Default is right-justify
130
+ # - '+': Always include a sign symbol
131
+ #
132
+ # Allowed lengths
133
+ # - '': double (the compiler better damned well have gotten this right).
134
+ # - 'L': long double
135
+ #
136
+ # Allowed conversions:
137
+ # - 'e': Floating-point, lowercase
138
+ # - 'E': Floating-point, uppercase
139
+ float_re = re.compile("%([#0 \\-+]*)([0-9]*|[*])((\\.[0-9]*|\\.[*])?)(L|)(f|F)")
140
+
141
+ # Size-sensitive floating point conversion
142
+ #
143
+ # DD.DD or D.DDeDD, depending on magnitude
144
+ #
145
+ # Allowed flags:
146
+ # - '#': Include decimal and fractional component if zero
147
+ # - '0': Justify the field with zeros
148
+ # - ' ': Add a space before a positive number
149
+ # - '-': Left-justify the field. Default is right-justify
150
+ # - '+': Always include a sign symbol
151
+ #
152
+ # Allowed lengths
153
+ # - '': double (the compiler better damned well have gotten this right).
154
+ # - 'L': long double
155
+ #
156
+ # Allowed conversions:
157
+ # - 'g': Floating-point, lowercase
158
+ # - 'G': Floating-point, uppercase
159
+ scifloat_re = re.compile("%([#0 \\-+]*)([0-9]*|[*])((\\.[0-9]*|\\.[*])?)(L|)(g|G)")
160
+
161
+ # Hexadecimal floating point conversion
162
+ #
163
+ # 0xXX.XX
164
+ #
165
+ # Allowed flags:
166
+ # - '#': Include decimal and fractional component if zero
167
+ # - '0': Justify the field with zeros
168
+ # - ' ': Add a space before a positive number
169
+ # - '-': Left-justify the field. Default is right-justify
170
+ # - '+': Always include a sign symbol
171
+ #
172
+ # Allowed lengths
173
+ # - '': double (the compiler better damned well have gotten this right).
174
+ # - 'L': long double
175
+ #
176
+ # Allowed conversions:
177
+ # - 'a': Hexadecimal floating-point, lowercase
178
+ # - 'A': Hexadecimal floating-point, uppercase
179
+ hexfloat_re = re.compile("%([#0 \\-+]*)([0-9]*|[*])((\\.[0-9]+|\\.[*])?)(L|)(g|G)")
180
+
181
+ # Character conversion
182
+ #
183
+ # c
184
+ #
185
+ # Allowed flags:
186
+ # - ' ': Add a space before a string
187
+ # - '-': Left-justify the field. Default is right-justify
188
+ #
189
+ # Allowed Lengths:
190
+ # - ' ': unsigned char
191
+ # - 'l': wchar_t
192
+ #
193
+ # Allowed conversions:
194
+ # - 'c': Character
195
+ char_re = re.compile("%([ \\-]*)([0-9]*|[*])()()(l|)(c)")
196
+
197
+ # String conversion
198
+ #
199
+ # cccccccc
200
+ #
201
+ # Allowed flags:
202
+ # - ' ': Add a space before a string
203
+ # - '-': Left-justify the field. Default is right-justify
204
+ #
205
+ # Allowed lengths
206
+ # - '': char *
207
+ # - 'l': wchar_t *
208
+ #
209
+ # Allowed conversions:
210
+ # - s: String
211
+ str_re = re.compile("%([ \\-]*)([0-9]*|[*])((\\.[0-9]+|\\.[*])?)(l|)(s)")
212
+
213
+ # Pointer conversion
214
+ #
215
+ # 0xXXXXXXXX
216
+ #
217
+ # Allowed flags:
218
+ # - '-': Left-justify the field.
219
+ #
220
+ # Allowed lengths:
221
+ # - '': void *
222
+ #
223
+ # Allowed conversions:
224
+ # - p: pointer
225
+ pointer_re = re.compile("%([\\-]?)([0-9]*|[*])()()()(p)")
226
+
227
+ # Length of current output
228
+ #
229
+ # <prints nothing>
230
+ #
231
+ # Allowed flags:
232
+ # - None
233
+ #
234
+ # Allowed lengths:
235
+ # - 'hh': char *
236
+ # - 'h': short *
237
+ # - '': int *
238
+ # - 'l': long *
239
+ # - 'll', 'q': long long *
240
+ # - 'z', 'Z': size_t *
241
+ # - 'j': intmax_t *
242
+ # - 't': ptrdiff_t *
243
+ #
244
+ # Allowed conversions:
245
+ # - 'n': Fetch length of current output
246
+ len_re = re.compile("%()()()()(hh|h|l|ll|q|z|Z|j|t|)(n)")
247
+
248
+ # Strerror conversion
249
+ #
250
+ # ssssssss # result of strerror(errno)
251
+ #
252
+ # Allowed flags:
253
+ # - ' ': Add a space before a string
254
+ # - '-': Left-justify the field. Default is right-justify
255
+ #
256
+ # Allowed lengths:
257
+ # - '': Default; this doesn't take an argument.
258
+ #
259
+ # Allowed conversions:
260
+ # - varargs: VariadicContext, m: strerror
261
+ strerror_re = re.compile("%([ \\-]*)([0-9]*|[*])()()()(m)")
262
+
263
+ # Percent sign
264
+ #
265
+ # %
266
+ #
267
+ # Allowed flags:
268
+ # - None
269
+ #
270
+ # Allowed lengths:
271
+ # - None
272
+ #
273
+ # Allowed conversions:
274
+ # - '%': Print a literal '%'
275
+ percent_re = re.compile("%%")
276
+
277
+
278
+ class FormatConversionError(Exception):
279
+ pass
280
+
281
+
282
+ def handle_flags(flags: str) -> str:
283
+ out = ""
284
+ if "-" in flags:
285
+ out += "<"
286
+ else:
287
+ out += ">"
288
+
289
+ if "+" in flags:
290
+ out += "+"
291
+
292
+ if " " in flags:
293
+ out += " "
294
+
295
+ if "0" in flags:
296
+ out += "0"
297
+
298
+ if "#" in flags:
299
+ out += "#"
300
+
301
+ return out
302
+
303
+
304
+ def handle_width(width: str, varargs: VariadicContext, emulator: Emulator) -> str:
305
+ if width == "*":
306
+ width_int = varargs.get_next_argument(ArgumentType.INT, emulator)
307
+ assert isinstance(width_int, int)
308
+ return str(width_int)
309
+ else:
310
+ return width
311
+
312
+
313
+ def handle_precision(
314
+ precision: str, varargs: VariadicContext, emulator: Emulator, use_default=True
315
+ ) -> str:
316
+ if precision == ".*":
317
+ precision_int = varargs.get_next_argument(ArgumentType.INT, emulator)
318
+ assert isinstance(precision_int, int)
319
+ return "." + str(precision_int)
320
+ elif precision == ".":
321
+ return ".0"
322
+ elif precision == "":
323
+ if use_default:
324
+ return ".6"
325
+ else:
326
+ return ""
327
+ else:
328
+ return precision
329
+
330
+
331
+ def handle_double_arg(
332
+ length: str, varargs: VariadicContext, emulator: Emulator
333
+ ) -> float:
334
+ if length == "":
335
+ val = varargs.get_next_argument(ArgumentType.DOUBLE, emulator)
336
+ elif length == "L":
337
+ raise NotImplementedError("Type 'long double' not handled")
338
+ else:
339
+ raise FormatConversionError(f"Unknown type specifier {length}")
340
+ assert isinstance(val, float)
341
+ return val
342
+
343
+
344
+ def handle_sdecint(
345
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
346
+ ) -> str:
347
+ flags = m.group(1)
348
+ width = m.group(2)
349
+ precision = m.group(3)
350
+ length = m.group(5)
351
+ # Conversion is irrelevant; they're aliases for the same thing.
352
+
353
+ fmt = "{:"
354
+
355
+ # Handle flags
356
+ # These mean the same for python as they do for C,
357
+ # with the exception of '-' in C being '<' in Python
358
+ flags = handle_flags(flags)
359
+ fmt += flags
360
+
361
+ # Handle width
362
+ width = handle_width(width, varargs, emulator)
363
+ fmt += width
364
+
365
+ # Handle precision
366
+ # Precision in ints acts like a second width,
367
+ # which always has the '0' flag enabled.
368
+ # This isn't implemented in python, which means we need to do it piecewise
369
+ if precision != "":
370
+ raise NotImplementedError("TODO: Implemente precision for ints")
371
+
372
+ # Handle length
373
+ if length in ("hh", "h", ""):
374
+ # Variadics don't go smaller than 'int'
375
+ val = varargs.get_next_argument(ArgumentType.INT, emulator)
376
+ elif length == "l":
377
+ val = varargs.get_next_argument(ArgumentType.LONG, emulator)
378
+ elif length in ("ll", "q"):
379
+ val = varargs.get_next_argument(ArgumentType.LONGLONG, emulator)
380
+ elif length in ("z", "Z"):
381
+ val = varargs.get_next_argument(ArgumentType.SSIZE_T, emulator)
382
+ elif length == "j":
383
+ raise NotImplementedError("Type 'intmax_t' not handled")
384
+ elif length == "t":
385
+ raise NotImplementedError("Type 'ptrdiff_t' not handled")
386
+ else:
387
+ raise FormatConversionError(f"Unknown type specifier {length}")
388
+
389
+ assert isinstance(val, int)
390
+
391
+ fmt += "d}"
392
+
393
+ return fmt.format(val)
394
+
395
+
396
+ def handle_udecint(
397
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
398
+ ) -> str:
399
+ flags = m.group(1)
400
+ width = m.group(2)
401
+ precision = m.group(3)
402
+ length = m.group(5)
403
+ # Conversion is irrelevant; they're aliases for the same thing.
404
+
405
+ fmt = "{:"
406
+
407
+ # Handle flags
408
+ # These mean the same for python as they do for C,
409
+ # with the exception of '-' in C being '<' in Python
410
+ flags = handle_flags(flags)
411
+ fmt += flags
412
+
413
+ # Handle width
414
+ width = handle_width(width, varargs, emulator)
415
+ fmt += width
416
+
417
+ # Handle precision
418
+ # Precision in ints acts like a second width,
419
+ # which always has the '0' flag enabled.
420
+ # This isn't implemented in python, which means we need to do it piecewise
421
+ if precision != "":
422
+ raise NotImplementedError("TODO: Implemente precision for ints")
423
+
424
+ # Handle length
425
+ if length in ("hh", "h", ""):
426
+ # Variadics don't go smaller than 'int'
427
+ val = varargs.get_next_argument(ArgumentType.UINT, emulator)
428
+ elif length == "l":
429
+ val = varargs.get_next_argument(ArgumentType.ULONG, emulator)
430
+ elif length in ("ll", "q"):
431
+ val = varargs.get_next_argument(ArgumentType.ULONGLONG, emulator)
432
+ elif length in ("z", "Z"):
433
+ val = varargs.get_next_argument(ArgumentType.SIZE_T, emulator)
434
+ elif length == "j":
435
+ raise NotImplementedError("Type 'intmax_t' not handled")
436
+ elif length == "t":
437
+ raise NotImplementedError("Type 'ptrdiff_t' not handled")
438
+ else:
439
+ raise FormatConversionError(f"Unknown type specifier {length}")
440
+ assert isinstance(val, int)
441
+
442
+ fmt += "d}"
443
+
444
+ return fmt.format(val)
445
+
446
+
447
+ def handle_uint(
448
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
449
+ ) -> str:
450
+ flags = m.group(1)
451
+ width = m.group(2)
452
+ precision = m.group(3)
453
+ length = m.group(5)
454
+ conv = m.group(6)
455
+
456
+ # Handle flags
457
+ # These mean the same for python as they do for C,
458
+ # with the exception of '-' in C being '<' in Python
459
+ flags = handle_flags(flags)
460
+ # In C, octals can have both '#' and '0' flags.
461
+ # In practice, the '#' is redundant, and forbidden by Python
462
+ if "0" in flags:
463
+ flags = flags.replace("#", "")
464
+
465
+ # Handle width
466
+ width = handle_width(width, varargs, emulator)
467
+
468
+ # Handle precision
469
+ # Precision in ints acts like a second width,
470
+ # which always has the '0' flag enabled.
471
+ # This isn't implemented in python, which means we need to do it piecewise
472
+ if precision != "":
473
+ raise NotImplementedError("TODO: Implemente precision for ints")
474
+
475
+ # Handle length
476
+ if length in ("hh", "h", ""):
477
+ # Variadics don't go smaller than 'int'
478
+ val = varargs.get_next_argument(ArgumentType.UINT, emulator)
479
+ elif length == "l":
480
+ val = varargs.get_next_argument(ArgumentType.ULONG, emulator)
481
+ elif length in ("ll", "q"):
482
+ val = varargs.get_next_argument(ArgumentType.ULONGLONG, emulator)
483
+ elif length in ("z", "Z"):
484
+ val = varargs.get_next_argument(ArgumentType.SIZE_T, emulator)
485
+ elif length == "j":
486
+ raise NotImplementedError("Type 'intmax_t' not handled")
487
+ elif length == "t":
488
+ raise NotImplementedError("Type 'ptrdiff_t' not handled")
489
+ else:
490
+ raise FormatConversionError(f"Unknown type specifier {length}")
491
+
492
+ assert isinstance(val, int)
493
+
494
+ fmt = "{:"
495
+ fmt += flags
496
+ fmt += width
497
+ fmt += conv
498
+ fmt += "}"
499
+ res = fmt.format(val)
500
+ if "#" in flags and conv == "o":
501
+ if width != "":
502
+ int_width = int(width)
503
+ else:
504
+ int_width = 0
505
+ extend = len(res) == int_width
506
+ res = res.replace("0o", "0")
507
+ if extend:
508
+ res = " " + res
509
+
510
+ return res
511
+
512
+
513
+ def handle_sci(
514
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
515
+ ) -> str:
516
+ flags = m.group(1)
517
+ width = m.group(2)
518
+ precision = m.group(3)
519
+ length = m.group(5)
520
+ conv = m.group(6)
521
+
522
+ fmt = "{:"
523
+
524
+ # Handle flags
525
+ # These mean the same for python as they do for C,
526
+ # with the exception of '-' in C being '<' in Python
527
+ flags = handle_flags(flags)
528
+ fmt += flags
529
+
530
+ # Handle width
531
+ width = handle_width(width, varargs, emulator)
532
+ fmt += width
533
+
534
+ # Handle precision
535
+ precision = handle_precision(precision, varargs, emulator)
536
+ fmt += precision
537
+
538
+ # Handle length
539
+ val = handle_double_arg(length, varargs, emulator)
540
+
541
+ fmt += conv
542
+ fmt += "}"
543
+
544
+ out = fmt.format(val)
545
+
546
+ # Handle negative NaN.
547
+ # Python prints it as positive NaN.
548
+ byteval = struct.pack("<d", val)
549
+ intval = int.from_bytes(byteval, "little")
550
+ if intval == 0xFFF8000000000000:
551
+ text = ("{:" + conv + "}").format(val)
552
+ index = out.index(text)
553
+ if index == 0:
554
+ out = out.replace(text, "-" + text)
555
+ else:
556
+ out = out[: index - 1] + "-" + text + out[index + 3 :]
557
+
558
+ return out
559
+
560
+
561
+ def handle_float(
562
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
563
+ ) -> str:
564
+ flags = m.group(1)
565
+ width = m.group(2)
566
+ precision = m.group(3)
567
+ length = m.group(5)
568
+ conv = m.group(6)
569
+
570
+ fmt = "{:"
571
+
572
+ # Handle flags
573
+ # These mean the same for python as they do for C,
574
+ # with the exception of '-' in C being '<' in Python
575
+ flags = handle_flags(flags)
576
+ fmt += flags
577
+
578
+ # Handle width
579
+ width = handle_width(width, varargs, emulator)
580
+ fmt += width
581
+
582
+ # Handle precision
583
+ precision = handle_precision(precision, varargs, emulator)
584
+ fmt += precision
585
+
586
+ # Handle length
587
+ val = handle_double_arg(length, varargs, emulator)
588
+
589
+ fmt += conv
590
+ fmt += "}"
591
+
592
+ out = fmt.format(val)
593
+
594
+ # Handle negative NaN.
595
+ # Python prints it as positive NaN.
596
+ byteval = struct.pack("<d", val)
597
+ intval = int.from_bytes(byteval, "little")
598
+ if intval == 0xFFF8000000000000:
599
+ text = ("{:" + conv + "}").format(val)
600
+ index = out.index(text)
601
+ if index == 0:
602
+ out = out.replace(text, "-" + text)
603
+ else:
604
+ out = out[: index - 1] + "-" + text + out[index + 3 :]
605
+
606
+ return out
607
+
608
+
609
+ def handle_scifloat(
610
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
611
+ ) -> str:
612
+ flags = m.group(1)
613
+ width = m.group(2)
614
+ precision = m.group(3)
615
+ length = m.group(5)
616
+ conv = m.group(6)
617
+
618
+ fmt = "{:"
619
+
620
+ # Handle flags
621
+ # These mean the same for python as they do for C,
622
+ # with the exception of '-' in C being '<' in Python
623
+ flags = handle_flags(flags)
624
+ fmt += flags
625
+
626
+ # Handle width
627
+ width = handle_width(width, varargs, emulator)
628
+ fmt += width
629
+
630
+ # Handle precision
631
+ precision = handle_precision(precision, varargs, emulator)
632
+ fmt += precision
633
+
634
+ # Handle length
635
+ val = handle_double_arg(length, varargs, emulator)
636
+
637
+ fmt += conv
638
+ fmt += "}"
639
+
640
+ out = fmt.format(val)
641
+
642
+ # Handle negative NaN.
643
+ # Python prints it as positive NaN.
644
+ byteval = struct.pack("<d", val)
645
+ intval = int.from_bytes(byteval, "little")
646
+
647
+ if intval == 0xFFF8000000000000:
648
+ text = ("{:" + conv + "}").format(val)
649
+ index = out.index(text)
650
+ if index == 0:
651
+ out = out.replace(text, "-" + text)
652
+ else:
653
+ out = out[: index - 1] + "-" + text + out[index + 3 :]
654
+
655
+ return out
656
+
657
+
658
+ def handle_hexfloat(
659
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
660
+ ) -> str:
661
+ # Python doesn't have an easy conversion for hex floats
662
+ # And I am not writing one unless you absolutely need it.
663
+ raise NotImplementedError("Hexadecimal float conversion not supported")
664
+
665
+
666
+ def handle_char(
667
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
668
+ ) -> str:
669
+ flags = m.group(1)
670
+ width = m.group(2)
671
+ length = m.group(5)
672
+ conv = m.group(6)
673
+
674
+ fmt = "{:"
675
+
676
+ # Handle flags
677
+ # These mean the same for python as they do for C,
678
+ # with the exception of '-' in C being '<' in Python
679
+ flags = handle_flags(flags)
680
+
681
+ # Handle width
682
+ width = handle_width(width, varargs, emulator)
683
+ fmt += width
684
+
685
+ # Handle length
686
+ if length == "":
687
+ val = varargs.get_next_argument(ArgumentType.UINT, emulator)
688
+ elif length == "l":
689
+ raise NotImplementedError("Type 'wchar_t' not handled")
690
+ else:
691
+ raise FormatConversionError(f"Unknown type specifier {length}")
692
+
693
+ assert isinstance(val, int)
694
+ val = val & 0xFF
695
+
696
+ fmt += conv
697
+ fmt += "}"
698
+
699
+ return fmt.format(val)
700
+
701
+
702
+ def handle_str(
703
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
704
+ ) -> str:
705
+ flags = m.group(1)
706
+ width = m.group(2)
707
+ precision = m.group(3)
708
+ length = m.group(5)
709
+ conv = m.group(6)
710
+
711
+ fmt = "{:"
712
+
713
+ # Handle flags
714
+ # These mean the same for python as they do for C,
715
+ # with the exception of '-' in C being '<' in Python
716
+ flags = handle_flags(flags)
717
+ fmt += flags
718
+
719
+ # Handle width
720
+ width = handle_width(width, varargs, emulator)
721
+ fmt += width
722
+
723
+ # Handle precision
724
+ # Highly fortunately, this works the same in python
725
+ precision = handle_precision(precision, varargs, emulator, use_default=False)
726
+ fmt += precision
727
+
728
+ # Handle length
729
+ if length == "":
730
+ addr = varargs.get_next_argument(ArgumentType.POINTER, emulator)
731
+ elif length == "l":
732
+ raise NotImplementedError("Type 'wchar_t *' not handled")
733
+ else:
734
+ raise FormatConversionError(f"Unknown type specifier {length}")
735
+
736
+ assert isinstance(addr, int)
737
+
738
+ val_len = _emu_strlen(emulator, addr)
739
+ val_bytes = emulator.read_memory(addr, val_len)
740
+ val = val_bytes.decode("utf-8")
741
+
742
+ fmt += conv
743
+ fmt += "}"
744
+
745
+ return fmt.format(val)
746
+
747
+
748
+ def handle_pointer(
749
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
750
+ ) -> str:
751
+ flags = m.group(1)
752
+ width = m.group(2)
753
+
754
+ fmt = "{:"
755
+
756
+ # Handle flags
757
+ # These mean the same for python as they do for C,
758
+ # with the exception of '-' in C being '<' in Python
759
+ flags = handle_flags(flags)
760
+ fmt += flags + "#"
761
+
762
+ # Handle width
763
+ width = handle_width(width, varargs, emulator)
764
+ fmt += width
765
+
766
+ val = varargs.get_next_argument(ArgumentType.POINTER, emulator)
767
+ assert isinstance(val, int)
768
+
769
+ fmt += "x}"
770
+
771
+ return fmt.format(val)
772
+
773
+
774
+ def handle_len(
775
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
776
+ ) -> str:
777
+ length = m.group(5)
778
+
779
+ addr = varargs.get_next_argument(ArgumentType.POINTER, emulator)
780
+ assert isinstance(addr, int)
781
+
782
+ val = len(output)
783
+ if length == "hh":
784
+ val = val & 0xFF
785
+ width = 1
786
+ elif length == "h":
787
+ val = val & 0xFFFF
788
+ width = 2
789
+ elif length == "":
790
+ val = val & varargs._int_inv_mask
791
+ width = 4
792
+ elif length in ("l", "z", "Z"):
793
+ val = val & varargs._long_inv_mask
794
+ width = 4 if ArgumentType.LONG in varargs._four_byte_types else 8
795
+ elif length in ("ll", "q"):
796
+ val = val & varargs._long_long_inv_mask
797
+ width = 8
798
+ elif length == "j":
799
+ raise NotImplementedError("Type 'intmax_t' not handled")
800
+ elif length == "t":
801
+ raise NotImplementedError("Type 'ptrdiff_t' not handled")
802
+ else:
803
+ raise FormatConversionError(f"Unknown type specifier {length}")
804
+
805
+ if varargs.platform.byteorder == Byteorder.LITTLE:
806
+ data = val.to_bytes(width, "little")
807
+ else:
808
+ data = val.to_bytes(width, "big")
809
+
810
+ emulator.write_memory(addr, data)
811
+
812
+ return ""
813
+
814
+
815
+ def handle_strerror(
816
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
817
+ ) -> str:
818
+ # This is weird. It's a glibc extension, but gcc errors if you use it.
819
+ # If you actually see this, please open a bug ticket.
820
+ raise NotImplementedError(
821
+ "%m conversion (strerror) not implemented; if you see this, submit a ticket."
822
+ )
823
+
824
+
825
+ def handle_percent(
826
+ output: str, varargs: VariadicContext, m: re.Match, emulator: Emulator
827
+ ) -> str:
828
+ # If this has any configurable options, I will be most displeased.
829
+ return "%"
830
+
831
+
832
+ def parse_printf_format(model: CStdModel, fmt: str, emulator: Emulator) -> str:
833
+ """Create a string based on a printf format
834
+
835
+ This doesn't model all features of the printf family.
836
+ The following features are missing:
837
+
838
+ - The %a and %A hexadecimal float conversions
839
+ - The %m strerror conversion
840
+ - intmax_t integer size
841
+ - ptrdiff_t integer size
842
+ - long double float size
843
+
844
+ Arguments:
845
+ model: The parent model
846
+ fmt: The format string
847
+ emulator: The emulator to query
848
+
849
+ Returns:
850
+ A string formatted according to 'fmt'
851
+ """
852
+ handlers: typing.List[
853
+ typing.Tuple[
854
+ re.Pattern, typing.Callable[[str, VariadicContext, re.Match, Emulator], str]
855
+ ]
856
+ ] = [
857
+ (sdecint_re, handle_sdecint),
858
+ (udecint_re, handle_udecint),
859
+ (uint_re, handle_uint),
860
+ (sci_re, handle_sci),
861
+ (float_re, handle_float),
862
+ (scifloat_re, handle_scifloat),
863
+ (hexfloat_re, handle_hexfloat),
864
+ (char_re, handle_char),
865
+ (str_re, handle_str),
866
+ (pointer_re, handle_pointer),
867
+ (len_re, handle_len),
868
+ (strerror_re, handle_strerror),
869
+ (percent_re, handle_percent),
870
+ ]
871
+
872
+ varargs = model.get_varargs()
873
+
874
+ orig_fmt = fmt
875
+ output = ""
876
+
877
+ while len(fmt) > 0:
878
+ if fmt.startswith("%"):
879
+ matched = False
880
+ for regex, handler in handlers:
881
+ if isinstance(regex, str):
882
+ raise Exception(f"String: {regex}")
883
+ m = regex.match(fmt)
884
+ if m is not None:
885
+ try:
886
+ output += handler(output, varargs, m, emulator)
887
+ except FormatConversionError as e:
888
+ print(f"Bad format conversion: {e.args[0]}")
889
+ print(orig_fmt)
890
+ print(" " * (len(orig_fmt) - len(fmt)) + "^")
891
+ raise e
892
+ except Exception as e:
893
+ print(f"Exception processing conversion: {type(e)}: {e}")
894
+ print(orig_fmt)
895
+ print(" " * (len(orig_fmt) - len(fmt)) + "^")
896
+ raise e
897
+
898
+ fmt = fmt[len(m.group(0)) :]
899
+ matched = True
900
+ break
901
+
902
+ if not matched:
903
+ print("Bad format conversion: Unmatched conversion")
904
+ print(orig_fmt)
905
+ print(" " * (len(orig_fmt) - len(fmt)) + "^")
906
+ raise Exception("Bad format conversion")
907
+
908
+ else:
909
+ output += fmt[0]
910
+ fmt = fmt[1:]
911
+
912
+ return output
913
+
914
+
915
+ __all__ = ["parse_printf_format"]