From 441442bf94c529940e9218f814474aec0ee7db26 Mon Sep 17 00:00:00 2001 From: ghidra_red Date: Wed, 11 Feb 2026 18:11:29 +0000 Subject: [PATCH] GP-6382: Add the Tenet loader --- .../Debug/Debugger-importers/Module.manifest | 0 Ghidra/Debug/Debugger-importers/README.md | 1 + Ghidra/Debug/Debugger-importers/build.gradle | 37 ++ .../Debugger-importers/certification.manifest | 3 + .../ghidra/app/util/opinion/TenetLoader.java | 526 ++++++++++++++++++ .../app/util/opinion/TenetLoaderTest.java | 293 ++++++++++ 6 files changed, 860 insertions(+) create mode 100644 Ghidra/Debug/Debugger-importers/Module.manifest create mode 100644 Ghidra/Debug/Debugger-importers/README.md create mode 100644 Ghidra/Debug/Debugger-importers/build.gradle create mode 100644 Ghidra/Debug/Debugger-importers/certification.manifest create mode 100644 Ghidra/Debug/Debugger-importers/src/main/java/ghidra/app/util/opinion/TenetLoader.java create mode 100644 Ghidra/Debug/Debugger-importers/src/test/java/ghidra/app/util/opinion/TenetLoaderTest.java diff --git a/Ghidra/Debug/Debugger-importers/Module.manifest b/Ghidra/Debug/Debugger-importers/Module.manifest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Debug/Debugger-importers/README.md b/Ghidra/Debug/Debugger-importers/README.md new file mode 100644 index 0000000000..9826cb3fdb --- /dev/null +++ b/Ghidra/Debug/Debugger-importers/README.md @@ -0,0 +1 @@ +# Debugger-importers diff --git a/Ghidra/Debug/Debugger-importers/build.gradle b/Ghidra/Debug/Debugger-importers/build.gradle new file mode 100644 index 0000000000..f26b4dfdbe --- /dev/null +++ b/Ghidra/Debug/Debugger-importers/build.gradle @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" +apply from: "${rootProject.projectDir}/gradle/helpProject.gradle" +apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" +apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" +apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" +apply from: "${rootProject.projectDir}/gradle/javadoc.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug Debugger-importers' + +dependencies { + api project(':ProposedUtils') + api project(':Pty') + api project(':Debugger') + api project(':Debugger-api') + + testImplementation project(path: ':Generic', configuration: 'testArtifacts') + testImplementation project(path: ':Debugger', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-TraceModeling', configuration: 'testArtifacts') +} + diff --git a/Ghidra/Debug/Debugger-importers/certification.manifest b/Ghidra/Debug/Debugger-importers/certification.manifest new file mode 100644 index 0000000000..6841c5219a --- /dev/null +++ b/Ghidra/Debug/Debugger-importers/certification.manifest @@ -0,0 +1,3 @@ +##VERSION: 2.0 +Module.manifest||GHIDRA||||END| +README.md||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger-importers/src/main/java/ghidra/app/util/opinion/TenetLoader.java b/Ghidra/Debug/Debugger-importers/src/main/java/ghidra/app/util/opinion/TenetLoader.java new file mode 100644 index 0000000000..d2b434c916 --- /dev/null +++ b/Ghidra/Debug/Debugger-importers/src/main/java/ghidra/app/util/opinion/TenetLoader.java @@ -0,0 +1,526 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.opinion; + +import java.io.*; +import java.math.BigInteger; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.jdom2.JDOMException; + +import db.Transaction; +import ghidra.app.util.Option; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.importer.DomainFileOption; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainObject; +import ghidra.program.database.ProgramContentHandler; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.Program; +import ghidra.program.model.symbol.RefType; +import ghidra.program.model.symbol.SourceType; +import ghidra.program.util.AddressEvaluator; +import ghidra.trace.database.DBTrace; +import ghidra.trace.database.target.DBTraceObjectManager; +import ghidra.trace.model.Lifespan; +import ghidra.trace.model.Trace; +import ghidra.trace.model.memory.TraceMemoryFlag; +import ghidra.trace.model.memory.TraceOverlappedRegionException; +import ghidra.trace.model.modules.TraceModuleManager; +import ghidra.trace.model.modules.TraceStaticMappingManager; +import ghidra.trace.model.stack.TraceStackFrame; +import ghidra.trace.model.target.TraceObject; +import ghidra.trace.model.target.TraceObject.ConflictResolution; +import ghidra.trace.model.target.TraceObjectManager; +import ghidra.trace.model.target.path.KeyPath; +import ghidra.trace.model.target.schema.*; +import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; +import ghidra.trace.model.thread.TraceThread; +import ghidra.trace.model.time.TraceSnapshot; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +public class TenetLoader implements Loader { + private static final String TRACE_SUFFIX = ".trace"; + private static final String TENET_SUFFIX = ".tenet"; + private static final Set SUFFIXES = Set.of(TRACE_SUFFIX, TENET_SUFFIX); + + private static final Pattern SLIDE_PATTERN = Pattern.compile("slide=0x([0-9a-fA-F]+)"); + private static final Pattern MEM_PATTERN = + Pattern.compile("(?:^|,)(mr|mw)=0x([^:]+):([0-9a-fA-F]+)"); + private static final Pattern REG_PATTERN = + Pattern.compile("(?:^|,)(?!(?:mr|mw|slide)=)([a-zA-Z0-9]+)=0x([0-9a-fA-F]+)"); + + static final int ERROR_THRESHOLD = 10; + + static final String DOMAIN_FILE_OPTION_NAME = "Program to associate trace file with"; + private static final String TENET_CTX_XML = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """; + private static final SchemaContext SAMPLE_CTX; + + private static final TraceObjectSchema TENET_SESSION_SCHEMA; + static { + try { + SAMPLE_CTX = XmlSchemaContext.deserialize(TENET_CTX_XML); + } + catch (final JDOMException e) { + throw new AssertionError(e); + } + TENET_SESSION_SCHEMA = SAMPLE_CTX.getSchema(new SchemaName("TenetSession")); + } + + private static AddressSpace defaultSpace; + private static Program program; + + private static boolean STORE_REG_ATTRS = false; + + /** + * Create an address in the processor's default space. + * + * @param offset the byte offset + * @return the address + */ + private static Address addr(final long offset) { + return defaultSpace.getAddress(offset); + } + + private static boolean isTenetFile(final ByteProvider provider) { + final String nameLower = provider.getName().toLowerCase(); + + if (!SUFFIXES.stream().anyMatch(nameLower::endsWith)) { + return false; + } + + return true; + } + + /** + * Create an address range in the processor's default space. + * + * @param min the minimum byte offset + * @param max the maximum (inclusive) byte offset + * @return the range + */ + private static AddressRange rng(final long min, final long max) { + return new AddressRangeImpl(addr(min), addr(max)); + } + + private static Address toAddr(final String addressString) { + return AddressEvaluator.evaluate(program, addressString); + } + + @Override + public Collection findSupportedLoadSpecs(final ByteProvider provider) + throws IOException { + final List loadSpecs = new ArrayList<>(); + if (isTenetFile(provider)) { + // TODO: This LanguageCompilerSpecPair is not used by the loader + // just putting a dummy value in so the user doesn't have to + loadSpecs.add(new LoadSpec(this, 0, + new LanguageCompilerSpecPair("DATA:LE:64:default", "pointer64"), true)); + } + return loadSpecs; + } + + @Override + public List