cle 9.3.2__tar.gz → 9.3.3__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 (164) hide show
  1. {cle-9.3.2/cle.egg-info → cle-9.3.3}/PKG-INFO +3 -3
  2. {cle-9.3.2 → cle-9.3.3}/cle/__init__.py +9 -1
  3. {cle-9.3.2 → cle-9.3.3}/cle/backends/__init__.py +6 -0
  4. {cle-9.3.2 → cle-9.3.3}/cle/backends/coff.py +15 -8
  5. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/elf.py +17 -0
  6. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/arm64.py +2 -1
  7. cle-9.3.3/cle/backends/gopclntab.py +313 -0
  8. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/macho.py +11 -4
  9. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/macho_enums.py +62 -1
  10. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/section.py +16 -4
  11. {cle-9.3.2 → cle-9.3.3}/cle/backends/stm32.py +45 -34
  12. {cle-9.3.2 → cle-9.3.3}/cle/backends/te.py +53 -16
  13. {cle-9.3.2 → cle-9.3.3}/cle/loader.py +37 -20
  14. {cle-9.3.2 → cle-9.3.3/cle.egg-info}/PKG-INFO +3 -3
  15. {cle-9.3.2 → cle-9.3.3}/cle.egg-info/SOURCES.txt +5 -0
  16. {cle-9.3.2 → cle-9.3.3}/cle.egg-info/requires.txt +2 -2
  17. {cle-9.3.2 → cle-9.3.3}/pyproject.toml +2 -2
  18. cle-9.3.3/tests/test_aarch64_relocations.py +119 -0
  19. {cle-9.3.2 → cle-9.3.3}/tests/test_arm_firmware.py +14 -0
  20. cle-9.3.3/tests/test_coff.py +79 -0
  21. cle-9.3.3/tests/test_gopclntab.py +151 -0
  22. {cle-9.3.2 → cle-9.3.3}/tests/test_macho.py +158 -0
  23. cle-9.3.3/tests/test_rebase.py +75 -0
  24. cle-9.3.3/tests/test_stm32.py +148 -0
  25. cle-9.3.3/tests/test_te.py +103 -0
  26. cle-9.3.2/tests/test_aarch64_relocations.py +0 -44
  27. cle-9.3.2/tests/test_coff.py +0 -37
  28. {cle-9.3.2 → cle-9.3.3}/LICENSE +0 -0
  29. {cle-9.3.2 → cle-9.3.3}/MANIFEST.in +0 -0
  30. {cle-9.3.2 → cle-9.3.3}/README.md +0 -0
  31. {cle-9.3.2 → cle-9.3.3}/cle/address_translator.py +0 -0
  32. {cle-9.3.2 → cle-9.3.3}/cle/backends/backend.py +0 -0
  33. {cle-9.3.2 → cle-9.3.3}/cle/backends/binja.py +0 -0
  34. {cle-9.3.2 → cle-9.3.3}/cle/backends/blob.py +0 -0
  35. {cle-9.3.2 → cle-9.3.3}/cle/backends/cartfile.py +0 -0
  36. {cle-9.3.2 → cle-9.3.3}/cle/backends/cgc/__init__.py +0 -0
  37. {cle-9.3.2 → cle-9.3.3}/cle/backends/cgc/backedcgc.py +0 -0
  38. {cle-9.3.2 → cle-9.3.3}/cle/backends/cgc/cgc.py +0 -0
  39. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/__init__.py +0 -0
  40. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/compilation_unit.py +0 -0
  41. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/elfcore.py +0 -0
  42. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/hashtable.py +0 -0
  43. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/lsda.py +0 -0
  44. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/metaelf.py +0 -0
  45. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/regions.py +0 -0
  46. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/__init__.py +0 -0
  47. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/amd64.py +0 -0
  48. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/arm.py +0 -0
  49. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/elfreloc.py +0 -0
  50. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/generic.py +0 -0
  51. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/i386.py +0 -0
  52. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/mips.py +0 -0
  53. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/ppc.py +0 -0
  54. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/ppc64.py +0 -0
  55. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/s390x.py +0 -0
  56. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/relocation/sparc.py +0 -0
  57. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/subprogram.py +0 -0
  58. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/symbol.py +0 -0
  59. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/symbol_type.py +0 -0
  60. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/variable.py +0 -0
  61. {cle-9.3.2 → cle-9.3.3}/cle/backends/elf/variable_type.py +0 -0
  62. {cle-9.3.2 → cle-9.3.3}/cle/backends/externs/__init__.py +0 -0
  63. {cle-9.3.2 → cle-9.3.3}/cle/backends/externs/simdata/__init__.py +0 -0
  64. {cle-9.3.2 → cle-9.3.3}/cle/backends/externs/simdata/common.py +0 -0
  65. {cle-9.3.2 → cle-9.3.3}/cle/backends/externs/simdata/glibc_startup.py +0 -0
  66. {cle-9.3.2 → cle-9.3.3}/cle/backends/externs/simdata/io_file.py +0 -0
  67. {cle-9.3.2 → cle-9.3.3}/cle/backends/externs/simdata/simdata.py +0 -0
  68. {cle-9.3.2 → cle-9.3.3}/cle/backends/ihex.py +0 -0
  69. {cle-9.3.2 → cle-9.3.3}/cle/backends/inlined_function.py +0 -0
  70. {cle-9.3.2 → cle-9.3.3}/cle/backends/java/__init__.py +0 -0
  71. {cle-9.3.2 → cle-9.3.3}/cle/backends/java/android_lifecycle.py +0 -0
  72. {cle-9.3.2 → cle-9.3.3}/cle/backends/java/apk.py +0 -0
  73. {cle-9.3.2 → cle-9.3.3}/cle/backends/java/jar.py +0 -0
  74. {cle-9.3.2 → cle-9.3.3}/cle/backends/java/soot.py +0 -0
  75. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/__init__.py +0 -0
  76. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/binding.py +0 -0
  77. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/encrypted_sentinel_backer.py +0 -0
  78. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/segment.py +0 -0
  79. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/structs.py +0 -0
  80. {cle-9.3.2 → cle-9.3.3}/cle/backends/macho/symbol.py +0 -0
  81. {cle-9.3.2 → cle-9.3.3}/cle/backends/minidump/__init__.py +0 -0
  82. {cle-9.3.2 → cle-9.3.3}/cle/backends/named_region.py +0 -0
  83. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/__init__.py +0 -0
  84. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/pe.py +0 -0
  85. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/regions.py +0 -0
  86. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/relocation/__init__.py +0 -0
  87. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/relocation/arm.py +0 -0
  88. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/relocation/generic.py +0 -0
  89. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/relocation/mips.py +0 -0
  90. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/relocation/pereloc.py +0 -0
  91. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/relocation/riscv.py +0 -0
  92. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/stubs.py +0 -0
  93. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/symbol.py +0 -0
  94. {cle-9.3.2 → cle-9.3.3}/cle/backends/pe/symbolserver.py +0 -0
  95. {cle-9.3.2 → cle-9.3.3}/cle/backends/region.py +0 -0
  96. {cle-9.3.2 → cle-9.3.3}/cle/backends/regions.py +0 -0
  97. {cle-9.3.2 → cle-9.3.3}/cle/backends/relocation.py +0 -0
  98. {cle-9.3.2 → cle-9.3.3}/cle/backends/srec.py +0 -0
  99. {cle-9.3.2 → cle-9.3.3}/cle/backends/static_archive.py +0 -0
  100. {cle-9.3.2 → cle-9.3.3}/cle/backends/symbol.py +0 -0
  101. {cle-9.3.2 → cle-9.3.3}/cle/backends/tls/__init__.py +0 -0
  102. {cle-9.3.2 → cle-9.3.3}/cle/backends/tls/elf_tls.py +0 -0
  103. {cle-9.3.2 → cle-9.3.3}/cle/backends/tls/elfcore_tls.py +0 -0
  104. {cle-9.3.2 → cle-9.3.3}/cle/backends/tls/minidump_tls.py +0 -0
  105. {cle-9.3.2 → cle-9.3.3}/cle/backends/tls/pe_tls.py +0 -0
  106. {cle-9.3.2 → cle-9.3.3}/cle/backends/tls/tls_object.py +0 -0
  107. {cle-9.3.2 → cle-9.3.3}/cle/backends/uefi_firmware.py +0 -0
  108. {cle-9.3.2 → cle-9.3.3}/cle/backends/universal2.py +0 -0
  109. {cle-9.3.2 → cle-9.3.3}/cle/backends/xbe.py +0 -0
  110. {cle-9.3.2 → cle-9.3.3}/cle/errors.py +0 -0
  111. {cle-9.3.2 → cle-9.3.3}/cle/gdb.py +0 -0
  112. {cle-9.3.2 → cle-9.3.3}/cle/memory.py +0 -0
  113. {cle-9.3.2 → cle-9.3.3}/cle/patched_stream.py +0 -0
  114. {cle-9.3.2 → cle-9.3.3}/cle/py.typed +0 -0
  115. {cle-9.3.2 → cle-9.3.3}/cle/structs/__init__.py +0 -0
  116. {cle-9.3.2 → cle-9.3.3}/cle/structs/mem_regions.py +0 -0
  117. {cle-9.3.2 → cle-9.3.3}/cle/utils.py +0 -0
  118. {cle-9.3.2 → cle-9.3.3}/cle.egg-info/dependency_links.txt +0 -0
  119. {cle-9.3.2 → cle-9.3.3}/cle.egg-info/top_level.txt +0 -0
  120. {cle-9.3.2 → cle-9.3.3}/setup.cfg +0 -0
  121. {cle-9.3.2 → cle-9.3.3}/tests/test_address_translator.py +0 -0
  122. {cle-9.3.2 → cle-9.3.3}/tests/test_amd64_relocations.py +0 -0
  123. {cle-9.3.2 → cle-9.3.3}/tests/test_arch_detect.py +0 -0
  124. {cle-9.3.2 → cle-9.3.3}/tests/test_blob.py +0 -0
  125. {cle-9.3.2 → cle-9.3.3}/tests/test_cart.py +0 -0
  126. {cle-9.3.2 → cle-9.3.3}/tests/test_clemory.py +0 -0
  127. {cle-9.3.2 → cle-9.3.3}/tests/test_compiler_detection.py +0 -0
  128. {cle-9.3.2 → cle-9.3.3}/tests/test_dwarf.py +0 -0
  129. {cle-9.3.2 → cle-9.3.3}/tests/test_dwarf_resiliency.py +0 -0
  130. {cle-9.3.2 → cle-9.3.3}/tests/test_elfcore.py +0 -0
  131. {cle-9.3.2 → cle-9.3.3}/tests/test_exceptions.py +0 -0
  132. {cle-9.3.2 → cle-9.3.3}/tests/test_extern.py +0 -0
  133. {cle-9.3.2 → cle-9.3.3}/tests/test_gdb.py +0 -0
  134. {cle-9.3.2 → cle-9.3.3}/tests/test_got.py +0 -0
  135. {cle-9.3.2 → cle-9.3.3}/tests/test_hex.py +0 -0
  136. {cle-9.3.2 → cle-9.3.3}/tests/test_macho_bindinghelper.py +0 -0
  137. {cle-9.3.2 → cle-9.3.3}/tests/test_macho_dyld.py +0 -0
  138. {cle-9.3.2 → cle-9.3.3}/tests/test_macho_dyld_structs.py +0 -0
  139. {cle-9.3.2 → cle-9.3.3}/tests/test_macho_libs.py +0 -0
  140. {cle-9.3.2 → cle-9.3.3}/tests/test_macho_reloc.py +0 -0
  141. {cle-9.3.2 → cle-9.3.3}/tests/test_minidump.py +0 -0
  142. {cle-9.3.2 → cle-9.3.3}/tests/test_mips_relocations.py +0 -0
  143. {cle-9.3.2 → cle-9.3.3}/tests/test_namedregion.py +0 -0
  144. {cle-9.3.2 → cle-9.3.3}/tests/test_optional_backends.py +0 -0
  145. {cle-9.3.2 → cle-9.3.3}/tests/test_overlap.py +0 -0
  146. {cle-9.3.2 → cle-9.3.3}/tests/test_patched_stream.py +0 -0
  147. {cle-9.3.2 → cle-9.3.3}/tests/test_pe.py +0 -0
  148. {cle-9.3.2 → cle-9.3.3}/tests/test_pe_coff_symbols.py +0 -0
  149. {cle-9.3.2 → cle-9.3.3}/tests/test_pe_delay_imports.py +0 -0
  150. {cle-9.3.2 → cle-9.3.3}/tests/test_pe_meta_regions.py +0 -0
  151. {cle-9.3.2 → cle-9.3.3}/tests/test_plt.py +0 -0
  152. {cle-9.3.2 → cle-9.3.3}/tests/test_ppc64_initial_rtoc.py +0 -0
  153. {cle-9.3.2 → cle-9.3.3}/tests/test_ppc_relocations.py +0 -0
  154. {cle-9.3.2 → cle-9.3.3}/tests/test_preload.py +0 -0
  155. {cle-9.3.2 → cle-9.3.3}/tests/test_regions.py +0 -0
  156. {cle-9.3.2 → cle-9.3.3}/tests/test_relocated.py +0 -0
  157. {cle-9.3.2 → cle-9.3.3}/tests/test_runpath.py +0 -0
  158. {cle-9.3.2 → cle-9.3.3}/tests/test_simdata.py +0 -0
  159. {cle-9.3.2 → cle-9.3.3}/tests/test_stream.py +0 -0
  160. {cle-9.3.2 → cle-9.3.3}/tests/test_symbolserver.py +0 -0
  161. {cle-9.3.2 → cle-9.3.3}/tests/test_tls_resiliency.py +0 -0
  162. {cle-9.3.2 → cle-9.3.3}/tests/test_universal2.py +0 -0
  163. {cle-9.3.2 → cle-9.3.3}/tests/test_unpackword.py +0 -0
  164. {cle-9.3.2 → cle-9.3.3}/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.3
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.3
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.3
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.3"
11
11
 
