objutils 0.10.2__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.2 → objutils-0.10.4}/PKG-INFO +6 -2
- {objutils-0.10.2 → objutils-0.10.4}/docs/README.rst +5 -1
- {objutils-0.10.2 → objutils-0.10.4}/objutils/__init__.py +1 -1
- {objutils-0.10.2 → objutils-0.10.4}/objutils/elf/model.py +7 -2
- {objutils-0.10.2 → objutils-0.10.4}/objutils/etek.py +2 -2
- {objutils-0.10.2 → objutils-0.10.4}/objutils/hexfile.py +4 -1
- {objutils-0.10.2 → objutils-0.10.4}/objutils/ihex.py +1 -2
- {objutils-0.10.2 → objutils-0.10.4}/objutils/image.py +30 -6
- {objutils-0.10.2 → objutils-0.10.4}/objutils/pecoff/__init__.py +12 -12
- {objutils-0.10.2 → objutils-0.10.4}/objutils/pecoff/model.py +4 -1
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_coff_extract.py +26 -2
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_coff_syms.py +2 -2
- {objutils-0.10.2 → objutils-0.10.4}/objutils/section.py +150 -8
- {objutils-0.10.2 → objutils-0.10.4}/objutils/shf.py +1 -1
- {objutils-0.10.2 → objutils-0.10.4}/objutils/srec.py +2 -2
- objutils-0.10.4/objutils/tests/test_etek.py +159 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_image.py +2 -2
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_section.py +339 -169
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_section_join.py +0 -1
- {objutils-0.10.2 → objutils-0.10.4}/objutils/version.py +1 -1
- {objutils-0.10.2 → objutils-0.10.4}/pyproject.toml +2 -2
- {objutils-0.10.2 → objutils-0.10.4}/CMakeLists.txt +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/LICENSE +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/build_ext.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/.coveragerc +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/.vscode/launch.json +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/.vscode/settings.json +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/a2l_test.shf +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/ash.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/binfile.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/checksums.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/cosmac.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/.traverser.py.un~ +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/__init__.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/attrparser.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/c_generator.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/constants.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/encoding.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/lineprog.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/readers.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/sm.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/dwarf/traverser.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/elf/__init__.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/elf/arm/__init__.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/elf/arm/attributes.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/elf/defs.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/emon52.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/exceptions.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/extensions/__init__.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/extensions/ctre.hpp +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/extensions/difflib.h +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/extensions/exceptions.cpp +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/extensions/exceptions.hpp +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/extensions/hexfile.cpp +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/extensions/wrapper.cpp +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/fpc.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/hexdump.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/ieee695.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/logger.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/mostec.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/objutils.code-workspace +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/pecoff/defs.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/pecoff/pdb/__init__.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/pickleif.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/rca.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/readers.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/registry.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/arduino_build_artifacts.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_cgen.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_coff_import.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_coff_info.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_dwarf_import.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_elf_arm_attrs.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_elf_extract.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_elf_import.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_elf_info.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_elf_syms.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/scripts/oj_hex_info.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/sig.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tek.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/__init__.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_arm_attributes.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_ash.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_c_generator.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_checksums.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_cygpath.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_diff_bin.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_elf.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_emon52.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_examples_cgen.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_fpc.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_hexdump.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_hexfile.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_ihex.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_mostec.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_readers.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_registry.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_repr.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_shf.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_sm.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_srec.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_tek.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/tests/test_titext.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/titxt.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/utils/__init__.py +0 -0
- {objutils-0.10.2 → objutils-0.10.4}/objutils/utils/arduino.py +0 -0
- {objutils-0.10.2 → 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/
|
|
@@ -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
|
|
|
@@ -67,9 +67,9 @@ class Reader(hexfile.Reader):
|
|
|
67
67
|
VALID_CHARS = re.compile(r"^[a-zA-Z0-9_ %\n\r]*$")
|
|
68
68
|
|
|
69
69
|
FORMAT_SPEC = (
|
|
70
|
-
(DATA, "%
|
|
70
|
+
(DATA, "%LL6CCAAAAAADD"),
|
|
71
71
|
(SYMBOL, "%LL3CCU"),
|
|
72
|
-
(EOF, "%
|
|
72
|
+
(EOF, "%LL8CCAAAAAADD"),
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
def check_line(self, line: Any, format_type: int) -> None:
|
|
@@ -508,6 +508,7 @@ class Container:
|
|
|
508
508
|
length: Optional[int] = None
|
|
509
509
|
type: Optional[int] = None
|
|
510
510
|
checksum: Optional[int] = None
|
|
511
|
+
addrChecksum: Optional[int] = None
|
|
511
512
|
chunk: Optional[bytearray] = None
|
|
512
513
|
junk: Optional[str] = None
|
|
513
514
|
processing_instructions: list[Any] = field(default_factory=list)
|
|
@@ -790,7 +791,7 @@ class Reader(BaseType):
|
|
|
790
791
|
self.stats = Statistics()
|
|
791
792
|
self.valid = True
|
|
792
793
|
self.formats: list[tuple[int, re.Pattern[str]]] = []
|
|
793
|
-
self.base_address = 0
|
|
794
|
+
self.base_address = 0 # Base address for relative addressing (if applicable - mainly Intel HEX)
|
|
794
795
|
|
|
795
796
|
# Parse FORMAT_SPEC into compiled patterns
|
|
796
797
|
if isinstance(self.FORMAT_SPEC, str):
|
|
@@ -892,6 +893,7 @@ class Reader(BaseType):
|
|
|
892
893
|
length = self._parse_optional_int(dict_, "length")
|
|
893
894
|
record_type = self._parse_optional_int(dict_, "type")
|
|
894
895
|
checksum = self._parse_optional_int(dict_, "checksum")
|
|
896
|
+
addr_checksum = self._parse_optional_int(dict_, "addrChecksum")
|
|
895
897
|
junk = dict_.get("junk")
|
|
896
898
|
|
|
897
899
|
# Parse data chunk
|
|
@@ -912,6 +914,7 @@ class Reader(BaseType):
|
|
|
912
914
|
length=length,
|
|
913
915
|
type=record_type,
|
|
914
916
|
checksum=checksum,
|
|
917
|
+
addrChecksum=addr_checksum,
|
|
915
918
|
chunk=chunk,
|
|
916
919
|
junk=junk,
|
|
917
920
|
)
|
|
@@ -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():
|
|
@@ -30,7 +30,7 @@ from hashlib import sha1
|
|
|
30
30
|
|
|
31
31
|
from objutils.image import Image
|
|
32
32
|
from objutils.logger import Logger
|
|
33
|
-
from objutils.section import Section
|
|
33
|
+
from objutils.section import Section, join_sections
|
|
34
34
|
from objutils.utils import create_string_buffer
|
|
35
35
|
|
|
36
36
|
SHF_DTD = """<!--
|
|
@@ -151,14 +151,14 @@ class Reader(hexfile.Reader):
|
|
|
151
151
|
)
|
|
152
152
|
else:
|
|
153
153
|
raise TypeError(f"Invalid format type {format_type}.")
|
|
154
|
-
if hasattr(line, "chunk"):
|
|
154
|
+
if hasattr(line, "chunk") and line.chunk is not None:
|
|
155
155
|
checksum = (~(sum([line.length, checksum_of_address]) + sum(line.chunk))) & 0xFF
|
|
156
156
|
else:
|
|
157
157
|
checksum = (~(sum([line.length, checksum_of_address]))) & 0xFF
|
|
158
158
|
if line.checksum != checksum:
|
|
159
159
|
raise hexfile.InvalidRecordChecksumError()
|
|
160
160
|
line.length -= BIAS[format_type] # calculate actual data length.
|
|
161
|
-
if hasattr(line, "chunk") and line.length and (line.length != len(line.chunk)):
|
|
161
|
+
if hasattr(line, "chunk") and line.chunk is not None and line.length and (line.length != len(line.chunk)):
|
|
162
162
|
raise hexfile.InvalidRecordLengthError("Byte count doesn't match length of actual data.")
|
|
163
163
|
|
|
164
164
|
def is_data_line(self, line: Any, format_type: int) -> bool:
|