wordcode.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. # (C) Copyright 2018, 2020-2021, 2023, 2025 by Rocky Bernstein
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. """Python disassembly functions specific to wordcode from Python 3.6+"""
  17. from xdis.bytecode import op_has_argument
  18. from xdis.cross_dis import get_cache_size_313, unpack_opargs_bytecode_310
  19. def unpack_opargs_wordcode(code, opc):
  20. extended_arg = 0
  21. try:
  22. n = len(code)
  23. except TypeError:
  24. code = code.co_code
  25. n = len(code)
  26. if isinstance(code[0], str):
  27. # This happens handling Python 3.x on a 2.x interpreter
  28. for i in range(0, n, 2):
  29. op = ord(code[i])
  30. if op_has_argument(op, opc):
  31. arg = ord(code[i + 1]) | extended_arg
  32. extended_arg = (arg << 8) if op == opc.EXTENDED_ARG else 0
  33. else:
  34. arg = None
  35. yield i, op, arg
  36. else:
  37. for i in range(0, n, 2):
  38. op = code[i]
  39. if op_has_argument(op, opc):
  40. arg = code[i + 1] | extended_arg
  41. extended_arg = (arg << 8) if op == opc.EXTENDED_ARG else 0
  42. else:
  43. arg = None
  44. yield i, op, arg
  45. def findlabels(code, opc):
  46. """Returns a list of instruction offsets in the supplied bytecode
  47. which are the targets of jump instruction.
  48. """
  49. unpack_opargs = (
  50. unpack_opargs_wordcode
  51. if opc.version_tuple < (3, 10)
  52. else unpack_opargs_bytecode_310
  53. )
  54. offsets = []
  55. for offset, op, arg in unpack_opargs(code, opc):
  56. if arg is not None:
  57. arg2 = arg * 2 if opc.version_tuple >= (3, 10) else arg
  58. if op in opc.JREL_OPS:
  59. if opc.version_tuple >= (3, 11) and opc.opname[op] in (
  60. "JUMP_BACKWARD",
  61. "JUMP_BACKWARD_NO_INTERRUPT",
  62. ):
  63. arg = -arg
  64. jump_offset = offset + 2 + arg2
  65. if opc.version_tuple >= (3, 13):
  66. jump_offset += 2 * get_cache_size_313(opc.opname[op])
  67. elif op in opc.JABS_OPS:
  68. jump_offset = arg2
  69. else:
  70. continue
  71. if jump_offset not in offsets:
  72. offsets.append(jump_offset)
  73. return offsets
  74. def get_jump_target_maps(code, opc) -> dict:
  75. """Returns a dictionary where the key is an offset, and the values are
  76. a list of instruction offsets which can get run before that
  77. instruction. This includes jump instructions as well as non-jump
  78. instructions. Therefore, the keys of the dictionary are reachable
  79. instructions. The values of the dictionary may be useful in control-flow
  80. analysis.
  81. """
  82. offset2prev = {}
  83. prev_offset = -1
  84. for offset, op, arg in unpack_opargs_wordcode(code, opc):
  85. if prev_offset >= 0:
  86. prev_list = offset2prev.get(offset, [])
  87. prev_list.append(prev_offset)
  88. offset2prev[offset] = prev_list
  89. prev_offset = offset
  90. if op in opc.NOFOLLOW:
  91. prev_offset = -1
  92. if arg is not None:
  93. jump_offset = -1
  94. if op in opc.JREL_OPS:
  95. jump_offset = offset + 2 + arg
  96. elif op in opc.JABS_OPS:
  97. jump_offset = arg
  98. if jump_offset >= 0:
  99. prev_list = offset2prev.get(jump_offset, [])
  100. prev_list.append(offset)
  101. offset2prev[jump_offset] = prev_list
  102. return offset2prev