GP-6039 Address parsing docs. Allow ProgramUtilities.parseAddress to handle external address and removed support for parsing block-name style addresses

This commit is contained in:
ghidra1
2025-10-15 09:45:54 -04:00
parent 131aa4ac9a
commit ffb7e653cc
19 changed files with 366 additions and 182 deletions

View File

@@ -35,7 +35,6 @@ import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.program.util.DiffUtility;
import ghidra.program.util.ProgramMerge;
import ghidra.util.*;
import ghidra.util.datastruct.ObjectIntHashtable;
@@ -2179,8 +2178,9 @@ abstract class AbstractFunctionMerger implements ListingMergeConstants {
// new FunctionDetailChangeListener(FUNC_RETURN_TYPE, entryPt, panel, monitor));
// }
if (((conflicts & FUNC_RETURN_ADDRESS_OFFSET) != 0)) {
String latest = DiffUtility.toSignedHexString(latestStack.getReturnAddressOffset());
String my = DiffUtility.toSignedHexString(myStack.getReturnAddressOffset());
String latest =
NumericUtilities.toSignedHexString(latestStack.getReturnAddressOffset());
String my = NumericUtilities.toSignedHexString(myStack.getReturnAddressOffset());
panel.addSingleChoice("Return Address Offset", new String[] { latest, my },
new FunctionDetailChangeListener(FUNC_RETURN_ADDRESS_OFFSET, functions, panel,
monitor));
@@ -2202,8 +2202,8 @@ abstract class AbstractFunctionMerger implements ListingMergeConstants {
// }
if ((conflicts & FUNC_STACK_PURGE_SIZE) != 0) {
String latest =
DiffUtility.toSignedHexString(functions[LATEST].getStackPurgeSize());
String my = DiffUtility.toSignedHexString(functions[MY].getStackPurgeSize());
NumericUtilities.toSignedHexString(functions[LATEST].getStackPurgeSize());
String my = NumericUtilities.toSignedHexString(functions[MY].getStackPurgeSize());
panel.addSingleChoice("Stack Purge Size", new String[] { latest, my },
new FunctionDetailChangeListener(FUNC_STACK_PURGE_SIZE, functions, panel,
monitor));

View File

@@ -4,9 +4,9 @@
* 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.
@@ -32,8 +32,8 @@ import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable;
import ghidra.program.util.DiffUtility;
import ghidra.program.util.ProgramDiffFilter;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -306,8 +306,8 @@ class EquateMerger extends AbstractListingMerger {
*/
@Override
public void mergeConflicts(ListingMergePanel listingPanel, Address addr,
int chosenConflictOption, TaskMonitor monitor) throws CancelledException,
MemoryAccessException {
int chosenConflictOption, TaskMonitor monitor)
throws CancelledException, MemoryAccessException {
if (!hasConflict(addr)) {
return;
}
@@ -332,8 +332,8 @@ class EquateMerger extends AbstractListingMerger {
monitor.checkCancelled();
}
else {
merge(equateConflict.address, equateConflict.opIndex,
equateConflict.scalar, chosenConflictOption);
merge(equateConflict.address, equateConflict.opIndex, equateConflict.scalar,
chosenConflictOption);
}
}
}
@@ -459,7 +459,7 @@ class EquateMerger extends AbstractListingMerger {
}
if (equate != null) {
info[1] = equate.getDisplayName();
info[2] = DiffUtility.toSignedHexString(equate.getValue());
info[2] = NumericUtilities.toSignedHexString(equate.getValue());
}
return info;
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -17,8 +17,8 @@ package ghidra.app.merge.util;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.util.DiffUtility;
import ghidra.util.HTMLUtilities;
import ghidra.util.NumericUtilities;
/**
* <code>ConflictUtility</code> provides some constants and static methods
@@ -280,7 +280,7 @@ public class ConflictUtility {
* @return the message string containing HTML tags.
*/
public static String getOffsetString(int offset) {
return colorString(OFFSET_COLOR, DiffUtility.toSignedHexString(offset));
return colorString(OFFSET_COLOR, NumericUtilities.toSignedHexString(offset));
}
/**

View File

@@ -4,9 +4,9 @@
* 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.
@@ -23,6 +23,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.*;
/**
@@ -529,29 +530,6 @@ public class DiffUtility extends SimpleDiffUtility {
return cu.getMaxAddress();
}
/**
* Returns the signed hex string representing the int value.
* Positive values are represented beginning with 0x. (i.e. value of 12 would be 0xc)
* Negative values are represented beginning with -0x. (i.e. value of -12 would be -0xc)
* @param value the value
* @return the signed hex string
*/
public static String toSignedHexString(int value) {
return (value >= 0 ? "0x" + Integer.toHexString(value)
: "-0x" + Integer.toHexString(-value));
}
/**
* Returns the signed hex string representing the long value.
* Positive values are represented beginning with 0x. (i.e. value of 12 would be 0xc)
* Negative values are represented beginning with -0x. (i.e. value of -12 would be -0xc)
* @param value the value
* @return the signed hex string
*/
public static String toSignedHexString(long value) {
return (value >= 0 ? "0x" + Long.toHexString(value) : "-0x" + Long.toHexString(-value));
}
/**
* Returns the string representation of the specified reference's "to" address.
* @param program the program containing the reference
@@ -571,13 +549,14 @@ public class DiffUtility extends SimpleDiffUtility {
if (ref.isStackReference()) {
int offset = ((StackReference) ref).getStackOffset();
return "Stack[" + toSignedHexString(offset) + "]";
return GenericAddress.STACK_ADDRESS_PREFIX +
NumericUtilities.toSignedHexString(offset) + GenericAddress.STACK_ADDRESS_SUFFIX;
}
else if (ref.isOffsetReference()) {
OffsetReference oref = (OffsetReference) ref;
return toAddr.toString() + " " + "base:" +
DiffUtility.getUserToAddressString(program, oref.getBaseAddress()) + " " +
"offset:" + DiffUtility.toSignedHexString(oref.getOffset());
"offset:" + NumericUtilities.toSignedHexString(oref.getOffset());
}
else if (ref.isShiftedReference()) {
@@ -619,7 +598,7 @@ public class DiffUtility extends SimpleDiffUtility {
}
}
else if (address.isStackAddress()) {
return "stack:" + toSignedHexString(address.getOffset());
return "stack:" + NumericUtilities.toSignedHexString(address.getOffset());
}
return address.toString();
}

View File

@@ -899,11 +899,12 @@ public class ProgramDiffDetails {
fieldName = dtc.getDefaultFieldName();
}
// TODO: how should we display bitfields?
buf.append(indent + "Offset=" + DiffUtility.toSignedHexString(offset) + " " + "Ordinal=" +
ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) +
" " + getCategoryName(actualDt) + " " + "DataTypeSize=" +
(actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " + "ComponentSize=" +
dtc.getLength() + " " + ((comment != null) ? comment : "") + " " + newLine);
buf.append(indent + "Offset=" + NumericUtilities.toSignedHexString(offset) + " " +
"Ordinal=" + ordinal + " " + fieldName + " " +
actualDt.getMnemonic(actualDt.getDefaultSettings()) + " " + getCategoryName(actualDt) +
" " + "DataTypeSize=" + (actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " +
"ComponentSize=" + dtc.getLength() + " " + ((comment != null) ? comment : "") + " " +
newLine);
return actualDt;
}
@@ -1862,7 +1863,7 @@ public class ProgramDiffDetails {
private void addReturnOffset(StyledDocument doc, StackFrame frame) {
addFrameInfo(doc, "Return Address Offset: ",
DiffUtility.toSignedHexString(frame.getReturnAddressOffset()));
NumericUtilities.toSignedHexString(frame.getReturnAddressOffset()));
}
private void addReturnOffset(StyledDocument doc1, StyledDocument doc2, StackFrame frame1,
@@ -1870,14 +1871,16 @@ public class ProgramDiffDetails {
int offset1 = frame1.getReturnAddressOffset();
int offset2 = frame2.getReturnAddressOffset();
if (offset1 != offset2) {
addFrameInfo(doc1, "Return Address Offset: ", DiffUtility.toSignedHexString(offset1));
addFrameInfo(doc2, "Return Address Offset: ", DiffUtility.toSignedHexString(offset2));
addFrameInfo(doc1, "Return Address Offset: ",
NumericUtilities.toSignedHexString(offset1));
addFrameInfo(doc2, "Return Address Offset: ",
NumericUtilities.toSignedHexString(offset2));
}
}
private void addParameterOffset(StyledDocument doc, StackFrame frame) {
addFrameInfo(doc, "Parameter Offset: ",
DiffUtility.toSignedHexString(frame.getParameterOffset()));
NumericUtilities.toSignedHexString(frame.getParameterOffset()));
}
private void addParameterOffset(StyledDocument doc1, StyledDocument doc2, StackFrame frame1,
@@ -1885,8 +1888,8 @@ public class ProgramDiffDetails {
int offset1 = frame1.getParameterOffset();
int offset2 = frame2.getParameterOffset();
if (offset1 != offset2) {
addFrameInfo(doc1, "Parameter Offset: ", DiffUtility.toSignedHexString(offset1));
addFrameInfo(doc2, "Parameter Offset: ", DiffUtility.toSignedHexString(offset2));
addFrameInfo(doc1, "Parameter Offset: ", NumericUtilities.toSignedHexString(offset1));
addFrameInfo(doc2, "Parameter Offset: ", NumericUtilities.toSignedHexString(offset2));
}
}
@@ -1925,7 +1928,7 @@ public class ProgramDiffDetails {
vl.dtLen = Math.max(vl.dtLen, var.getDataType().getPathName().length());
vl.offsetLen = Math.max(vl.offsetLen, var.getVariableStorage().toString().length());
vl.firstUseLen = Math.max(vl.firstUseLen,
DiffUtility.toSignedHexString(var.getFirstUseOffset()).length());
NumericUtilities.toSignedHexString(var.getFirstUseOffset()).length());
vl.nameLen = Math.max(vl.nameLen, var.getName().length());
vl.sizeLen = Math.max(vl.sizeLen, Integer.toString(var.getLength()).length());
vl.sourceLen = Math.max(vl.sourceLen, var.getSource().toString().length());
@@ -2125,7 +2128,7 @@ public class ProgramDiffDetails {
else {
String dt = var.getDataType().getPathName();
String offset = var.getVariableStorage().toString();
String firstUse = DiffUtility.toSignedHexString(var.getFirstUseOffset());
String firstUse = NumericUtilities.toSignedHexString(var.getFirstUseOffset());
String name = var.getName();
String size = "" + var.getLength();
String source = var.getSource().toString();

View File

@@ -4,9 +4,9 @@
* 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.
@@ -15,14 +15,14 @@
*/
package ghidra.util.table.field;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.CodeUnitFormatOptions.ShowBlockName;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.*;
import ghidra.util.NumericUtilities;
import ghidra.util.SystemUtilities;
/**
@@ -195,17 +195,13 @@ public class AddressBasedLocation implements Comparable<AddressBasedLocation> {
}
private static String getStackAddressRepresentation(Address address) {
int offset = (int) address.getOffset();
boolean neg = (offset < 0);
return "Stack[" + (neg ? "-" : "+") + "0x" + Integer.toHexString(neg ? -offset : offset) +
"]";
return GenericAddress.STACK_ADDRESS_PREFIX +
NumericUtilities.toSignedHexString(address.getOffset()) +
GenericAddress.STACK_ADDRESS_SUFFIX;
}
private static String getConstantAddressRepresentation(Address address) {
int offset = (int) address.getOffset();
boolean neg = (offset < 0);
return "Constant[" + (neg ? "-" : "+") + "0x" +
Integer.toHexString(neg ? -offset : offset) + "]";
return "Constant[" + NumericUtilities.toSignedHexString(address.getOffset()) + "]";
}
private static String getVariableAddressRepresentation() {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -15,12 +15,12 @@
*/
package ghidra.program.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import org.junit.*;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
@@ -34,37 +34,99 @@ public class ProgramUtilitiesTest extends AbstractGhidraHeadedIntegrationTest {
super();
}
@Before
@Before
public void setUp() throws Exception {
env = new TestEnv();
builder = new ProgramBuilder("notepad", ProgramBuilder._TOY);
builder.createMemory("test1", Long.toHexString(0x1001000), 0x2000);
}
@After
public void tearDown() {
@After
public void tearDown() {
env.dispose();
builder.dispose();
}
@Test
public void testParseAddress() throws Exception {
@Test
public void testParseAddress() throws Exception {
Program p = builder.getProgram();
p.withTransaction("Add Overlay Block", () -> {
Address addr = ProgramUtilities.parseAddress(p, "100");
p.getMemory().createUninitializedBlock("OVLY", addr, 0x100, true);
});
AddressFactory addressFactory = p.getAddressFactory();
// Verify that special spaces were defined for TOY language
assertNotNull(addressFactory.getAddressSpace("const"));
assertNotNull(addressFactory.getAddressSpace("unique"));
assertNotNull(addressFactory.getAddressSpace("register"));
//
// Stack addresses (signed decimal and hex offsets)
//
Address addr = ProgramUtilities.parseAddress(p, "Stack[54]");
assertEquals(p.getAddressFactory().getStackSpace(), addr.getAddressSpace());
assertEquals(addressFactory.getStackSpace(), addr.getAddressSpace());
assertEquals(54, addr.getOffset());
addr = ProgramUtilities.parseAddress(p, "Stack[0x54]");
assertEquals(p.getAddressFactory().getStackSpace(), addr.getAddressSpace());
assertEquals(addressFactory.getStackSpace(), addr.getAddressSpace());
assertEquals(0x54, addr.getOffset());
addr = ProgramUtilities.parseAddress(p, "Stack[-54]");
assertEquals(p.getAddressFactory().getStackSpace(), addr.getAddressSpace());
assertEquals(addressFactory.getStackSpace(), addr.getAddressSpace());
assertEquals(-54, addr.getOffset());
addr = ProgramUtilities.parseAddress(p, "Stack[-0x54]");
assertEquals(p.getAddressFactory().getStackSpace(), addr.getAddressSpace());
assertEquals(addressFactory.getStackSpace(), addr.getAddressSpace());
assertEquals(-0x54, addr.getOffset());
//
// Memory address
//
addr = ProgramUtilities.parseAddress(p, "ram:00001234");
assertEquals(addressFactory.getAddressSpace("ram"), addr.getAddressSpace());
assertEquals(0x1234, addr.getOffset());
//
// Overlay address
//
addr = ProgramUtilities.parseAddress(p, "OVLY::0x010");
assertEquals(addressFactory.getAddressSpace("OVLY"), addr.getAddressSpace());
assertEquals(0x10, addr.getOffset());
assertTrue(addr.getAddressSpace().isOverlaySpace());
addr = ProgramUtilities.parseAddress(p, "OVLY::0x2000"); // outside overlay block range
assertEquals(addressFactory.getAddressSpace("OVLY"), addr.getAddressSpace());
assertEquals(0x2000, addr.getOffset());
assertTrue(addr.getAddressSpace().isOverlaySpace());
//
// External address
//
addr = ProgramUtilities.parseAddress(p, "EXTERNAL:00001234");
assertEquals(AddressSpace.EXTERNAL_SPACE, addr.getAddressSpace());
assertEquals(0x1234, addr.getOffset());
//
// Block-name style address (Not supported)
//
assertNull(ProgramUtilities.parseAddress(p, "test1:00001001000"));
assertNull(ProgramUtilities.parseAddress(p, "test1:00001234"));
//
// Special spaces (Not supported)
//
assertNull(ProgramUtilities.parseAddress(p, "register:00000000"));
assertNull(ProgramUtilities.parseAddress(p, "const:00000000"));
assertNull(ProgramUtilities.parseAddress(p, "unique:00000000"));
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -25,6 +25,7 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.graph.ProgramGraphDisplayOptions;
import ghidra.graph.ProgramGraphType;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.GenericAddress;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*;
@@ -423,7 +424,9 @@ public class PCodeCfgGraphTask extends Task {
return var.getName();
}
}
return "Stack[" + NumericUtilities.toSignedHexString(addr.getOffset()) + "]";
return GenericAddress.STACK_ADDRESS_PREFIX +
NumericUtilities.toSignedHexString(addr.getOffset()) +
GenericAddress.STACK_ADDRESS_SUFFIX;
}
else if (addr.isMemoryAddress()) {
return addr.toString(true);

View File

@@ -205,14 +205,15 @@ public class ProgramAddressFactory extends DefaultAddressFactory {
@Override
public Address getAddress(String addrString) {
Address addr = null;
if (addrString.startsWith("Stack[") && addrString.endsWith("]")) {
if (addrString.startsWith(GenericAddress.STACK_ADDRESS_PREFIX) &&
addrString.endsWith(GenericAddress.STACK_ADDRESS_SUFFIX)) {
try {
long stackOffset =
NumericUtilities.parseHexLong(addrString.substring(6, addrString.length() - 1));
addr = stackSpace.getAddress(stackOffset);
}
catch (NumberFormatException e) {
// bad string
catch (AddressOutOfBoundsException | NumberFormatException e) {
// bad stack address string
}
}
else {

View File

@@ -820,9 +820,15 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
}
}
}
// TODO: Support for trailing 'h' should be dropped since it is not consistently
// supported by other parse methods (block-based above and within AddressFactory).
// AbstractAddressSpace.getAddress allows for '0x' or '0X' offset prefix but not
// a trailing 'h'.
if (addrStr.endsWith("h")) {
addrStr = addrStr.substring(0, addrStr.length() - 1);
}
return addressFactory.getAllAddresses(addrStr, caseSensitive);
}

View File

@@ -4,9 +4,9 @@
* 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.

View File

@@ -4,9 +4,9 @@
* 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.
@@ -19,25 +19,41 @@ public interface AddressFactory {
/**
* Create an address from String. Attempts to use the "default" address space
* first. Otherwise loops through each addressSpace, returning the first valid
* address that any addressSpace creates from the string.
* Returns an Address if the string is valid, otherwise null.
* first. Otherwise factory will loop through each defined address space, returning the first
* valid address that any address space creates from the string. For this reason, only the
* default memory address space should be specified with an offset only. All other
* address space addresses should be specified with the space name prefix and hex offset
* (e.g., MYSPACE:abcd). In addition to all define memory and overlay address spaces,
* special purpose address spaces known to this address factory will be considered
* (e.g., CONST, UNIQUE, etc.).
* @param addrString the address string to parse.
* @return an Address if the string is valid, otherwise null.
*/
public Address getAddress(String addrString);
/**
* Generates all reasonable addresses that can be interpreted from
* the given string. Each Address Space is given a change to parse
* the string and all the valid results are return in the array.
* Generates all reasonable memory addresses that can be interpreted from the given string.
* Each defined memory Address Space is given a change to parse the string and all the valid
* results are returned in the array.
* <p>
* NOTE: Only when unable to parse any valid loaded memory addresses (see
* {@link AddressSpace#isLoadedMemorySpace()}) will one uniquely specified non-loaded memory
* address be considered and returned.
*
* @param addrString the address string to parse.
* @return Address[] The list of addresses generated from the string.
*/
public Address[] getAllAddresses(String addrString);
/**
* Generates all reasonable addresses that can be interpreted from
* the given string. Each Address Space is given a change to parse
* the string and all the valid results are return in the array.
* Generates all reasonable memory addresses that can be interpreted from the given string.
* Each defined memory Address Space is given a change to parse the string and all the valid
* results are returned in the array.
* <p>
* NOTE: Only when unable to parse any valid loaded memory addresses (see
* {@link AddressSpace#isLoadedMemorySpace()}) will one uniquely specified non-loaded memory
* address be considered and returned.
*
* @param addrString the address string to parse.
* @param caseSensitive determines if addressSpace names must be case sensitive to match.
* @return Address[] The list of addresses generated from the string.
@@ -45,12 +61,12 @@ public interface AddressFactory {
public Address[] getAllAddresses(String addrString, boolean caseSensitive);
/**
* Returns the default AddressSpace
* {@return the default AddressSpace}
*/
public AddressSpace getDefaultAddressSpace();
/**
* Get the array of all "physical" AddressSpaces.
* {@return the array of all "physical" AddressSpaces.}
*/
public AddressSpace[] getAddressSpaces();
@@ -61,18 +77,20 @@ public interface AddressFactory {
public AddressSpace[] getAllAddressSpaces();
/**
* Returns the space with the given name or null if no space
* exists with that name.
* {@return the space with the given name or null if no space
* exists with that name.}
* @param name address space name
*/
public AddressSpace getAddressSpace(String name);
/**
* Returns the space with the given spaceID or null if none exists
* {@return the space with the given spaceID or null if none exists}
* @param spaceID address space unique ID
*/
public AddressSpace getAddressSpace(int spaceID);
/**
* Returns the number of physical AddressSpaces.
* {@return the number of physical AddressSpaces.}
*/
public int getNumAddressSpaces();
@@ -84,14 +102,11 @@ public interface AddressFactory {
*/
public boolean isValidAddress(Address addr);
/**
* @see java.lang.Object#equals(Object)
*/
@Override
public boolean equals(Object o);
/**
* Returns the index (old encoding) for the given address.
* {@return the index (old encoding) for the given address.}
* @param addr the address to encode.
*/
public long getIndex(Address addr);
@@ -119,22 +134,22 @@ public interface AddressFactory {
public Address getAddress(int spaceID, long offset);
/**
* Returns the "constant" address space.
* {@return the "constant" address space.}
*/
public AddressSpace getConstantSpace();
/**
* Returns the "unique" address space.
* {@return the "unique" address space.}
*/
public AddressSpace getUniqueSpace();
/**
* Returns the "stack" address space.
* {@return the "stack" address space.}
*/
public AddressSpace getStackSpace();
/**
* Returns the "register" address space.
* {@return the "register" address space.}
*/
public AddressSpace getRegisterSpace();
@@ -157,18 +172,18 @@ public interface AddressFactory {
public AddressSet getAddressSet(Address min, Address max);
/**
* Returns an addressSet containing all possible "real" addresses for this address factory.
* {@return an addressSet containing all possible "real" addresses for this address factory.}
*/
public AddressSet getAddressSet();
/**
* Returns the address using the old encoding format.
* {@return the address using the old encoding format.}
* @param value to decode into an address.
*/
public Address oldGetAddressFromLong(long value);
/**
* Returns true if there is more than one memory address space
* {@return true if there is more than one memory address space}
*/
public boolean hasMultipleMemorySpaces();

View File

@@ -4,9 +4,9 @@
* 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.
@@ -147,9 +147,6 @@ public class DefaultAddressFactory implements AddressFactory {
}
}
/**
* @see ghidra.program.model.address.AddressFactory#getAddress(java.lang.String)
*/
@Override
public Address getAddress(String addrString) {
try {
@@ -218,7 +215,6 @@ public class DefaultAddressFactory implements AddressFactory {
}
Address[] addrs = new Address[loadedMemoryList.size()];
return loadedMemoryList.toArray(addrs);
}

View File

@@ -30,6 +30,9 @@ public class GenericAddress implements Address {
protected static final String zeros = "0000000000000000";
public final static String STACK_ADDRESS_PREFIX = "Stack[";
public final static String STACK_ADDRESS_SUFFIX = "]"; // parse code assumes length == 1
protected AddressSpace addrSpace;
protected long offset;
@@ -245,7 +248,7 @@ public class GenericAddress implements Address {
StringBuilder buf = new StringBuilder();
if (addrSpace.isStackSpace()) {
stackFormat = true;
buf.append("Stack[");
buf.append(STACK_ADDRESS_PREFIX);
minNumDigits = 1;
}
else if (showAddressSpace) {
@@ -287,7 +290,7 @@ public class GenericAddress implements Address {
buf.append(mod);
}
if (stackFormat) {
buf.append("]");
buf.append(STACK_ADDRESS_SUFFIX);
}
return buf.toString();
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -93,25 +93,16 @@ public class GenericAddressSpace extends AbstractAddressSpace {
return name;
}
/**
* @see ghidra.program.model.address.AddressSpace#getAddress(long)
*/
@Override
public Address getAddress(long offset) throws AddressOutOfBoundsException {
return new GenericAddress(this, offset);
}
/**
* @see ghidra.program.model.address.AddressSpace#getAddressInThisSpaceOnly(long)
*/
@Override
public Address getAddressInThisSpaceOnly(long offset) {
return new GenericAddress(this, offset);
}
/**
* @see ghidra.program.model.address.AbstractAddressSpace#getUncheckedAddress(long)
*/
@Override
protected Address getUncheckedAddress(long offset) {
return new GenericAddress(offset, this);

View File

@@ -4,9 +4,9 @@
* 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.

View File

@@ -4,9 +4,9 @@
* 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.
@@ -349,19 +349,45 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur
public AddressFactory getAddressFactory();
/**
* Return an array of Addresses that could represent the given
* string.
* @param addrStr the string to parse.
* Return an array of memory Addresses that could correspond to the given
* string. Non-memory spaces are not considered. Since this method allows
* memory-block style addresses first it can be slower to parse than using
* {@link AddressFactory#getAddress(String)} or {@link AddressFactory#getAllAddresses(String)}
* if block-name based address need not be handled.
* <p>
* Supported addresses include (order also indicates precedence):
* <ul>
* <li>Memory block-name based address (e.g., 'MyBlk:abcd', 'MyBlk::abcd' ; only one address
* will be returned)</li>
* <li>Default memory space (hex-offset only or with space-name, e.g., 'abcd', '0xabcd')</li>
* <li>Memory space-name based address (with hex-offset, e.g., 'ram:abc')</li>
* </ul>
* <p>
* NOTE: Names are case-sensitive.
*
* @param addrStr the string to parse (memory block style addresses are also supported).
* @return zero length array if addrStr is properly formatted but
* no matching addresses were found or if the address is improperly formatted.
*/
public Address[] parseAddress(String addrStr);
/**
* Return an array of Addresses that could represent the given
* string.
* @param addrStr the string to parse.
* @param caseSensitive whether or not to process any addressSpace names as case sensitive.
* Return an array of memory Addresses that could correspond to the given
* string. Non-memory spaces are not considered. Since this method allows
* memory-block style addresses first it can be slower to parse than using
* {@link AddressFactory#getAddress(String)} or {@link AddressFactory#getAllAddresses(String)}
* if block-name based address need not be handled.
* <p>
* Supported addresses include (order also indicates precedence):
* <ul>
* <li>Memory block-name based address (e.g., 'MyBlk:abcd', 'MyBlk::abcd' ; only one address
* will be returned)</li>
* <li>Default memory space (hex-offset only or with space-name, e.g., 'abcd', '0xabcd')</li>
* <li>Memory space-name based address (with hex-offset, e.g., 'ram:abc')</li>
* </ul>
*
* @param addrStr the string to parse (memory block style addresses are also supported).
* @param caseSensitive whether or not to process space/block names as case sensitive.
* @return zero length array if addrStr is properly formatted but
* no matching addresses were found or if the address is improperly formatted.
*/

View File

@@ -4,9 +4,9 @@
* 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.
@@ -17,7 +17,7 @@ package ghidra.program.util;
import java.util.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.*;
@@ -31,6 +31,8 @@ import ghidra.util.exception.InvalidInputException;
*/
public class ProgramUtilities {
private final static String EXTERNAL_ADDRESS_PREFIX = AddressSpace.EXTERNAL_SPACE.toString();
private ProgramUtilities() {
}
@@ -66,25 +68,72 @@ public class ProgramUtilities {
return Collections.unmodifiableSet(openProgramsWeakMap.keySet()).iterator();
}
/**
* Parse an {@link Address} string which corresponds to the specified program.
* Supported addresses include (order also indicates precedence):
* <ul>
* <li>Default loaded memory space (hex-offset only or with space-name, e.g., 'abcd', '0xabcd')</li>
* <li>Memory space-name based address (with hex-offset, e.g., 'ram:abc', see Note-1)</li>
* <li>External address (e.g., EXTERNAL:00001234, see Note-2)</li>
* <li>Stack address (e.g., Stack[0xa], Stack[-0xa], Stack[10], Stack[-10])</li>
* </ul>
* <p>
* NOTES:
* <ol>
* <li>Specifying only a hex offset should be restricted to a valid default address space offset
* to avoid having an arbitrary address space address returned. A non-default space address
* should include the appropriate address space name prefix.</li>
* <li>If an external address is returned it does not indicate that it is defined by the
* program.</li>
* </ol>
*
* @param program program whose memory spaces should be considered
* @param addressString address string to be parsed (use of address space name prefix is
* case-sensitive).
* @return parsed address or null if parse failed
*/
public static Address parseAddress(Program program, String addressString) {
Address[] addrs = program.parseAddress(addressString);
Address[] addrs = program.getAddressFactory().getAllAddresses(addressString);
if (addrs != null && addrs.length > 0) {
return addrs[0];
}
String stackPrefix = "Stack[";
if (addressString.startsWith(stackPrefix)) {
String offsetString =
addressString.substring(stackPrefix.length(), addressString.length() - 1);
Address addr = tryParseExternalAddress(addressString);
if (addr == null) {
addr = tryParseStackAddress(program, addressString);
}
return addr;
}
private static Address tryParseStackAddress(Program program, String addressString) {
if (addressString.startsWith(GenericAddress.STACK_ADDRESS_PREFIX) &&
addressString.endsWith(GenericAddress.STACK_ADDRESS_SUFFIX)) {
try {
AddressSpace stackSpace = program.getAddressFactory().getStackSpace();
String offsetString = // hex (0x) or decimal
addressString.substring(GenericAddress.STACK_ADDRESS_PREFIX.length(),
addressString.length() - 1);
long offset = NumericUtilities.parseLong(offsetString);
return program.getAddressFactory().getStackSpace().getAddress(offset);
return stackSpace.getAddress(offset);
}
catch (NumberFormatException e) {
return null;
catch (AddressOutOfBoundsException | NumberFormatException e) {
// ignore - return null below
}
}
return null;
}
private static Address tryParseExternalAddress(String addressString) {
if (addressString.startsWith(EXTERNAL_ADDRESS_PREFIX)) {
try {
String offsetString = addressString.substring(EXTERNAL_ADDRESS_PREFIX.length());
long offset = Long.parseLong(offsetString, 16); // hex offset only
return AddressSpace.EXTERNAL_SPACE.getAddress(offset);
}
catch (AddressOutOfBoundsException | NumberFormatException e) {
// ignore - return null below
}
}
return null;
}
/**
@@ -119,6 +168,7 @@ public class ProgramUtilities {
/**
* Convert old function wrapped external pointers. Migrate function to
* external function.
* @param functionSymbol old fake IAT function to be migrated
*/
public static void convertFunctionWrappedExternalPointer(Symbol functionSymbol) {
if (functionSymbol.getSymbolType() != SymbolType.FUNCTION) {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -25,23 +25,28 @@ public class AddressFactoryTest extends AbstractGenericTest {
AddressSpace space = null;
AddressFactory factory = null;
boolean isValidAddress = false;
final int ADDRESS_SPACES = 5;
final int ADDRESSES = 13;
final int ADDRESSES = 14;
final int PHYSICAL_ADDRESS_SPACES = 5; // first 5 in spaces and ADDRESS_SPACE_NAMES
AddressSpace[] spaces = new AddressSpace[ADDRESS_SPACES];
final String[] ADDRESS_SPACE_NAMES =
{ "ONE", "TWO", "THREE", "SegSpaceOne", "SegSpaceTwo", "register", "const", "unique" };
AddressSpace[] spaces = new AddressSpace[ADDRESS_SPACE_NAMES.length];
Address[] addrs = new Address[ADDRESSES];
final String[] spaceName = { "ONE", "TWO", "THREE", "SegSpaceOne", "SegSpaceTwo" };
////////////////////////////////////////////////////////////////////////////////////
@Before
public void setUp() {
spaces[0] = new GenericAddressSpace(spaceName[0], 8, AddressSpace.TYPE_RAM, 0);
spaces[1] = new GenericAddressSpace(spaceName[1], 16, AddressSpace.TYPE_RAM, 1);
spaces[2] = new GenericAddressSpace(spaceName[2], 32, AddressSpace.TYPE_RAM, 2);
spaces[0] = new GenericAddressSpace(ADDRESS_SPACE_NAMES[0], 8, AddressSpace.TYPE_RAM, 0);
spaces[1] = new GenericAddressSpace(ADDRESS_SPACE_NAMES[1], 16, AddressSpace.TYPE_RAM, 1);
spaces[2] = new GenericAddressSpace(ADDRESS_SPACE_NAMES[2], 32, AddressSpace.TYPE_RAM, 2);
spaces[3] = new SegmentedAddressSpace(spaceName[3], 3);
spaces[4] = new SegmentedAddressSpace(spaceName[4], 4);
spaces[3] = new SegmentedAddressSpace(ADDRESS_SPACE_NAMES[3], 3);
spaces[4] = new SegmentedAddressSpace(ADDRESS_SPACE_NAMES[4], 4);
spaces[5] = new GenericAddressSpace("register", 32, AddressSpace.TYPE_REGISTER, 0); // not physical
spaces[6] = new GenericAddressSpace("const", 32, AddressSpace.TYPE_CONSTANT, 0); // not physical
spaces[7] = new GenericAddressSpace("unique", 32, AddressSpace.TYPE_UNIQUE, 0); // not physical
factory = new DefaultAddressFactory(spaces);
}
@@ -62,8 +67,8 @@ public class AddressFactoryTest extends AbstractGenericTest {
isValidAddress = factory.isValidAddress(new GenericAddress(space, 0));
assertTrue(!isValidAddress);
for (int i = 0; i < ADDRESS_SPACES; i++) {
isValidAddress = factory.isValidAddress(new GenericAddress(spaces[i], 0));
for (AddressSpace element : spaces) {
isValidAddress = factory.isValidAddress(new GenericAddress(element, 0));
assertTrue(isValidAddress);
}
@@ -90,37 +95,75 @@ public class AddressFactoryTest extends AbstractGenericTest {
// //////////////////////////////////////////////////////////////////////////////////
@Test
public void testGetAllAddresses() {
// Limited to memory address spaces only
Address[] addresses;
addresses = factory.getAllAddresses("SegSpace*:0");
Assert.assertEquals(addresses.length, 0);
assertEquals(0, addresses.length);
addresses = factory.getAllAddresses("SegSpaceOne:0");
Assert.assertEquals(addresses.length, 1);
assertEquals(1, addresses.length);
addresses = factory.getAllAddresses("segspaceOne:0");
assertEquals(0, addresses.length);
addresses = factory.getAllAddresses("0");
assertEquals(5, addresses.length);
assertEquals("ONE:00", addresses[0].toString());
assertEquals("TWO:0000", addresses[1].toString());
assertEquals("THREE:00000000", addresses[2].toString());
assertEquals("SegSpaceOne:0000:0000", addresses[3].toString());
assertEquals("SegSpaceTwo:0000:0000", addresses[4].toString());
}
@Test
public void testGetAllAddressesCaseInsensitive() {
Address[] addresses;
addresses = factory.getAllAddresses("SegSpace*:0", false);
assertEquals(0, addresses.length);
addresses = factory.getAllAddresses("SegSpaceOne:0", false);
assertEquals(1, addresses.length);
addresses = factory.getAllAddresses("segspaceOne:0", false);
assertEquals(1, addresses.length);
addresses = factory.getAllAddresses("0", false);
assertEquals(5, addresses.length);
assertEquals("ONE:00", addresses[0].toString());
assertEquals("TWO:0000", addresses[1].toString());
assertEquals("THREE:00000000", addresses[2].toString());
assertEquals("SegSpaceOne:0000:0000", addresses[3].toString());
assertEquals("SegSpaceTwo:0000:0000", addresses[4].toString());
}
////////////////////////////////////////////////////////////////////////////////////
@Test
public void testGetAddressSpce() {
space = factory.getAddressSpace(spaceName[0]);
public void testGetAddressSpace() {
space = factory.getAddressSpace(ADDRESS_SPACE_NAMES[0]);
space = factory.getAddressSpace("xyz");
AddressSpace[] as = factory.getAddressSpaces();
assertTrue(as.length == ADDRESS_SPACES);
assertTrue(as.length == factory.getNumAddressSpaces());
assertEquals(PHYSICAL_ADDRESS_SPACES, as.length);
assertEquals(PHYSICAL_ADDRESS_SPACES, factory.getNumAddressSpaces());
for (int i = 0; i < as.length; i++) {
Assert.assertEquals(spaceName[i], as[i].getName());
Assert.assertEquals(ADDRESS_SPACE_NAMES[i], as[i].getName());
Assert.assertTrue(spaces[i] == as[i]);
}
}
//////////////////////////////////////////////////////////////////////////////////
@Test
public void testGetDefaultAddressSpce() {
public void testGetDefaultAddressSpace() {
AddressSpace defASP = factory.getDefaultAddressSpace();
Assert.assertEquals(defASP.getName(), spaces[0].getName());
@@ -151,6 +194,12 @@ public class AddressFactoryTest extends AbstractGenericTest {
Assert.assertEquals(addrs[10], factory.getAddress("f000:ffff"));
Assert.assertEquals(addrs[11], factory.getAddress("unique:0100"));
Assert.assertEquals(addrs[12], factory.getAddress("const:0200"));
Assert.assertEquals(addrs[13], factory.getAddress("register:0300"));
}
////////////////////////////////////////////////////////////////////////
@@ -172,6 +221,10 @@ public class AddressFactoryTest extends AbstractGenericTest {
addrs[10] = new SegmentedAddress((SegmentedAddressSpace) spaces[3], 0xf000, 0xffff);
addrs[11] = factory.getAddressSpace("unique").getAddress(0x100);
addrs[12] = factory.getAddressSpace("const").getAddress(0x200);
addrs[13] = factory.getAddressSpace("register").getAddress(0x300);
}
}