Preserve SoftDevice memory side effects under nRF52 LTO
This commit is contained in:
1 parent
b47e4c137e
commit
72982fa55f
3 files changed
+110
No files matched your search
@@ -0,0 +1,74 @@
|
||||
"""Regression for pointer side effects hidden by Nordic's SVC assembly."""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
HEADER = ROOT / "src/helpers/nrf52/SoftDeviceSvcCompat.h"
|
||||
CORE = Path(os.environ.get("PLATFORMIO_CORE_DIR", Path.home() / ".platformio"))
|
||||
BIN = CORE / "packages/toolchain-gccarmnoneeabi/bin"
|
||||
PROBE = r'''
|
||||
#include <stdint.h>
|
||||
SVCALL(0x80, uint32_t, system_call(uint32_t *value));
|
||||
__attribute__((used, noinline)) uint32_t read_output(void) {
|
||||
uint32_t value = 0;
|
||||
system_call(&value);
|
||||
return value;
|
||||
}
|
||||
__attribute__((used, noinline)) uint32_t write_input(void) {
|
||||
uint32_t value = 0x12345678;
|
||||
return system_call(&value);
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
class Nrf52SvcCompatTest(unittest.TestCase):
|
||||
def test_framework_and_application_receive_the_guard(self):
|
||||
script = (ROOT / "scripts/nrf52_lto_toolchain.py").read_text()
|
||||
self.assertIn('env.AppendUnique(CCFLAGS=[', script)
|
||||
self.assertIn('"-include"', script)
|
||||
self.assertIn('SoftDeviceSvcCompat.h', script)
|
||||
self.assertIn('pre:scripts/nrf52_lto_toolchain.py',
|
||||
(ROOT / "platformio.ini").read_text())
|
||||
|
||||
def test_lto_preserves_supervisor_input_and_output_memory(self):
|
||||
cc = shutil.which("arm-none-eabi-gcc") or str(BIN / "arm-none-eabi-gcc")
|
||||
dump = shutil.which("arm-none-eabi-objdump") or str(BIN / "arm-none-eabi-objdump")
|
||||
if not Path(cc).is_file() or not Path(dump).is_file():
|
||||
self.skipTest("ARM compiler required for the LTO regression")
|
||||
with tempfile.TemporaryDirectory(prefix="meshcore-svc-lto-") as temp:
|
||||
folder = Path(temp)
|
||||
probe = folder / "probe.c"
|
||||
probe.write_text(PROBE)
|
||||
# Reproduce the SDK wrapper's missing IPA and memory protections.
|
||||
broken = folder / "broken.h"
|
||||
broken.write_text(HEADER.read_text().replace(", noipa", "")
|
||||
.replace(', "memory"', ""))
|
||||
assemblies = {}
|
||||
for name, header in (("fixed", HEADER), ("broken", broken)):
|
||||
elf = folder / (name + ".elf")
|
||||
result = subprocess.run([
|
||||
cc, "-DNRF52_PLATFORM", "-include", str(header),
|
||||
"-mcpu=cortex-m4", "-mthumb", "-Os", "-flto",
|
||||
"-fipa-pta", "-fmerge-all-constants", "-nostdlib",
|
||||
"-Wl,-e,read_output", str(probe), "-o", str(elf),
|
||||
], capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
assemblies[name] = subprocess.check_output([
|
||||
dump, "-d", "--disassemble=read_output", str(elf),
|
||||
], text=True)
|
||||
# The broken wrapper returns a compile-time zero after the SVC;
|
||||
# the fixed wrapper reloads the value written by the supervisor.
|
||||
self.assertRegex(assemblies["fixed"], r"\bldr\s+r0,\s*\[sp")
|
||||
self.assertNotRegex(assemblies["broken"], r"\bldr\s+r0,\s*\[sp")
|
||||
self.assertRegex(assemblies["fixed"], r"\bstr\s+r\d+,\s*\[sp")
|
||||
self.assertNotRegex(assemblies["broken"], r"\bstr\s+r\d+,\s*\[sp")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user