microchip-devtools 0.1.0__py3-none-any.whl
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.
- microchip_devtools/__init__.py +0 -0
- microchip_devtools/_project.py +14 -0
- microchip_devtools/format/__init__.py +0 -0
- microchip_devtools/format/uncrustify.py +119 -0
- microchip_devtools/list_cmds.py +31 -0
- microchip_devtools/mcc/__init__.py +0 -0
- microchip_devtools/mcc/check_peripheral.py +207 -0
- microchip_devtools/mcc/mcc_refresh.py +343 -0
- microchip_devtools/mcc/parse_hardware.py +374 -0
- microchip_devtools/setup_env/__init__.py +0 -0
- microchip_devtools/setup_env/_ui.py +63 -0
- microchip_devtools/setup_env/checks.py +178 -0
- microchip_devtools/setup_env/defaults.py +33 -0
- microchip_devtools/setup_env/runner.py +334 -0
- microchip_devtools/xc32/__init__.py +0 -0
- microchip_devtools/xc32/merge_hex.py +238 -0
- microchip_devtools/xc32/validate_fmt3.py +230 -0
- microchip_devtools-0.1.0.dist-info/METADATA +16 -0
- microchip_devtools-0.1.0.dist-info/RECORD +21 -0
- microchip_devtools-0.1.0.dist-info/WHEEL +4 -0
- microchip_devtools-0.1.0.dist-info/entry_points.txt +10 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Detect initialized static variables that would trigger XC32 __dinit_copy_val_data bug.
|
|
3
|
+
|
|
4
|
+
XC32 selects fmt=3 when all array elements equal the same non-zero constant.
|
|
5
|
+
The fmt=3 handler (__dinit_copy_val_data) decrements nbytes by 4 per iteration
|
|
6
|
+
and exits only when nbytes == 0. If nbytes % 4 != 0 the counter wraps to
|
|
7
|
+
0xFFFFFFFC and the loop writes forever, corrupting all memory before crashing
|
|
8
|
+
with a Data Bus Error at an unmapped address.
|
|
9
|
+
|
|
10
|
+
The emulator build uses mipsel-linux-gnu-gcc and --gc-sections, so some
|
|
11
|
+
translation units are eliminated from the final ELF even though they ARE linked
|
|
12
|
+
in the real XC32 build. Scanning only the final ELF misses those. This script
|
|
13
|
+
scans individual object files (.o) where all symbols are present before GC.
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
mchp-validate-xc32 <elf_or_obj> [<elf_or_obj> ...]
|
|
17
|
+
mchp-validate-xc32 --objects-dir <dir>
|
|
18
|
+
|
|
19
|
+
Exit codes:
|
|
20
|
+
0 — no violations found
|
|
21
|
+
1 — one or more XC32 fmt=3 risks detected (also exits 1 on parse error)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import os
|
|
25
|
+
import struct
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
SHT_SYMTAB = 2
|
|
29
|
+
SHT_STRTAB = 3
|
|
30
|
+
SHT_PROGBITS = 1
|
|
31
|
+
SHF_ALLOC = 0x2
|
|
32
|
+
SHF_WRITE = 0x1
|
|
33
|
+
STT_OBJECT = 1
|
|
34
|
+
|
|
35
|
+
MIN_SYM_SIZE = 4
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _read_shdr(f, endian, shoff, shentsize, idx):
|
|
39
|
+
f.seek(shoff + idx * shentsize)
|
|
40
|
+
raw = f.read(shentsize)
|
|
41
|
+
if len(raw) < 40:
|
|
42
|
+
return None
|
|
43
|
+
sh_name, sh_type, sh_flags, sh_addr, sh_offset, sh_size, \
|
|
44
|
+
sh_link, _info, _align, sh_entsize = \
|
|
45
|
+
struct.unpack_from(f"{endian}10I", raw)
|
|
46
|
+
return {
|
|
47
|
+
"name_idx": sh_name,
|
|
48
|
+
"type": sh_type,
|
|
49
|
+
"flags": sh_flags,
|
|
50
|
+
"addr": sh_addr,
|
|
51
|
+
"offset": sh_offset,
|
|
52
|
+
"size": sh_size,
|
|
53
|
+
"link": sh_link,
|
|
54
|
+
"entsize": sh_entsize,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _section_name(f, shstrtab, name_idx):
|
|
59
|
+
f.seek(shstrtab["offset"] + name_idx)
|
|
60
|
+
buf = bytearray()
|
|
61
|
+
while True:
|
|
62
|
+
c = f.read(1)
|
|
63
|
+
if not c or c == b"\x00":
|
|
64
|
+
break
|
|
65
|
+
buf.extend(c)
|
|
66
|
+
return buf.decode("ascii", errors="replace")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _read_cstr(data, offset):
|
|
70
|
+
end = data.index(b"\x00", offset)
|
|
71
|
+
return data[offset:end].decode("ascii", errors="replace")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _is_fmt3_trigger(data):
|
|
75
|
+
"""True if XC32 would choose fmt=3: all bytes non-zero and bytes[0:4] repeated."""
|
|
76
|
+
if len(data) < 4:
|
|
77
|
+
return False
|
|
78
|
+
if all(b == 0 for b in data):
|
|
79
|
+
return False
|
|
80
|
+
fill = data[0:4]
|
|
81
|
+
for i in range(0, len(data), 4):
|
|
82
|
+
chunk = data[i:i + 4]
|
|
83
|
+
if chunk != fill[:len(chunk)]:
|
|
84
|
+
return False
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def scan_elf(path):
|
|
89
|
+
"""Scan one ELF32 file (.o or final ELF). Return list of violation dicts."""
|
|
90
|
+
violations = []
|
|
91
|
+
|
|
92
|
+
with open(path, "rb") as f:
|
|
93
|
+
e_ident = f.read(16)
|
|
94
|
+
if e_ident[:4] != b"\x7fELF" or e_ident[4] != 1:
|
|
95
|
+
return violations
|
|
96
|
+
|
|
97
|
+
endian = "<" if e_ident[5] == 1 else ">"
|
|
98
|
+
hdr = f.read(36)
|
|
99
|
+
e_shoff = struct.unpack_from(f"{endian}I", hdr, 16)[0]
|
|
100
|
+
e_shentsize = struct.unpack_from(f"{endian}H", hdr, 30)[0]
|
|
101
|
+
e_shnum = struct.unpack_from(f"{endian}H", hdr, 32)[0]
|
|
102
|
+
e_shstrndx = struct.unpack_from(f"{endian}H", hdr, 34)[0]
|
|
103
|
+
|
|
104
|
+
if e_shoff == 0 or e_shnum == 0:
|
|
105
|
+
return violations
|
|
106
|
+
|
|
107
|
+
shdrs = []
|
|
108
|
+
for i in range(e_shnum):
|
|
109
|
+
sh = _read_shdr(f, endian, e_shoff, e_shentsize, i)
|
|
110
|
+
if sh is None:
|
|
111
|
+
return violations
|
|
112
|
+
shdrs.append(sh)
|
|
113
|
+
|
|
114
|
+
shstrtab = shdrs[e_shstrndx]
|
|
115
|
+
for sh in shdrs:
|
|
116
|
+
sh["name"] = _section_name(f, shstrtab, sh["name_idx"])
|
|
117
|
+
|
|
118
|
+
symtab = next((s for s in shdrs if s["type"] == SHT_SYMTAB), None)
|
|
119
|
+
if symtab is None:
|
|
120
|
+
return violations
|
|
121
|
+
|
|
122
|
+
strtab_sh = shdrs[symtab["link"]]
|
|
123
|
+
f.seek(strtab_sh["offset"])
|
|
124
|
+
strtab = f.read(strtab_sh["size"])
|
|
125
|
+
|
|
126
|
+
data_sections = {}
|
|
127
|
+
for idx, sh in enumerate(shdrs):
|
|
128
|
+
if (sh["type"] == SHT_PROGBITS
|
|
129
|
+
and (sh["flags"] & SHF_ALLOC)
|
|
130
|
+
and (sh["flags"] & SHF_WRITE)
|
|
131
|
+
and sh["size"] > 0):
|
|
132
|
+
f.seek(sh["offset"])
|
|
133
|
+
data_sections[idx] = (sh, f.read(sh["size"]))
|
|
134
|
+
|
|
135
|
+
sym_entsize = symtab["entsize"] or 16
|
|
136
|
+
n_syms = symtab["size"] // sym_entsize
|
|
137
|
+
f.seek(symtab["offset"])
|
|
138
|
+
for _ in range(n_syms):
|
|
139
|
+
raw = f.read(sym_entsize)
|
|
140
|
+
st_name, st_value, st_size, st_info, _, st_shndx = \
|
|
141
|
+
struct.unpack_from(f"{endian}IIIBBH", raw)
|
|
142
|
+
|
|
143
|
+
if (st_info & 0x0F) != STT_OBJECT:
|
|
144
|
+
continue
|
|
145
|
+
if st_size < MIN_SYM_SIZE:
|
|
146
|
+
continue
|
|
147
|
+
if st_shndx not in data_sections:
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
sh, sec_bytes = data_sections[st_shndx]
|
|
151
|
+
offset = st_value - sh["addr"]
|
|
152
|
+
if offset < 0 or offset + st_size > len(sec_bytes):
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
sym_bytes = sec_bytes[offset:offset + st_size]
|
|
156
|
+
if not _is_fmt3_trigger(sym_bytes):
|
|
157
|
+
continue
|
|
158
|
+
if st_size % 4 == 0:
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
name = _read_cstr(strtab, st_name)
|
|
162
|
+
fill_word = int.from_bytes(sym_bytes[:4], "little")
|
|
163
|
+
violations.append({
|
|
164
|
+
"file": path,
|
|
165
|
+
"name": name,
|
|
166
|
+
"size": st_size,
|
|
167
|
+
"fill_word": fill_word,
|
|
168
|
+
"addr": st_value,
|
|
169
|
+
"section": sh["name"],
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
return violations
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def find_objects(root):
|
|
176
|
+
for dirpath, _, filenames in os.walk(root):
|
|
177
|
+
for fn in filenames:
|
|
178
|
+
if fn.endswith(".o"):
|
|
179
|
+
yield os.path.join(dirpath, fn)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def main():
|
|
183
|
+
args = sys.argv[1:]
|
|
184
|
+
if not args:
|
|
185
|
+
print(f"Usage: {sys.argv[0]} <file.o|file.elf> [...] | --objects-dir <dir>",
|
|
186
|
+
file=sys.stderr)
|
|
187
|
+
sys.exit(1)
|
|
188
|
+
|
|
189
|
+
paths = []
|
|
190
|
+
if args[0] == "--objects-dir":
|
|
191
|
+
if len(args) < 2:
|
|
192
|
+
print("--objects-dir requires a path", file=sys.stderr)
|
|
193
|
+
sys.exit(1)
|
|
194
|
+
paths = list(find_objects(args[1]))
|
|
195
|
+
if not paths:
|
|
196
|
+
print("validate_xc32_fmt3: no .o files found under " + args[1])
|
|
197
|
+
sys.exit(0)
|
|
198
|
+
else:
|
|
199
|
+
paths = args
|
|
200
|
+
|
|
201
|
+
all_violations = []
|
|
202
|
+
for p in paths:
|
|
203
|
+
try:
|
|
204
|
+
all_violations.extend(scan_elf(p))
|
|
205
|
+
except (OSError, struct.error, ValueError) as e:
|
|
206
|
+
print(f"validate_xc32_fmt3: warning: could not parse {p}: {e}",
|
|
207
|
+
file=sys.stderr)
|
|
208
|
+
|
|
209
|
+
if not all_violations:
|
|
210
|
+
sys.exit(0)
|
|
211
|
+
|
|
212
|
+
for v in all_violations:
|
|
213
|
+
leftover = v["size"] % 4
|
|
214
|
+
print(
|
|
215
|
+
f"ERROR: XC32 fmt=3 risk in '{os.path.basename(v['file'])}': "
|
|
216
|
+
f"symbol '{v['name']}'\n"
|
|
217
|
+
f" size={v['size']} bytes ({leftover} leftover after /4) "
|
|
218
|
+
f"fill_word=0x{v['fill_word']:08X} "
|
|
219
|
+
f"section={v['section']}\n"
|
|
220
|
+
f" __dinit_copy_val_data will loop forever on hardware.\n"
|
|
221
|
+
f" Fix: make sizeof({v['name']}) divisible by 4, "
|
|
222
|
+
f"or use non-uniform init, or change to uint32_t."
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
print(f"\nvalidate_xc32_fmt3: FAIL — {len(all_violations)} violation(s) found")
|
|
226
|
+
sys.exit(1)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
if __name__ == "__main__":
|
|
230
|
+
main()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: microchip-devtools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Shared build and dev environment scripts for Microchip firmware projects
|
|
5
|
+
Author: Ericson Joseph
|
|
6
|
+
Author-email: joseph.ericson@allthings.me
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: python-dotenv (>=1.2.2,<2.0.0)
|
|
15
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
16
|
+
Requires-Dist: rich (>=13.0,<15.0)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
microchip_devtools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
microchip_devtools/_project.py,sha256=vkZ12yK0MJyBhAYS8t8W8VbcVbTL4zQWbqjGBp1QMvA,455
|
|
3
|
+
microchip_devtools/format/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
microchip_devtools/format/uncrustify.py,sha256=XYzEey4snVqhJhdGX5CkQNHuLM0rlhg_2O4EtP9290o,3378
|
|
5
|
+
microchip_devtools/list_cmds.py,sha256=cym2BSKMJiYX4jhEWs7YqOMB_OylknZLawagDKKhv6g,1026
|
|
6
|
+
microchip_devtools/mcc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
microchip_devtools/mcc/check_peripheral.py,sha256=KJWCIeRmnLnyyxggy7DTR6zyQGLgZTa9opQImb1XLA4,7065
|
|
8
|
+
microchip_devtools/mcc/mcc_refresh.py,sha256=28BDvw5jF5PORB84K9P0qtE_VcO78RHyy5hmhMtdmKs,11504
|
|
9
|
+
microchip_devtools/mcc/parse_hardware.py,sha256=s3_g7XyFXXzmkNoPsKVJdtPLCIXJO0_OH4Zx17ScAmE,12863
|
|
10
|
+
microchip_devtools/setup_env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
microchip_devtools/setup_env/_ui.py,sha256=SWcayUN65EGCkAZBDj9QKXl6rBCOrBnIyX37BCIvroQ,1866
|
|
12
|
+
microchip_devtools/setup_env/checks.py,sha256=E-gEZBKQAVoPOnflGHTBBIPNjsXJSLtEKSeaIxnNZZI,5644
|
|
13
|
+
microchip_devtools/setup_env/defaults.py,sha256=9h4m7RB0CT0kSoZddHqxfBp1_cC05dRo4kMSNTFLETE,999
|
|
14
|
+
microchip_devtools/setup_env/runner.py,sha256=HjIJjM-8Dmf0E3XO-rBx4XsTFe1wZhBY0JqP0EAYj5w,10845
|
|
15
|
+
microchip_devtools/xc32/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
microchip_devtools/xc32/merge_hex.py,sha256=LGmsuPvTGEHz-9VF7LagoCFsY7p3j4bYwftSBYd3p1Q,7951
|
|
17
|
+
microchip_devtools/xc32/validate_fmt3.py,sha256=Su-3h3TTSjVOfRJymnbdBwkiVNm_oQLpiaIwxY6pYNg,7257
|
|
18
|
+
microchip_devtools-0.1.0.dist-info/METADATA,sha256=ruSXx5zYPqtopb9kHPdJkljaXYIuRbX3dwMbAFq3m6Q,661
|
|
19
|
+
microchip_devtools-0.1.0.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
|
|
20
|
+
microchip_devtools-0.1.0.dist-info/entry_points.txt,sha256=Lia67trwqHwmfgBXV8elcAYV00-u1lTLY2ogq7EPtnY,440
|
|
21
|
+
microchip_devtools-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[console_scripts]
|
|
2
|
+
check-peripheral=microchip_devtools.mcc.check_peripheral:main
|
|
3
|
+
format=microchip_devtools.format.uncrustify:main
|
|
4
|
+
list=microchip_devtools.list_cmds:main
|
|
5
|
+
mcc-refresh=microchip_devtools.mcc.mcc_refresh:main
|
|
6
|
+
merge-hex=microchip_devtools.xc32.merge_hex:main
|
|
7
|
+
parse-hardware=microchip_devtools.mcc.parse_hardware:main
|
|
8
|
+
project-setup=microchip_devtools.setup_env.runner:main
|
|
9
|
+
validate-fmt3=microchip_devtools.xc32.validate_fmt3:main
|
|
10
|
+
|