mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
GP-2966: Consider aliases in debugger register mapping.
This commit is contained in:
@@ -51,16 +51,30 @@ public class DefaultDebuggerRegisterMapper implements DebuggerRegisterMapper {
|
|||||||
collectFilteredLanguageRegs();
|
collectFilteredLanguageRegs();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean testTraceRegister(Register lReg) {
|
protected boolean includeTraceRegister(Register lReg) {
|
||||||
return lReg.isBaseRegister();
|
return lReg.isBaseRegister();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void collectFilteredLanguageRegs() {
|
protected synchronized void collectFilteredLanguageRegs() {
|
||||||
for (Register lReg : language.getRegisters()) {
|
for (Register lReg : language.getRegisters()) {
|
||||||
if (!testTraceRegister(lReg)) {
|
if (!includeTraceRegister(lReg)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
filtLanguageRegs.put(normalizeName(lReg.getName()), lReg);
|
putLanguageRegister(filtLanguageRegs, lReg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void putLanguageRegister(Map<String, Register> map, Register lReg) {
|
||||||
|
map.put(normalizeName(lReg.getName()), lReg);
|
||||||
|
for (String alias : lReg.getAliases()) {
|
||||||
|
map.put(normalizeName(alias), lReg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void removeLanguageRegister(Map<String, Register> map, Register lReg) {
|
||||||
|
map.remove(normalizeName(lReg.getName()));
|
||||||
|
for (String alias : lReg.getAliases()) {
|
||||||
|
map.remove(normalizeName(alias));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +84,7 @@ public class DefaultDebuggerRegisterMapper implements DebuggerRegisterMapper {
|
|||||||
if (lReg == null) {
|
if (lReg == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
languageRegs.put(name, lReg);
|
putLanguageRegister(languageRegs, lReg);
|
||||||
return lReg;
|
return lReg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +95,7 @@ public class DefaultDebuggerRegisterMapper implements DebuggerRegisterMapper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
targetRegs.put(name, tReg);
|
targetRegs.put(name, tReg);
|
||||||
languageRegs.put(name, lReg);
|
putLanguageRegister(languageRegs, lReg);
|
||||||
return lReg;
|
return lReg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +106,7 @@ public class DefaultDebuggerRegisterMapper implements DebuggerRegisterMapper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
targetRegs.remove(name);
|
targetRegs.remove(name);
|
||||||
languageRegs.remove(name);
|
removeLanguageRegister(languageRegs, lReg);
|
||||||
return lReg;
|
return lReg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +129,17 @@ public class DefaultDebuggerRegisterMapper implements DebuggerRegisterMapper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized TargetRegister traceToTarget(Register lReg) {
|
public synchronized TargetRegister traceToTarget(Register lReg) {
|
||||||
return targetRegs.get(normalizeName(lReg.getName()));
|
TargetRegister tReg = targetRegs.get(normalizeName(lReg.getName()));
|
||||||
|
if (tReg != null) {
|
||||||
|
return tReg;
|
||||||
|
}
|
||||||
|
for (String alias : lReg.getAliases()) {
|
||||||
|
tReg = targetRegs.get(normalizeName(alias));
|
||||||
|
if (tReg != null) {
|
||||||
|
return tReg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class LargestSubDebuggerRegisterMapper extends DefaultDebuggerRegisterMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean testTraceRegister(Register lReg) {
|
protected boolean includeTraceRegister(Register lReg) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ public class LargestSubDebuggerRegisterMapper extends DefaultDebuggerRegisterMap
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Register lReg = subs.last(); // largest
|
Register lReg = subs.last(); // largest
|
||||||
return targetRegs.get(normalizeName(lReg.getName()));
|
return super.traceToTarget(lReg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,7 +156,7 @@ public class LargestSubDebuggerRegisterMapper extends DefaultDebuggerRegisterMap
|
|||||||
Msg.warn(this, "Potential register cache aliasing: " + lReg + " vs " + subs.last());
|
Msg.warn(this, "Potential register cache aliasing: " + lReg + " vs " + subs.last());
|
||||||
/**
|
/**
|
||||||
* After testing, there is a problem with data truncation. If, e.g., EAX is sent,
|
* After testing, there is a problem with data truncation. If, e.g., EAX is sent,
|
||||||
* followed by AX, and we expand to fill RAX, the we're going to truncate the upper 16
|
* followed by AX, and we expand to fill RAX, then we're going to truncate the upper 16
|
||||||
* bits of EAX :( . To avoid this, we'll only map the largest register from target to
|
* bits of EAX :( . To avoid this, we'll only map the largest register from target to
|
||||||
* trace. We'll still warn as a courtesy, but we'll abort the mapping.
|
* trace. We'll still warn as a courtesy, but we'll abort the mapping.
|
||||||
*/
|
*/
|
||||||
@@ -164,7 +164,7 @@ public class LargestSubDebuggerRegisterMapper extends DefaultDebuggerRegisterMap
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* TODO: A mapping with masks may be useful in the future, but as it is, the trace database
|
* TODO: A mapping with masks may be useful in the future, but as it is, the trace database
|
||||||
* will reject it. Furthermore, we'd still want to mark the whole base register is known,
|
* will reject it. Furthermore, we'd still want to mark the whole base register as known,
|
||||||
* which is not intuitive, and makes me worry about that method.
|
* which is not intuitive, and makes me worry about that method.
|
||||||
*/
|
*/
|
||||||
// return new RegisterValue(lReg, new BigInteger(1,value)).getBaseRegisterValue();
|
// return new RegisterValue(lReg, new BigInteger(1,value)).getBaseRegisterValue();
|
||||||
|
|||||||
@@ -543,9 +543,9 @@ public class ObjectBasedTraceRecorder implements TraceRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected TargetRegisterBank isExactRegisterOnTarget(TracePlatform platform,
|
protected TargetRegisterBank isExactRegisterOnTarget(TracePlatform platform,
|
||||||
TargetRegisterContainer regContainer, String name) {
|
TargetRegisterContainer regContainer, Register register) {
|
||||||
PathMatcher matcher =
|
PathMatcher matcher =
|
||||||
platform.getConventionalRegisterPath(regContainer.getSchema(), List.of(), name);
|
platform.getConventionalRegisterPath(regContainer.getSchema(), List.of(), register);
|
||||||
for (TargetObject targetObject : matcher.getCachedSuccessors(regContainer).values()) {
|
for (TargetObject targetObject : matcher.getCachedSuccessors(regContainer).values()) {
|
||||||
if (!(targetObject instanceof TargetRegister targetRegister)) {
|
if (!(targetObject instanceof TargetRegister targetRegister)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -568,28 +568,7 @@ public class ObjectBasedTraceRecorder implements TraceRecorder {
|
|||||||
if (regContainer == null) {
|
if (regContainer == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
TargetRegisterBank result;
|
return isExactRegisterOnTarget(platform, regContainer, register);
|
||||||
String name = platform.getConventionalRegisterObjectName(register);
|
|
||||||
result = isExactRegisterOnTarget(platform, regContainer, name);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// Not totally case insensitive, but the sane cases
|
|
||||||
String upperName = name.toUpperCase();
|
|
||||||
if (!name.equals(upperName)) {
|
|
||||||
result = isExactRegisterOnTarget(platform, regContainer, upperName);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String lowerName = name.toLowerCase();
|
|
||||||
if (!name.equals(lowerName)) {
|
|
||||||
result = isExactRegisterOnTarget(platform, regContainer, lowerName);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.plugin.core.debug.mapping;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||||
|
import ghidra.app.services.ActionSource;
|
||||||
|
import ghidra.app.services.TraceRecorder;
|
||||||
|
import ghidra.dbg.model.TestTargetRegister;
|
||||||
|
import ghidra.dbg.model.TestTargetRegisterValue;
|
||||||
|
import ghidra.dbg.target.TargetObject;
|
||||||
|
import ghidra.dbg.target.TargetRegister;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.test.ToyProgramBuilder;
|
||||||
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
|
|
||||||
|
public class DefaultDebuggerRegisterMapperTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||||
|
|
||||||
|
static class TestTargetMapper extends DefaultDebuggerTargetTraceMapper {
|
||||||
|
public TestTargetMapper(TargetObject target)
|
||||||
|
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
||||||
|
super(target, new LanguageID(ToyProgramBuilder._TOY64_BE),
|
||||||
|
new CompilerSpecID("default"), Set.of());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void assertSameRegister(TargetRegister expected, TargetObject actual) {
|
||||||
|
if (actual instanceof TestTargetRegister tr) {
|
||||||
|
assertEquals(expected, tr);
|
||||||
|
}
|
||||||
|
else if (actual instanceof TestTargetRegisterValue rv) {
|
||||||
|
assertEquals(expected, rv.desc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUpMapperTest() throws Throwable {
|
||||||
|
createTestModel();
|
||||||
|
mb.createTestProcessesAndThreads();
|
||||||
|
mb.createTestThreadRegisterBanks();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DebuggerRegisterMapper doGetRegisterMapper(String toConfirm)
|
||||||
|
throws Throwable {
|
||||||
|
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
|
||||||
|
new TestTargetMapper(mb.testProcess1), ActionSource.AUTOMATIC);
|
||||||
|
TraceThread thread1 = waitForValue(() -> recorder.getTraceThread(mb.testThread1));
|
||||||
|
DebuggerRegisterMapper rm = waitForValue(() -> recorder.getRegisterMapper(thread1));
|
||||||
|
waitForValue(() -> rm.getTargetRegister(toConfirm));
|
||||||
|
return rm;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DebuggerRegisterMapper getRegisterMapper() throws Throwable {
|
||||||
|
mb.testProcess1.regs.addRegistersFromLanguage(getToyBE64Language(), r -> true);
|
||||||
|
return doGetRegisterMapper("r0");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DebuggerRegisterMapper getRegisterMapperAliased() throws Throwable {
|
||||||
|
Register r0 = getToyBE64Language().getRegister("r0");
|
||||||
|
mb.testProcess1.regs.addRegistersFromLanguage(getToyBE64Language(), r -> r != r0);
|
||||||
|
mb.testProcess1.regs.addRegister("a0", r0);
|
||||||
|
return doGetRegisterMapper("r1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTraceToTargetRegCanonical() throws Throwable {
|
||||||
|
DebuggerRegisterMapper rm = getRegisterMapper();
|
||||||
|
TestTargetRegister tR0 =
|
||||||
|
Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("r0"));
|
||||||
|
Register lR0 = Objects.requireNonNull(getToyBE64Language().getRegister("r0"));
|
||||||
|
|
||||||
|
TargetRegister tReg = waitForValue(() -> rm.traceToTarget(lR0));
|
||||||
|
assertSameRegister(tR0, tReg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTargetToTraceRegCanonical() throws Throwable {
|
||||||
|
DebuggerRegisterMapper rm = getRegisterMapper();
|
||||||
|
TestTargetRegister tR0 =
|
||||||
|
Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("r0"));
|
||||||
|
Register lR0 = Objects.requireNonNull(getToyBE64Language().getRegister("r0"));
|
||||||
|
|
||||||
|
Register lReg = waitForValue(() -> rm.targetToTrace(tR0));
|
||||||
|
assertEquals(lR0, lReg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTraceToTargetRegAlias() throws Throwable {
|
||||||
|
DebuggerRegisterMapper rm = getRegisterMapperAliased();
|
||||||
|
TestTargetRegister tA0 =
|
||||||
|
Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("a0"));
|
||||||
|
Register lR0 = Objects.requireNonNull(getToyBE64Language().getRegister("r0"));
|
||||||
|
|
||||||
|
TargetRegister tReg = waitForValue(() -> rm.traceToTarget(lR0));
|
||||||
|
assertSameRegister(tA0, tReg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTargetToTraceRegAlias() throws Throwable {
|
||||||
|
DebuggerRegisterMapper rm = getRegisterMapperAliased();
|
||||||
|
TestTargetRegister tA0 =
|
||||||
|
Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("a0"));
|
||||||
|
Register lR0 = Objects.requireNonNull(getToyBE64Language().getRegister("r0"));
|
||||||
|
|
||||||
|
Register lReg = waitForValue(() -> rm.targetToTrace(tA0));
|
||||||
|
assertEquals(lR0, lReg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,10 @@ public class PathMatcher implements PathPredicates {
|
|||||||
patterns.add(pattern);
|
patterns.add(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addAll(PathMatcher matcher) {
|
||||||
|
patterns.addAll(matcher.patterns);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("<PathMatcher\n %s\n>", StringUtils.join(patterns, "\n "));
|
return String.format("<PathMatcher\n %s\n>", StringUtils.join(patterns, "\n "));
|
||||||
|
|||||||
@@ -156,6 +156,11 @@ public class TestDebuggerObjectModel extends EmptyDebuggerObjectModel {
|
|||||||
return TestTargetRegister.fromLanguageRegister(container, register);
|
return TestTargetRegister.fromLanguageRegister(container, register);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected TestTargetRegister newTestTargetRegister(TestTargetRegisterContainer container,
|
||||||
|
String index, Register register) {
|
||||||
|
return TestTargetRegister.fromLanguageRegister(container, index, register);
|
||||||
|
}
|
||||||
|
|
||||||
protected TestTargetThreadContainer newTestTargetThreadContainer(TestTargetProcess process) {
|
protected TestTargetThreadContainer newTestTargetThreadContainer(TestTargetProcess process) {
|
||||||
return new TestTargetThreadContainer(process);
|
return new TestTargetThreadContainer(process);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,15 @@ public class TestTargetRegister
|
|||||||
extends DefaultTestTargetObject<TestTargetObject, TestTargetRegisterContainer>
|
extends DefaultTestTargetObject<TestTargetObject, TestTargetRegisterContainer>
|
||||||
implements TargetRegister {
|
implements TargetRegister {
|
||||||
|
|
||||||
|
public static TestTargetRegister fromLanguageRegister(TestTargetRegisterContainer parent,
|
||||||
|
String index, Register register) {
|
||||||
|
return new TestTargetRegister(parent, PathUtils.makeKey(index),
|
||||||
|
(register.getBitLength() + 7) / 8, register.isProgramCounter());
|
||||||
|
}
|
||||||
|
|
||||||
public static TestTargetRegister fromLanguageRegister(
|
public static TestTargetRegister fromLanguageRegister(
|
||||||
TestTargetRegisterContainer parent, Register register) {
|
TestTargetRegisterContainer parent, Register register) {
|
||||||
return new TestTargetRegister(parent, PathUtils.makeKey(register.getName()),
|
return fromLanguageRegister(parent, register.getName(), register);
|
||||||
(register.getBitLength() + 7) / 8, register.isProgramCounter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int byteLength;
|
protected final int byteLength;
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ public class TestTargetRegisterContainer
|
|||||||
public TestTargetRegister addRegister(Register register) {
|
public TestTargetRegister addRegister(Register register) {
|
||||||
TestTargetRegister tr =
|
TestTargetRegister tr =
|
||||||
getModel().newTestTargetRegister(this, Objects.requireNonNull(register));
|
getModel().newTestTargetRegister(this, Objects.requireNonNull(register));
|
||||||
|
doAddRegister(tr, register);
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doAddRegister(TestTargetRegister tr, Register register) {
|
||||||
String reason = "Added " + register + " from Ghidra language";
|
String reason = "Added " + register + " from Ghidra language";
|
||||||
changeElements(List.of(), List.of(tr), reason);
|
changeElements(List.of(), List.of(tr), reason);
|
||||||
List<AbstractTestTargetRegisterBank<?>> banks;
|
List<AbstractTestTargetRegisterBank<?>> banks;
|
||||||
@@ -70,6 +75,12 @@ public class TestTargetRegisterContainer
|
|||||||
for (AbstractTestTargetRegisterBank<?> bank : banks) {
|
for (AbstractTestTargetRegisterBank<?> bank : banks) {
|
||||||
bank.addRegisterDescs(List.of(tr), reason);
|
bank.addRegisterDescs(List.of(tr), reason);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestTargetRegister addRegister(String index, Register register) {
|
||||||
|
TestTargetRegister tr =
|
||||||
|
getModel().newTestTargetRegister(this, index, Objects.requireNonNull(register));
|
||||||
|
doAddRegister(tr, register);
|
||||||
return tr;
|
return tr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.trace.database.guest;
|
package ghidra.trace.database.guest;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ghidra.dbg.target.TargetObject;
|
import ghidra.dbg.target.TargetObject;
|
||||||
import ghidra.dbg.target.TargetRegister;
|
import ghidra.dbg.target.TargetRegister;
|
||||||
@@ -80,41 +79,58 @@ public interface InternalTracePlatform extends TracePlatform {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default List<String> listRegNames(Register register) {
|
||||||
|
Set<String> result = new LinkedHashSet<>();
|
||||||
|
result.add(register.getName());
|
||||||
|
result.add(register.getName().toUpperCase());
|
||||||
|
result.add(register.getName().toLowerCase());
|
||||||
|
for (String alias : register.getAliases()) {
|
||||||
|
result.add(alias);
|
||||||
|
result.add(alias.toUpperCase());
|
||||||
|
result.add(alias.toLowerCase());
|
||||||
|
}
|
||||||
|
return List.copyOf(result);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default String getConventionalRegisterObjectName(Register register) {
|
default Collection<String> getConventionalRegisterObjectNames(Register register) {
|
||||||
Address pmin = mapGuestToHost(register.getAddress());
|
Address pmin = mapGuestToHost(register.getAddress());
|
||||||
if (pmin == null) {
|
if (pmin == null) {
|
||||||
return register.getName();
|
return listRegNames(register);
|
||||||
}
|
}
|
||||||
TraceSymbolManager symbolManager = getTrace().getSymbolManager();
|
TraceSymbolManager symbolManager = getTrace().getSymbolManager();
|
||||||
TraceNamespaceSymbol nsRegMap = symbolManager.namespaces().getGlobalNamed(regMap(register));
|
TraceNamespaceSymbol nsRegMap = symbolManager.namespaces().getGlobalNamed(regMap(register));
|
||||||
Collection<? extends TraceLabelSymbol> labels = symbolManager.labels()
|
Collection<String> labels = symbolManager.labels()
|
||||||
.getAt(0, null, pmin, false)
|
.getAt(0, null, pmin, false)
|
||||||
.stream()
|
.stream()
|
||||||
.filter(s -> s.getParentNamespace() == nsRegMap)
|
.filter(s -> s.getParentNamespace() == nsRegMap)
|
||||||
|
.map(TraceSymbol::getName)
|
||||||
.toList();
|
.toList();
|
||||||
if (labels.isEmpty()) {
|
if (!labels.isEmpty()) {
|
||||||
return register.getName();
|
return labels;
|
||||||
}
|
}
|
||||||
// primary is listed first, so take it
|
return listRegNames(register);
|
||||||
return labels.iterator().next().getName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
|
default PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
|
||||||
String name) {
|
Collection<String> names) {
|
||||||
PathMatcher matcher = schema.searchFor(TargetRegister.class, path, true);
|
PathMatcher matcher = schema.searchFor(TargetRegister.class, path, true);
|
||||||
if (matcher.isEmpty()) {
|
if (matcher.isEmpty()) {
|
||||||
return matcher;
|
return matcher;
|
||||||
}
|
}
|
||||||
return matcher.applyKeys(Align.RIGHT, List.of(name));
|
PathMatcher result = new PathMatcher();
|
||||||
|
for (String name:names) {
|
||||||
|
result.addAll(matcher.applyKeys(Align.RIGHT, List.of(name)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
|
default PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
|
||||||
Register register) {
|
Register register) {
|
||||||
return getConventionalRegisterPath(schema, path,
|
return getConventionalRegisterPath(schema, path,
|
||||||
getConventionalRegisterObjectName(register));
|
getConventionalRegisterObjectNames(register));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,6 +172,11 @@ public interface InternalTracePlatform extends TracePlatform {
|
|||||||
if (nsRegMap == null) {
|
if (nsRegMap == null) {
|
||||||
nsRegMap = namespaces.add(regMap, globals, SourceType.USER_DEFINED);
|
nsRegMap = namespaces.add(regMap, globals, SourceType.USER_DEFINED);
|
||||||
}
|
}
|
||||||
|
TraceLabelSymbol exists = symbolManager.labels()
|
||||||
|
.getChildWithNameAt(objectName, getIntKey(), null, hostAddr, nsRegMap);
|
||||||
|
if (exists != null) {
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
return symbolManager.labels()
|
return symbolManager.labels()
|
||||||
.create(0, null, hostAddr, objectName, nsRegMap, SourceType.USER_DEFINED);
|
.create(0, null, hostAddr, objectName, nsRegMap, SourceType.USER_DEFINED);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.trace.model.guest;
|
package ghidra.trace.model.guest;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.dbg.target.TargetObject;
|
import ghidra.dbg.target.TargetObject;
|
||||||
@@ -171,27 +172,29 @@ public interface TracePlatform {
|
|||||||
AddressRange getConventionalRegisterRange(AddressSpace overlay, Register register);
|
AddressRange getConventionalRegisterRange(AddressSpace overlay, Register register);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name or index of the register object for the given platform register
|
* Get the names or indices of the register object for the given platform register
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This will check for a label in the host physical space, allowing a mapper to specify an
|
* This will check for a label in the host physical space, allowing a mapper to specify an
|
||||||
* alternative register object name. See {@link #addRegisterMapOverride(Register, String)}.
|
* alternative register object name. See {@link #addRegisterMapOverride(Register, String)}. If
|
||||||
|
* one exists, then only that name is returned. Otherwise, the given register's names and
|
||||||
|
* aliases are all returned as defined and in all-upper and all-lower case.
|
||||||
*
|
*
|
||||||
* @param register the platform register
|
* @param register the platform register
|
||||||
* @return the mapped name
|
* @return the mapped name
|
||||||
*/
|
*/
|
||||||
String getConventionalRegisterObjectName(Register register);
|
Collection<String> getConventionalRegisterObjectNames(Register register);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the expected path where an object defining the register value would be
|
* Get the expected path where an object defining the register value would be
|
||||||
*
|
*
|
||||||
* @param schema the schema of the register container
|
* @param schema the schema of the register container
|
||||||
* @param path the path to the register container
|
* @param path the path to the register container
|
||||||
* @param name the name of the register on the target
|
* @param names the possible names of the register on the target
|
||||||
* @return the path matcher, possibly empty
|
* @return the path matcher, possibly empty
|
||||||
*/
|
*/
|
||||||
PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
|
PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
|
||||||
String name);
|
Collection<String> names);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the expected path where an object defining the register value would be
|
* Get the expected path where an object defining the register value would be
|
||||||
|
|||||||
@@ -459,4 +459,23 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||||||
assertMatches("Targets[0].Threads[0].Registers.User[r0]",
|
assertMatches("Targets[0].Threads[0].Registers.User[r0]",
|
||||||
b.host.getConventionalRegisterPath(overlay, b.reg("r0")));
|
b.host.getConventionalRegisterPath(overlay, b.reg("r0")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPlatformGetConventionalRegisterPathAlias() throws Throwable {
|
||||||
|
AddressSpace registers = b.trace.getBaseAddressFactory().getRegisterSpace();
|
||||||
|
AddressSpace overlay;
|
||||||
|
Register r0;
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
root = manager.createRootObject(ctx.getSchema(new SchemaName("Session"))).getChild();
|
||||||
|
r0 = b.language.getRegister("r0");
|
||||||
|
overlay = b.trace.getMemoryManager()
|
||||||
|
.createOverlayAddressSpace("Targets[0].Threads[0].Registers", registers);
|
||||||
|
}
|
||||||
|
|
||||||
|
PathMatcher matcher = b.host.getConventionalRegisterPath(overlay, r0);
|
||||||
|
assertMatches("Targets[0].Threads[0].Registers.User[r0]", matcher);
|
||||||
|
assertMatches("Targets[0].Threads[0].Registers.User[a0]", matcher);
|
||||||
|
assertMatches("Targets[0].Threads[0].Registers.User[R0]", matcher);
|
||||||
|
assertMatches("Targets[0].Threads[0].Registers.User[A0]", matcher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ data/languages/old/v01stuff/toyInstructions.sinc||GHIDRA||||END|
|
|||||||
data/languages/old/v01stuff/toyPosStack.cspec||GHIDRA||||END|
|
data/languages/old/v01stuff/toyPosStack.cspec||GHIDRA||||END|
|
||||||
data/languages/toy.cspec||GHIDRA||||END|
|
data/languages/toy.cspec||GHIDRA||||END|
|
||||||
data/languages/toy.ldefs||GHIDRA||||END|
|
data/languages/toy.ldefs||GHIDRA||||END|
|
||||||
data/languages/toy.pspec||GHIDRA||reviewed||END|
|
data/languages/toy.pspec||GHIDRA||||END|
|
||||||
data/languages/toy.sinc||GHIDRA||||END|
|
data/languages/toy.sinc||GHIDRA||||END|
|
||||||
data/languages/toy64-long8.cspec||GHIDRA||||END|
|
data/languages/toy64-long8.cspec||GHIDRA||||END|
|
||||||
data/languages/toy64.cspec||GHIDRA||||END|
|
data/languages/toy64.cspec||GHIDRA||||END|
|
||||||
|
|||||||
@@ -2,4 +2,22 @@
|
|||||||
|
|
||||||
<processor_spec>
|
<processor_spec>
|
||||||
<programcounter register="pc"/>
|
<programcounter register="pc"/>
|
||||||
|
<register_data>
|
||||||
|
<register name="r0" alias="a0"/>
|
||||||
|
<register name="r1" alias="a1"/>
|
||||||
|
<register name="r2" alias="a2"/>
|
||||||
|
<register name="r3" alias="a3"/>
|
||||||
|
<register name="r4" alias="a4"/>
|
||||||
|
<register name="r5" alias="a5"/>
|
||||||
|
<register name="r6" alias="a6"/>
|
||||||
|
<register name="r7" alias="a7"/>
|
||||||
|
<register name="r8" alias="a8"/>
|
||||||
|
<register name="r9" alias="a9"/>
|
||||||
|
<register name="r10" alias="a10"/>
|
||||||
|
<register name="r11" alias="a11"/>
|
||||||
|
<register name="r12" alias="a12"/>
|
||||||
|
<register name="sp" alias="a13"/>
|
||||||
|
<register name="lr" alias="a14"/>
|
||||||
|
<register name="pc" alias="a15"/>
|
||||||
|
</register_data>
|
||||||
</processor_spec>
|
</processor_spec>
|
||||||
|
|||||||
Reference in New Issue
Block a user