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,515 @@
1
+ import io
2
+ import sys
3
+ import typing
4
+
5
+ from ...exceptions import ConfigurationError
6
+ from ...platforms import ABI, Platform
7
+
8
+
9
+ class FDIOError(Exception):
10
+ """Exception indicating an error case in the file IO model"""
11
+
12
+ pass
13
+
14
+
15
+ class FileDescriptor:
16
+ """File Descriptor Representation"""
17
+
18
+ def __init__(
19
+ self,
20
+ name: str,
21
+ readable: bool = False,
22
+ writable: bool = False,
23
+ seekable: bool = False,
24
+ ):
25
+ self.name = name
26
+
27
+ self.readable = readable
28
+ self.writable = writable
29
+ self.seekable = seekable
30
+
31
+ self.ungetc_buf = b""
32
+
33
+ self.cursor = 0
34
+
35
+ self.eof = False
36
+
37
+ @property
38
+ def _backing(self) -> typing.IO:
39
+ # Files aren't pickleable,
40
+ # and anyway we don't want contention on one file object.
41
+ # Instead, dynamically produce the backing file-like,
42
+ # with the understanding that it may need special handling.
43
+ raise FDIOError("File {self.name} has no backing")
44
+
45
+ def read(self, size: int, ungetc: bool = False) -> bytes:
46
+ """Read data from this file descriptor
47
+
48
+ Arguments:
49
+ size: Number of bytes to read
50
+ ungetc: Set to true to use the 'ungetc' buffer
51
+ """
52
+ if not self.readable:
53
+ raise FDIOError(f"File {self.name} is not readable")
54
+
55
+ # NOTE: This integrates support for ungetc.
56
+ # This feature is technically part of FILE * objects;
57
+ # ungetc effectively appends bytes to the beg
58
+ ungetc_data = b""
59
+ if ungetc and len(self.ungetc_buf) > 0:
60
+ if size == -1:
61
+ ungetc_data = self.ungetc_buf
62
+ self.ungetc_buf = b""
63
+ else:
64
+ ungetc_data = self.ungetc_buf[0:size]
65
+ self.ungetc_buf = self.ungetc_buf[size:]
66
+
67
+ size -= len(ungetc_data)
68
+
69
+ file = self._backing
70
+
71
+ if self.seekable:
72
+ # File is seekable; mimic it against the back end.
73
+ file.seek(self.cursor, 0)
74
+
75
+ data: typing.Union[str, bytes] = b""
76
+ if size != 0:
77
+ data = file.read(size)
78
+ self.cursor += len(data)
79
+ if len(data) != size:
80
+ print("EOF")
81
+ self.eof = True
82
+
83
+ if isinstance(data, str):
84
+ # This is a text file. Need to encode the result
85
+ assert hasattr(file, "encoding")
86
+ return ungetc_data + data.encode(file.encoding)
87
+ else:
88
+ return ungetc_data + data
89
+
90
+ def read_string(self, size: int = -1) -> bytes:
91
+ """Read a newline-terminated string from this file
92
+
93
+ Arguments
94
+ size: Maximum number of bytes to read. Defaults to as many as possible
95
+
96
+ Returns:
97
+ String read from file
98
+ """
99
+ out = b""
100
+ size -= 1
101
+ while size != 0:
102
+ c = self.read(1)
103
+ if len(c) < 1:
104
+ break
105
+ out += c
106
+
107
+ if c == b"\n":
108
+ break
109
+
110
+ if size > 0:
111
+ size -= 1
112
+ out += b"\0"
113
+ return out
114
+
115
+ def write(self, data: bytes) -> None:
116
+ """Write data to this file
117
+
118
+ Arguments:
119
+ data: Bytes to write to this file
120
+ """
121
+ if not self.writable:
122
+ raise FDIOError(f"File {self.name} is not writable")
123
+
124
+ # TODO: Model the effects of ungetc.
125
+ # By observation, mixing writes with ungetc is unsupported;
126
+ # the test program segfaulted when I wran fputc after ungetc
127
+
128
+ file = self._backing
129
+
130
+ if self.seekable:
131
+ file.seek(self.cursor, 0)
132
+
133
+ if hasattr(file, "encoding"):
134
+ # File is a text file. Need to encode the results
135
+ file.write(data.decode(file.encoding))
136
+ else:
137
+ file.write(data)
138
+
139
+ self.cursor += len(data)
140
+
141
+ def seek(self, pos: int, whence: int) -> int:
142
+ """Set the cursor for this file
143
+
144
+ Arguments:
145
+ pos: Position, possibly relative
146
+ whence: How to interpret 'pos'. See real implementations for possible values
147
+ """
148
+ if not self.seekable:
149
+ raise FDIOError(f"File {self.name} is not seekable")
150
+
151
+ if whence == 0:
152
+ self.cursor = pos
153
+ elif whence == 1:
154
+ self.cursor += pos
155
+ elif whence == 2:
156
+ # Figure out how to find the end of the stream
157
+ raise NotImplementedError()
158
+ else:
159
+ raise FDIOError(f"Unknown 'whence' {whence} when seeking {self.name}")
160
+
161
+ return self.cursor
162
+
163
+ def ungetc(self, char: int) -> None:
164
+ """Push a character back to the buffer
165
+
166
+ In real life, this is only supported by stdio FILE* objects.
167
+ It pushes the character to an internal buffer that's
168
+ read from before the actual stream gets read.
169
+
170
+ Its interactions with 'write', 'seek', and readingf from 'cursor'
171
+ are all undefined.
172
+
173
+ Arguments:
174
+ char: Character to push
175
+ """
176
+ if not self.readable:
177
+ raise FDIOError(f"File {self.name} is not readable")
178
+
179
+ self.ungetc_buf = bytes([char]) + self.ungetc_buf
180
+
181
+
182
+ class BytesFileDescriptor(FileDescriptor):
183
+ """File descriptor backed by a byte string."""
184
+
185
+ def __init__(
186
+ self,
187
+ name: str,
188
+ initial: typing.Union[bytes, io.BytesIO] = b"",
189
+ readable: bool = False,
190
+ writable: bool = False,
191
+ seekable: bool = False,
192
+ ):
193
+ super().__init__(name, readable=readable, writable=writable, seekable=seekable)
194
+ if isinstance(initial, bytes):
195
+ initial = io.BytesIO(initial)
196
+ self.backing_io = initial
197
+
198
+ @property
199
+ def _backing(self) -> typing.IO:
200
+ return self.backing_io
201
+
202
+
203
+ class StdinFileDescriptor(FileDescriptor):
204
+ """File descriptor backed by host's stdin.
205
+
206
+ Read-only, not seekable.
207
+ """
208
+
209
+ def __init__(self):
210
+ super().__init__("stdin", readable=True)
211
+
212
+ @property
213
+ def _backing(self) -> typing.IO:
214
+ return sys.stdin
215
+
216
+
217
+ class StdoutFileDescriptor(FileDescriptor):
218
+ """File descriptor backed by host's stdout.
219
+
220
+ Write-only, not seekable.
221
+ """
222
+
223
+ def __init__(self):
224
+ super().__init__("stdout", writable=True)
225
+
226
+ @property
227
+ def _backing(self) -> typing.IO:
228
+ return sys.stdout
229
+
230
+
231
+ class StderrFileDescriptor(FileDescriptor):
232
+ """File descriptor backed by host's stderr.
233
+
234
+ Write-only, not seekable.
235
+ """
236
+
237
+ def __init__(self):
238
+ super().__init__("stderr", writable=True)
239
+
240
+ @property
241
+ def _backing(self) -> typing.IO:
242
+ return sys.stderr
243
+
244
+
245
+ class FileDescriptorManager:
246
+ """Manager for wrangling file descriptors
247
+
248
+ This handles open, close, and other file descriptor
249
+ manipulation operations. It also lets you access the file descriptor
250
+ representations themselves.
251
+
252
+ This is a unified model that supports C99,
253
+ and is pretty much identical between System V and Windows.
254
+
255
+ It also supports an extermely basic filesystem model.
256
+ It's disabled by default, and enabled by setting 'model_fs' to True.
257
+ Its utility is currently extremely limited;
258
+ it has no directory tree or permissions model,
259
+ but it can help for very basic interactions.
260
+
261
+ There can absolutely be ABI-specific subclasses.
262
+ """
263
+
264
+ _singletons: typing.Dict[
265
+ typing.Tuple[Platform, ABI], "FileDescriptorManager"
266
+ ] = dict()
267
+
268
+ def __init__(self):
269
+ # Enables full FS modeling.
270
+ # By default, this will
271
+ self.model_fs = False
272
+
273
+ self._fds = dict()
274
+ self._files = dict()
275
+
276
+ # Allocate stdstreams
277
+ # NOTE: This is POSIX-specific
278
+ self._fds[0] = StdinFileDescriptor()
279
+ self._fds[1] = StdoutFileDescriptor()
280
+ self._fds[2] = StderrFileDescriptor()
281
+
282
+ def add_file(self, name: str, init: bytes = b"") -> None:
283
+ """Add a file to the manager
284
+
285
+ This allows basic support for opening files
286
+ without creating them.
287
+
288
+ As of now, there is no actual file tree support;
289
+ whatever string is used as the name is what will match.
290
+
291
+ Arguments:
292
+ name: Name or path of the file to place
293
+ init: Initial bytes contained in that file
294
+ """
295
+ if not self.model_fs:
296
+ raise ConfigurationError("Full FS support not enabled.")
297
+
298
+ self._files[name] = io.BytesIO(init)
299
+
300
+ def get_file(self, name: str) -> io.BytesIO:
301
+ """Get the backing stream behind a file
302
+
303
+ Arguments:
304
+ name: The name to look up
305
+
306
+ Returns:
307
+ The file-like object storing the byte stream.
308
+ """
309
+ if name in self._files:
310
+ return self._files[name]
311
+
312
+ raise KeyError(f"No file named {name}")
313
+
314
+ def open(
315
+ self,
316
+ name: str,
317
+ readable: bool,
318
+ writable: bool,
319
+ create: bool,
320
+ truncate: bool,
321
+ append: bool,
322
+ seekable: bool = True,
323
+ ) -> int:
324
+ """Mimic opening a file
325
+
326
+ This will behave a bit differently depending
327
+ on whether 'self.model_fs' is set.
328
+
329
+ If it's unset, it will create file descriptors blindly,
330
+ and attempting to interact with them will raise exceptions.
331
+
332
+ If it's set, it will use the basic file model; see add_file() and get_file().
333
+
334
+ As of now, there is no actual file tree support;
335
+ the name is merely used as a label.
336
+
337
+ Arguments:
338
+ name: Name of the file to open
339
+ readable: Whether the file should be readable
340
+ writable: Whether the file should be writable
341
+ create: Whether the file should be created if it doesn't exist.
342
+ truncate: Whether the file should be truncated when opened
343
+ append: Whether the cursor should be set at the end of the file
344
+ seekable: Whether the file should be seekable
345
+
346
+ Returns:
347
+ An integer file descriptor
348
+ """
349
+
350
+ # Limited to 256 file descriptors thanks to shenanigans with FILE * management.
351
+ # If you need more than 256 file descriptors in a micro-execution context,
352
+ # I have many questions.
353
+ for fd in range(0, 1 << 8):
354
+ if fd not in self._fds:
355
+ if self.model_fs:
356
+ if name not in self._files:
357
+ if not create:
358
+ raise FDIOError(
359
+ f"{name} does not exist, and creation not specified"
360
+ )
361
+ self.add_file(name, b"")
362
+
363
+ initial = self._files[name]
364
+
365
+ if truncate:
366
+ initial.truncate()
367
+
368
+ self._fds[fd] = BytesFileDescriptor(
369
+ name,
370
+ initial,
371
+ readable=readable,
372
+ writable=writable,
373
+ seekable=seekable,
374
+ )
375
+
376
+ if append:
377
+ self._fds[fd].cursor = len(initial.getvalue())
378
+ else:
379
+ self._fds[fd] = FileDescriptor(
380
+ name, readable=readable, writable=writable, seekable=seekable
381
+ )
382
+ return fd
383
+ raise FDIOError("Ran out of fds")
384
+
385
+ def close(self, fd: int) -> None:
386
+ """Close a file
387
+
388
+ Arguments:
389
+ fd: Integer file descriptor
390
+ """
391
+ if fd not in self._fds:
392
+ raise FDIOError(f"Unknown fd {fd}")
393
+
394
+ del self._fds[fd]
395
+
396
+ def get(self, fd: int) -> FileDescriptor:
397
+ """Get the file representaiton by its integer file descriptor
398
+
399
+ Arguments:
400
+ fd: The integer file descriptor
401
+
402
+ Returns:
403
+ The file representation tied to 'fd'.
404
+ """
405
+ if fd not in self._fds:
406
+ raise FDIOError(f"Unknown fd {fd}")
407
+
408
+ return self._fds[fd]
409
+
410
+ def rename(self, old: str, new: str) -> None:
411
+ """Rename a file.
412
+
413
+ Only available if model_fs is set.
414
+
415
+ As of now, there is no actual file tree support;
416
+ the name is merely used as a label.
417
+
418
+ Arguments:
419
+ old: The current name of the file
420
+ new: The new name of the file
421
+ """
422
+ if not self.model_fs:
423
+ return
424
+
425
+ if old not in self._files:
426
+ raise FDIOError(f"Unknown file {old}")
427
+ self._files[new] = self._files[old]
428
+ del self._files[old]
429
+
430
+ def remove(self, name: str) -> bool:
431
+ """Remove a file
432
+
433
+ Is a no-op if model_fs is not set.
434
+
435
+ As of now, there is no actual file tree support;
436
+ the name is merely used as a label.
437
+
438
+ Arguments:
439
+ name: Name of the file to remove
440
+ Returns:
441
+ True if model_fs is not set, or if the file was removed
442
+ """
443
+
444
+ if self.model_fs:
445
+ if name in self._files:
446
+ del self._files[name]
447
+ return True
448
+ return False
449
+ else:
450
+ return True
451
+
452
+ # Magic number for identifying FILE * pointers created by this API.
453
+ # ASCII for 'FI*'
454
+ #
455
+ # FILE * created by these models don't point to real structs.
456
+ # Rather, they're an encoded form of the file descriptor;
457
+ # the fd number is the LSB, with magic in bytes 1 through 3.
458
+ #
459
+ # On a 64-bit ABI, the upper four bytes should be zero;
460
+ # I don't want to write two models for this.
461
+ #
462
+ # Functions that allow for multiple FILE * structs per FD
463
+ # can go jump in a puddle.
464
+ filestar_magic = 0x47492A
465
+
466
+ @classmethod
467
+ def filestar_to_fd(cls, ptr: int) -> int:
468
+ """Convert a FILE* pointer to an integer file descriptor
469
+
470
+ This will attempt to detect if the FILE* was actually created
471
+ by SmallWorld's model. If not, it raises an exception.
472
+
473
+ Arguments:
474
+ ptr: The FILE* pointer to decode
475
+
476
+ Returns:
477
+ The associated integer file descriptor
478
+ """
479
+ if (ptr >> 8) != cls.filestar_magic:
480
+ raise FDIOError(f"FILE * {hex(ptr)} is not a FILE * created by this model.")
481
+
482
+ return ptr & 0xFF
483
+
484
+ @classmethod
485
+ def fd_to_filestar(cls, fd: int) -> int:
486
+ """Convert an integer file descriptor to a FILE* pointer
487
+
488
+ Arguments:
489
+ fd: The integer file descriptor to encode
490
+
491
+ Returns:
492
+ The associated FILE* pointer
493
+ """
494
+ return cls.filestar_magic << 8 | fd
495
+
496
+ @classmethod
497
+ def for_platform(cls, platform: Platform, abi: ABI):
498
+ """Get an instance of this class for the desired platform
499
+
500
+ NOTE: This isn't a true singleton, and I want it that way.
501
+ Everything that asks for a manager during setup
502
+ should get the same instance,
503
+ but deep-copies of Machines should get their own managers
504
+
505
+ Arguments:
506
+ platform: The desired platform
507
+ abi: The desired ABI
508
+
509
+ Returns:
510
+ An instance of the manager for the platform
511
+ """
512
+ if (platform, abi) not in cls._singletons:
513
+ # TODO: Actually implement this when I have multiple implementations
514
+ cls._singletons[(platform, abi)] = cls()
515
+ return cls._singletons[(platform, abi)]
@@ -0,0 +1 @@
1
+ from .systemv import __all__ as __sysv__ # noqa: F401
@@ -0,0 +1,6 @@
1
+ from .c99 import * # noqa: F401,F403
2
+ from .c99 import __all__ as __c99__
3
+ from .posix import * # noqa: F401,F403
4
+ from .posix import __all__ as __posix__
5
+
6
+ __all__ = __c99__ + __posix__
@@ -0,0 +1,12 @@
1
+ from .signal import * # noqa F401,F403
2
+ from .signal import __all__ as __signal__
3
+ from .stdio import * # noqa F401,F403
4
+ from .stdio import __all__ as __stdio__
5
+ from .stdlib import * # noqa F401,F403
6
+ from .stdlib import __all__ as __stdlib__
7
+ from .string import * # noqa F401,F403
8
+ from .string import __all__ as __string__
9
+ from .time import * # noqa F401,F403
10
+ from .time import __all__ as __time__
11
+
12
+ __all__ = __signal__ + __stdlib__ + __string__ + __stdio__ + __time__
@@ -0,0 +1,16 @@
1
+ from ....c99.signal import Raise, Signal
2
+ from ..systemv import I386SysVModel
3
+
4
+
5
+ class I386SysVRaise(Raise, I386SysVModel):
6
+ pass
7
+
8
+
9
+ class I386SysVSignal(Signal, I386SysVModel):
10
+ pass
11
+
12
+
13
+ __all__ = [
14
+ "I386SysVRaise",
15
+ "I386SysVSignal",
16
+ ]