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.
- {cle-9.3.2/cle.egg-info → cle-9.3.4}/PKG-INFO +3 -3
- {cle-9.3.2 → cle-9.3.4}/cle/__init__.py +11 -1
- {cle-9.3.2 → cle-9.3.4}/cle/backends/__init__.py +8 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/coff.py +15 -8
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/elf.py +33 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/metaelf.py +2 -2
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/__init__.py +1 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/arm64.py +2 -1
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/ppc64.py +6 -0
- cle-9.3.4/cle/backends/gopclntab.py +313 -0
- cle-9.3.4/cle/backends/java/dex.py +78 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/java/soot.py +2 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/binding.py +42 -26
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/macho.py +33 -12
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/macho_enums.py +62 -1
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/section.py +16 -4
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/pe.py +28 -1
- {cle-9.3.2 → cle-9.3.4}/cle/backends/relocation.py +7 -1
- {cle-9.3.2 → cle-9.3.4}/cle/backends/stm32.py +45 -34
- {cle-9.3.2 → cle-9.3.4}/cle/backends/te.py +56 -16
- {cle-9.3.2 → cle-9.3.4}/cle/loader.py +37 -20
- {cle-9.3.2 → cle-9.3.4/cle.egg-info}/PKG-INFO +3 -3
- {cle-9.3.2 → cle-9.3.4}/cle.egg-info/SOURCES.txt +8 -0
- {cle-9.3.2 → cle-9.3.4}/cle.egg-info/requires.txt +2 -2
- {cle-9.3.2 → cle-9.3.4}/pyproject.toml +17 -2
- cle-9.3.4/tests/test_aarch64_relocations.py +119 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_arm_firmware.py +14 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_clemory.py +3 -0
- cle-9.3.4/tests/test_coff.py +79 -0
- cle-9.3.4/tests/test_dex.py +34 -0
- cle-9.3.4/tests/test_extern.py +84 -0
- cle-9.3.4/tests/test_gopclntab.py +151 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_macho.py +178 -0
- cle-9.3.4/tests/test_mips_n32.py +78 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_optional_backends.py +2 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_pe.py +17 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_ppc64_initial_rtoc.py +27 -0
- cle-9.3.4/tests/test_rebase.py +75 -0
- cle-9.3.4/tests/test_stm32.py +148 -0
- cle-9.3.4/tests/test_te.py +110 -0
- cle-9.3.2/tests/test_aarch64_relocations.py +0 -44
- cle-9.3.2/tests/test_coff.py +0 -37
- cle-9.3.2/tests/test_extern.py +0 -45
- {cle-9.3.2 → cle-9.3.4}/LICENSE +0 -0
- {cle-9.3.2 → cle-9.3.4}/MANIFEST.in +0 -0
- {cle-9.3.2 → cle-9.3.4}/README.md +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/address_translator.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/backend.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/binja.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/blob.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/cartfile.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/cgc/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/cgc/backedcgc.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/cgc/cgc.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/compilation_unit.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/elfcore.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/hashtable.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/lsda.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/regions.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/amd64.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/arm.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/elfreloc.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/generic.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/i386.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/mips.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/ppc.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/s390x.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/relocation/sparc.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/subprogram.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/symbol.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/symbol_type.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/variable.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/elf/variable_type.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/common.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/glibc_startup.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/io_file.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/externs/simdata/simdata.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/ihex.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/inlined_function.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/java/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/java/android_lifecycle.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/java/apk.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/java/jar.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/encrypted_sentinel_backer.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/segment.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/structs.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/macho/symbol.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/minidump/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/named_region.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/regions.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/arm.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/generic.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/mips.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/pereloc.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/relocation/riscv.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/stubs.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/symbol.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/pe/symbolserver.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/region.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/regions.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/srec.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/static_archive.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/symbol.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/elf_tls.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/elfcore_tls.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/minidump_tls.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/pe_tls.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/tls/tls_object.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/uefi_firmware.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/universal2.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/backends/xbe.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/errors.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/gdb.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/memory.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/patched_stream.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/py.typed +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/structs/__init__.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/structs/mem_regions.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle/utils.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle.egg-info/dependency_links.txt +0 -0
- {cle-9.3.2 → cle-9.3.4}/cle.egg-info/top_level.txt +0 -0
- {cle-9.3.2 → cle-9.3.4}/setup.cfg +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_address_translator.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_amd64_relocations.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_arch_detect.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_blob.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_cart.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_compiler_detection.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_dwarf.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_dwarf_resiliency.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_elfcore.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_exceptions.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_gdb.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_got.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_hex.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_macho_bindinghelper.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_macho_dyld.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_macho_dyld_structs.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_macho_libs.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_macho_reloc.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_minidump.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_mips_relocations.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_namedregion.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_overlap.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_patched_stream.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_pe_coff_symbols.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_pe_delay_imports.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_pe_meta_regions.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_plt.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_ppc_relocations.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_preload.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_regions.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_relocated.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_runpath.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_simdata.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_stream.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_symbolserver.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_tls_resiliency.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_universal2.py +0 -0
- {cle-9.3.2 → cle-9.3.4}/tests/test_unpackword.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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 = "<
|
|
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
|
-
|
|
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
|