Keep VOC's read/write symbol-file opens inside the build directory.
Discover the installed toolchain without importing a symbol file, then use
writable local copies of installed symbols for the protocol build.

diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -76,6 +76,7 @@ VOCROOT=$(VOCROOT)
 VOCLIBDIR=$(VOCLIBDIR)
 VOC_INCLUDE_DIR=$(VOC_INCLUDE_DIR)
 VOC_ARCHIVE=$(VOC_ARCHIVE)
+VOC_SYMBOL_DIR=$(VOC_SYMBOL_DIR)
 CC=$(CC)
 LINK=$(LINK)
 CFLAGS=$(CFLAGS)
@@ -93,7 +94,7 @@ $(BUILD)/config: FORCE | $(BUILD)
 
 $(BUILD)/generated.stamp $(GENERATED)/BarevBridge.o &: $(ROOT)/Makefile $(SOURCE)/build-protocol.py $(wildcard $(SOURCE)/*.Mod) \
  $(SOURCE)/host.h $(VOC_ARCHIVE) $(BUILD)/config $(BUILD)/voc-toolchain.mk | check
-	CC="$(CC)" CFLAGS="$(CFLAGS)" OBERON_CFLAGS="$(OBERON_CFLAGS)" $(PYTHON) "$(SOURCE)/build-protocol.py" "$(SOURCE)" "$(BUILD)" "$(VOC_INCLUDE_DIR)" "$(VOC_ARCHIVE)" "$(VOC_BINARY)"
+	CC="$(CC)" CFLAGS="$(CFLAGS)" OBERON_CFLAGS="$(OBERON_CFLAGS)" $(PYTHON) "$(SOURCE)/build-protocol.py" "$(SOURCE)" "$(BUILD)" "$(VOC_INCLUDE_DIR)" "$(VOC_ARCHIVE)" "$(VOC_BINARY)" "$(VOC_SYMBOL_DIR)"
 	touch "$(BUILD)/generated.stamp"
 
 $(BUILD)/runtime.o: $(SOURCE)/runtime.c $(SOURCE)/runtime.h $(BUILD)/generated.stamp
diff --git a/src/build-protocol.py b/src/build-protocol.py
--- a/src/build-protocol.py
+++ b/src/build-protocol.py
@@ -10,8 +10,17 @@ import sys
 
 source, build, headers, archive = map(Path, sys.argv[1:5])
 voc = Path(shutil.which(sys.argv[5]) or sys.argv[5])
+installed_symbols = Path(sys.argv[6])
 generated = build / "oberon"
 generated.mkdir(parents=True, exist_ok=True)
+symbols = build / "voc-symbols"
+symbols.mkdir(parents=True, exist_ok=True)
+# VOC opens imported symbol files read/write before trying read-only access.
+# Local writable copies keep those opens inside a packaging sandbox.
+for path in (*installed_symbols.glob("*.sym"), *installed_symbols.glob("Errors.Txt")):
+    target = symbols / path.name
+    if not target.exists() or target.read_bytes() != path.read_bytes():
+        shutil.copyfile(path, target)
 sources = {p.stem: p for p in source.glob("*.Mod")}
 seen, order, dependencies = set(), [], {}
 
@@ -62,7 +71,7 @@ visit("BarevBridge")
 visit("BarevStream")
 if (source / "TestBarev.Mod").exists():
     visit("TestBarev")
-environment = dict(os.environ, OBERON=str(generated), MODULES="")
+environment = dict(os.environ, OBERON=str(generated)+";"+str(symbols), MODULES="")
 cc = shlex.split(os.environ.get("CC", "cc"))
 flags = shlex.split(os.environ.get("CFLAGS", "-O0 -g"))
 flags += shlex.split(os.environ.get("OBERON_CFLAGS", ""))
diff --git a/src/voc-toolchain.py b/src/voc-toolchain.py
--- a/src/voc-toolchain.py
+++ b/src/voc-toolchain.py
@@ -14,7 +14,7 @@ build = Path(sys.argv[2]).resolve()
 probe = build / "voc-probe"
 probe.mkdir(parents=True, exist_ok=True)
 name = "VocToolchainProbe"
-(probe / (name+".Mod")).write_text(f'MODULE {name}; IMPORT Out; BEGIN Out.String("") END {name}.\n')
+(probe / (name+".Mod")).write_text(f'MODULE {name}; END {name}.\n')
 environment = dict(os.environ, CFLAGS="", LDFLAGS="", LDLIBS="", OBERON=".", MODULES="")
 result = subprocess.run([voc, "-O2", "-sFmV", name+".Mod"], cwd=probe,
                         env=environment, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -49,14 +49,17 @@ archives += [Path(word) for word in command if word.endswith("-O2.a")]
 archive = next((path.resolve() for path in archives if path.is_file()), None)
 if headers is None or archive is None:
     raise SystemExit("VOC needs installed model-2 headers and a position-independent static library; see "+str(probe / "driver.log"))
+symbols = headers.parent / "sym"
+if not (symbols / "Heap.sym").is_file():
+    raise SystemExit("VOC needs installed model-2 symbol files: "+str(symbols))
 for module in ("XmppItems", "XmppXmlStream", "XmppXmlGen"):
-    if not (headers / (module+".h")).is_file():
-        raise SystemExit("The VOC installation needs its XML/XMPP libraries: missing "+module+".h")
-for path in (headers, archive):
+    if not (headers / (module+".h")).is_file() or not (symbols / (module+".sym")).is_file():
+        raise SystemExit("The VOC installation needs XML/XMPP headers and symbols: missing "+module)
+for path in (headers, archive, symbols):
     if any(char in str(path) for char in "\n\r#$"):
         raise SystemExit("Unsupported character in VOC installation path: "+str(path))
 text = ("# Generated by VOC toolchain discovery.\n"
-        f"VOC_INCLUDE_DIR := {headers}\nVOC_ARCHIVE := {archive}\n")
+        f"VOC_INCLUDE_DIR := {headers}\nVOC_ARCHIVE := {archive}\nVOC_SYMBOL_DIR := {symbols}\n")
 target = build / "voc-toolchain.mk"
 target.write_text(text)
 print(f"Using {voc}: {headers}, {archive}")
