objutils 0.10.3__tar.gz → 0.10.5__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.
- {objutils-0.10.3 → objutils-0.10.5}/PKG-INFO +6 -2
- {objutils-0.10.3 → objutils-0.10.5}/docs/README.rst +5 -1
- {objutils-0.10.3 → objutils-0.10.5}/objutils/__init__.py +1 -1
- {objutils-0.10.3 → objutils-0.10.5}/objutils/cosmac.py +4 -7
- {objutils-0.10.3 → objutils-0.10.5}/objutils/elf/model.py +7 -2
- {objutils-0.10.3 → objutils-0.10.5}/objutils/emon52.py +1 -3
- {objutils-0.10.3 → objutils-0.10.5}/objutils/etek.py +4 -10
- {objutils-0.10.3 → objutils-0.10.5}/objutils/hexfile.py +1 -1
- {objutils-0.10.3 → objutils-0.10.5}/objutils/ihex.py +1 -2
- {objutils-0.10.3 → objutils-0.10.5}/objutils/image.py +30 -6
- {objutils-0.10.3 → objutils-0.10.5}/objutils/pecoff/__init__.py +12 -12
- {objutils-0.10.3 → objutils-0.10.5}/objutils/pecoff/model.py +4 -1
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_coff_extract.py +26 -2
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_coff_syms.py +2 -2
- objutils-0.10.5/objutils/scripts/oj_dwarf_info.py +143 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/section.py +150 -8
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_etek.py +3 -5
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_image.py +2 -2
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_section.py +339 -169
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_section_join.py +0 -1
- {objutils-0.10.3 → objutils-0.10.5}/objutils/version.py +1 -1
- {objutils-0.10.3 → objutils-0.10.5}/pyproject.toml +2 -2
- {objutils-0.10.3 → objutils-0.10.5}/CMakeLists.txt +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/LICENSE +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/build_ext.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/.coveragerc +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/.vscode/launch.json +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/.vscode/settings.json +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/a2l_test.shf +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/ash.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/binfile.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/checksums.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/.traverser.py.un~ +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/attrparser.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/c_generator.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/constants.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/encoding.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/lineprog.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/readers.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/sm.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/dwarf/traverser.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/elf/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/elf/arm/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/elf/arm/attributes.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/elf/defs.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/exceptions.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/extensions/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/extensions/ctre.hpp +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/extensions/difflib.h +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/extensions/exceptions.cpp +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/extensions/exceptions.hpp +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/extensions/hexfile.cpp +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/extensions/wrapper.cpp +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/fpc.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/hexdump.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/ieee695.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/logger.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/mostec.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/objutils.code-workspace +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/pecoff/defs.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/pecoff/pdb/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/pickleif.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/rca.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/readers.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/registry.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/arduino_build_artifacts.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_cgen.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_coff_import.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_coff_info.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_dwarf_import.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_elf_arm_attrs.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_elf_extract.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_elf_import.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_elf_info.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_elf_syms.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/scripts/oj_hex_info.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/shf.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/sig.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/srec.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tek.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_arm_attributes.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_ash.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_c_generator.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_checksums.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_cygpath.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_diff_bin.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_elf.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_emon52.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_examples_cgen.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_fpc.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_hexdump.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_hexfile.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_ihex.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_mostec.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_readers.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_registry.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_repr.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_shf.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_sm.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_srec.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_tek.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/tests/test_titext.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/titxt.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/utils/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/utils/arduino.py +0 -0
- {objutils-0.10.3 → objutils-0.10.5}/objutils/utils/diff.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: objutils
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.5
|
|
4
4
|
Summary: Objectfile library for Python
|
|
5
5
|
License: GPLv2
|
|
6
6
|
License-File: LICENSE
|
|
@@ -36,7 +36,7 @@ Readme
|
|
|
36
36
|
.. image:: https://github.com/christoph2/objutils/raw/master/docs/objutils_banner.png
|
|
37
37
|
:align: center
|
|
38
38
|
|
|
39
|
-
|PyPI| |Python Versions| |License: GPLv2| |Code style: black|
|
|
39
|
+
|PyPI| |Python Versions| |License: GPLv2| |Code style: black| |Ask DeepWiki| |PDF Manual|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
Binary data stored in hex-files is in widespread use especially in embedded systems applications.
|
|
@@ -467,4 +467,8 @@ If you contribute code to this project, you are implicitly allowing your code to
|
|
|
467
467
|
:target: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
468
468
|
.. |Code style: black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
469
469
|
:target: https://github.com/psf/black
|
|
470
|
+
.. |Ask DeepWiki| image:: https://deepwiki.com/badge.svg
|
|
471
|
+
:target: https://deepwiki.com/christoph2/objutils
|
|
472
|
+
.. |PDF Manual| image:: https://img.shields.io/badge/docs-PDF%20manual-blue.svg
|
|
473
|
+
:target: https://objutils.readthedocs.io/_/downloads/en/latest/pdf/
|
|
470
474
|
|
|
@@ -5,7 +5,7 @@ Readme
|
|
|
5
5
|
.. image:: https://github.com/christoph2/objutils/raw/master/docs/objutils_banner.png
|
|
6
6
|
:align: center
|
|
7
7
|
|
|
8
|
-
|PyPI| |Python Versions| |License: GPLv2| |Code style: black|
|
|
8
|
+
|PyPI| |Python Versions| |License: GPLv2| |Code style: black| |Ask DeepWiki| |PDF Manual|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
Binary data stored in hex-files is in widespread use especially in embedded systems applications.
|
|
@@ -436,3 +436,7 @@ If you contribute code to this project, you are implicitly allowing your code to
|
|
|
436
436
|
:target: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
437
437
|
.. |Code style: black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
438
438
|
:target: https://github.com/psf/black
|
|
439
|
+
.. |Ask DeepWiki| image:: https://deepwiki.com/badge.svg
|
|
440
|
+
:target: https://deepwiki.com/christoph2/objutils
|
|
441
|
+
.. |PDF Manual| image:: https://img.shields.io/badge/docs-PDF%20manual-blue.svg
|
|
442
|
+
:target: https://objutils.readthedocs.io/_/downloads/en/latest/pdf/
|
|
@@ -5,14 +5,11 @@ This module handles the RCA Cosmac hex format, used with RCA 1802/1804/1805
|
|
|
5
5
|
microprocessors and COSMAC development systems.
|
|
6
6
|
|
|
7
7
|
Format specification:
|
|
8
|
-
|
|
9
8
|
- Four data record formats with optional address:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
4. ``DD`` – Data only (address continues from previous)
|
|
15
|
-
|
|
9
|
+
1. !MAAAA DD - Full format with start symbol
|
|
10
|
+
2. ?MAAAA DD - Alternate start symbol
|
|
11
|
+
3. AAAA DD - Address only (no symbol)
|
|
12
|
+
4. DD - Data only (address continues from previous)
|
|
16
13
|
- M: Memory identifier (single hex digit)
|
|
17
14
|
- AAAA: 16-bit address (hex)
|
|
18
15
|
- DD: Data bytes (hex, space-separated)
|
|
@@ -130,7 +130,7 @@ from sqlalchemy.engine import Engine
|
|
|
130
130
|
from sqlalchemy.exc import SQLAlchemyError
|
|
131
131
|
from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
|
|
132
132
|
from sqlalchemy.orm import Session, declarative_base, relationship
|
|
133
|
-
from sqlalchemy.pool import NullPool
|
|
133
|
+
from sqlalchemy.pool import NullPool, StaticPool
|
|
134
134
|
from sqlalchemy.sql import func
|
|
135
135
|
|
|
136
136
|
from objutils.elf import defs
|
|
@@ -1312,6 +1312,11 @@ class Model:
|
|
|
1312
1312
|
# else:
|
|
1313
1313
|
self.dbname = filename
|
|
1314
1314
|
|
|
1315
|
+
# NullPool creates a fresh connection each time, which doesn't work for
|
|
1316
|
+
# :memory: SQLite (each connection gets its own empty database).
|
|
1317
|
+
# Use StaticPool for in-memory databases to reuse the same connection.
|
|
1318
|
+
pool_class = StaticPool if self.dbname == ":memory:" else NullPool
|
|
1319
|
+
|
|
1315
1320
|
self._engine = create_engine(
|
|
1316
1321
|
f"sqlite:///{self.dbname}",
|
|
1317
1322
|
echo=debug,
|
|
@@ -1319,7 +1324,7 @@ class Model:
|
|
|
1319
1324
|
"detect_types": sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES,
|
|
1320
1325
|
"timeout": SQLITE_TIMEOUT_SECONDS,
|
|
1321
1326
|
},
|
|
1322
|
-
poolclass=
|
|
1327
|
+
poolclass=pool_class,
|
|
1323
1328
|
native_datetime=True,
|
|
1324
1329
|
)
|
|
1325
1330
|
|
|
@@ -5,9 +5,7 @@ This module handles the EMON52 hex format used by the Elektor
|
|
|
5
5
|
Electronics EMON52 8052 development system.
|
|
6
6
|
|
|
7
7
|
Format specification:
|
|
8
|
-
|
|
9
|
-
- Data records: ``LL AAAA:DD CCCC``
|
|
10
|
-
|
|
8
|
+
- Data records: LL AAAA:DD CCCC
|
|
11
9
|
- LL: Length/byte count (hex)
|
|
12
10
|
- AAAA: 16-bit address (hex)
|
|
13
11
|
- DD: Data bytes (hex, space-separated)
|
|
@@ -5,24 +5,18 @@ This module handles the Extended Tektronix hex format, an extension
|
|
|
5
5
|
of the standard Tektronix format with 24-bit addressing and symbol support.
|
|
6
6
|
|
|
7
7
|
Format specification:
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
- LL: Length field (``2 * (data_length + 5)`` in hex)
|
|
8
|
+
- Data records: %LL6CCAAAAADD
|
|
9
|
+
- LL: Length field (2 * (data_length + 5) in hex)
|
|
12
10
|
- 6: Record type identifier
|
|
13
11
|
- CC: Checksum (nibble sum)
|
|
14
12
|
- AAAAAA: 24-bit address (hex)
|
|
15
13
|
- DD: Data bytes (hex)
|
|
16
|
-
|
|
17
|
-
- Symbol records: ``%LL3CCU``
|
|
18
|
-
|
|
14
|
+
- Symbol records: %LL3CCU
|
|
19
15
|
- LL: Length field
|
|
20
16
|
- 3: Symbol type identifier
|
|
21
17
|
- CC: Checksum
|
|
22
18
|
- U: Symbol string (name + address)
|
|
23
|
-
|
|
24
|
-
- EOF records: ``%LL8CCAAAAADD``
|
|
25
|
-
|
|
19
|
+
- EOF records: %LL8CCAAAAADD
|
|
26
20
|
- 8: EOF type identifier
|
|
27
21
|
"""
|
|
28
22
|
|
|
@@ -791,7 +791,7 @@ class Reader(BaseType):
|
|
|
791
791
|
self.stats = Statistics()
|
|
792
792
|
self.valid = True
|
|
793
793
|
self.formats: list[tuple[int, re.Pattern[str]]] = []
|
|
794
|
-
self.base_address = 0
|
|
794
|
+
self.base_address = 0 # Base address for relative addressing (if applicable - mainly Intel HEX)
|
|
795
795
|
|
|
796
796
|
# Parse FORMAT_SPEC into compiled patterns
|
|
797
797
|
if isinstance(self.FORMAT_SPEC, str):
|
|
@@ -25,10 +25,9 @@ __copyright__ = """
|
|
|
25
25
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
import operator
|
|
29
28
|
from collections.abc import Mapping, Sequence
|
|
30
29
|
from functools import partial
|
|
31
|
-
from typing import Any,
|
|
30
|
+
from typing import Any, Optional
|
|
32
31
|
|
|
33
32
|
import objutils.checksums as checksums
|
|
34
33
|
import objutils.hexfile as hexfile
|
|
@@ -716,11 +716,20 @@ class Image:
|
|
|
716
716
|
array: Any,
|
|
717
717
|
dtype: str,
|
|
718
718
|
byte_order: str = "MSB_LAST",
|
|
719
|
-
|
|
719
|
+
index_mode: str = "ROW_DIR",
|
|
720
720
|
**kws: Any,
|
|
721
721
|
) -> None:
|
|
722
|
-
"""Write a NumPy ndarray using ASAM datatype and ECU byte order semantics.
|
|
723
|
-
|
|
722
|
+
"""Write a NumPy ndarray using ASAM datatype and ECU byte order semantics.
|
|
723
|
+
|
|
724
|
+
Args:
|
|
725
|
+
addr: Absolute memory address to write to.
|
|
726
|
+
array: NumPy ndarray (shape in numpy convention).
|
|
727
|
+
dtype: ASAM datatype name.
|
|
728
|
+
byte_order: ASAM byte order string.
|
|
729
|
+
index_mode: ``"ROW_DIR"`` (default) or ``"COLUMN_DIR"``.
|
|
730
|
+
**kws: Passed through to section method.
|
|
731
|
+
"""
|
|
732
|
+
self._call_address_function("write_asam_ndarray", addr, array, dtype, byte_order, index_mode=index_mode, **kws)
|
|
724
733
|
|
|
725
734
|
def read_ndarray(
|
|
726
735
|
self,
|
|
@@ -755,12 +764,27 @@ class Image:
|
|
|
755
764
|
length: int,
|
|
756
765
|
dtype: str,
|
|
757
766
|
shape: Optional[tuple[int, ...]] = None,
|
|
758
|
-
order: Optional[str] = None,
|
|
759
767
|
byte_order: str = "MSB_LAST",
|
|
768
|
+
index_mode: str = "ROW_DIR",
|
|
760
769
|
**kws: Any,
|
|
761
770
|
) -> Any:
|
|
762
|
-
"""Read a NumPy ndarray using ASAM datatype and ECU byte order semantics.
|
|
763
|
-
|
|
771
|
+
"""Read a NumPy ndarray using ASAM datatype and ECU byte order semantics.
|
|
772
|
+
|
|
773
|
+
Args:
|
|
774
|
+
addr: Start address to read from.
|
|
775
|
+
length: Number of **elements** (not bytes).
|
|
776
|
+
dtype: ASAM datatype name.
|
|
777
|
+
shape: Dimensions in **ASAM** order ``(X, Y, Z, ...)``.
|
|
778
|
+
byte_order: ASAM byte order string.
|
|
779
|
+
index_mode: ``"ROW_DIR"`` (default) or ``"COLUMN_DIR"``.
|
|
780
|
+
**kws: Passed through to section method.
|
|
781
|
+
|
|
782
|
+
Returns:
|
|
783
|
+
NumPy ndarray with shape in numpy convention.
|
|
784
|
+
"""
|
|
785
|
+
return self._call_address_function(
|
|
786
|
+
"read_asam_ndarray", addr, length, dtype, shape=shape, byte_order=byte_order, index_mode=index_mode, **kws
|
|
787
|
+
)
|
|
764
788
|
|
|
765
789
|
def read_string(self, addr: int, encoding: str = "latin1", length: int = -1, **kws: Any) -> str:
|
|
766
790
|
"""Read string from image.
|
|
@@ -739,12 +739,12 @@ class SectionAPI:
|
|
|
739
739
|
def __init__(self, parent: PeParser):
|
|
740
740
|
self.parent = parent
|
|
741
741
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
742
|
+
def fetch(self, name_pattern: str | None = None) -> list[model.Pe_Section]:
|
|
743
|
+
self.parent.create_db_on_demand()
|
|
744
|
+
if self.parent.db is None:
|
|
745
|
+
raise RuntimeError("PE database is not initialized")
|
|
746
|
+
with self.parent.db.session() as ses:
|
|
747
|
+
q = ses.query(model.Pe_Section)
|
|
748
748
|
if name_pattern:
|
|
749
749
|
q = q.filter(model.Pe_Section.name.like(f"%{name_pattern}%"))
|
|
750
750
|
return q.order_by(model.Pe_Section.vaddr).all()
|
|
@@ -754,12 +754,12 @@ class SymbolAPI:
|
|
|
754
754
|
def __init__(self, parent: PeParser):
|
|
755
755
|
self.parent = parent
|
|
756
756
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
757
|
+
def fetch(self, name_pattern: str | None = None) -> list[model.Pe_Symbol]:
|
|
758
|
+
self.parent.create_db_on_demand()
|
|
759
|
+
if self.parent.db is None:
|
|
760
|
+
raise RuntimeError("PE database is not initialized")
|
|
761
|
+
with self.parent.db.session() as ses:
|
|
762
|
+
q = ses.query(model.Pe_Symbol)
|
|
763
763
|
if name_pattern:
|
|
764
764
|
q = q.filter(model.Pe_Symbol.name.like(f"%{name_pattern}%"))
|
|
765
765
|
return q.order_by(model.Pe_Symbol.value).all()
|
|
@@ -114,7 +114,10 @@ def StdInteger(default=0, primary_key=False, unique=False, nullable=False, index
|
|
|
114
114
|
count = StdInteger(default=0, index=True)
|
|
115
115
|
```
|
|
116
116
|
"""
|
|
117
|
-
|
|
117
|
+
kw = dict(primary_key=primary_key, unique=unique, nullable=nullable, index=index)
|
|
118
|
+
if not primary_key:
|
|
119
|
+
kw["default"] = default
|
|
120
|
+
return Column(Integer, **kw)
|
|
118
121
|
|
|
119
122
|
|
|
120
123
|
class MixInBase:
|
|
@@ -52,6 +52,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
52
52
|
dest="include",
|
|
53
53
|
default=None,
|
|
54
54
|
)
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"-r",
|
|
57
|
+
"--no-image-base",
|
|
58
|
+
help="Use relative virtual addresses (RVAs) instead of absolute addresses. Required for 64-bit PE files "
|
|
59
|
+
"whose image base pushes addresses beyond 32-bit hex format limits.",
|
|
60
|
+
dest="no_image_base",
|
|
61
|
+
action="store_true",
|
|
62
|
+
)
|
|
55
63
|
parser.add_argument("-n", help="Number of data bytes per line", dest="row_length", default=16, type=int)
|
|
56
64
|
args = parser.parse_args(argv)
|
|
57
65
|
|
|
@@ -60,17 +68,33 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
60
68
|
except (OSError, ValueError, RuntimeError) as e:
|
|
61
69
|
print(f"\n'{args.pe_file}' is not valid PE/COFF file. Raised exception: '{repr(e)}'.")
|
|
62
70
|
return 1
|
|
63
|
-
|
|
71
|
+
|
|
72
|
+
add_image_base = not args.no_image_base
|
|
73
|
+
image_base = pp.image_base()
|
|
74
|
+
if args.no_image_base:
|
|
75
|
+
print(f"\nUsing relative addresses (image base {image_base:#010x} subtracted).\n")
|
|
76
|
+
else:
|
|
77
|
+
print(f"\nUsing absolute addresses (image base: {image_base:#010x}).\n")
|
|
78
|
+
|
|
79
|
+
print("Extracting from...\n")
|
|
64
80
|
print("Section Address Length")
|
|
65
81
|
print("-" * 45)
|
|
66
82
|
img = pp.create_image(
|
|
67
83
|
callback=callback,
|
|
68
84
|
join=args.join,
|
|
85
|
+
add_image_base=add_image_base,
|
|
69
86
|
exclude_pattern=args.exclude or "",
|
|
70
87
|
include_pattern=args.include or "",
|
|
71
88
|
)
|
|
72
89
|
if img:
|
|
73
|
-
|
|
90
|
+
try:
|
|
91
|
+
dump(args.file_type, args.output_file_name, img, row_length=args.row_length)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
if "address too large" in str(e).lower():
|
|
94
|
+
print(f"\nError: {e}")
|
|
95
|
+
print(f"Hint: Try using --no-image-base / -r to use relative addresses (subtract image base {image_base:#010x}).")
|
|
96
|
+
return 1
|
|
97
|
+
raise
|
|
74
98
|
print(f"HEX image written to: '{args.output_file_name}' [{len(img)} total bytes]")
|
|
75
99
|
return 0
|
|
76
100
|
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
|
|
6
6
|
import argparse
|
|
7
7
|
|
|
8
|
-
from objutils.pecoff import PeParser
|
|
8
|
+
from objutils.pecoff import PeParser, SymbolAPI
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def main(argv: list[str] | None = None) -> int:
|
|
@@ -35,7 +35,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
35
35
|
return 1
|
|
36
36
|
|
|
37
37
|
# Fetch via SymbolAPI to ensure DB is created/reused
|
|
38
|
-
syms =
|
|
38
|
+
syms = SymbolAPI(pp).fetch(name_pattern=args.pattern)
|
|
39
39
|
# Fallback: if SymbolAPI attr is not present (static type), use direct list
|
|
40
40
|
if not syms and pp.symbols:
|
|
41
41
|
syms = [type("_S", (), s) for s in pp.symbols] # quick adapter for printing
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""DWARF database inspector (read-only).
|
|
3
|
+
|
|
4
|
+
Lists compilation units or traverses DIE attributes from an existing ``.prgdb``
|
|
5
|
+
database. This script intentionally does **not** perform DWARF import; use
|
|
6
|
+
``oj-dwarf-import`` to build the database first.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
from collections.abc import Iterable
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from sqlalchemy.exc import SQLAlchemyError
|
|
17
|
+
|
|
18
|
+
from objutils.dwarf.constants import Tag
|
|
19
|
+
from objutils.dwarf.traverser import AttributeParser
|
|
20
|
+
from objutils.elf import model
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _parse_offset(text: str) -> int:
|
|
24
|
+
try:
|
|
25
|
+
return int(text, 0)
|
|
26
|
+
except ValueError as exc:
|
|
27
|
+
raise argparse.ArgumentTypeError(f"Invalid offset '{text}'") from exc
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _attr_value(die: Any, name: str) -> Any:
|
|
31
|
+
attrs = getattr(die, "attributes_map", {}) or {}
|
|
32
|
+
attr = attrs.get(name)
|
|
33
|
+
return None if attr is None else getattr(attr, "raw_value", None)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _print_cus(cus: Iterable[Any], *, quiet: bool) -> None:
|
|
37
|
+
if quiet:
|
|
38
|
+
return
|
|
39
|
+
print("Compilation Units:")
|
|
40
|
+
print("-------------------")
|
|
41
|
+
for idx, cu in enumerate(cus, 1):
|
|
42
|
+
name = _attr_value(cu, "name") or ""
|
|
43
|
+
low_pc = _attr_value(cu, "low_pc")
|
|
44
|
+
high_pc = _attr_value(cu, "high_pc")
|
|
45
|
+
low_pc_str = f"0x{int(low_pc):08x}" if low_pc is not None else "-"
|
|
46
|
+
high_pc_str = f"0x{int(high_pc):08x}" if high_pc is not None else "-"
|
|
47
|
+
print(f"[{idx:03d}] off=0x{int(cu.offset):08x} name={name} range={low_pc_str}..{high_pc_str}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _print_summary(session, *, quiet: bool) -> None:
|
|
51
|
+
if quiet:
|
|
52
|
+
return
|
|
53
|
+
die_count = session.query(model.DebugInformationEntry).count()
|
|
54
|
+
attr_count = session.query(model.DIEAttribute).count()
|
|
55
|
+
print(f"DIEs: {die_count:,}")
|
|
56
|
+
print(f"Attributes: {attr_count:,}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main(argv: list[str] | None = None) -> int:
|
|
60
|
+
parser = argparse.ArgumentParser(description="Inspect DWARF debug info stored in a .prgdb database (no import).")
|
|
61
|
+
parser.add_argument("db", help="Path to .prgdb database generated by oj-dwarf-import")
|
|
62
|
+
parser.add_argument("--quiet", "-q", action="store_true", help="Suppress non-error output")
|
|
63
|
+
parser.add_argument("--list-cus", action="store_true", help="List compile units")
|
|
64
|
+
parser.add_argument("--summary", action="store_true", help="Print DIE/attribute counts")
|
|
65
|
+
parser.add_argument(
|
|
66
|
+
"--walk-attrs",
|
|
67
|
+
action="store_true",
|
|
68
|
+
help="Traverse DIE attributes with AttributeParser (starts at first DIE unless --offset is set).",
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--offset",
|
|
72
|
+
type=_parse_offset,
|
|
73
|
+
help="Absolute DIE offset (decimal or 0x-prefixed hex) used for --walk-attrs start point.",
|
|
74
|
+
)
|
|
75
|
+
args = parser.parse_args(argv)
|
|
76
|
+
|
|
77
|
+
db_path = Path(args.db)
|
|
78
|
+
if db_path.suffix.lower() != model.DB_EXTENSION:
|
|
79
|
+
parser.error(f"Expected a '{model.DB_EXTENSION}' database; got '{db_path}'")
|
|
80
|
+
if not db_path.exists():
|
|
81
|
+
parser.error(f"Database not found: {db_path}")
|
|
82
|
+
|
|
83
|
+
post_actions = args.list_cus or args.summary or args.walk_attrs
|
|
84
|
+
if not post_actions:
|
|
85
|
+
args.list_cus = True
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
db = model.Model(str(db_path))
|
|
89
|
+
except (OSError, SQLAlchemyError, ValueError) as exc:
|
|
90
|
+
if not args.quiet:
|
|
91
|
+
print(f"Failed to open database '{db_path}': {exc}")
|
|
92
|
+
return 4
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
session = db.session
|
|
96
|
+
if args.summary:
|
|
97
|
+
_print_summary(session, quiet=args.quiet)
|
|
98
|
+
|
|
99
|
+
if args.list_cus or args.walk_attrs:
|
|
100
|
+
DIE = model.DebugInformationEntry
|
|
101
|
+
raw_cus = session.query(DIE).filter(DIE.tag == Tag.compile_unit).order_by(DIE.offset, DIE.cu_start).all()
|
|
102
|
+
seen = set()
|
|
103
|
+
cus = []
|
|
104
|
+
for cu in raw_cus:
|
|
105
|
+
key = (cu.offset, cu.cu_start)
|
|
106
|
+
if key in seen:
|
|
107
|
+
continue
|
|
108
|
+
seen.add(key)
|
|
109
|
+
cus.append(cu)
|
|
110
|
+
if args.list_cus:
|
|
111
|
+
_print_cus(cus, quiet=args.quiet)
|
|
112
|
+
|
|
113
|
+
if args.walk_attrs:
|
|
114
|
+
ap = AttributeParser(session)
|
|
115
|
+
if args.offset is not None:
|
|
116
|
+
start_die = session.query(DIE).filter(DIE.offset == int(args.offset)).first()
|
|
117
|
+
if start_die is None:
|
|
118
|
+
if not args.quiet:
|
|
119
|
+
print(f"No DIE found at offset 0x{int(args.offset):08x}.")
|
|
120
|
+
return 5
|
|
121
|
+
ap.traverse_tree(start_die)
|
|
122
|
+
else:
|
|
123
|
+
if not cus:
|
|
124
|
+
if not args.quiet:
|
|
125
|
+
print("No DebugInformationEntry available in the database to traverse.")
|
|
126
|
+
return 5
|
|
127
|
+
for cu in cus:
|
|
128
|
+
ap.traverse_tree(cu)
|
|
129
|
+
except (OSError, SQLAlchemyError, RuntimeError, ValueError) as exc:
|
|
130
|
+
if not args.quiet:
|
|
131
|
+
print(f"Traversal failed: {exc}")
|
|
132
|
+
return 5
|
|
133
|
+
finally:
|
|
134
|
+
try:
|
|
135
|
+
db.close()
|
|
136
|
+
except SQLAlchemyError:
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
return 0
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
raise SystemExit(main())
|