objutils 0.10.3__tar.gz → 0.10.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.
- {objutils-0.10.3 → objutils-0.10.4}/PKG-INFO +6 -2
- {objutils-0.10.3 → objutils-0.10.4}/docs/README.rst +5 -1
- {objutils-0.10.3 → objutils-0.10.4}/objutils/__init__.py +1 -1
- {objutils-0.10.3 → objutils-0.10.4}/objutils/cosmac.py +4 -7
- {objutils-0.10.3 → objutils-0.10.4}/objutils/elf/model.py +7 -2
- {objutils-0.10.3 → objutils-0.10.4}/objutils/emon52.py +1 -3
- {objutils-0.10.3 → objutils-0.10.4}/objutils/etek.py +4 -10
- {objutils-0.10.3 → objutils-0.10.4}/objutils/hexfile.py +1 -1
- {objutils-0.10.3 → objutils-0.10.4}/objutils/ihex.py +1 -2
- {objutils-0.10.3 → objutils-0.10.4}/objutils/image.py +30 -6
- {objutils-0.10.3 → objutils-0.10.4}/objutils/pecoff/__init__.py +12 -12
- {objutils-0.10.3 → objutils-0.10.4}/objutils/pecoff/model.py +4 -1
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_coff_extract.py +26 -2
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_coff_syms.py +2 -2
- {objutils-0.10.3 → objutils-0.10.4}/objutils/section.py +150 -8
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_etek.py +3 -5
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_image.py +2 -2
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_section.py +339 -169
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_section_join.py +0 -1
- {objutils-0.10.3 → objutils-0.10.4}/objutils/version.py +1 -1
- {objutils-0.10.3 → objutils-0.10.4}/pyproject.toml +2 -2
- {objutils-0.10.3 → objutils-0.10.4}/CMakeLists.txt +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/LICENSE +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/build_ext.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/.coveragerc +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/.vscode/launch.json +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/.vscode/settings.json +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/a2l_test.shf +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/ash.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/binfile.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/checksums.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/.traverser.py.un~ +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/attrparser.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/c_generator.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/constants.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/encoding.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/lineprog.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/readers.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/sm.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/dwarf/traverser.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/elf/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/elf/arm/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/elf/arm/attributes.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/elf/defs.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/exceptions.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/extensions/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/extensions/ctre.hpp +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/extensions/difflib.h +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/extensions/exceptions.cpp +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/extensions/exceptions.hpp +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/extensions/hexfile.cpp +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/extensions/wrapper.cpp +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/fpc.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/hexdump.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/ieee695.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/logger.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/mostec.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/objutils.code-workspace +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/pecoff/defs.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/pecoff/pdb/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/pickleif.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/rca.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/readers.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/registry.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/arduino_build_artifacts.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_cgen.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_coff_import.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_coff_info.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_dwarf_import.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_elf_arm_attrs.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_elf_extract.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_elf_import.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_elf_info.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_elf_syms.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/scripts/oj_hex_info.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/shf.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/sig.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/srec.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tek.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_arm_attributes.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_ash.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_c_generator.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_checksums.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_cygpath.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_diff_bin.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_elf.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_emon52.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_examples_cgen.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_fpc.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_hexdump.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_hexfile.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_ihex.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_mostec.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_readers.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_registry.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_repr.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_shf.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_sm.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_srec.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_tek.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/tests/test_titext.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/titxt.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/utils/__init__.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/objutils/utils/arduino.py +0 -0
- {objutils-0.10.3 → objutils-0.10.4}/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.4
|
|
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
|
|
@@ -419,6 +419,78 @@ def fortran_array_to_buffer(array: np.ndarray) -> bytearray:
|
|
|
419
419
|
return result
|
|
420
420
|
|
|
421
421
|
|
|
422
|
+
ASAM_INDEX_MODES = {"ROW_DIR", "COLUMN_DIR"}
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _asam_column_dir_from_buffer(data: bytes, numpy_shape: tuple, dtype: Any) -> np.ndarray:
|
|
426
|
+
"""Reconstruct array from ASAM COLUMN_DIR memory layout.
|
|
427
|
+
|
|
428
|
+
In COLUMN_DIR only the X and Y dimensions (last two in numpy convention)
|
|
429
|
+
are swapped compared to ROW_DIR. Higher dimensions (Z, Z4, Z5) keep
|
|
430
|
+
C-order. This is **not** true column-major (Fortran) order for dims > 2.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
data: Raw byte buffer.
|
|
434
|
+
numpy_shape: Target shape in numpy convention (rows, cols, …).
|
|
435
|
+
dtype: NumPy dtype.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
NumPy array with the specified shape.
|
|
439
|
+
"""
|
|
440
|
+
dt = np.dtype(dtype)
|
|
441
|
+
flat = np.frombuffer(data, dtype=dt)
|
|
442
|
+
|
|
443
|
+
if numpy_shape is None or len(numpy_shape) <= 1:
|
|
444
|
+
return flat.reshape(numpy_shape) if numpy_shape else flat
|
|
445
|
+
|
|
446
|
+
if len(numpy_shape) == 2:
|
|
447
|
+
return flat.reshape(numpy_shape, order="F")
|
|
448
|
+
|
|
449
|
+
# 3D+: only the last two dims (Y, X) are Fortran-ordered per slice.
|
|
450
|
+
higher_dims = numpy_shape[:-2]
|
|
451
|
+
yx_shape = numpy_shape[-2:]
|
|
452
|
+
num_slices = reduce(mul, higher_dims, 1)
|
|
453
|
+
slice_size = reduce(mul, yx_shape, 1)
|
|
454
|
+
|
|
455
|
+
sliced = flat.reshape(num_slices, slice_size)
|
|
456
|
+
out = np.empty((num_slices, *yx_shape), dtype=dt)
|
|
457
|
+
for idx in range(num_slices):
|
|
458
|
+
out[idx] = sliced[idx].reshape(yx_shape, order="F")
|
|
459
|
+
return out.reshape(numpy_shape)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _asam_column_dir_to_buffer(arr: np.ndarray) -> bytearray:
|
|
463
|
+
"""Serialize array for ASAM COLUMN_DIR memory layout.
|
|
464
|
+
|
|
465
|
+
Only the X and Y dimensions (last two in numpy convention) are
|
|
466
|
+
transposed. Higher dimensions keep C-order.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
arr: NumPy array to serialize.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
Byte buffer with COLUMN_DIR memory layout.
|
|
473
|
+
"""
|
|
474
|
+
if arr.ndim <= 1:
|
|
475
|
+
return bytearray(arr.tobytes())
|
|
476
|
+
|
|
477
|
+
if arr.ndim == 2:
|
|
478
|
+
return bytearray(arr.tobytes("F"))
|
|
479
|
+
|
|
480
|
+
shape = arr.shape
|
|
481
|
+
higher_dims = shape[:-2]
|
|
482
|
+
yx_shape = shape[-2:]
|
|
483
|
+
num_slices = reduce(mul, higher_dims, 1)
|
|
484
|
+
slice_bytes = reduce(mul, yx_shape, 1) * arr.dtype.itemsize
|
|
485
|
+
rs_arr = arr.reshape(num_slices, *yx_shape)
|
|
486
|
+
result = bytearray(arr.nbytes)
|
|
487
|
+
offset = 0
|
|
488
|
+
for idx in range(num_slices):
|
|
489
|
+
result[offset : offset + slice_bytes] = rs_arr[idx].tobytes("F")
|
|
490
|
+
offset += slice_bytes
|
|
491
|
+
return result
|
|
492
|
+
|
|
493
|
+
|
|
422
494
|
@dataclass(repr=False, order=True)
|
|
423
495
|
class Section:
|
|
424
496
|
"""Continuous block of bytes with start address and known length.
|
|
@@ -978,17 +1050,44 @@ class Section:
|
|
|
978
1050
|
array: np.ndarray,
|
|
979
1051
|
dtype: str,
|
|
980
1052
|
byte_order: str = "MSB_LAST",
|
|
981
|
-
|
|
1053
|
+
index_mode: str = "ROW_DIR",
|
|
982
1054
|
**kws,
|
|
983
1055
|
) -> None:
|
|
1056
|
+
"""Write a NumPy ndarray using ASAM datatype and byte order semantics.
|
|
1057
|
+
|
|
1058
|
+
Unlike :meth:`write_ndarray`, this method uses ASAM conventions:
|
|
1059
|
+
|
|
1060
|
+
- ``index_mode`` controls the memory layout (``ROW_DIR`` or
|
|
1061
|
+
``COLUMN_DIR``) instead of the generic ``order`` parameter.
|
|
1062
|
+
- Byte-order permutations (word-swap variants) are applied per
|
|
1063
|
+
element.
|
|
1064
|
+
|
|
1065
|
+
Args:
|
|
1066
|
+
addr: Absolute memory address to write to.
|
|
1067
|
+
array: NumPy array to write (shape in **numpy** convention,
|
|
1068
|
+
i.e. rows × columns).
|
|
1069
|
+
dtype: ASAM datatype name (e.g. ``"UWORD"``, ``"ULONG"``).
|
|
1070
|
+
byte_order: ASAM byte order string.
|
|
1071
|
+
index_mode: ``"ROW_DIR"`` (default) – X increments fastest
|
|
1072
|
+
(C-like row-major). ``"COLUMN_DIR"`` – Y increments
|
|
1073
|
+
fastest; only X and Y are swapped (not true column-major
|
|
1074
|
+
for dims > 2).
|
|
1075
|
+
**kws: Passed through to :meth:`write`.
|
|
1076
|
+
|
|
1077
|
+
Raises:
|
|
1078
|
+
TypeError: If *array* is not an ndarray.
|
|
1079
|
+
ValueError: If *index_mode* is unsupported.
|
|
1080
|
+
"""
|
|
984
1081
|
if not isinstance(array, np.ndarray):
|
|
985
1082
|
raise TypeError("array must be of type numpy.ndarray.")
|
|
1083
|
+
if index_mode not in ASAM_INDEX_MODES:
|
|
1084
|
+
raise ValueError(f"Unsupported index_mode {index_mode!r}; use ROW_DIR or COLUMN_DIR.")
|
|
986
1085
|
|
|
987
1086
|
asam_byte_order = self._resolve_asam_byteorder(byte_order)
|
|
988
1087
|
internal_dtype = self._asam_numeric_dtype_to_internal(dtype, asam_byte_order)
|
|
989
1088
|
typed_array = np.asarray(array, dtype=self._numpy_dtype_from_internal(internal_dtype))
|
|
990
|
-
if
|
|
991
|
-
raw_data =
|
|
1089
|
+
if index_mode == "COLUMN_DIR":
|
|
1090
|
+
raw_data = _asam_column_dir_to_buffer(typed_array)
|
|
992
1091
|
else:
|
|
993
1092
|
raw_data = typed_array.tobytes()
|
|
994
1093
|
permuted = self._permute_asam_buffer(raw_data, internal_dtype, asam_byte_order)
|
|
@@ -1015,20 +1114,63 @@ class Section:
|
|
|
1015
1114
|
length: int,
|
|
1016
1115
|
dtype: str,
|
|
1017
1116
|
shape: tuple = None,
|
|
1018
|
-
order: str = None,
|
|
1019
1117
|
byte_order: str = "MSB_LAST",
|
|
1118
|
+
index_mode: str = "ROW_DIR",
|
|
1020
1119
|
**kws,
|
|
1021
1120
|
) -> np.ndarray:
|
|
1121
|
+
"""Read a NumPy ndarray using ASAM datatype and byte order semantics.
|
|
1122
|
+
|
|
1123
|
+
Unlike :meth:`read_ndarray`, this method uses ASAM conventions:
|
|
1124
|
+
|
|
1125
|
+
- ``length`` is the **element count** (not byte count).
|
|
1126
|
+
- ``shape`` uses ASAM dimension order ``(X, Y, Z, Z4, Z5)``
|
|
1127
|
+
which is **reversed** compared to numpy ``(…, Z, Y, X)``.
|
|
1128
|
+
- ``index_mode`` controls the memory layout instead of the
|
|
1129
|
+
generic ``order`` parameter.
|
|
1130
|
+
|
|
1131
|
+
Args:
|
|
1132
|
+
addr: Absolute memory address to read from.
|
|
1133
|
+
length: Number of **elements** to read.
|
|
1134
|
+
dtype: ASAM datatype name (e.g. ``"UWORD"``, ``"ULONG"``).
|
|
1135
|
+
shape: Array dimensions in **ASAM** convention ``(X, Y, …)``.
|
|
1136
|
+
Converted internally to numpy convention ``(…, Y, X)``.
|
|
1137
|
+
byte_order: ASAM byte order string.
|
|
1138
|
+
index_mode: ``"ROW_DIR"`` (default) – X increments fastest
|
|
1139
|
+
(C-like row-major). ``"COLUMN_DIR"`` – Y increments
|
|
1140
|
+
fastest; only X and Y are swapped (not true column-major
|
|
1141
|
+
for dims > 2).
|
|
1142
|
+
**kws: Passed through to :meth:`read`.
|
|
1143
|
+
|
|
1144
|
+
Returns:
|
|
1145
|
+
NumPy array with shape in **numpy** convention.
|
|
1146
|
+
|
|
1147
|
+
Raises:
|
|
1148
|
+
ValueError: If *index_mode* is unsupported.
|
|
1149
|
+
"""
|
|
1150
|
+
if index_mode not in ASAM_INDEX_MODES:
|
|
1151
|
+
raise ValueError(f"Unsupported index_mode {index_mode!r}; use ROW_DIR or COLUMN_DIR.")
|
|
1152
|
+
|
|
1022
1153
|
asam_byte_order = self._resolve_asam_byteorder(byte_order)
|
|
1023
1154
|
internal_dtype = self._asam_numeric_dtype_to_internal(dtype, asam_byte_order)
|
|
1024
1155
|
dt = self._numpy_dtype_from_internal(internal_dtype)
|
|
1025
|
-
|
|
1156
|
+
|
|
1157
|
+
# Compute byte count from element count.
|
|
1158
|
+
type_name = internal_dtype.split("_")[0]
|
|
1159
|
+
type_size = TYPE_SIZES[type_name]
|
|
1160
|
+
byte_count = length * type_size
|
|
1161
|
+
|
|
1162
|
+
# Convert ASAM shape (X, Y, Z …) → numpy shape (… Z, Y, X).
|
|
1163
|
+
numpy_shape = tuple(reversed(shape)) if shape else None
|
|
1164
|
+
|
|
1165
|
+
raw_data = self.read(addr, byte_count, **kws)
|
|
1026
1166
|
permuted = self._permute_asam_buffer(raw_data, internal_dtype, asam_byte_order)
|
|
1027
|
-
if order is not None and order == "F":
|
|
1028
|
-
return fortran_array_from_buffer(arr=permuted, shape=shape, dtype=dt)
|
|
1029
1167
|
|
|
1168
|
+
if index_mode == "COLUMN_DIR":
|
|
1169
|
+
return _asam_column_dir_from_buffer(permuted, numpy_shape, dt)
|
|
1170
|
+
|
|
1171
|
+
# ROW_DIR: standard C-order.
|
|
1030
1172
|
flat = np.frombuffer(permuted, dtype=dt)
|
|
1031
|
-
return flat.reshape(
|
|
1173
|
+
return flat.reshape(numpy_shape) if numpy_shape else flat
|
|
1032
1174
|
|
|
1033
1175
|
"""
|
|
1034
1176
|
def write_timestamp():
|
|
@@ -15,7 +15,6 @@ import pytest
|
|
|
15
15
|
from objutils import dumps, loads
|
|
16
16
|
from objutils.hexfile import InvalidRecordChecksumError, InvalidRecordLengthError
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
# ---------------------------------------------------------------------------
|
|
20
19
|
# Test vectors
|
|
21
20
|
# ---------------------------------------------------------------------------
|
|
@@ -39,7 +38,7 @@ S19 = (
|
|
|
39
38
|
)
|
|
40
39
|
|
|
41
40
|
# Single-byte records for boundary / 24-bit address tests.
|
|
42
|
-
ETEK_SINGLE_BYTE_LOW = b"%0C628000001AB\n"
|
|
41
|
+
ETEK_SINGLE_BYTE_LOW = b"%0C628000001AB\n" # address 0x000001, data 0xAB
|
|
43
42
|
ETEK_SINGLE_BYTE_HIGH = b"%0C649FF0000CD\n" # address 0xFF0000, data 0xCD
|
|
44
43
|
|
|
45
44
|
|
|
@@ -72,13 +71,13 @@ def test_loads_produces_correct_srec_output():
|
|
|
72
71
|
def test_loads_single_byte_low_address():
|
|
73
72
|
"""Single data byte at address 0x000001 is read correctly."""
|
|
74
73
|
img = _etek_loads(ETEK_SINGLE_BYTE_LOW)
|
|
75
|
-
assert img.read(0x000001, 1) == b"\
|
|
74
|
+
assert img.read(0x000001, 1) == b"\xab"
|
|
76
75
|
|
|
77
76
|
|
|
78
77
|
def test_loads_single_byte_high_address():
|
|
79
78
|
"""Single data byte at address 0xFF0000 (max 24-bit) is read correctly."""
|
|
80
79
|
img = _etek_loads(ETEK_SINGLE_BYTE_HIGH)
|
|
81
|
-
assert img.read(0xFF0000, 1) == b"\
|
|
80
|
+
assert img.read(0xFF0000, 1) == b"\xcd"
|
|
82
81
|
|
|
83
82
|
|
|
84
83
|
def test_loads_image_min_address():
|
|
@@ -158,4 +157,3 @@ def test_invalid_length_raises():
|
|
|
158
157
|
bad = b"%2C6C200B000576F77212044696420796F7520726561\n"
|
|
159
158
|
with pytest.raises(InvalidRecordLengthError):
|
|
160
159
|
_etek_loads(bad)
|
|
161
|
-
|
|
@@ -1364,9 +1364,9 @@ def test_image_asam_numeric_array_word_swap_32bit():
|
|
|
1364
1364
|
def test_image_asam_ndarray_fortran_roundtrip():
|
|
1365
1365
|
img = Image(Section(data=bytearray(32), start_address=0x1000))
|
|
1366
1366
|
arr = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint16)
|
|
1367
|
-
img.write_asam_ndarray(0x1000, arr, "UWORD", byte_order="MSB_FIRST_MSW_LAST",
|
|
1367
|
+
img.write_asam_ndarray(0x1000, arr, "UWORD", byte_order="MSB_FIRST_MSW_LAST", index_mode="COLUMN_DIR")
|
|
1368
1368
|
assert img.read(0x1000, 12) == b"\x01\x00\x04\x00\x02\x00\x05\x00\x03\x00\x06\x00"
|
|
1369
|
-
result = img.read_asam_ndarray(0x1000,
|
|
1369
|
+
result = img.read_asam_ndarray(0x1000, 6, "UWORD", shape=(3, 2), index_mode="COLUMN_DIR", byte_order="MSB_FIRST_MSW_LAST")
|
|
1370
1370
|
assert np.array_equal(result, arr)
|
|
1371
1371
|
|
|
1372
1372
|
|