cle 9.3.2__tar.gz → 9.3.4__tar.gz

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 (168) hide show
  1. {cle-9.3.2/cle.egg-info → cle-9.3.4}/PKG-INFO +3 -3
  2. {cle-9.3.2 → cle-9.3.4}/cle/__init__.py +11 -1
  3. {cle-9.3.2 → cle-9.3.4}/cle/backends/__init__.py +8 -0
  4. {cle-9.3.2 → cle-9.3.4}/cle/backends/coff.py +15 -8
  5. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/elf.py +33 -0
  6. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/metaelf.py +2 -2
  7. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/__init__.py +1 -0
  8. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/arm64.py +2 -1
  9. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/ppc64.py +6 -0
  10. cle-9.3.4/cle/backends/gopclntab.py +313 -0
  11. cle-9.3.4/cle/backends/java/dex.py +78 -0
  12. {cle-9.3.2 → cle-9.3.4}/cle/backends/java/soot.py +2 -0
  13. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/binding.py +42 -26
  14. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/macho.py +33 -12
  15. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/macho_enums.py +62 -1
  16. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/section.py +16 -4
  17. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/pe.py +28 -1
  18. {cle-9.3.2 → cle-9.3.4}/cle/backends/relocation.py +7 -1
  19. {cle-9.3.2 → cle-9.3.4}/cle/backends/stm32.py +45 -34
  20. {cle-9.3.2 → cle-9.3.4}/cle/backends/te.py +56 -16
  21. {cle-9.3.2 → cle-9.3.4}/cle/loader.py +37 -20
  22. {cle-9.3.2 → cle-9.3.4/cle.egg-info}/PKG-INFO +3 -3
  23. {cle-9.3.2 → cle-9.3.4}/cle.egg-info/SOURCES.txt +8 -0
  24. {cle-9.3.2 → cle-9.3.4}/cle.egg-info/requires.txt +2 -2
  25. {cle-9.3.2 → cle-9.3.4}/pyproject.toml +17 -2
  26. cle-9.3.4/tests/test_aarch64_relocations.py +119 -0
  27. {cle-9.3.2 → cle-9.3.4}/tests/test_arm_firmware.py +14 -0
  28. {cle-9.3.2 → cle-9.3.4}/tests/test_clemory.py +3 -0
  29. cle-9.3.4/tests/test_coff.py +79 -0
  30. cle-9.3.4/tests/test_dex.py +34 -0
  31. cle-9.3.4/tests/test_extern.py +84 -0
  32. cle-9.3.4/tests/test_gopclntab.py +151 -0
  33. {cle-9.3.2 → cle-9.3.4}/tests/test_macho.py +178 -0
  34. cle-9.3.4/tests/test_mips_n32.py +78 -0
  35. {cle-9.3.2 → cle-9.3.4}/tests/test_optional_backends.py +2 -0
  36. {cle-9.3.2 → cle-9.3.4}/tests/test_pe.py +17 -0
  37. {cle-9.3.2 → cle-9.3.4}/tests/test_ppc64_initial_rtoc.py +27 -0
  38. cle-9.3.4/tests/test_rebase.py +75 -0
  39. cle-9.3.4/tests/test_stm32.py +148 -0
  40. cle-9.3.4/tests/test_te.py +110 -0
  41. cle-9.3.2/tests/test_aarch64_relocations.py +0 -44
  42. cle-9.3.2/tests/test_coff.py +0 -37
  43. cle-9.3.2/tests/test_extern.py +0 -45
  44. {cle-9.3.2 → cle-9.3.4}/LICENSE +0 -0
  45. {cle-9.3.2 → cle-9.3.4}/MANIFEST.in +0 -0
  46. {cle-9.3.2 → cle-9.3.4}/README.md +0 -0
  47. {cle-9.3.2 → cle-9.3.4}/cle/address_translator.py +0 -0
  48. {cle-9.3.2 → cle-9.3.4}/cle/backends/backend.py +0 -0
  49. {cle-9.3.2 → cle-9.3.4}/cle/backends/binja.py +0 -0
  50. {cle-9.3.2 → cle-9.3.4}/cle/backends/blob.py +0 -0
  51. {cle-9.3.2 → cle-9.3.4}/cle/backends/cartfile.py +0 -0
  52. {cle-9.3.2 → cle-9.3.4}/cle/backends/cgc/__init__.py +0 -0
  53. {cle-9.3.2 → cle-9.3.4}/cle/backends/cgc/backedcgc.py +0 -0
  54. {cle-9.3.2 → cle-9.3.4}/cle/backends/cgc/cgc.py +0 -0
  55. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/__init__.py +0 -0
  56. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/compilation_unit.py +0 -0
  57. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/elfcore.py +0 -0
  58. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/hashtable.py +0 -0
  59. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/lsda.py +0 -0
  60. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/regions.py +0 -0
  61. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/amd64.py +0 -0
  62. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/arm.py +0 -0
  63. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/elfreloc.py +0 -0
  64. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/generic.py +0 -0
  65. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/i386.py +0 -0
  66. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/mips.py +0 -0
  67. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/ppc.py +0 -0
  68. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/s390x.py +0 -0
  69. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/sparc.py +0 -0
  70. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/subprogram.py +0 -0
  71. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/symbol.py +0 -0
  72. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/symbol_type.py +0 -0
  73. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/variable.py +0 -0
  74. {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/variable_type.py +0 -0
  75. {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/__init__.py +0 -0
  76. {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/__init__.py +0 -0
  77. {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/common.py +0 -0
  78. {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/glibc_startup.py +0 -0
  79. {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/io_file.py +0 -0
  80. {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/simdata.py +0 -0
  81. {cle-9.3.2 → cle-9.3.4}/cle/backends/ihex.py +0 -0
  82. {cle-9.3.2 → cle-9.3.4}/cle/backends/inlined_function.py +0 -0
  83. {cle-9.3.2 → cle-9.3.4}/cle/backends/java/__init__.py +0 -0
  84. {cle-9.3.2 → cle-9.3.4}/cle/backends/java/android_lifecycle.py +0 -0
  85. {cle-9.3.2 → cle-9.3.4}/cle/backends/java/apk.py +0 -0
  86. {cle-9.3.2 → cle-9.3.4}/cle/backends/java/jar.py +0 -0
  87. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/__init__.py +0 -0
  88. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/encrypted_sentinel_backer.py +0 -0
  89. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/segment.py +0 -0
  90. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/structs.py +0 -0
  91. {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/symbol.py +0 -0
  92. {cle-9.3.2 → cle-9.3.4}/cle/backends/minidump/__init__.py +0 -0
  93. {cle-9.3.2 → cle-9.3.4}/cle/backends/named_region.py +0 -0
  94. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/__init__.py +0 -0
  95. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/regions.py +0 -0
  96. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/__init__.py +0 -0
  97. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/arm.py +0 -0
  98. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/generic.py +0 -0
  99. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/mips.py +0 -0
  100. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/pereloc.py +0 -0
  101. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/riscv.py +0 -0
  102. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/stubs.py +0 -0
  103. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/symbol.py +0 -0
  104. {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/symbolserver.py +0 -0
  105. {cle-9.3.2 → cle-9.3.4}/cle/backends/region.py +0 -0
  106. {cle-9.3.2 → cle-9.3.4}/cle/backends/regions.py +0 -0
  107. {cle-9.3.2 → cle-9.3.4}/cle/backends/srec.py +0 -0
  108. {cle-9.3.2 → cle-9.3.4}/cle/backends/static_archive.py +0 -0
  109. {cle-9.3.2 → cle-9.3.4}/cle/backends/symbol.py +0 -0
  110. {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/__init__.py +0 -0
  111. {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/elf_tls.py +0 -0
  112. {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/elfcore_tls.py +0 -0
  113. {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/minidump_tls.py +0 -0
  114. {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/pe_tls.py +0 -0
  115. {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/tls_object.py +0 -0
  116. {cle-9.3.2 → cle-9.3.4}/cle/backends/uefi_firmware.py +0 -0
  117. {cle-9.3.2 → cle-9.3.4}/cle/backends/universal2.py +0 -0
  118. {cle-9.3.2 → cle-9.3.4}/cle/backends/xbe.py +0 -0
  119. {cle-9.3.2 → cle-9.3.4}/cle/errors.py +0 -0
  120. {cle-9.3.2 → cle-9.3.4}/cle/gdb.py +0 -0
  121. {cle-9.3.2 → cle-9.3.4}/cle/memory.py +0 -0
  122. {cle-9.3.2 → cle-9.3.4}/cle/patched_stream.py +0 -0
  123. {cle-9.3.2 → cle-9.3.4}/cle/py.typed +0 -0
  124. {cle-9.3.2 → cle-9.3.4}/cle/structs/__init__.py +0 -0
  125. {cle-9.3.2 → cle-9.3.4}/cle/structs/mem_regions.py +0 -0
  126. {cle-9.3.2 → cle-9.3.4}/cle/utils.py +0 -0
  127. {cle-9.3.2 → cle-9.3.4}/cle.egg-info/dependency_links.txt +0 -0
  128. {cle-9.3.2 → cle-9.3.4}/cle.egg-info/top_level.txt +0 -0
  129. {cle-9.3.2 → cle-9.3.4}/setup.cfg +0 -0
  130. {cle-9.3.2 → cle-9.3.4}/tests/test_address_translator.py +0 -0
  131. {cle-9.3.2 → cle-9.3.4}/tests/test_amd64_relocations.py +0 -0
  132. {cle-9.3.2 → cle-9.3.4}/tests/test_arch_detect.py +0 -0
  133. {cle-9.3.2 → cle-9.3.4}/tests/test_blob.py +0 -0
  134. {cle-9.3.2 → cle-9.3.4}/tests/test_cart.py +0 -0
  135. {cle-9.3.2 → cle-9.3.4}/tests/test_compiler_detection.py +0 -0
  136. {cle-9.3.2 → cle-9.3.4}/tests/test_dwarf.py +0 -0
  137. {cle-9.3.2 → cle-9.3.4}/tests/test_dwarf_resiliency.py +0 -0
  138. {cle-9.3.2 → cle-9.3.4}/tests/test_elfcore.py +0 -0
  139. {cle-9.3.2 → cle-9.3.4}/tests/test_exceptions.py +0 -0
  140. {cle-9.3.2 → cle-9.3.4}/tests/test_gdb.py +0 -0
  141. {cle-9.3.2 → cle-9.3.4}/tests/test_got.py +0 -0
  142. {cle-9.3.2 → cle-9.3.4}/tests/test_hex.py +0 -0
  143. {cle-9.3.2 → cle-9.3.4}/tests/test_macho_bindinghelper.py +0 -0
  144. {cle-9.3.2 → cle-9.3.4}/tests/test_macho_dyld.py +0 -0
  145. {cle-9.3.2 → cle-9.3.4}/tests/test_macho_dyld_structs.py +0 -0
  146. {cle-9.3.2 → cle-9.3.4}/tests/test_macho_libs.py +0 -0
  147. {cle-9.3.2 → cle-9.3.4}/tests/test_macho_reloc.py +0 -0
  148. {cle-9.3.2 → cle-9.3.4}/tests/test_minidump.py +0 -0
  149. {cle-9.3.2 → cle-9.3.4}/tests/test_mips_relocations.py +0 -0
  150. {cle-9.3.2 → cle-9.3.4}/tests/test_namedregion.py +0 -0
  151. {cle-9.3.2 → cle-9.3.4}/tests/test_overlap.py +0 -0
  152. {cle-9.3.2 → cle-9.3.4}/tests/test_patched_stream.py +0 -0
  153. {cle-9.3.2 → cle-9.3.4}/tests/test_pe_coff_symbols.py +0 -0
  154. {cle-9.3.2 → cle-9.3.4}/tests/test_pe_delay_imports.py +0 -0
  155. {cle-9.3.2 → cle-9.3.4}/tests/test_pe_meta_regions.py +0 -0
  156. {cle-9.3.2 → cle-9.3.4}/tests/test_plt.py +0 -0
  157. {cle-9.3.2 → cle-9.3.4}/tests/test_ppc_relocations.py +0 -0
  158. {cle-9.3.2 → cle-9.3.4}/tests/test_preload.py +0 -0
  159. {cle-9.3.2 → cle-9.3.4}/tests/test_regions.py +0 -0
  160. {cle-9.3.2 → cle-9.3.4}/tests/test_relocated.py +0 -0
  161. {cle-9.3.2 → cle-9.3.4}/tests/test_runpath.py +0 -0
  162. {cle-9.3.2 → cle-9.3.4}/tests/test_simdata.py +0 -0
  163. {cle-9.3.2 → cle-9.3.4}/tests/test_stream.py +0 -0
  164. {cle-9.3.2 → cle-9.3.4}/tests/test_symbolserver.py +0 -0
  165. {cle-9.3.2 → cle-9.3.4}/tests/test_tls_resiliency.py +0 -0
  166. {cle-9.3.2 → cle-9.3.4}/tests/test_universal2.py +0 -0
  167. {cle-9.3.2 → cle-9.3.4}/tests/test_unpackword.py +0 -0
  168. {cle-9.3.2 → cle-9.3.4}/tests/test_xbe.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cle
3
- Version: 9.3.2
3
+ Version: 9.3.4
4
4
  Summary: CLE Loads Everything (at least, many binary formats!) and provides a pythonic interface to analyze what they are and what they would look like in memory.
5
5
  License-Expression: BSD-2-Clause
6
6
  Project-URL: Homepage, https://api.angr.io/projects/cle/en/latest/
@@ -13,13 +13,13 @@ Classifier: Programming Language :: Python :: 3.14
13
13
  Requires-Python: >=3.12
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
- Requires-Dist: archinfo==9.3.2
16
+ Requires-Dist: archinfo==9.3.4
17
17
  Requires-Dist: arpy==1.1.1
18
18
  Requires-Dist: cart
19
19
  Requires-Dist: minidump>=0.0.10
20
20
  Requires-Dist: pefile==2024.8.26
21
21
  Requires-Dist: pyelftools>=0.29
22
- Requires-Dist: pyvex==9.3.2
22
+ Requires-Dist: pyvex==9.3.4
23
23
  Requires-Dist: pyxbe~=1.0.3
24
24
  Requires-Dist: pyxdia~=0.1; sys_platform != "emscripten"
25
25
  Requires-Dist: sortedcontainers>=2.0
@@ -7,7 +7,7 @@ The primary interface to CLE is the Loader class.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- __version__ = "9.3.2"
10
+ __version__ = "9.3.4"
11
11
 
12
12
  from .address_translator import AT, AddressTranslator
13
13
  from .backends import (
@@ -22,10 +22,14 @@ from .backends import (
22
22
  Backend,
23
23
  Blob,
24
24
  Coff,
25
+ Dex,
25
26
  ELFCore,
26
27
  ExceptionHandling,
27
28
  FunctionHint,
28
29
  FunctionHintSource,
30
+ GoFunction,
31
+ GoPclntab,
32
+ GoSymbol,
29
33
  Hex,
30
34
  Jar,
31
35
  MachO,
@@ -40,6 +44,7 @@ from .backends import (
40
44
  Soot,
41
45
  SRec,
42
46
  StaticArchive,
47
+ STM32Backend,
43
48
  Symbol,
44
49
  SymbolSubType,
45
50
  SymbolType,
@@ -91,10 +96,14 @@ __all__ = [
91
96
  "Backend",
92
97
  "Blob",
93
98
  "Coff",
99
+ "Dex",
94
100
  "ELFCore",
95
101
  "ExceptionHandling",
96
102
  "FunctionHint",
97
103
  "FunctionHintSource",
104
+ "GoFunction",
105
+ "GoPclntab",
106
+ "GoSymbol",
98
107
  "Hex",
99
108
  "SRec",
100
109
  "Jar",
@@ -108,6 +117,7 @@ __all__ = [
108
117
  "Segment",
109
118
  "Soot",
110
119
  "StaticArchive",
120
+ "STM32Backend",
111
121
  "Universal2",
112
122
  "register_backend",
113
123
  "ExternObject",
@@ -6,8 +6,10 @@ from .cartfile import CARTFile
6
6
  from .cgc import CGC, BackedCGC
7
7
  from .coff import Coff
8
8
  from .elf import ELF, ELFCore, MetaELF
9
+ from .gopclntab import GoFunction, GoPclntab, GoSymbol
9
10
  from .ihex import Hex
10
11
  from .java.apk import Apk
12
+ from .java.dex import Dex
11
13
  from .java.jar import Jar
12
14
  from .java.soot import Soot
13
15
  from .macho import MachO
@@ -18,6 +20,7 @@ from .region import Region, Section, Segment
18
20
  from .regions import Regions
19
21
  from .srec import SRec
20
22
  from .static_archive import StaticArchive
23
+ from .stm32 import STM32Backend
21
24
  from .symbol import Symbol, SymbolSubType, SymbolType
22
25
  from .te import TE
23
26
  from .uefi_firmware import UefiFirmware
@@ -50,9 +53,11 @@ __all__ = [
50
53
  "NamedRegion",
51
54
  "Jar",
52
55
  "Apk",
56
+ "Dex",
53
57
  "Soot",
54
58
  "XBE",
55
59
  "StaticArchive",
60
+ "STM32Backend",
56
61
  "Region",
57
62
  "Segment",
58
63
  "Section",
@@ -60,6 +65,9 @@ __all__ = [
60
65
  "Symbol",
61
66
  "SymbolType",
62
67
  "SymbolSubType",
68
+ "GoFunction",
69
+ "GoPclntab",
70
+ "GoSymbol",
63
71
  "UefiFirmware",
64
72
  "TE",
65
73
  "Universal2",
@@ -53,6 +53,7 @@ class IMAGE_SCN(IntFlag):
53
53
  Section Flags (Characteristics field)
54
54
  """
55
55
 
56
+ MEM_DISCARDABLE = 0x02000000
56
57
  MEM_EXECUTE = 0x20000000
57
58
  MEM_READ = 0x40000000
58
59
  MEM_WRITE = 0x80000000
@@ -236,13 +237,19 @@ class CoffParser:
236
237
  name_encoded = bytes(self.symbols[symbol_idx].Name)
237
238
  name_as_dwords = struct.unpack("<II", name_encoded)
238
239
  if name_as_dwords[0] == 0:
239
- name_encoded = extract_null_terminated_bytestr(self.strings, offset=name_as_dwords[1])
240
+ return self.get_string(name_as_dwords[1])
240
241
  return name_encoded.rstrip(b"\x00").decode("ascii")
241
242
 
243
+ def get_string(self, offset: int) -> str:
244
+ """
245
+ Read the string table entry at `offset`, counted from the start of the table.
246
+ """
247
+ return extract_null_terminated_bytestr(self.strings, offset=offset).decode("ascii")
248
+
242
249
  def get_section_name(self, section_idx: int) -> str:
243
250
  name = bytes(self.sections[section_idx].Name).rstrip(b"\x00").decode("ascii")
244
251
  if name.startswith("/"):
245
- return self.get_symbol_name(int(name[1:]))
252
+ return self.get_string(int(name[1:]))
246
253
  return name
247
254
 
248
255
 
@@ -288,13 +295,16 @@ class CoffRelocation(Relocation):
288
295
 
289
296
  __slots__ = ()
290
297
 
291
- PACK_FORMAT = "<i"
298
+ PACK_FORMAT = "<I"
292
299
 
293
300
  def relocate(self):
294
301
  value = self.value
295
302
  if value is None:
296
303
  log.debug("Unresolved relocation with no symbol.")
297
304
  return False
305
+ # The field holds a fixed-width bit pattern, so the value is truncated to that width
306
+ # rather than required to fit in it.
307
+ value &= (1 << (struct.calcsize(self.PACK_FORMAT) * 8)) - 1
298
308
  self.owner.memory.store(self.relative_addr, struct.pack(self.PACK_FORMAT, value))
299
309
  return True
300
310
 
@@ -310,7 +320,8 @@ class CoffRelocationREL32(CoffRelocation):
310
320
  def value(self):
311
321
  assert self.resolvedby is not None
312
322
  org_bytes = self.owner.memory.load(self.relative_addr, 4)
313
- org_value = struct.unpack("<I", org_bytes)[0]
323
+ # The field is a signed displacement, so its addend is too.
324
+ org_value = struct.unpack("<i", org_bytes)[0]
314
325
  return org_value + self.resolvedby.rebased_addr - (self.rebased_addr + 4)
315
326
 
316
327
 
@@ -351,8 +362,6 @@ class CoffRelocationADDR32NB(CoffRelocation):
351
362
 
352
363
  __slots__ = ()
353
364
 
354
- PACK_FORMAT = "<I"
355
-
356
365
  @property
357
366
  def value(self) -> int:
358
367
  assert self.resolvedby is not None
@@ -397,8 +406,6 @@ class CoffRelocationSECREL(CoffRelocation):
397
406
 
398
407
  __slots__ = ()
399
408
 
400
- PACK_FORMAT = "<I"
401
-
402
409
  @property
403
410
  def value(self):
404
411
  assert isinstance(self.owner, Coff)
@@ -24,6 +24,7 @@ from sortedcontainers import SortedDict
24
24
 
25
25
  from cle.address_translator import AT
26
26
  from cle.backends.backend import ExceptionHandling, FunctionHint, FunctionHintSource, register_backend
27
+ from cle.backends.gopclntab import GoPclntab, register_gopclntab_symbols
27
28
  from cle.backends.inlined_function import InlinedFunction
28
29
  from cle.errors import CLECompatibilityError, CLEError, CLEInvalidBinaryError
29
30
  from cle.patched_stream import PatchedStream
@@ -57,6 +58,12 @@ _NON_ALLOCATED_SECTION_NAMES = { # Sections that do not occupy memory at runtim
57
58
  }
58
59
 
59
60
 
61
+ # MIPS e_flags bits that name the ABI. binutils include/elf/mips.h.
62
+ EF_MIPS_ABI2 = 0x20 # n32
63
+ EF_MIPS_ABI = 0x0000F000
64
+ E_MIPS_ABI_O64 = 0x00002000
65
+
66
+
60
67
  # map 'e_machine' ELF header values (represented as `short int`s) to human-readable format (string)
61
68
  # There are mappings missing currently in `elftools`, so we provide them ourselves
62
69
  additional_e_machine_mappings: dict[int, str] = {
@@ -171,6 +178,9 @@ class ELF(MetaELF):
171
178
  self.compilation_units: list[CompilationUnit] | None = None
172
179
  self.functions_debug_info: dict[int, Subprogram] = {}
173
180
 
181
+ # Go
182
+ self.gopclntab: GoPclntab | None = None
183
+
174
184
  # misc
175
185
  self._entry = self._reader.header.e_entry
176
186
  self.is_relocatable = self._reader.header.e_type == "ET_REL"
@@ -238,6 +248,9 @@ class ELF(MetaELF):
238
248
  if os.path.isfile(debug_filename):
239
249
  self.__process_debug_file(debug_filename)
240
250
 
251
+ # Go binaries keep a full function table even when stripped
252
+ self.gopclntab = register_gopclntab_symbols(self)
253
+
241
254
  # call the methods defined by MetaELF
242
255
  self._ppc64_abiv1_entry_fix()
243
256
  self._load_plt()
@@ -345,6 +358,16 @@ class ELF(MetaELF):
345
358
  elif reader.header.e_flags & 0x400:
346
359
  return archinfo.ArchARMHF("Iend_LE" if reader.little_endian else "Iend_BE")
347
360
 
361
+ if arch_str == "EM_MIPS" and reader.elfclass == 32:
362
+ # The n32 and O64 ABIs put a 64-bit MIPS instruction stream in an ELFCLASS32
363
+ # container, and say so in e_flags. Resolved by the class alone they become
364
+ # 32-bit MIPS and the instruction stream does not decode. The class is still
365
+ # right about everything else in the file -- Elf32_Rel, Elf32_Sym, 4-byte GOT
366
+ # slots -- so the word size must stay 32; only the instruction set is 64-bit.
367
+ e_flags = reader.header.e_flags
368
+ if e_flags & EF_MIPS_ABI2 or (e_flags & EF_MIPS_ABI) == E_MIPS_ABI_O64:
369
+ return archinfo.ArchMIPSN32(archinfo.Endness.LE if reader.little_endian else archinfo.Endness.BE)
370
+
348
371
  try:
349
372
  return archinfo.arch_from_id(arch_str, "le" if reader.little_endian else "be", reader.elfclass)
350
373
  except archinfo.ArchNotFound:
@@ -1289,6 +1312,16 @@ class ELF(MetaELF):
1289
1312
  if isinstance(symtab, sections.NullSection):
1290
1313
  # Oh my god Atmel please stop
1291
1314
  symtab = self._reader.get_section_by_name(".symtab")
1315
+ if symtab is None and section.header.get("sh_type") != "SHT_RELR":
1316
+ # A stripped object can still carry a relocation section whose linked symbol table was stripped away.
1317
+ # Without the table there is nothing that can be registered.
1318
+ # RELR sections are exempt: their entries carry no symbol index.
1319
+ log.warning(
1320
+ "%s: relocation section %s has no symbol table to resolve its entries against; skipping it.",
1321
+ self.binary,
1322
+ section.name,
1323
+ )
1324
+ return
1292
1325
  relocs = []
1293
1326
  for readelf_reloc in section.iter_relocations():
1294
1327
  # Handle packed RELR relocations specially
@@ -161,7 +161,7 @@ class MetaELF(Backend):
161
161
  )
162
162
 
163
163
  # ATTEMPT 1: some arches will just leave the plt stub addr in the import symbol
164
- if self.arch.name in ("ARM", "ARMEL", "ARMHF", "ARMCortexM", "AARCH64", "MIPS32", "MIPS64"):
164
+ if self.arch.name in ("ARM", "ARMEL", "ARMHF", "ARMCortexM", "AARCH64", "MIPS32", "MIPS64", "MIPSN32"):
165
165
  for name, reloc in func_jmprel.items():
166
166
  if not plt_secs or any(plt_sec.contains_addr(reloc.symbol.linked_addr) for plt_sec in plt_secs):
167
167
  self._add_plt_stub(name, reloc.symbol.linked_addr, sanity_check=bool(plt_secs))
@@ -454,7 +454,7 @@ class MetaELF(Backend):
454
454
 
455
455
  Utter bollocks, but this function should fix it.
456
456
  """
457
- if self.is_ppc64_abiv1:
457
+ if self.is_ppc64_abiv1 and self._entry != 0: # entry == 0 means the ELF has no entry point or rtoc
458
458
  ep_offset = self._entry
459
459
  self._entry = self.memory.unpack_word(AT.from_lva(ep_offset, self).to_rva())
460
460
  self._ppc64_abiv1_initial_rtoc = self.memory.unpack_word(AT.from_lva(ep_offset + 8, self).to_rva())
@@ -22,6 +22,7 @@ ALL_RELOCATIONS = {
22
22
  "X86": relocation_table_i386,
23
23
  "MIPS32": relocation_table_mips,
24
24
  "MIPS64": relocation_table_mips,
25
+ "MIPSN32": relocation_table_mips,
25
26
  "PPC32": relocation_table_ppc,
26
27
  "PPC64": relocation_table_ppc64,
27
28
  "S390X": relocation_table_s390x,
@@ -19,6 +19,7 @@ from .generic import (
19
19
  GenericTLSDoffsetReloc,
20
20
  GenericTLSModIdReloc,
21
21
  GenericTLSOffsetReloc,
22
+ RelocTruncate32Mixin,
22
23
  )
23
24
 
24
25
  log = logging.getLogger(name=__name__)
@@ -66,7 +67,7 @@ class R_AARCH64_TLSDESC(GenericTLSDescriptorReloc):
66
67
  RESOLVER_ADDR = 0xFFFF_FFFF_FFFF_FE00
67
68
 
68
69
 
69
- class R_AARCH64_PREL32(GenericPCRelativeAddendReloc):
70
+ class R_AARCH64_PREL32(RelocTruncate32Mixin, GenericPCRelativeAddendReloc):
70
71
  """
71
72
  Relocation Type: 261
72
73
  Calculation: (S + A - P)
@@ -7,6 +7,8 @@ from __future__ import annotations
7
7
 
8
8
  import logging
9
9
 
10
+ from cle.backends.symbol import SymbolType
11
+
10
12
  from .elfreloc import ELFReloc
11
13
  from .generic import (
12
14
  GenericAbsoluteAddendReloc,
@@ -26,6 +28,10 @@ log = logging.getLogger(name=__name__)
26
28
  class R_PPC64_JMP_SLOT(ELFReloc):
27
29
  __slots__ = ()
28
30
 
31
+ @property
32
+ def extern_symbol_type(self):
33
+ return SymbolType.TYPE_FUNCTION
34
+
29
35
  def relocate(self):
30
36
  if self.owner.is_ppc64_abiv1:
31
37
  # R_PPC64_JMP_SLOT
@@ -0,0 +1,313 @@
1
+ """
2
+ Recovery of Go function symbols from the Go runtime's ``pclntab``.
3
+
4
+ Every binary produced by the Go linker carries this table (``.gopclntab`` on ELF,
5
+ ``__gopclntab`` on Mach-O, embedded in ``.rdata`` on PE). It lists the entry point and the
6
+ name of every function in the image, which makes it the only reliable source of function
7
+ starts for stripped Go binaries.
8
+
9
+ Only the Go 1.18+ layouts (magic ``0xfffffff0`` and ``0xfffffff1``) are supported. The
10
+ header magic and the ``textStart`` field are deliberately not trusted: obfuscated binaries
11
+ clobber them, so the table is instead accepted or rejected on structural grounds.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ import struct
18
+ from typing import TYPE_CHECKING, NamedTuple
19
+
20
+ from cle.address_translator import AT
21
+
22
+ from .symbol import Symbol, SymbolType
23
+
24
+ if TYPE_CHECKING:
25
+ from collections.abc import Callable
26
+
27
+ from .backend import Backend
28
+
29
+ log = logging.getLogger(name=__name__)
30
+
31
+ __all__ = ["GoFunction", "GoPclntab", "GoSymbol", "load_gopclntab", "register_gopclntab_symbols"]
32
+
33
+ # magic -> minimum Go version that emits it
34
+ GO_PCLNTAB_MAGICS = {
35
+ 0xFFFFFFF1: (1, 20),
36
+ 0xFFFFFFF0: (1, 18),
37
+ }
38
+
39
+ # Section names that hold nothing but a pclntab.
40
+ PCLNTAB_SECTION_NAMES = frozenset({".gopclntab", "__gopclntab", ".go.pclntab", "__go_pclntab"})
41
+
42
+ # Sections a pclntab may be embedded in, searched by magic as a fallback.
43
+ _EMBEDDING_SECTION_NAMES = frozenset({".rdata", ".rodata", "__rodata", "__const", "__DATA_CONST"})
44
+
45
+ # Sections whose presence means "this was built by the Go toolchain".
46
+ _GO_MARKER_SECTION_NAMES = frozenset({".gosymtab", ".typelink", ".itablink", ".noptrdata", ".noptrbss"})
47
+
48
+ _VALID_PTR_SIZES = (4, 8)
49
+ _VALID_MIN_LC = (1, 2, 4)
50
+ _MAX_NAME_LEN = 4096
51
+
52
+
53
+ class GoFunction(NamedTuple):
54
+ """
55
+ One entry of the pclntab's function table.
56
+
57
+ :ivar addr: Entry point of the function, as a linked virtual address.
58
+ :ivar size: Distance to the next function; includes inter-function padding.
59
+ :ivar name: Fully qualified Go name, e.g. ``net/http.(*Server).Serve``.
60
+ """
61
+
62
+ addr: int
63
+ size: int
64
+ name: str
65
+
66
+
67
+ class GoSymbol(Symbol):
68
+ """
69
+ A function symbol recovered from a Go pclntab.
70
+ """
71
+
72
+ def __init__(self, owner: Backend, name: str, relative_addr: int, size: int):
73
+ super().__init__(owner, name, relative_addr, size, SymbolType.TYPE_FUNCTION)
74
+
75
+
76
+ class GoPclntab:
77
+ """
78
+ A parsed Go pclntab.
79
+
80
+ :ivar magic: The raw magic word. May be garbage: it is not used for validation.
81
+ :ivar min_lc: Minimum instruction length of the target architecture.
82
+ :ivar ptr_size: Pointer size, in bytes.
83
+ :ivar text_start: The base the function entry offsets are relative to, after recovery.
84
+ :ivar functions: The function table, sorted by address.
85
+ """
86
+
87
+ __slots__ = ("magic", "min_lc", "ptr_size", "text_start", "functions")
88
+
89
+ def __init__(self, magic: int, min_lc: int, ptr_size: int, text_start: int, functions: list[GoFunction]):
90
+ self.magic = magic
91
+ self.min_lc = min_lc
92
+ self.ptr_size = ptr_size
93
+ self.text_start = text_start
94
+ self.functions = functions
95
+
96
+ def __repr__(self):
97
+ return f"<GoPclntab: {len(self.functions)} functions, text at {self.text_start:#x}>"
98
+
99
+ @property
100
+ def go_version(self) -> tuple[int, int] | None:
101
+ return GO_PCLNTAB_MAGICS.get(self.magic)
102
+
103
+ @classmethod
104
+ def parse(
105
+ cls,
106
+ data: bytes,
107
+ endness: str = "<",
108
+ text_start_fallback: int | None = None,
109
+ is_text_addr: Callable[[int], bool] | None = None,
110
+ ) -> GoPclntab | None:
111
+ """
112
+ Parse a pclntab out of ``data``, which must start at the table header.
113
+
114
+ Returns None if ``data`` does not structurally look like a pclntab.
115
+
116
+ :param data: The bytes of the table.
117
+ :param endness: ``<`` or ``>``.
118
+ :param text_start_fallback: Address to use when the header's ``textStart`` is unusable.
119
+ :param is_text_addr: Predicate deciding whether ``textStart`` points at code.
120
+ """
121
+ header = _parse_header(data, endness)
122
+ if header is None:
123
+ return None
124
+ magic, min_lc, ptr_size, nfunc, text_start, funcname_off, pcln_off = header
125
+
126
+ if text_start == 0 or (is_text_addr is not None and not is_text_addr(text_start)):
127
+ if text_start_fallback is None:
128
+ log.warning("gopclntab: textStart %#x is not code and there is no fallback", text_start)
129
+ return None
130
+ log.debug("gopclntab: textStart %#x is not code, using %#x instead", text_start, text_start_fallback)
131
+ text_start = text_start_fallback
132
+
133
+ # nfunc pairs of (entryoff, funcoff), then one final entryoff marking the end of the last function
134
+ entries = struct.unpack_from(f"{endness}{2 * nfunc + 1}I", data, pcln_off)
135
+ entry_offs = entries[0::2]
136
+ func_offs = entries[1::2]
137
+ if any(a >= b for a, b in zip(entry_offs, entry_offs[1:])):
138
+ log.debug("gopclntab: function entry offsets are not monotonically increasing")
139
+ return None
140
+
141
+ functions = []
142
+ for i, func_off in enumerate(func_offs):
143
+ # the _func struct is entryOff uint32 followed by nameOff int32
144
+ name_ptr = pcln_off + func_off + 4
145
+ if name_ptr + 4 > len(data):
146
+ log.debug("gopclntab: _func %d lies outside the table", i)
147
+ return None
148
+ name_off = funcname_off + struct.unpack_from(endness + "i", data, name_ptr)[0]
149
+ if not funcname_off <= name_off < len(data):
150
+ log.debug("gopclntab: name of function %d lies outside the table", i)
151
+ return None
152
+ end = data.find(b"\0", name_off, name_off + _MAX_NAME_LEN)
153
+ if end == -1:
154
+ log.debug("gopclntab: name of function %d is unterminated", i)
155
+ return None
156
+ name = data[name_off:end].decode("utf-8", "replace")
157
+ functions.append(GoFunction(text_start + entry_offs[i], entry_offs[i + 1] - entry_offs[i], name))
158
+
159
+ return cls(magic, min_lc, ptr_size, text_start, functions)
160
+
161
+
162
+ def _parse_header(data: bytes, endness: str) -> tuple[int, int, int, int, int, int, int] | None:
163
+ """
164
+ Validate and decode a pclntab header. Returns
165
+ ``(magic, min_lc, ptr_size, nfunc, text_start, funcname_off, pcln_off)`` or None.
166
+
167
+ The magic is decoded but never checked, so that binaries with a clobbered one still load.
168
+ """
169
+ if len(data) < 16:
170
+ return None
171
+ magic, _pad, min_lc, ptr_size = struct.unpack_from(endness + "IHBB", data, 0)
172
+ if ptr_size not in _VALID_PTR_SIZES or min_lc not in _VALID_MIN_LC or _pad != 0:
173
+ return None
174
+
175
+ header_size = 8 + 8 * ptr_size
176
+ if len(data) < header_size:
177
+ return None
178
+ word = endness + ("Q" if ptr_size == 8 else "I")
179
+ nfunc, nfiles, text_start, funcname_off, cu_off, filetab_off, pctab_off, pcln_off = (
180
+ struct.unpack_from(word, data, 8 + i * ptr_size)[0] for i in range(8)
181
+ )
182
+
183
+ size = len(data)
184
+ # the five sub-tables follow the header in a fixed order and all live inside the table
185
+ offsets = (funcname_off, cu_off, filetab_off, pctab_off, pcln_off)
186
+ if offsets[0] < header_size or offsets[-1] >= size:
187
+ return None
188
+ if any(a > b for a, b in zip(offsets, offsets[1:])):
189
+ return None
190
+ # a function costs 8 bytes of functab plus a _func struct, a file name at least 2 bytes
191
+ if nfunc <= 0 or pcln_off + (2 * nfunc + 1) * 4 > size:
192
+ return None
193
+ if nfiles * 2 > size:
194
+ return None
195
+
196
+ return magic, min_lc, ptr_size, nfunc, text_start, funcname_off, pcln_off
197
+
198
+
199
+ #
200
+ # Backend integration
201
+ #
202
+
203
+
204
+ def _read_section(backend: Backend, section) -> bytes | None:
205
+ try:
206
+ if section.memsize == 0 or section.only_contains_uninitialized_data:
207
+ return None
208
+ return backend.memory.load(AT.from_lva(section.vaddr, backend).to_rva(), section.memsize)
209
+ except Exception: # pylint: disable=broad-except
210
+ return None
211
+
212
+
213
+ def _executable_sections(backend: Backend):
214
+ return [sec for sec in backend.sections if sec.is_executable and sec.memsize > 0]
215
+
216
+
217
+ def _text_start_fallback(execs) -> int | None:
218
+ """
219
+ The vaddr of the section holding the executable code, used when ``textStart`` is unusable.
220
+ """
221
+ if not execs:
222
+ return None
223
+ for sec in execs:
224
+ if sec.name in (".text", "__text"):
225
+ return sec.vaddr
226
+ return min(sec.vaddr for sec in execs)
227
+
228
+
229
+ def _looks_like_go(backend: Backend) -> bool:
230
+ return any(sec.name in _GO_MARKER_SECTION_NAMES or sec.name.startswith(".go.") for sec in backend.sections)
231
+
232
+
233
+ def _find_pclntab_data(backend: Backend, endness: str):
234
+ """
235
+ Yield candidate ``bytes`` objects, each starting at a possible pclntab header.
236
+ """
237
+ embedding = []
238
+ for section in backend.sections:
239
+ if section.name in PCLNTAB_SECTION_NAMES:
240
+ data = _read_section(backend, section)
241
+ if data is not None:
242
+ yield data
243
+ elif section.name in _EMBEDDING_SECTION_NAMES and not section.is_executable:
244
+ embedding.append(section)
245
+
246
+ # PE and Mach-O bury the table in a generic read-only section; find it by magic. Only worth
247
+ # doing when something else already says this is a Go binary.
248
+ if not embedding or not _looks_like_go(backend):
249
+ return
250
+ magics = [struct.pack(endness + "I", magic) for magic in GO_PCLNTAB_MAGICS]
251
+ for section in embedding:
252
+ data = _read_section(backend, section)
253
+ if data is None:
254
+ continue
255
+ for magic in magics:
256
+ pos = data.find(magic)
257
+ while pos != -1:
258
+ yield data[pos:]
259
+ pos = data.find(magic, pos + 4)
260
+
261
+
262
+ def load_gopclntab(backend: Backend) -> GoPclntab | None:
263
+ """
264
+ Find and parse the Go pclntab of an already-loaded object. Returns None if there is none.
265
+ """
266
+ if not backend.sections:
267
+ return None
268
+ endness = ">" if backend.arch is not None and backend.arch.memory_endness == "Iend_BE" else "<"
269
+
270
+ execs: list = []
271
+
272
+ def is_text_addr(addr: int) -> bool:
273
+ return any(sec.contains_addr(addr) for sec in execs)
274
+
275
+ fallback = None
276
+ for i, data in enumerate(_find_pclntab_data(backend, endness)):
277
+ if i == 0:
278
+ execs = _executable_sections(backend)
279
+ fallback = _text_start_fallback(execs)
280
+ tab = GoPclntab.parse(data, endness, text_start_fallback=fallback, is_text_addr=is_text_addr)
281
+ if tab is not None:
282
+ return tab
283
+ return None
284
+
285
+
286
+ def register_gopclntab_symbols(backend: Backend) -> GoPclntab | None:
287
+ """
288
+ Parse the object's Go pclntab, if it has one, and add a function symbol for every Go
289
+ function that is not already covered by a symbol table.
290
+ """
291
+ try:
292
+ tab = load_gopclntab(backend)
293
+ except (struct.error, ValueError):
294
+ log.warning("Failed to parse the Go pclntab of %s", backend.binary_basename, exc_info=True)
295
+ return None
296
+ if tab is None:
297
+ return None
298
+
299
+ covered = {sym.relative_addr for sym in backend.symbols if sym.size and sym.is_function}
300
+ by_name = getattr(backend, "_symbols_by_name", None)
301
+ added = 0
302
+ for func in tab.functions:
303
+ relative_addr = AT.from_lva(func.addr, backend).to_rva()
304
+ if relative_addr in covered:
305
+ continue
306
+ symbol = GoSymbol(backend, func.name, relative_addr, func.size)
307
+ backend.symbols.add(symbol)
308
+ if by_name is not None and func.name and func.name not in by_name:
309
+ by_name[func.name] = symbol
310
+ added += 1
311
+
312
+ log.info("Recovered %d Go functions from the pclntab of %s (%d new)", len(tab.functions), backend, added)
313
+ return tab