12
12
  from .address_translator import AT, AddressTranslator
13
13
  from .backends import (
@@ -26,6 +26,9 @@ from .backends import (
26
26
  ExceptionHandling,
27
27
  FunctionHint,
28
28
  FunctionHintSource,
29
+ GoFunction,
30
+ GoPclntab,
31
+ GoSymbol,
29
32
  Hex,
30
33
  Jar,
31
34
  MachO,
@@ -40,6 +43,7 @@ from .backends import (
40
43
  Soot,
41
44
  SRec,
42
45
  StaticArchive,
46
+ STM32Backend,
43
47
  Symbol,
44
48
  SymbolSubType,
45
49
  SymbolType,
@@ -95,6 +99,9 @@ __all__ = [
95
99
  "ExceptionHandling",
96
100
  "FunctionHint",
97
101
  "FunctionHintSource",
102
+ "GoFunction",
103
+ "GoPclntab",
104
+ "GoSymbol",
98
105
  "Hex",
99
106
  "SRec",
100
107
  "Jar",
@@ -108,6 +115,7 @@ __all__ = [
108
115
  "Segment",
109
116
  "Soot",
110
117
  "StaticArchive",
118
+ "STM32Backend",
111
119
  "Universal2",
112
120
  "register_backend",
113
121
  "ExternObject",
@@ -6,6 +6,7 @@ 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
11
12
  from .java.jar import Jar
@@ -18,6 +19,7 @@ from .region import Region, Section, Segment
18
19
  from .regions import Regions
19
20
  from .srec import SRec
20
21
  from .static_archive import StaticArchive
22
+ from .stm32 import STM32Backend
21
23
  from .symbol import Symbol, SymbolSubType, SymbolType
22
24
  from .te import TE
23
25
  from .uefi_firmware import UefiFirmware
@@ -53,6 +55,7 @@ __all__ = [
53
55
  "Soot",
54
56
  "XBE",
55
57
  "StaticArchive",
58
+ "STM32Backend",
56
59
  "Region",
57
60
  "Segment",
58
61
  "Section",
@@ -60,6 +63,9 @@ __all__ = [
60
63
  "Symbol",
61
64
  "SymbolType",
62
65
  "SymbolSubType",
66
+ "GoFunction",
67
+ "GoPclntab",
68
+ "GoSymbol",
63
69
  "UefiFirmware",
64
70
  "TE",
65
71
  "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
@@ -171,6 +172,9 @@ class ELF(MetaELF):
171
172
  self.compilation_units: list[CompilationUnit] | None = None
172
173
  self.functions_debug_info: dict[int, Subprogram] = {}
173
174
 
175
+ # Go
176
+ self.gopclntab: GoPclntab | None = None
177
+
174
178
  # misc
175
179
  self._entry = self._reader.header.e_entry
176
180
  self.is_relocatable = self._reader.header.e_type == "ET_REL"
@@ -238,6 +242,9 @@ class ELF(MetaELF):
238
242
  if os.path.isfile(debug_filename):
239
243
  self.__process_debug_file(debug_filename)
240
244
 
245
+ # Go binaries keep a full function table even when stripped
246
+ self.gopclntab = register_gopclntab_symbols(self)
247
+
241
248
  # call the methods defined by MetaELF
242
249
  self._ppc64_abiv1_entry_fix()
243
250
  self._load_plt()
@@ -1289,6 +1296,16 @@ class ELF(MetaELF):
1289
1296
  if isinstance(symtab, sections.NullSection):
1290
1297
  # Oh my god Atmel please stop
1291
1298
  symtab = self._reader.get_section_by_name(".symtab")
1299
+ if symtab is None and section.header.get("sh_type") != "SHT_RELR":
1300
+ # A stripped object can still carry a relocation section whose linked symbol table was stripped away.
1301
+ # Without the table there is nothing that can be registered.
1302
+ # RELR sections are exempt: their entries carry no symbol index.
1303
+ log.warning(
1304
+ "%s: relocation section %s has no symbol table to resolve its entries against; skipping it.",
1305
+ self.binary,
1306
+ section.name,
1307
+ )
1308
+ return
1292
1309
  relocs = []
1293
1310
  for readelf_reloc in section.iter_relocations():
1294
1311
  # Handle packed RELR relocations specially
@@ -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)
@@ -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
@@ -1041,11 +1041,18 @@ class MachO(Backend):
1041
1041
  # It would allocate 4GB of unneeded memory and also break rebasing
1042
1042
  # because now there is a segment that must be at address 0, while the other segments should be slid
1043
1043
  log.info("Found PAGEZERO, skipping backer for memory conservation")
1044
+ elif seg.memsize == 0:
1045
+ # A segment with vmsize 0 occupies no address space, so it must not be backed no matter how much file
1046
+ # content it has. Debug info kept in the executable rather than a .dSYM is emitted like this: __DWARF has
1047
+ # vmsize 0 and shares its vmaddr with the segment that follows it, so backing it would map the debug info
1048
+ # over that segment.
1049
+ log.info("Segment %r is not mapped at runtime, skipping backer", segname)
1044
1050
  elif seg.filesize > 0:
1045
- # Append segment data to memory
1046
- blob = self._read(f, seg.offset, seg.filesize)
1047
- if seg.filesize < seg.memsize:
1048
- blob += b"\0" * (seg.memsize - seg.filesize) # padding
1051
+ # Append segment data to memory. A segment maps memsize bytes and the file contributes at most that many
1052
+ # of them, so anything the file has beyond memsize is not part of the mapping.
1053
+ blob = self._read(f, seg.offset, min(seg.filesize, seg.memsize))
1054
+ if len(blob) < seg.memsize:
1055
+ blob += b"\0" * (seg.memsize - len(blob)) # padding
1049
1056
 
1050
1057
  # The memory of the Backend itself should start at 0, where 0 is the lowest meaningful address
1051
1058
  # In our case this would be the Mach header magic
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from enum import IntEnum
3
+ from enum import IntEnum, IntFlag
4
4
 
5
5
 
6
6
  class LoadCommands(IntEnum):
@@ -70,6 +70,67 @@ class LoadCommands(IntEnum):
70
70
  LC_DYLD_CHAINED_FIXUPS = 0x34 | LC_REQ_DYLD
71
71
 
72
72
 
73
+ class SectionType(IntEnum):
74
+ """
75
+ from mach-o/loader.h
76
+
77
+ Constants for the low byte of a section's flags field, which says what the section holds.
78
+ """
79
+
80
+ S_REGULAR = 0x0
81
+ S_ZEROFILL = 0x1
82
+ S_CSTRING_LITERALS = 0x2
83
+ S_4BYTE_LITERALS = 0x3
84
+ S_8BYTE_LITERALS = 0x4
85
+ S_LITERAL_POINTERS = 0x5
86
+ S_NON_LAZY_SYMBOL_POINTERS = 0x6
87
+ S_LAZY_SYMBOL_POINTERS = 0x7
88
+ S_SYMBOL_STUBS = 0x8
89
+ S_MOD_INIT_FUNC_POINTERS = 0x9
90
+ S_MOD_TERM_FUNC_POINTERS = 0xA
91
+ S_COALESCED = 0xB
92
+ S_GB_ZEROFILL = 0xC
93
+ S_INTERPOSING = 0xD
94
+ S_16BYTE_LITERALS = 0xE
95
+ S_DTRACE_DOF = 0xF
96
+ S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10
97
+ S_THREAD_LOCAL_REGULAR = 0x11
98
+ S_THREAD_LOCAL_ZEROFILL = 0x12
99
+ S_THREAD_LOCAL_VARIABLES = 0x13
100
+ S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14
101
+ S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15
102
+ S_INIT_FUNC_OFFSETS = 0x16
103
+
104
+
105
+ class SectionAttributes(IntFlag):
106
+ """
107
+ from mach-o/loader.h
108
+
109
+ Constants for the section attributes, which occupy the high three bytes of a section's flags field.
110
+ """
111
+
112
+ # section contains only true machine instructions
113
+ S_ATTR_PURE_INSTRUCTIONS = 0x80000000
114
+ # section contains coalesced symbols that are not to be in a ranlib table of contents
115
+ S_ATTR_NO_TOC = 0x40000000
116
+ # ok to strip static symbols in this section in files with the MH_DYLDLINK flag
117
+ S_ATTR_STRIP_STATIC_SYMS = 0x20000000
118
+ # no dead stripping
119
+ S_ATTR_NO_DEAD_STRIP = 0x10000000
120
+ # blocks are live if they reference live blocks
121
+ S_ATTR_LIVE_SUPPORT = 0x08000000
122
+ # used in code generated by the UNIX C++ compiler for virtual function tables
123
+ S_ATTR_SELF_MODIFYING_CODE = 0x04000000
124
+ # a debug section
125
+ S_ATTR_DEBUG = 0x02000000
126
+ # section contains some machine instructions
127
+ S_ATTR_SOME_INSTRUCTIONS = 0x00000400
128
+ # section has external relocation entries
129
+ S_ATTR_EXT_RELOC = 0x00000200
130
+ # section has local relocation entries
131
+ S_ATTR_LOC_RELOC = 0x00000100
132
+
133
+
73
134
  class MachoFiletype(IntEnum):
74
135
  """
75
136
  from mach-o/loader.h