GP-3013 Refactor of Relocation API (created V6 DB adapter) to include

status and stored length when original FileBytes are used.
This commit is contained in:
ghidra1
2023-01-17 15:04:22 -05:00
parent f022b9a4d5
commit 5b433f35ca
63 changed files with 2146 additions and 1147 deletions
@@ -25,6 +25,7 @@ import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.util.NumericUtilities;
@@ -51,15 +52,19 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
};
static final int ADDRESS_COL = 0;
static final int TYPE_COL = 1;
static final int VALUE_COL = 2;
static final int BYTES_COL = 3;
static final int NAME_COL = 4;
static final int STATUS_COL = 1;
static final int TYPE_COL = 2;
static final int VALUE_COL = 3;
static final int ORIGINAL_BYTES_COL = 4;
static final int MODIFIED_BYTES_COL = 5;
static final int NAME_COL = 6;
static final String RELOCATION_ADDRESS = "Address";
static final String RELOCATION_STATUS = "Status";
static final String RELOCATION_TYPE = "Type";
static final String RELOCATION_VALUE = "Values";
static final String RELOCATION_BYTES = "Original Bytes";
static final String RELOCATION_ORIGINAL_BYTES = "Original Bytes";
static final String RELOCATION_CURRENT_BYTES = "Current Bytes";
static final String RELOCATION_NAME = "Name";
public RelocationTableModel(ServiceProvider serviceProvider, Program program,
@@ -73,9 +78,11 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
descriptor.addVisibleColumn(
DiscoverableTableUtils.adaptColumForModel(this, new AddressTableColumn()), 1, true);
descriptor.addVisibleColumn(new RelocationStatusColumn());
descriptor.addVisibleColumn(new RelocationTypeColumn());
descriptor.addVisibleColumn(new RelocationValueColumn());
descriptor.addVisibleColumn(new RelocationBytesColumn());
descriptor.addVisibleColumn(new RelocationOriginalBytesColumn());
descriptor.addHiddenColumn(new RelocationCurrentBytesColumn());
descriptor.addVisibleColumn(new RelocationNameColumn());
return descriptor;
@@ -118,6 +125,24 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
return rowObject.relocation.getAddress();
}
private static String packBytes(byte[] bytes) {
if (bytes == null) {
return "";
}
StringBuffer buf = new StringBuffer();
for (long b : bytes) {
if (buf.length() != 0) {
buf.append(' ');
}
String byteStr = Long.toHexString(b & 0xff);
if (byteStr.length() == 1) {
buf.append('0');
}
buf.append(byteStr);
}
return buf.toString();
}
//==================================================================================================
// Inner Classes
//==================================================================================================
@@ -137,6 +162,22 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
}
}
private static class RelocationStatusColumn extends
AbstractProgramBasedDynamicTableColumn<RelocationRowObject, String> {
@Override
public String getColumnName() {
return RELOCATION_STATUS;
}
@Override
public String getValue(RelocationRowObject rowObject, Settings settings, Program program,
ServiceProvider serviceProvider) throws IllegalArgumentException {
return rowObject.relocation.getStatus().name();
}
}
private static class RelocationTypeColumn extends
AbstractProgramBasedDynamicTableColumn<RelocationRowObject, String> {
@@ -182,12 +223,12 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
}
}
private static class RelocationBytesColumn extends
private static class RelocationOriginalBytesColumn extends
AbstractProgramBasedDynamicTableColumn<RelocationRowObject, String> {
@Override
public String getColumnName() {
return RELOCATION_BYTES;
return RELOCATION_ORIGINAL_BYTES;
}
@Override
@@ -195,23 +236,33 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
ServiceProvider serviceProvider) throws IllegalArgumentException {
return packBytes(rowObject.relocation.getBytes());
}
}
private String packBytes(byte[] bytes) {
if (bytes == null) {
return "";
private static class RelocationCurrentBytesColumn extends
AbstractProgramBasedDynamicTableColumn<RelocationRowObject, String> {
@Override
public String getColumnName() {
return RELOCATION_CURRENT_BYTES;
}
@Override
public String getValue(RelocationRowObject rowObject, Settings settings, Program program,
ServiceProvider serviceProvider) throws IllegalArgumentException {
Relocation relocation = rowObject.relocation;
byte[] originalBytes = rowObject.relocation.getBytes();
if (originalBytes == null) {
return null;
}
StringBuffer buf = new StringBuffer();
for (long b : bytes) {
if (buf.length() != 0) {
buf.append(' ');
}
String byteStr = Long.toHexString(b & 0xff);
if (byteStr.length() == 1) {
buf.append('0');
}
buf.append(byteStr);
byte[] bytes = new byte[originalBytes.length];
try {
program.getMemory().getBytes(relocation.getAddress(), bytes);
}
return buf.toString();
catch (MemoryAccessException e) {
return null;
}
return packBytes(bytes);
}
}
@@ -20,8 +20,9 @@ import ghidra.app.util.bin.format.coff.CoffFileHeader;
import ghidra.app.util.bin.format.coff.CoffRelocation;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.NotFoundException;
/**
* An abstract class used to perform COFF relocations. Classes should extend this class to
@@ -44,12 +45,15 @@ public interface CoffRelocationHandler extends ExtensionPoint {
* @param address The address at which to perform the relocation.
* @param relocation The relocation information to use to perform the relocation.
* @param relocationContext relocation context data
* @return applied relocation result (conveys status and applied byte-length)
* @throws MemoryAccessException If there is a problem accessing memory during the relocation.
* @throws NotFoundException If this handler didn't find a way to perform the relocation.
* @throws RelocationException if supported relocation encountered an error during processing.
* This exception should be thrown in place of returning {@link RelocationResult#FAILURE} or
* a status of {@link Status#FAILURE} which will facilitate a failure reason via
* {@link RelocationException#getMessage()}.
*/
public void relocate(Address address, CoffRelocation relocation,
public RelocationResult relocate(Address address, CoffRelocation relocation,
CoffRelocationContext relocationContext)
throws MemoryAccessException, NotFoundException, RelocationException;
throws MemoryAccessException, RelocationException;
}
@@ -379,11 +379,11 @@ public class ElfDefaultGotPltMarkup {
// TODO: record artificial relative relocation for reversion/export concerns
entry1Value += imageBaseAdj; // adjust first entry value
if (elf.is64Bit()) {
elfLoadHelper.addFakeRelocTableEntry(gotStart, 8);
elfLoadHelper.addArtificialRelocTableEntry(gotStart, 8);
memory.setLong(gotStart, entry1Value);
}
else {
elfLoadHelper.addFakeRelocTableEntry(gotStart, 4);
elfLoadHelper.addArtificialRelocTableEntry(gotStart, 4);
memory.setInt(gotStart, (int) entry1Value);
}
}
@@ -22,6 +22,7 @@ import ghidra.program.model.address.AddressRange;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.exception.InvalidInputException;
@@ -213,10 +214,10 @@ public interface ElfLoadHelper {
throws MemoryAccessException;
/**
* Add a fake relocation table entry if none previously existed for the specified address.
* Add an artificial relocation table entry if none previously existed for the specified address.
* This is intended to record original file bytes when forced modifications have been
* performed during the ELF import processing. A relocation type of 0 will be specified for
* fake entry.
* performed during the ELF import processing. A relocation type of 0 and a status of
* {@link Status#APPLIED_OTHER} will be applied to the relocation entry.
* NOTE: The number of recorded original FileBytes currently ignores the specified length.
* However, the length is still used to verify that that the intended modification region
* dose not intersect another relocation.
@@ -225,6 +226,6 @@ public interface ElfLoadHelper {
* @return true if recorded successfully, or false if conflict with existing relocation
* entry and memory addressing error occurs
*/
public boolean addFakeRelocTableEntry(Address address, int length);
public boolean addArtificialRelocTableEntry(Address address, int length);
}
@@ -26,6 +26,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.util.exception.*;
/**
@@ -85,12 +86,14 @@ public class ElfRelocationContext {
* All relocation entries must be processed in the order they appear within the table.
* @param relocation relocation to be processed
* @param relocationAddress relocation address where it should be applied
* @return applied relocation result
*/
public final void processRelocation(ElfRelocation relocation, Address relocationAddress) {
public final RelocationResult processRelocation(ElfRelocation relocation,
Address relocationAddress) {
if (handler == null) {
handleNoHandlerError(relocation, relocationAddress);
return;
return RelocationResult.FAILURE;
}
int symbolIndex = relocation.getSymbolIndex();
@@ -98,21 +101,22 @@ public class ElfRelocationContext {
if (sym == null) {
ElfRelocationHandler.markAsUnhandled(program, relocationAddress, relocation.getType(),
symbolIndex, "index " + symbolIndex, getLog());
return;
return RelocationResult.FAILURE;
}
if (sym.isTLS()) {
handleUnsupportedTLSRelocation(relocation, relocationAddress, sym);
return;
return RelocationResult.FAILURE;
}
try {
handler.relocate(this, relocation, relocationAddress);
return handler.relocate(this, relocation, relocationAddress);
}
catch (MemoryAccessException | NotFoundException e) {
loadHelper.log(e);
ElfRelocationHandler.markAsUnhandled(program, relocationAddress, relocation.getType(),
symbolIndex, sym.getNameAsString(), getLog());
}
return RelocationResult.FAILURE;
}
/**
@@ -27,6 +27,7 @@ import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.data.PointerTypedef;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
import ghidra.util.classfinder.ExtensionPoint;
@@ -74,10 +75,11 @@ abstract public class ElfRelocationHandler implements ExtensionPoint {
* @param elfRelocationContext relocation context
* @param relocation ELF relocation
* @param relocationAddress relocation target address (fixup location)
* @return applied relocation result (conveys status and applied byte-length)
* @throws MemoryAccessException memory access failure
* @throws NotFoundException required relocation data not found
*/
abstract public void relocate(ElfRelocationContext elfRelocationContext,
abstract public RelocationResult relocate(ElfRelocationContext elfRelocationContext,
ElfRelocation relocation, Address relocationAddress)
throws MemoryAccessException, NotFoundException;
@@ -25,8 +25,10 @@ import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.*;
import ghidra.util.*;
import ghidra.util.DataConverter;
import ghidra.util.SystemUtilities;
import ghidra.util.task.TaskMonitor;
public abstract class AbstractClassicProcessor {
@@ -70,12 +72,9 @@ public abstract class AbstractClassicProcessor {
long offset = symbol.getAddress().getOffset();
boolean handled = false;
int fileType = header.getFileType();
byte originalBytes[] = new byte[0];
int byteLength = 0;
switch (fileType) {
case MachHeaderFileTypes.MH_EXECUTE:
@@ -85,13 +84,8 @@ public abstract class AbstractClassicProcessor {
byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset)
: converter.getBytes((int) offset);
originalBytes = new byte[bytes.length];
memory.getBytes(address, originalBytes);
memory.setBytes(address, bytes);
handled = true;
byteLength = bytes.length;
break;
}
case MachHeaderFileTypes.MH_KEXT_BUNDLE: {
@@ -108,20 +102,15 @@ public abstract class AbstractClassicProcessor {
long difference = offset - addressValue - 4;
byte[] bytes = converter.getBytes((int) difference);
originalBytes = new byte[bytes.length];
memory.getBytes(address, originalBytes);
memory.setBytes(address, bytes);
handled = true;
byteLength = bytes.length;
}
}
else {
byte[] bytes = (program.getDefaultPointerSize() == 8)
? converter.getBytes(offset) : converter.getBytes((int) offset);
originalBytes = new byte[bytes.length];
memory.getBytes(address, originalBytes);
memory.setBytes(address, bytes);
handled = true;
byteLength = bytes.length;
}
}
else if (header.getCpuType() == CpuTypes.CPU_TYPE_POWERPC) {//TODO powerpc kext files
@@ -140,11 +129,8 @@ public abstract class AbstractClassicProcessor {
case MachHeaderFileTypes.MH_OBJECT: {
byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset)
: converter.getBytes((int) offset);
originalBytes = new byte[bytes.length];
memory.getBytes(address, originalBytes);
memory.setBytes(address, bytes);
handled = true;
byteLength = bytes.length;
break;
}
default: {
@@ -152,15 +138,21 @@ public abstract class AbstractClassicProcessor {
}
}
// put an entry in the relocation table, handled or not
String symbolName = symbol.getName();
program.getRelocationTable().add(address, fileType, new long[0], originalBytes, symbolName);
if (!handled) {
Status status;
if (byteLength <= 0) {
program.getBookmarkManager().setBookmark(address, BookmarkType.ERROR,
"Unhandled Classic Binding", "Unable to fixup classic binding. " +
"This instruction will contain an invalid destination / fixup.");
status = Status.UNSUPPORTED;
}
else {
status = Status.APPLIED;
}
// put an entry in the relocation table, handled or not
String symbolName = symbol.getName();
program.getRelocationTable()
.add(address, status, fileType, null, byteLength, symbolName);
}
/**
@@ -138,8 +138,6 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
monitor.setMessage("Fixing V1 chained data page pointers...");
monitor.setMaximum(entries_count);
@@ -174,8 +172,7 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
// not actually changing bytes, so not really a relocation, but a relocate-able place
if (addRelocations) {
addRelocationTableEntry(program, addr, 0x1000, value, origBytes,
null);
addRelocationTableEntry(program, addr, 0x1000, value, 8, null);
}
//memory.setLong(addr, value);
@@ -212,8 +212,6 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
Memory memory = program.getMemory();
List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
long valueMask = 0xffffffffffffffffL >>> (64 - deltaShift);
long delta = -1;
@@ -230,7 +228,7 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
// chainValue += slideAmount - if we were sliding
}
if (addRelocations) {
addRelocationTableEntry(program, chainLoc, 2, chainValue, origBytes, null);
addRelocationTableEntry(program, chainLoc, 2, chainValue, 8, null);
}
memory.setLong(chainLoc, chainValue);
@@ -172,8 +172,6 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
long delta = -1;
while (delta != 0) {
monitor.checkCanceled();
@@ -201,7 +199,7 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
if (addRelocation) {
addRelocationTableEntry(program, chainLoc, 3 * (isAuthenticated ? -1 : 1),
chainValue, origBytes, null);
chainValue, 8, null);
}
memory.setLong(chainLoc, chainValue);
@@ -225,8 +225,6 @@ public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
List<Address> unchainedLocList = new ArrayList<Address>(1024);
byte origBytes[] = new byte[4];
int valueMask = 0xffffffff >>> (32 - deltaShift);
long delta = -1;
@@ -250,7 +248,7 @@ public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
}
if (addRelocations) {
addRelocationTableEntry(program, chainLoc, 4, chainValue, origBytes, null);
addRelocationTableEntry(program, chainLoc, 4, chainValue, 4, null);
}
memory.setInt(chainLoc, chainValue);
@@ -26,6 +26,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
@@ -129,11 +130,11 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
throws MemoryAccessException, CancelledException;
protected void addRelocationTableEntry(Program program, Address chainLoc, int type,
long chainValue, byte[] origBytes, String name) throws MemoryAccessException {
long chainValue, int appliedByteLength, String name) {
// Add entry to relocation table for the pointer fixup
program.getMemory().getBytes(chainLoc, origBytes);
program.getRelocationTable()
.add(chainLoc, type, new long[] { chainValue }, origBytes, name);
.add(chainLoc, Status.APPLIED, type, new long[] { chainValue }, appliedByteLength,
name);
}
/**
@@ -18,6 +18,8 @@ package ghidra.app.util.bin.format.macho.dyld;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult;
/**
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
@@ -112,8 +114,10 @@ public class DyldChainedPtr {
}
}
public static void setChainValue(Memory memory, Address chainLoc, DyldChainType ptrFormat,
public static RelocationResult setChainValue(Memory memory, Address chainLoc,
DyldChainType ptrFormat,
long value) throws MemoryAccessException {
int byteLength;
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
@@ -125,16 +129,20 @@ public class DyldChainedPtr {
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
memory.setLong(chainLoc, value);
byteLength = 8;
break;
case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
memory.setInt(chainLoc, (int) (value & 0xFFFFFFFFL));
byteLength = 4;
break;
default:
break;
return RelocationResult.UNSUPPORTED;
}
return new RelocationResult(Status.APPLIED_OTHER, byteLength);
}
public static long getChainValue(Memory memory, Address chainLoc, DyldChainType ptrFormat)
@@ -15,6 +15,7 @@
*/
package ghidra.app.util.bin.format.macho.relocation;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.NList;
import ghidra.app.util.bin.format.macho.commands.SymbolTableCommand;
@@ -25,7 +26,6 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.NotFoundException;
/**
* A representation of a single Mach-O relocation that the {@link MachoRelocationHandler} will use
@@ -144,9 +144,9 @@ public class MachoRelocation {
* Gets the {@link Address} of the relocation target
*
* @return The {@link Address} of the relocation target
* @throws NotFoundException If the {@link Address} of the relocation target could not be found
* @throws RelocationException If the {@link Address} of the relocation target could not be found
*/
public Address getTargetAddress() throws NotFoundException {
public Address getTargetAddress() throws RelocationException {
if (targetSymbol != null) {
return targetSymbol.getAddress();
}
@@ -156,17 +156,17 @@ public class MachoRelocation {
if (targetPointer != null) {
return targetPointer;
}
throw new NotFoundException("Relocation target not found");
throw new RelocationException("Relocation target not found");
}
/**
* Gets the {@link Address} of the extra relocation target
*
* @return The {@link Address} of the extra relocation target
* @throws NotFoundException If the {@link Address} of the extra relocation target could not be
* @throws RelocationException If the {@link Address} of the extra relocation target could not be
* found (of if there wasn't an extra relocation target).
*/
public Address getTargetAddressExtra() throws NotFoundException {
public Address getTargetAddressExtra() throws RelocationException {
if (targetSymbolExtra != null) {
return targetSymbolExtra.getAddress();
}
@@ -176,7 +176,7 @@ public class MachoRelocation {
if (targetPointerExtra != null) {
return targetPointerExtra;
}
throw new NotFoundException("Extra relocation target not found");
throw new RelocationException("Extra relocation target not found");
}
/**
@@ -15,13 +15,15 @@
*/
package ghidra.app.util.bin.format.macho.relocation;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.RelocationInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.NotFoundException;
/**
* An abstract class used to perform Mach-O relocations. Classes should extend this class to
@@ -51,13 +53,17 @@ abstract public class MachoRelocationHandler implements ExtensionPoint {
/**
* Performs a relocation
* @param relocation The relocation to perform
* @return applied relocation result
* @throws MemoryAccessException If there is a problem accessing memory during the relocation
* @throws NotFoundException If this handler didn't find a way to perform the relocation
* @throws RelocationException if supported relocation encountered an error during processing.
* This exception should be thrown in place of returning {@link RelocationResult#FAILURE} or
* a status of {@link Status#FAILURE} which will facilitate a failure reason via
* {@link RelocationException#getMessage()}.
*/
abstract public void relocate(MachoRelocation relocation)
throws MemoryAccessException, NotFoundException;
abstract public RelocationResult relocate(MachoRelocation relocation)
throws MemoryAccessException, RelocationException;
/**
* Reads bytes at the given address. The size of the read is determined by the length of the
@@ -90,23 +96,25 @@ abstract public class MachoRelocationHandler implements ExtensionPoint {
*
* @param relocation The relocation to write
* @param value The value to write
* @return number of bytes written
* @throws MemoryAccessException If there is a problem accessing memory during the write
*/
public static void write(MachoRelocation relocation, long value) throws MemoryAccessException {
public static int write(MachoRelocation relocation, long value) throws MemoryAccessException {
Memory mem = relocation.getProgram().getMemory();
int len = relocation.getRelocationInfo().getLength();
Address addr = relocation.getRelocationAddress();
if (len == 3) {
mem.setLong(addr, value);
}
else if (len == 2) {
mem.setInt(addr, (int) value);
}
else if (len == 1) {
mem.setShort(addr, (short) value);
}
else {
mem.setByte(addr, (byte) value);
switch (relocation.getRelocationInfo().getLength()) {
case 3:
mem.setLong(addr, value);
return 8;
case 2:
mem.setInt(addr, (int) value);
return 4;
case 1:
mem.setShort(addr, (short) value);
return 2;
default:
mem.setByte(addr, (byte) value);
return 1;
}
}
}
@@ -20,6 +20,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
/**
* This class maintains the running state while
@@ -200,23 +201,21 @@ public class RelocationState {
if (block == null || !block.isInitialized()) {
return;
}
boolean success = false;
try {
int value = memory.getInt(address);
byte[] bytes = new byte[4];
memory.getBytes(address, bytes);
long[] values = new long[] { addend };
// TODO does PEF have symbol names?
String symbolName = null;
program.getRelocationTable().add(address, -1, values, bytes, symbolName);
value += addend;
memory.setInt(address, value);
success = true;
}
catch (MemoryAccessException e) {
log.appendMsg("Unable to perform change memory at " + address);
}
// TODO does PEF have symbol names?
Status status = success ? Status.APPLIED : Status.FAILURE;
program.getRelocationTable().add(address, status, -1, new long[] { addend }, 4, null);
}
private MemoryBlock getBlockContaining(Address address) {
@@ -35,10 +35,13 @@ import ghidra.program.model.data.Undefined;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
import ghidra.util.exception.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public class CoffLoader extends AbstractLibrarySupportLoader {
@@ -666,6 +669,9 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
sectionStartAddr.add(relocation.getAddress() - section.getVirtualAddress());
short relocationType = relocation.getType();
Status status = Status.FAILURE;
int byteLength = 0;
if (handler == null) {
++failureCount;
handleRelocationError(program, address, relocationType,
@@ -677,6 +683,7 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
// skip relocation if previous failed relocation was at the same address
// since it is likely dependent on the previous failed relocation result
++failureCount;
status = Status.SKIPPED;
String logMessage =
String.format("Skipped dependent COFF Relocation type 0x%x at %s",
@@ -684,20 +691,24 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
Msg.error(this, program.getName() + ": " + logMessage);
}
else {
handler.relocate(address, relocation, relocationContext);
RelocationResult result =
handler.relocate(address, relocation, relocationContext);
status = result.status();
byteLength = result.byteLength();
if (status == Status.UNSUPPORTED) {
++failureCount;
failedAddr = address;
handleRelocationError(program, address, relocationType,
"Unsupported COFF relocation type", null);
}
}
}
catch (MemoryAccessException e) {
++failureCount;
failedAddr = address;
handleRelocationError(program, address, relocationType,
"Error accessing memory", null);
}
catch (NotFoundException e) {
++failureCount;
failedAddr = address;
handleRelocationError(program, address, relocationType,
"Unsupported COFF relocation type", null);
"error accessing memory", null);
}
catch (RelocationException e) {
++failureCount;
@@ -705,7 +716,7 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
handleRelocationError(program, address, relocationType, e.getMessage(),
null);
}
catch (Exception e) {
catch (Exception e) { // handle unexpected exceptions
++failureCount;
failedAddr = address;
String msg = e.getMessage();
@@ -721,13 +732,9 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
Symbol symbol =
symbolsMap.get(header.getSymbolAtIndex(relocation.getSymbolIndex()));
// TODO: There may be multiple relocations at the same address.
// The RelocationTable for retaining relocations needs to be revised to handle
// this. At present only the last one will remain in the DB-backed address-based
// table. (see GP-2128)
program.getRelocationTable()
.add(address, relocation.getType(),
new long[] { relocation.getSymbolIndex() }, null,
.add(address, status, relocation.getType(),
new long[] { relocation.getSymbolIndex() }, byteLength,
symbol != null ? symbol.getName() : "<null>");
}
}
@@ -46,8 +46,8 @@ import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.reloc.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap;
@@ -992,8 +992,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
reloc.setType(relrRelocationType);
}
Status status = Status.SKIPPED;
int byteLength = 0;
try {
if (unableToApplyRelocs) {
status = Status.FAILURE;
ElfRelocationHandler.markAsError(program, relocAddr, type, symbolName,
"missing symbol table", log);
continue;
@@ -1008,6 +1011,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
memory.convertToInitialized(relocBlock, (byte) 0);
}
catch (Exception e) {
status = Status.FAILURE;
Msg.error(this,
"Unexpected exception while converting block to initialized", e);
ElfRelocationHandler.markAsUninitializedMemory(program, relocAddr, type,
@@ -1018,15 +1022,19 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
if (context != null) {
if (relrTypeUnknown) {
status = Status.UNSUPPORTED;
ElfRelocationHandler.markAsUnsupportedRelr(program, relocAddr);
}
else {
context.processRelocation(reloc, relocAddr);
RelocationResult result = context.processRelocation(reloc, relocAddr);
byteLength = result.byteLength();
status = result.status();
}
}
}
catch (MemoryAccessException e) {
if (type != 0) { // ignore if type 0 which is always NONE (no relocation performed)
status = Status.FAILURE;
log("Unable to perform relocation: Type = " + type + " (0x" +
Long.toHexString(type) + ") at " + relocAddr + " (Symbol = " + symbolName +
") - " + getMessage(e));
@@ -1035,7 +1043,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
finally {
// Save relocation data - uses original FileBytes
program.getRelocationTable()
.add(relocAddr, reloc.getType(), values, null, symbolName);
.add(relocAddr, status, reloc.getType(), values, byteLength, symbolName);
}
}
@@ -1063,19 +1071,22 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
@Override
public boolean addFakeRelocTableEntry(Address address, int length) {
public boolean addArtificialRelocTableEntry(Address address, int length) {
try {
Address maxAddr = address.addNoWrap(length - 1);
RelocationTable relocationTable = program.getRelocationTable();
List<Relocation> relocations = relocationTable.getRelocations(address);
if (!relocations.isEmpty()) {
return false;
Msg.warn(this, "Artificial relocation at " + address +
" conflicts with a previous relocation");
}
Address nextRelocAddr = relocationTable.getRelocationAddressAfter(address);
if (nextRelocAddr == null || nextRelocAddr.compareTo(maxAddr) > 0) {
relocationTable.add(address, 0, new long[0], null, null);
return true;
if (nextRelocAddr != null && nextRelocAddr.compareTo(maxAddr) <= 0) {
Msg.warn(this,
"Artificial relocation at " + address + " overlaps a previous relocation");
}
relocationTable.add(address, Status.APPLIED_OTHER, 0, null, length, null);
return true;
}
catch (AddressOverflowException e) {
Msg.error(this, "Failed to generate fake relocation data at " + address, e);
@@ -23,6 +23,7 @@ import java.util.*;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.commands.ExportTrie.ExportEntry;
@@ -42,6 +43,8 @@ import ghidra.program.model.lang.Processor;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
@@ -1284,38 +1287,50 @@ public class MachoProgramBuilder {
Address address = relocationMap.get(relocationInfo);
MachoRelocation relocation = null;
RelocationResult result = RelocationResult.FAILURE;
if (handler == null) {
handleRelocationError(address, String.format(
"No relocation handler for machine type 0x%x to process relocation at %s with type 0x%x",
machoHeader.getCpuType(), address, relocationInfo.getType()));
}
else {
relocation = handler.isPairedRelocation(relocationInfo)
? new MachoRelocation(program, machoHeader, address, relocationInfo,
iter.next())
: new MachoRelocation(program, machoHeader, address, relocationInfo);
try {
relocation = handler.isPairedRelocation(relocationInfo)
? new MachoRelocation(program, machoHeader, address, relocationInfo,
iter.next())
: new MachoRelocation(program, machoHeader, address, relocationInfo);
handler.relocate(relocation);
result = handler.relocate(relocation);
if (result.status() == Status.UNSUPPORTED) {
handleRelocationError(address,
String.format("Relocation type 0x%x at address %s is not supported",
relocationInfo.getType(), address));
}
}
catch (MemoryAccessException e) {
handleRelocationError(address, String.format(
"Error accessing memory at address %s. Relocation failed.", address));
"Relocation failure at address %s: error accessing memory.", address));
}
catch (NotFoundException e) {
handleRelocationError(address,
String.format("Relocation type 0x%x at address %s is not supported: %s",
relocationInfo.getType(), address, e.getMessage()));
}
catch (AddressOutOfBoundsException e) {
handleRelocationError(address,
String.format("Error computing relocation at address %s.", address));
catch (RelocationException e) {
handleRelocationError(address, String.format(
"Relocation failure at address %s: %s", address, e.getMessage()));
}
catch (Exception e) { // handle unexpected exceptions
String msg = e.getMessage();
if (msg == null) {
msg = e.toString();
}
msg = String.format("Relocation failure at address %s: %s", address, msg);
handleRelocationError(address, msg);
Msg.error(this, msg, e);
}
}
program.getRelocationTable()
.add(address, relocationInfo.getType(), new long[] { relocationInfo.getValue(),
.add(address, result.status(), relocationInfo.getType(),
new long[] { relocationInfo.getValue(),
relocationInfo.getLength(), relocationInfo.isPcRelocated() ? 1 : 0,
relocationInfo.isExternal() ? 1 : 0, relocationInfo.isScattered() ? 1 : 0 },
null, relocation.getTargetDescription());
result.byteLength(), relocation.getTargetDescription());
}
}
@@ -1325,7 +1340,7 @@ public class MachoProgramBuilder {
* @param address The address of the relocation error
* @param message The error message
*/
private void handleRelocationError(Address address, String message) {
private void handleRelocationError(Address address, String message) {
program.getBookmarkManager()
.setBookmark(address, BookmarkType.ERROR, "Relocations", message);
log.appendMsg(message);
@@ -1783,10 +1798,21 @@ public class MachoProgramBuilder {
}
if (!start || !program.getRelocationTable().hasRelocation(chainLoc)) {
addRelocationTableEntry(chainLoc,
(start ? 0x8000 : 0x4000) | (isAuthenticated ? 4 : 0) | (isBound ? 2 : 0) | 1,
newChainValue, symName);
DyldChainedPtr.setChainValue(memory, chainLoc, pointerFormat, newChainValue);
int byteLength = 0;
Status status = Status.FAILURE;
try {
RelocationResult result =
DyldChainedPtr.setChainValue(memory, chainLoc, pointerFormat,
newChainValue);
status = result.status();
byteLength = result.byteLength();
}
finally {
addRelocationTableEntry(chainLoc, status,
(start ? 0x8000 : 0x4000) | (isAuthenticated ? 4 : 0) | (isBound ? 2 : 0) |
1,
newChainValue, byteLength, symName);
}
}
// delay creating data until after memory has been changed
unchainedLocList.add(chainLoc);
@@ -1797,11 +1823,12 @@ public class MachoProgramBuilder {
}
}
private void addRelocationTableEntry(Address chainLoc, int type, long chainValue, String name) {
private void addRelocationTableEntry(Address chainLoc, Status status, int type, long chainValue,
int byteLength, String name) {
if (shouldAddChainedFixupsRelocations) {
// Add entry to relocation table for the pointer fixup
program.getRelocationTable()
.add(chainLoc, type, new long[] { chainValue }, null, name);
.add(chainLoc, status, type, new long[] { chainValue }, byteLength, name);
}
}
@@ -1871,12 +1898,20 @@ public class MachoProgramBuilder {
// Add entry to relocation table for the pointer fixup
byte origBytes[] = new byte[8];
memory.getBytes(pointerAddr, origBytes);
program.getRelocationTable()
.add(pointerAddr, (int) fixedPointerType, new long[] { fixedPointerValue },
origBytes, null);
// Fixup the pointer
memory.setLong(pointerAddr, fixedPointerValue);
boolean success = false;
try {
// Fixup the pointer
memory.setLong(pointerAddr, fixedPointerValue);
success = true;
}
finally {
Status status = success ? Status.APPLIED : Status.FAILURE;
program.getRelocationTable()
.add(pointerAddr, status, (int) fixedPointerType,
new long[] { fixedPointerValue },
origBytes, null);
}
}
/**
@@ -32,6 +32,7 @@ import ghidra.program.model.data.DataUtilities;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.*;
import ghidra.util.DataConverter;
import ghidra.util.LittleEndianDataConverter;
@@ -302,20 +303,20 @@ public class MzLoader extends AbstractLibrarySupportLoader {
for (RelocationFixup relocationFixup : relocationFixups) {
SegmentedAddress relocationAddress = relocationFixup.address();
Status status = Status.FAILURE;
try {
byte[] origBytes = new byte[2];
memory.getBytes(relocationAddress, origBytes);
memory.setShort(relocationAddress, (short) relocationFixup.segment());
// Add to relocation table
program.getRelocationTable()
.add(relocationAddress, 0, new long[] { relocationAddress.getSegment(),
relocationAddress.getSegmentOffset() }, origBytes, null);
status = Status.APPLIED;
}
catch (AddressOutOfBoundsException | MemoryAccessException e) {
catch (MemoryAccessException e) {
log.appendMsg(String.format("Failed to apply relocation: %s (%s)",
relocationAddress, e.getMessage()));
}
// Add to relocation table
program.getRelocationTable()
.add(relocationAddress, status, 0, new long[] { relocationAddress.getSegment(),
relocationAddress.getSegmentOffset() }, 2, null);
}
}
@@ -35,6 +35,7 @@ import ghidra.program.model.data.StringDataType;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
@@ -655,15 +656,15 @@ public class NeLoader extends AbstractOrdinalSupportLoader {
}
int relocType = reloc.getType();
int byteLength = SegmentRelocation.TYPE_LENGTHS[relocType];
do {
// TODO: it appears that offset may be a far-offset and not always a segment-offset
SegmentedAddress address = space.getAddress(segment, offset);
try {
byte[] bytes = new byte[SegmentRelocation.TYPE_LENGTHS[relocType]];
memory.getBytes(address, bytes);
relocTable.add(address, relocType, reloc.getValues(), bytes, null);
offset = relocate(memory, reloc, address, relocAddr);
relocTable.add(address, Status.APPLIED, relocType, reloc.getValues(),
byteLength, null);
}
catch (MemoryAccessException e) {
log.appendMsg("Relocation does not exist in memory: " + relocAddr);
@@ -32,6 +32,7 @@ import ghidra.program.model.data.Undefined;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.DataConverter;
@@ -268,8 +269,9 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
}
long[] values = new long[1];
values[0] = finalvalue;
program.getRelocationTable().add(state.locAddress, state.locationType, values,
origbytes, null);
program.getRelocationTable()
.add(state.locAddress, Status.APPLIED,
state.locationType, values, origbytes, null);
}
}
}
@@ -37,6 +37,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap;
@@ -305,7 +306,8 @@ public class PeLoader extends AbstractPeDebugLoader {
int baseAddr = reloc.getVirtualAddress();
for (int i = 0; i < reloc.getCount(); ++i) {
long addr = optionalHeader.getImageBase() + baseAddr + reloc.getOffset(i);
relocTable.add(space.getAddress(addr), reloc.getType(i), null, null, null);
relocTable.add(space.getAddress(addr), Status.SKIPPED, reloc.getType(i), null, null,
null);
}
}
}
@@ -19,11 +19,13 @@ import java.util.Iterator;
import java.util.StringTokenizer;
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXParseException;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.util.XmlProgramUtilities;
import ghidra.util.exception.CancelledException;
@@ -42,15 +44,14 @@ class RelocationTableXmlMgr {
this.log = log;
}
void read(XmlPullParser parser, TaskMonitor monitor) throws CancelledException {
void read(XmlPullParser parser, TaskMonitor monitor)
throws SAXParseException, CancelledException {
RelocationTable relocTable = program.getRelocationTable();
AddressFactory factory = program.getAddressFactory();
XmlElement element = parser.next();
while (true) {
if (monitor.isCancelled()) {
throw new CancelledException();
}
monitor.checkCanceled();
element = parser.next();
if (!element.getName().equals("RELOCATION")) {
break;
@@ -70,7 +71,29 @@ class RelocationTableXmlMgr {
byte[] bytes = unpackBytes(element.getAttribute("BYTES")); // optional
String symbolName = element.getAttribute("SYMBOL_NAME"); // optional
relocTable.add(addr, type, values, bytes, symbolName);
String statusAttr = element.getAttribute("STATUS");
Status status = Status.UNKNOWN;
if (statusAttr != null) {
try {
status = Status.valueOf(statusAttr.toUpperCase());
}
catch (IllegalArgumentException e) {
throw new SAXParseException("Invalid relocation status: " + statusAttr,
null, null, parser.getLineNumber(), parser.getColumnNumber());
}
}
if (bytes == null) {
if (status != null && status.hasBytes()) {
log.appendMsg("Relocation at " + addrStr +
" missing required bytes - forced UNKNOWN status.");
status = Status.UNKNOWN;
}
}
else if (status == null) {
status = type == 0 ? Status.APPLIED_OTHER : Status.APPLIED;
}
relocTable.add(addr, status, type, values, bytes, symbolName);
}
catch (Exception e) {
log.appendException(e);
@@ -83,6 +106,9 @@ class RelocationTableXmlMgr {
}
private long[] unpackLongs(String attrValue) {
if (attrValue == null) {
return null;
}
StringTokenizer st = new StringTokenizer(attrValue, ",");
long[] values = new long[st.countTokens()];
int index = 0;
@@ -1704,9 +1704,10 @@ public class FlatProgramAPI {
* @param address the address at which to create a new Data object.
* @param datatype the Data Type that describes the type of Data object to create.
* @return the newly created Data object
* @throws Exception if there is any exception
* @throws CodeUnitInsertionException if a conflicting code unit already exists
*/
public final Data createData(Address address, DataType datatype) throws Exception {
public final Data createData(Address address, DataType datatype)
throws CodeUnitInsertionException {
Listing listing = currentProgram.getListing();
Data d = listing.getDefinedDataAt(address);
if (d != null) {
@@ -160,8 +160,12 @@
<!ELEMENT RELOCATION EMPTY>
<!ATTLIST RELOCATION ADDRESS CDATA #REQUIRED>
<!ATTLIST RELOCATION STATUS ( UNKNOWN | SKIPPED | UNSUPPORTED | FAILURE | PARTIAL | APPLIED | APPLIED_OTHER ) #IMPLIED>
<!-- STATUS defaults to APPLIED if BYTES specified and TYPE is non-zero -->
<!-- STATUS defaults to APPLIED_OTHER if BYTES specified and TYPE is zero -->
<!-- STATUS defaults to UNKNOWN if BYTES are not specified -->
<!ATTLIST RELOCATION TYPE CDATA #REQUIRED>
<!ATTLIST RELOCATION VALUE CDATA #REQUIRED>
<!ATTLIST RELOCATION VALUE CDATA #IMPLIED>
<!ATTLIST RELOCATION BYTES CDATA #IMPLIED>
<!ATTLIST RELOCATION SYMBOL_NAME CDATA #IMPLIED>
@@ -974,42 +974,27 @@ public class DexHeaderFormatMarkup {
}
String string = stringDataItem.getString();
DataType stringDataType = stringDataItem.toDataType();
api.createData(stringDataAddress, stringDataType);
api.setPlateComment(stringDataAddress,
Integer.toHexString(index) + "\n" + string.trim());
fragmentManager.stringDataAddressSet.add(stringDataAddress,
stringDataAddress.add(stringDataType.getLength() - 1));
try {
DataType stringDataType = stringDataItem.toDataType();
api.createData(stringDataAddress, stringDataType);
api.setPlateComment(stringDataAddress,
Integer.toHexString(index) + "\n" + string.trim());
fragmentManager.stringDataAddressSet.add(stringDataAddress,
stringDataAddress.add(stringDataType.getLength() - 1));
createStringSymbol(stringDataAddress, string, "strings");
}
catch (DuplicateNameException e) {
log.appendException(e); // Report the exception but keep going
}
catch (InvalidInputException e) {
log.appendException(e);
}
createStringSymbol(stringDataAddress, string, "strings");
// markup string Id items
DataType dataType = item.toDataType();
try {
Data data = api.createData(address, dataType);
fragmentManager.stringsDataSet.add(address, address.add(dataType.getLength() - 1));
api.setPlateComment(address, "String Index: 0x" + Integer.toHexString(index) +
"\nString: " + string.trim() + "\nString Data Address: " + stringDataAddress);
createStringSymbol(address, string, "string_data");
Data data = api.createData(address, dataType);
fragmentManager.stringsDataSet.add(address, address.add(dataType.getLength() - 1));
api.setPlateComment(address, "String Index: 0x" + Integer.toHexString(index) +
"\nString: " + string.trim() + "\nString Data Address: " + stringDataAddress);
createStringSymbol(address, string, "string_data");
api.createMemoryReference(data, stringDataAddress, RefType.DATA);
api.createMemoryReference(data, stringDataAddress, RefType.DATA);
}
catch (DuplicateNameException e) {
log.appendException(e); // Report the exception but keep going
}
catch (InvalidInputException e) {
log.appendException(e);
}
++index;
address = address.add(dataType.getLength());
@@ -22,11 +22,8 @@ import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.DuplicateNameException;
/**
@@ -73,11 +70,17 @@ public class StringDataItem implements StructConverter {
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
public DataType toDataType() {
Structure structure = new StructureDataType("string_data_item_" + actualLength, 0);
structure.add(new ArrayDataType(BYTE, lebLength, BYTE.getLength()), "utf16_size", null);
structure.add(UTF8, actualLength, "data", null);
structure.setCategoryPath(new CategoryPath("/dex/string_data_item"));
try {
structure.setCategoryPath(new CategoryPath("/dex/string_data_item"));
}
catch (DuplicateNameException e) {
// will not occur for new StructureDataType
throw new AssertException(e);
}
return structure;
}
@@ -107,9 +107,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* TypeDefSettingsDefinition. Added DataTypeManager versioning
* support. Added support for auto-named TypeDef datatypes.
* Transitioned string data translation storage to use address-based
* property map (StringTranslations).
* property map (StringTranslations).
* 19-Jan-2023 - version 26 Improved relocation data records to incorporate status and
* byte-length when original FileBytes should be used.
*/
static final int DB_VERSION = 25;
static final int DB_VERSION = 26;
/**
* UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the
@@ -129,6 +131,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public static final int EXTERNAL_FUNCTIONS_ADDED_VERSION = 17;
public static final int COMPOUND_VARIABLE_STORAGE_ADDED_VERSION = 18;
public static final int AUTO_PARAMETERS_ADDED_VERSION = 19;
public static final int RELOCATION_STATUS_ADDED_VERSION = 26;
private static final String DATA_MAP_TABLE_NAME = "Program";
@@ -16,12 +16,19 @@
package ghidra.program.database.reloc;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import db.*;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.util.exception.VersionException;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
abstract class RelocationDBAdapter {
@@ -32,30 +39,46 @@ abstract class RelocationDBAdapter {
// V3 - added Bytes
// V4 - added Name, switched Value to binary coded long[] from long
// V5 - moved Addr key to column and indexed, use one-up key
// V6 - added FLAGS columns (status and optional length)
final static int ADDR_COL = 0; // indexed
final static int TYPE_COL = 1;
final static int VALUE_COL = 2;
final static int BYTES_COL = 3;
final static int SYMBOL_NAME_COL = 4;
final static int FLAGS_COL = 1; // includes status enum and optional length when bytes==null
final static int TYPE_COL = 2;
final static int VALUE_COL = 3; // binary coded long[]
final static int BYTES_COL = 4; // null defers to FileBytes (see length)
final static int SYMBOL_NAME_COL = 5;
/**
* FLAGS bit encoding:
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* | | length | status |
* status: Relocation.Status enum index
* length: Optional byte-length used when bytes is null (0..31)
*/
final static int STATUS_FLAGS_MASK = 0x7;
final static int LENGTH_FLAGS_MASK = 0xF;
final static int LENGTH_FLAGS_SHIFT = 3;
final static int LENGTH_MAX = 31;
final static String TABLE_NAME = "Relocations";
final static Schema SCHEMA = new Schema(
RelocationDBAdapterV5.VERSION, "Index", new Field[] { LongField.INSTANCE, IntField.INSTANCE,
RelocationDBAdapterV6.VERSION, "Index",
new Field[] { LongField.INSTANCE, ByteField.INSTANCE, IntField.INSTANCE,
BinaryField.INSTANCE, BinaryField.INSTANCE, StringField.INSTANCE },
new String[] { "Address", "Type", "Values", "Bytes", "Symbol Name" });
new String[] { "Address", "Status", "Type", "Values", "Bytes", "Symbol Name" });
static RelocationDBAdapter getAdapter(DBHandle dbHandle, int openMode, AddressMap addrMap,
TaskMonitor monitor) throws VersionException, IOException {
try {
return new RelocationDBAdapterV5(dbHandle, addrMap, openMode == DBConstants.CREATE);
return new RelocationDBAdapterV6(dbHandle, addrMap, openMode == DBConstants.CREATE);
}
catch (VersionException e) {
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
throw e;
}
RelocationDBAdapter adapter = findReadOnlyAdapter(dbHandle, addrMap);
RelocationDBAdapter adapter =
findReadOnlyAdapter(dbHandle, addrMap);
if (openMode == DBConstants.UPGRADE) {
adapter = upgrade(dbHandle, addrMap, adapter, monitor);
}
@@ -65,6 +88,12 @@ abstract class RelocationDBAdapter {
private static RelocationDBAdapter findReadOnlyAdapter(DBHandle handle, AddressMap addrMap)
throws IOException, VersionException {
try {
return new RelocationDBAdapterV5(handle, addrMap);
}
catch (VersionException e) {
// try the next version
}
try {
return new RelocationDBAdapterV4(handle, addrMap);
}
@@ -102,7 +131,8 @@ abstract class RelocationDBAdapter {
try {
tmpHandle.startTransaction();
RelocationDBAdapter tmpAdapter = new RelocationDBAdapterV5(tmpHandle, addrMap, true);
RelocationDBAdapter tmpAdapter =
new RelocationDBAdapterV6(tmpHandle, addrMap, true);
RecordIterator iter = oldAdapter.iterator();
while (iter.hasNext()) {
DBRecord rec = iter.next();
@@ -110,19 +140,20 @@ abstract class RelocationDBAdapter {
Address addr = oldAddrMap.decodeAddress(rec.getLongValue(ADDR_COL));
BinaryCodedField values =
new BinaryCodedField((BinaryField) rec.getFieldValue(VALUE_COL));
tmpAdapter.add(addr, rec.getIntValue(TYPE_COL),
tmpAdapter.add(addr, rec.getByteValue(FLAGS_COL), rec.getIntValue(TYPE_COL),
values.getLongArray(), rec.getBinaryData(BYTES_COL),
rec.getString(SYMBOL_NAME_COL));
}
dbHandle.deleteTable(TABLE_NAME);
RelocationDBAdapterV5 newAdapter = new RelocationDBAdapterV5(dbHandle, addrMap, true);
RelocationDBAdapterV6 newAdapter =
new RelocationDBAdapterV6(dbHandle, addrMap, true);
iter = tmpAdapter.iterator();
while (iter.hasNext()) {
DBRecord rec = iter.next();
newAdapter.add(rec);
newAdapter.put(rec);
}
return newAdapter;
}
@@ -131,21 +162,61 @@ abstract class RelocationDBAdapter {
}
}
//==================================================================================================
// Adapter Required Methods
//==================================================================================================
/**
* Generate flags value for specified status and original bytes length for relocation.
* @param status relocation status
* @param byteLength byte length (specify 0 if bytes length is known, otherwise 1..31)
* @return flags value
*/
static byte getFlags(Relocation.Status status, int byteLength) {
if (byteLength < 0 || byteLength > LENGTH_MAX) {
throw new IllegalArgumentException("unsupport byte-length: " + byteLength);
}
int flags = (status.getValue() & STATUS_FLAGS_MASK);
flags |= byteLength << LENGTH_FLAGS_SHIFT;
return (byte) flags;
}
/**
* Get the status specified by the relocation flags.
* @param flags relocation flags
* @return relocation status
*/
static Status getStatus(byte flags) {
try {
return Status.getStatus(flags & STATUS_FLAGS_MASK);
}
catch (Exception e) {
return Status.UNKNOWN;
}
}
/**
* Get the byte length specified by the relocation flags. This length should only be used
* if stored bytes is null and relocation has an appropriate status.
* @param flags relocation flags
* @return byte length specified by the relocation flags (0..31)
*/
static int getByteLength(byte flags) {
return (flags >> LENGTH_FLAGS_SHIFT) & LENGTH_FLAGS_MASK;
}
//==================================================================================================
// Adapter Required Methods
//==================================================================================================
/**
* Add new relocation record
* @param addr relocation address
* @param flags encoded flags (status, length), see {@link #getFlags(Status, int)}.
* @param type relocation type
* @param values relocation value (e.g., symbol index)
* @param bytes original memory bytes
* @param symbolName symbol name
* @throws IOException if a database error occurs
*/
abstract void add(Address addr, int type, long[] values, byte[] bytes, String symbolName)
throws IOException;
abstract void add(Address addr, byte flags, int type, long[] values, byte[] bytes,
String symbolName) throws IOException;
/**
* Iterator over all records in address order.
@@ -183,9 +254,151 @@ abstract class RelocationDBAdapter {
*/
abstract DBRecord adaptRecord(DBRecord rec);
//==================================================================================================
// Inner Classes
//==================================================================================================
//==================================================================================================
// Complex Upgrade Methods
//==================================================================================================
/**
* Perform relocation data migration following an adapter upgrade from a version prior to
* V6 (see {@link ProgramDB#RELOCATION_STATUS_ADDED_VERSION}) It is assumed that records have
* already been migrated during the table upgrade with a {@link Status#UNKNOWN} status.
* @param adapter latest relocation adapter which permits record updates
* @param program program which is "ready"
* @param monitor task monitor
* @throws IOException if IO error occurs
* @throws CancelledException if task cancelled
*/
static void preV6DataMigrationUpgrade(RelocationDBAdapter adapter, Program program,
TaskMonitor monitor) throws IOException, CancelledException {
if (!(adapter instanceof RelocationDBAdapterV6 latestAdapter)) {
throw new AssertException("latest relocation adapter version expected");
}
AddressMap addressMap = program.getAddressMap();
Memory memory = program.getMemory();
AddressSetView loadedAndInitializedAddressSet = memory.getLoadedAndInitializedAddressSet();
ArrayList<DBRecord> list = new ArrayList<>();
RecordIterator recIter = latestAdapter.iterator();
while (recIter.hasNext()) {
list.add(recIter.next());
}
// Sort on address and key order
Collections.sort(list, (r1,r2) -> {
Address a1 = addressMap.decodeAddress(r1.getLongValue(ADDR_COL));
Address a2 = addressMap.decodeAddress(r2.getLongValue(ADDR_COL));
int c = a1.compareTo(a2);
if (c == 0) { // assumes positive keys only
c = Long.compare(r1.getKey(), r2.getKey());
}
return c;
});
// Update status/length of each relocation record
for (int i = 0; i < list.size(); i++) {
monitor.checkCanceled();
DBRecord rec = list.get(i);
int byteLength = 0;
Status status;
Address relocAddr = addressMap.decodeAddress(rec.getLongValue(ADDR_COL));
byte[] bytes = rec.getBinaryData(BYTES_COL);
if (!loadedAndInitializedAddressSet.contains(relocAddr)) {
status = Status.FAILURE;
}
else if (bytes != null) {
status = rec.getIntValue(TYPE_COL) == 0 ? Status.APPLIED_OTHER : Status.APPLIED;
}
else {
byteLength = computeOriginalFileBytesLength(list, relocAddr, i, program);
if (byteLength < 0) {
status = Status.PARTIAL;
byteLength = 0;
}
else if (byteLength == 0) {
status = Status.UNKNOWN;
}
else {
status = rec.getIntValue(TYPE_COL) == 0 ? Status.APPLIED_OTHER
: Status.APPLIED;
}
}
rec.setByteValue(FLAGS_COL, getFlags(status, byteLength));
latestAdapter.put(rec);
}
}
/**
* Computes a relocation byte length which will be no greater than the default length
* (see {@link RelocationManager#getDefaultOriginalByteLength(Program)} which should not
* overlap a subsequent relocation record.
* <p>
* NOTE: it is possible for a patched instruction to interfere with this logic, however
* the result should be no worse than the previously over-extended relocation byte range.
*
* @param list sorted relocation list
* @param relocAddr relocation address
* @param index relocation index within list
* @param program program containing relocations
* @return number of original bytes modified or -1 if there is a subsequent relocation at the
* same address
* @throws IOException if an IO error occurs
*/
private static int computeOriginalFileBytesLength(ArrayList<DBRecord> list, Address relocAddr,
int index, Program program) throws IOException {
AddressSpace space = relocAddr.getAddressSpace();
AddressMap addrMap = program.getAddressMap();
Memory memory = program.getMemory();
int defaultLength = RelocationManager.getDefaultOriginalByteLength(program);
int nextIndex = index + 1;
if (nextIndex < list.size()) {
DBRecord nextRec = list.get(nextIndex);
Address nextAddr = addrMap.decodeAddress(nextRec.getLongValue(ADDR_COL));
if (nextAddr.getAddressSpace().equals(relocAddr.getAddressSpace())) {
defaultLength = (int) Math.min(defaultLength, nextAddr.subtract(relocAddr));
}
}
if (defaultLength == 0) {
return 0;
}
// Limit relocation length based upon end of address space
try {
relocAddr.addNoWrap(defaultLength - 1); // test for valid length
}
catch (AddressOverflowException e) {
defaultLength = (int) space.getMaxAddress().subtract(relocAddr) + 1;
}
// Obtain original and current memory bytes at relocation address for comparison
byte[] originalBytes = RelocationManager.getOriginalBytes(memory, relocAddr, defaultLength);
byte[] currentBytes = new byte[defaultLength];
try {
defaultLength = memory.getBytes(relocAddr, currentBytes);
}
catch (MemoryAccessException e) {
throw new AssertException(e); // unexpected - already checked relocAddr
}
// Indentify modification length
while (defaultLength != 0) {
int byteIndex = defaultLength - 1;
if (originalBytes[byteIndex] != currentBytes[byteIndex]) {
break;
}
--defaultLength;
}
return defaultLength;
}
//==================================================================================================
// Inner Classes
//==================================================================================================
class RecordIteratorAdapter implements RecordIterator {
RecordIterator it;
@@ -43,7 +43,8 @@ class RelocationDBAdapterNoTable extends RelocationDBAdapter {
}
@Override
void add(Address addrKey, int type, long[] values, byte[] bytes, String symbolName) {
void add(Address addrKey, byte flags, int type, long[] values, byte[] bytes,
String symbolName) {
throw new UnsupportedOperationException();
}
@@ -22,6 +22,7 @@ import ghidra.program.database.map.AddressKeyRecordIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException;
class RelocationDBAdapterV1 extends RelocationDBAdapter {
@@ -29,6 +30,7 @@ class RelocationDBAdapterV1 extends RelocationDBAdapter {
private final static int V1_TYPE_COL = 0;
/* Do not remove the following commented out schema! It shows the version 1 relocation table schema. */
// final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV1.VERSION, "Address", new Field[] { IntField.INSTANCE },
// new String[] { "Type" });
@@ -43,8 +45,8 @@ class RelocationDBAdapterV1 extends RelocationDBAdapter {
* @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V1
*/
RelocationDBAdapterV1(DBHandle handle, AddressMap addrMap) throws IOException,
VersionException {
RelocationDBAdapterV1(DBHandle handle, AddressMap addrMap)
throws IOException, VersionException {
this.addrMap = addrMap;
relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
@@ -53,7 +55,8 @@ class RelocationDBAdapterV1 extends RelocationDBAdapter {
}
@Override
void add(Address addrKey, int type, long[] values, byte[] bytes, String symbolName) {
void add(Address addrKey, byte flags, int type, long[] values, byte[] bytes,
String symbolName) {
throw new UnsupportedOperationException();
}
@@ -88,6 +91,7 @@ class RelocationDBAdapterV1 extends RelocationDBAdapter {
}
DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getKey()); // key was encoded address
newRec.setByteValue(FLAGS_COL, getFlags(Status.UNKNOWN, 0));
newRec.setIntValue(TYPE_COL, rec.getIntValue(V1_TYPE_COL));
return newRec;
}
@@ -22,6 +22,7 @@ import ghidra.program.database.map.AddressKeyRecordIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException;
class RelocationDBAdapterV2 extends RelocationDBAdapter {
@@ -30,6 +31,7 @@ class RelocationDBAdapterV2 extends RelocationDBAdapter {
private final static int V2_TYPE_COL = 0;
private final static int V2_VALUE_COL = 1;
/* Do not remove the following commented out schema! It shows the version 2 relocation table schema. */
// final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV2.VERSION, "Address", new Field[] { IntField.INSTANCE, LongField.INSTANCE },
// new String[] { "Type", "Values" });
@@ -44,8 +46,8 @@ class RelocationDBAdapterV2 extends RelocationDBAdapter {
* @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V2
*/
RelocationDBAdapterV2(DBHandle handle, AddressMap addrMap) throws IOException,
VersionException {
RelocationDBAdapterV2(DBHandle handle, AddressMap addrMap)
throws IOException, VersionException {
this.addrMap = addrMap;
relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
@@ -54,7 +56,8 @@ class RelocationDBAdapterV2 extends RelocationDBAdapter {
}
@Override
void add(Address addrKey, int type, long[] values, byte[] bytes, String symbolName) {
void add(Address addrKey, byte flags, int type, long[] values, byte[] bytes,
String symbolName) {
throw new UnsupportedOperationException();
}
@@ -89,6 +92,7 @@ class RelocationDBAdapterV2 extends RelocationDBAdapter {
}
DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getKey()); // key was encoded address
newRec.setByteValue(FLAGS_COL, getFlags(Status.UNKNOWN, 0));
newRec.setIntValue(TYPE_COL, rec.getIntValue(V2_TYPE_COL));
long[] values = new long[] { rec.getLongValue(V2_VALUE_COL) };
newRec.setField(VALUE_COL, new BinaryCodedField(values));
@@ -22,6 +22,7 @@ import ghidra.program.database.map.AddressKeyRecordIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException;
class RelocationDBAdapterV3 extends RelocationDBAdapter {
@@ -32,6 +33,7 @@ class RelocationDBAdapterV3 extends RelocationDBAdapter {
private final static int V3_VALUE_COL = 1;
private final static int V3_BYTES_COL = 2;
/* Do not remove the following commented out schema! It shows the version 3 relocation table schema. */
// final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV3.VERSION, "Address", new Field[] { IntField.INSTANCE,
// BinaryField.INSTANCE, BinaryField.INSTANCE },
@@ -47,8 +49,8 @@ class RelocationDBAdapterV3 extends RelocationDBAdapter {
* @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V3
*/
RelocationDBAdapterV3(DBHandle handle, AddressMap addrMap) throws IOException,
VersionException {
RelocationDBAdapterV3(DBHandle handle, AddressMap addrMap)
throws IOException, VersionException {
this.addrMap = addrMap;
relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
@@ -57,7 +59,7 @@ class RelocationDBAdapterV3 extends RelocationDBAdapter {
}
@Override
void add(Address addrKey, int type, long[] values, byte[] bytes, String symbolName)
void add(Address addrKey, byte flags, int type, long[] values, byte[] bytes, String symbolName)
throws IOException {
throw new UnsupportedOperationException();
}
@@ -93,6 +95,7 @@ class RelocationDBAdapterV3 extends RelocationDBAdapter {
}
DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getKey()); // key was encoded address
newRec.setByteValue(FLAGS_COL, getFlags(Status.UNKNOWN, 0));
newRec.setIntValue(TYPE_COL, rec.getIntValue(V3_TYPE_COL));
newRec.setBinaryData(VALUE_COL, rec.getBinaryData(V3_VALUE_COL));
newRec.setBinaryData(BYTES_COL, rec.getBinaryData(V3_BYTES_COL));
@@ -22,6 +22,7 @@ import ghidra.program.database.map.AddressKeyRecordIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException;
public class RelocationDBAdapterV4 extends RelocationDBAdapter {
@@ -32,6 +33,7 @@ public class RelocationDBAdapterV4 extends RelocationDBAdapter {
private final static int V4_BYTES_COL = 2;
private final static int V4_SYMBOL_NAME_COL = 3;
/* Do not remove the following commented out schema! It shows the version 4 relocation table schema. */
// final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV4.VERSION, "Address", new Field[] { IntField.INSTANCE,
// BinaryField.INSTANCE, BinaryField.INSTANCE, StringField.INSTANCE },
@@ -47,8 +49,8 @@ public class RelocationDBAdapterV4 extends RelocationDBAdapter {
* @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V4
*/
RelocationDBAdapterV4(DBHandle handle, AddressMap addrMap) throws IOException,
VersionException {
RelocationDBAdapterV4(DBHandle handle, AddressMap addrMap)
throws IOException, VersionException {
this.addrMap = addrMap;
relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
@@ -57,7 +59,7 @@ public class RelocationDBAdapterV4 extends RelocationDBAdapter {
}
@Override
void add(Address addr, int type, long[] values, byte[] bytes, String symbolName)
void add(Address addr, byte flags, int type, long[] values, byte[] bytes, String symbolName)
throws IOException {
throw new UnsupportedOperationException();
}
@@ -93,6 +95,7 @@ public class RelocationDBAdapterV4 extends RelocationDBAdapter {
}
DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getKey()); // key was encoded address
newRec.setByteValue(FLAGS_COL, getFlags(Status.UNKNOWN, 0));
newRec.setIntValue(TYPE_COL, rec.getIntValue(V4_TYPE_COL));
newRec.setBinaryData(VALUE_COL, rec.getBinaryData(V4_VALUE_COL)); // binary coded long[]
newRec.setBinaryData(BYTES_COL, rec.getBinaryData(V4_BYTES_COL));
@@ -22,42 +22,47 @@ import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException;
public class RelocationDBAdapterV5 extends RelocationDBAdapter {
final static int VERSION = 5;
final static int V5_ADDR_COL = 0; // indexed
final static int V5_TYPE_COL = 1;
final static int V5_VALUE_COL = 2;
final static int V5_BYTES_COL = 3;
final static int V5_SYMBOL_NAME_COL = 4;
/* Do not remove the following commented out schema! It shows the version 5 relocation table schema. */
// final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV5.VERSION, "Index", new Field[] { LongField.INSTANCE, IntField.INSTANCE,
// BinaryField.INSTANCE, BinaryField.INSTANCE, StringField.INSTANCE },
// new String[] { "Address", "Type", "Values", "Bytes", "Symbol Name" });
private Table relocTable;
private AddressMap addrMap;
RelocationDBAdapterV5(DBHandle handle, AddressMap addrMap, boolean create) throws IOException,
VersionException {
/**
* Construct V5 read-only adapter
* @param handle database adapter
* @param addrMap address map for decode
* @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V5
*/
RelocationDBAdapterV5(DBHandle handle, AddressMap addrMap)
throws IOException, VersionException {
this.addrMap = addrMap;
if (create) {
relocTable = handle.createTable(TABLE_NAME, SCHEMA, new int[] { ADDR_COL });
}
else {
relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null) {
throw new VersionException(true);
}
int version = relocTable.getSchema().getVersion();
if (version != VERSION) {
throw new VersionException(version < VERSION);
}
relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
throw new VersionException();
}
}
@Override
void add(Address addr, int type, long[] values, byte[] bytes, String symbolName)
void add(Address addr, byte flags, int type, long[] values, byte[] bytes, String symbolName)
throws IOException {
long key = relocTable.getKey();
DBRecord r = SCHEMA.createRecord(key);
r.setLongValue(ADDR_COL, addrMap.getKey(addr, true));
r.setIntValue(TYPE_COL, type);
r.setField(VALUE_COL, new BinaryCodedField(values));
r.setBinaryData(BYTES_COL, bytes);
r.setString(SYMBOL_NAME_COL, symbolName);
relocTable.putRecord(r);
throw new UnsupportedOperationException();
}
@Override
@@ -67,35 +72,41 @@ public class RelocationDBAdapterV5 extends RelocationDBAdapter {
@Override
RecordIterator iterator() throws IOException {
return new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
RecordIterator recIter =
new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
ADDR_COL, addrMap, true));
return new RecordIteratorAdapter(recIter);
}
@Override
RecordIterator iterator(AddressSetView set) throws IOException {
return new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
RecordIterator recIter =
new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
ADDR_COL, addrMap, set, true));
return new RecordIteratorAdapter(recIter);
}
@Override
RecordIterator iterator(Address start) throws IOException {
return new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
RecordIterator recIter =
new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
ADDR_COL, addrMap, start, true));
return new RecordIteratorAdapter(recIter);
}
@Override
DBRecord adaptRecord(DBRecord rec) {
// my guess is that we don't need to do this until there is a version newer than us
throw new UnsupportedOperationException("Don't know how to adapt to the new version");
}
/**
* Add V5 relocation record to table.
* @param rec relocation record
* @throws IOException if database IO error occurs
*/
void add(DBRecord rec) throws IOException {
relocTable.putRecord(rec);
if (rec == null) {
return null;
}
DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getLongValue(V5_ADDR_COL));
newRec.setByteValue(FLAGS_COL, getFlags(Status.UNKNOWN, 0));
newRec.setIntValue(TYPE_COL, rec.getIntValue(V5_TYPE_COL));
newRec.setBinaryData(VALUE_COL, rec.getBinaryData(V5_VALUE_COL)); // binary coded long[]
newRec.setBinaryData(BYTES_COL, rec.getBinaryData(V5_BYTES_COL));
newRec.setString(SYMBOL_NAME_COL, rec.getString(V5_SYMBOL_NAME_COL));
return newRec;
}
}
@@ -0,0 +1,131 @@
/* ###
* 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.program.database.reloc;
import java.io.IOException;
import db.*;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException;
/**
* Relocation Adapter (v6) introduced a stored status and length value. The byte-length value
* is only stored/used when stored bytes are not used and the original bytes are obtained from
* the underlying {@link FileBytes} via associated {@link Memory}. Older program's may
* have a stored bytes array but is unneccessary when original FileBytes are available.
* <br>
* During the transition of older relocation records we are unable to determine a proper status
* without comparing current memory to the original bytes. It may also be neccessary to reconcile
* overlapping relocations when the stored bytes value is null to obtain a valid length. This
* transition is too complicated for a low-level record translation so it must be deferred to
* a higher-level program upgrade (see {@link ProgramDB}). This also holds true for establishing
* a reasonable status for existing relocation records. During the initial record migration a
* status of {@link Status#UNKNOWN} and default length will be used. After the program is
* ready another high-level upgrade, based on Program version, will then attempt to refine these
* records further.
*/
public class RelocationDBAdapterV6 extends RelocationDBAdapter {
final static int VERSION = 6;
private Table relocTable;
private AddressMap addrMap;
/**
* Construct V6 relocation adapter
* @param handle database adapter
* @param addrMap address map for decode
* @param create true if new table should be created, else open existing table
* @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V6
*/
RelocationDBAdapterV6(DBHandle handle, AddressMap addrMap, boolean create) throws IOException,
VersionException {
this.addrMap = addrMap;
if (create) {
relocTable = handle.createTable(TABLE_NAME, SCHEMA, new int[] { ADDR_COL });
}
else {
relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null) {
throw new VersionException(true);
}
int version = relocTable.getSchema().getVersion();
if (version != VERSION) {
throw new VersionException(version < VERSION);
}
}
}
@Override
void add(Address addr, byte flags, int type, long[] values, byte[] bytes, String symbolName)
throws IOException {
long key = relocTable.getKey();
DBRecord r = SCHEMA.createRecord(key);
r.setLongValue(ADDR_COL, addrMap.getKey(addr, true));
r.setByteValue(FLAGS_COL, flags);
r.setIntValue(TYPE_COL, type);
r.setField(VALUE_COL, new BinaryCodedField(values));
r.setBinaryData(BYTES_COL, bytes);
r.setString(SYMBOL_NAME_COL, symbolName);
relocTable.putRecord(r);
}
@Override
int getRecordCount() {
return relocTable.getRecordCount();
}
@Override
RecordIterator iterator() throws IOException {
return new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
ADDR_COL, addrMap, true));
}
@Override
RecordIterator iterator(AddressSetView set) throws IOException {
return new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
ADDR_COL, addrMap, set, true));
}
@Override
RecordIterator iterator(Address start) throws IOException {
return new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
ADDR_COL, addrMap, start, true));
}
@Override
DBRecord adaptRecord(DBRecord rec) {
// my guess is that we don't need to do this until there is a version newer than us
throw new UnsupportedOperationException("Don't know how to adapt to the new version");
}
/**
* Add or update relocation table record.
* @param rec relocation record
* @throws IOException if database IO error occurs
*/
void put(DBRecord rec) throws IOException {
relocTable.putRecord(rec);
}
}
@@ -24,12 +24,12 @@ import ghidra.program.database.ManagerDB;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.map.AddressMap;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.util.Lock;
import ghidra.util.exception.CancelledException;
@@ -84,39 +84,91 @@ public class RelocationManager implements RelocationTable, ManagerDB {
@Override
public void programReady(int openMode, int currentRevision, TaskMonitor monitor)
throws IOException, CancelledException {
// Nothing to do
if (currentRevision < ProgramDB.RELOCATION_STATUS_ADDED_VERSION) {
RelocationDBAdapter.preV6DataMigrationUpgrade(adapter, program, monitor);
}
}
private byte[] getOriginalBytes(Address addr, byte[] bytes) throws IOException {
if (bytes != null) {
return bytes;
}
int byteCount = program.getDefaultPointerSize() > 4 ? 8 : 4;
/**
* Get default byte length when unknown
* @param program program containing relocation
* @return default byte length
*/
static int getDefaultOriginalByteLength(Program program) {
return program.getDefaultPointerSize() > 4 ? 8 : 4;
}
/**
* Get the specified number of original file bytes for the specified address. Any offsets
* not backed by file bytes will have a 0-byte value.
* @param memory program memory
* @param addr memory address
* @param byteCount number of original file bytes to read
* @return byte array of length byteCount
* @throws IOException if an IO error occurs
*/
static byte[] getOriginalBytes(Memory memory, Address addr, int byteCount) throws IOException {
byte[] originalBytes = new byte[byteCount];
AddressSourceInfo addressSourceInfo = program.getMemory().getAddressSourceInfo(addr);
if (addressSourceInfo == null) {
return null;
}
MemoryBlockSourceInfo memoryBlockSourceInfo = addressSourceInfo.getMemoryBlockSourceInfo();
Optional<FileBytes> optional = memoryBlockSourceInfo.getFileBytes();
if (!optional.isEmpty()) {
FileBytes fileBytes = optional.get();
long fileBytesOffset = addressSourceInfo.getFileOffset();
long offsetIntoSourceRange =
fileBytesOffset - memoryBlockSourceInfo.getFileBytesOffset();
long available = memoryBlockSourceInfo.getLength() - offsetIntoSourceRange;
int readSize = (int) Math.min(available, byteCount);
fileBytes.getOriginalBytes(fileBytesOffset, originalBytes, 0, readSize);
// must get one byte at a time due to the possibility of byte-mapped memory use
for (int i = 0; i < byteCount; i++) {
if (i != 0) {
addr = addr.next();
}
if (addr == null) {
break;
}
AddressSourceInfo addressSourceInfo = memory.getAddressSourceInfo(addr);
if (addressSourceInfo == null) {
return originalBytes;
}
originalBytes[i] = addressSourceInfo.getOriginalValue();
}
return originalBytes;
}
private byte[] getOriginalBytes(Address addr, Status status, byte[] bytes, int defaultLength)
throws IOException {
if (bytes != null || !status.hasBytes()) {
return bytes;
}
int byteCount = defaultLength;
if (defaultLength <= 0) {
byteCount = getDefaultOriginalByteLength(program);
}
return getOriginalBytes(program.getMemory(), addr, byteCount);
}
@Override
public Relocation add(Address addr, int type, long[] values, byte[] bytes, String symbolName) {
public Relocation add(Address addr, Status status, int type, long[] values, byte[] bytes,
String symbolName) {
lock.acquire();
try {
adapter.add(addr, type, values, bytes, symbolName);
return new Relocation(addr, type, values, getOriginalBytes(addr, bytes), symbolName);
byte flags = RelocationDBAdapter.getFlags(status, 0);
adapter.add(addr, flags, type, values, bytes, symbolName);
return new Relocation(addr, status, type, values,
getOriginalBytes(addr, status, bytes, 0),
symbolName);
}
catch (IOException e) {
program.dbError(e);
}
finally {
lock.release();
}
return null;
}
@Override
public Relocation add(Address addr, Status status, int type, long[] values, int byteLength,
String symbolName) {
lock.acquire();
try {
byte flags = RelocationDBAdapter.getFlags(status, byteLength);
adapter.add(addr, flags, type, values, null, symbolName);
return new Relocation(addr, status, type, values,
getOriginalBytes(addr, status, null, byteLength),
symbolName);
}
catch (IOException e) {
program.dbError(e);
@@ -178,11 +230,15 @@ public class RelocationManager implements RelocationTable, ManagerDB {
private Relocation getRelocation(DBRecord rec) throws IOException {
Address addr = addrMap.decodeAddress(rec.getLongValue(RelocationDBAdapter.ADDR_COL));
byte flags = rec.getByteValue(RelocationDBAdapter.FLAGS_COL);
Status status = RelocationDBAdapter.getStatus(flags);
int length = RelocationDBAdapter.getByteLength(flags);
BinaryCodedField valuesField =
new BinaryCodedField((BinaryField) rec.getFieldValue(RelocationDBAdapter.VALUE_COL));
byte[] originalBytes =
getOriginalBytes(addr, rec.getBinaryData(RelocationDBAdapter.BYTES_COL));
return new Relocation(addr, rec.getIntValue(RelocationDBAdapter.TYPE_COL),
getOriginalBytes(addr, status, rec.getBinaryData(RelocationDBAdapter.BYTES_COL),
length);
return new Relocation(addr, status, rec.getIntValue(RelocationDBAdapter.TYPE_COL),
valuesField.getLongArray(),
originalBytes, rec.getString(RelocationDBAdapter.SYMBOL_NAME_COL));
}
@@ -22,7 +22,96 @@ import ghidra.program.model.address.Address;
* program relocation.
*/
public class Relocation {
/**
* Relocation status.
*/
public enum Status {
// NOTE: associated values must not change since they are retained within the database
/**
* Relocation status is unknown and is assumed to have modified memory bytes.
* This status is intended for relocation data upgrades when actual status can not
* be determined.
*/
UNKNOWN(0, true),
/**
* Relocation has be intentionally skipped and should not be treated as a failure.
*/
SKIPPED(1, false),
/**
* Relocation type is not supported at the time relocations were applied.
*/
UNSUPPORTED(2, false),
/**
* A supported relocation fail to apply properly. This may be the result of an unexpected
* or unsupported condition which prevented its application.
*/
FAILURE(3, false),
/**
* Relocation was processed successfully although relies on a subsequent relocation to
* affect memory.
*/
PARTIAL(4, false),
/**
* Relocation was applied successfully and resulted in the modification of memory bytes.
*/
APPLIED(5, true),
/**
* Loaded memory has been altered during the load process and may, or may not, be directly
* associated with a standard relocation type.
*/
APPLIED_OTHER(6, true);
private int value;
private boolean hasBytes;
private Status(int value, boolean hasBytes) {
this.value = value;
this.hasBytes = hasBytes;
}
/**
* @return true if relocation reflects original bytes that may have been modified,
* else false.
*/
public boolean hasBytes() {
return hasBytes;
}
/**
* Get storage value associated
* @return storage value associated with status
*/
public int getValue() {
return value;
}
/**
* Get the Status which corresponds to the specified value.
* @param value status value
* @return status enum
*/
public static Status getStatus(int value) {
for (Status s : values()) {
if (s.value == value) {
return s;
}
}
throw new IllegalArgumentException(
"Undefined Status value: " + value);
}
}
private Address addr;
private Status status;
private int type;
private long[] values;
private byte[] bytes;
@@ -32,14 +121,17 @@ public class Relocation {
* Constructs a new relocation.
*
* @param addr the address where the relocation is required
* @param status relocation status
* @param type the type of relocation to perform
* @param values the values needed when performing the relocation. Definition of values is
* specific to loader used and relocation type.
* @param bytes original instruction bytes affected by relocation
* @param symbolName the name of the symbol being relocated
*/
public Relocation(Address addr, int type, long[] values, byte[] bytes, String symbolName) {
public Relocation(Address addr, Status status, int type, long[] values, byte[] bytes,
String symbolName) {
this.addr = addr;
this.status = status;
this.type = type;
this.values = values;
this.bytes = bytes;
@@ -55,6 +147,15 @@ public class Relocation {
return addr;
}
/**
* Return the relocation's application status within the program.
*
* @return relocation's application status within the program.
*/
public Status getStatus() {
return status;
}
/**
* Returns the type of the relocation to perform.
*
@@ -0,0 +1,57 @@
/* ###
* 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.program.model.reloc;
import ghidra.program.model.listing.Program;
import ghidra.program.model.reloc.Relocation.Status;
/**
* {@link RelocationResult} provides the status and byte-length of a processed relocation during
* the {@link Program} load process. Intended to be used internally by a relocation handler.
* A positive byte-length is only required for a status of {@link Status#APPLIED} or
* {@link Status#APPLIED_OTHER}. Use if {@link Status#UNKNOWN} should be avoided and is intended
* for relocation data upgrades when actual status can not be determined.
* <br>
* Singleton instances are provided for relocations which did not directly results in original
* loaded memory modification.
*
* @param status the relocation status
* @param byteLength the number of original bytes modified at relocation offset if successfully
* applied and memory bytes were modified.
*/
public record RelocationResult(Status status, int byteLength) {
/**
* See {@link Status#FAILURE}
*/
public static final RelocationResult FAILURE = new RelocationResult(Status.FAILURE, 0);
/**
* See {@link Status#UNSUPPORTED}
*/
public static final RelocationResult UNSUPPORTED = new RelocationResult(Status.UNSUPPORTED, 0);
/**
* See {@link Status#SKIPPED}
*/
public static final RelocationResult SKIPPED = new RelocationResult(Status.SKIPPED, 0);
/**
* See {@link Status#PARTIAL}
*/
public static final RelocationResult PARTIAL = new RelocationResult(Status.PARTIAL, 0);
}
@@ -21,6 +21,7 @@ import java.util.List;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
/**
* An interface for storing the relocations defined in a program.
@@ -32,19 +33,40 @@ public interface RelocationTable {
public static final String RELOCATABLE_PROP_NAME = "Relocatable";
/**
* Creates and adds a new relocation with the specified
* address, type, and value.
* Adds a new relocation entry when the original bytes being replaced are to be specified.
*
* @param addr the address where the relocation is required
* @param addr the memory address where the relocation is required
* @param status relocation status (use {@link Status#UNKNOWN} if not known).
* @param type the type of relocation to perform
* @param values the values needed when performing the relocation. Definition of values is
* specific to loader used and relocation type.
* @param bytes original instruction bytes affected by relocation. A null value should be
* passed to rely on original underlying {@link FileBytes}.
* @param values relocation-specific values which may be useful in diagnosing relocation;
* may be null.
* @param bytes original memory bytes affected by relocation. A null value may be
* passed but this case is deprecated (see {@link #add(Address, Status, int, long[], int, String)}.
* If null is specified and {@link Status#hasBytes()} is true a default number of original
* bytes will be assumed and obtained from the underlying memory {@link FileBytes} if possible.
* @param symbolName the name of the symbol being relocated; may be null
* @return the newly added relocation object
*/
public Relocation add(Address addr, int type, long[] values, byte[] bytes, String symbolName);
public Relocation add(Address addr, Status status, int type, long[] values, byte[] bytes,
String symbolName);
/**
* Adds a new relocation entry when the original bytes being replaced should be determined
* from the underlying {@link FileBytes}.
*
* @param addr the memory address where the relocation is required
* @param status relocation status (use {@link Status#UNKNOWN} if not known).
* @param type the type of relocation to perform
* @param values relocation-specific values which may be useful in diagnosing relocation;
* may be null.
* @param byteLength the number of bytes affected by this relocation. This value is only
* used with a status of {@link Status#UNKNOWN}, {@link Status#APPLIED} or
* {@link Status#APPLIED_OTHER}. Valid range is 1..8 bytes.
* @param symbolName the name of the symbol being relocated; may be null
* @return the newly added relocation object
*/
public Relocation add(Address addr, Status status, int type, long[] values, int byteLength,
String symbolName);
/**
* Returns the ordered list of relocations which have been defined for the specified address.
@@ -22,6 +22,8 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.NotFoundException;
public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
@@ -37,12 +39,13 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
}
@Override
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation,
public RelocationResult relocate(ElfRelocationContext elfRelocationContext,
ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException {
ElfHeader elf = elfRelocationContext.getElfHeader();
if (elf.e_machine() != ElfConstants.EM_AARCH64) {
return;
return RelocationResult.FAILURE;
}
Program program = elfRelocationContext.getProgram();
@@ -50,7 +53,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
int type = relocation.getType();
if (type == AARCH64_ElfRelocationConstants.R_AARCH64_NONE) {
return;
return RelocationResult.SKIPPED;
}
int symbolIndex = relocation.getSymbolIndex();
@@ -72,6 +75,8 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
long symbolValue = elfRelocationContext.getSymbolValue(sym);
long newValue = 0;
int byteLength = 4; // most relocations affect 4-bytes (change if different)
switch (type) {
// .xword: (S+A)
case AARCH64_ElfRelocationConstants.R_AARCH64_ABS64: {
@@ -82,6 +87,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
symbolAddr, symbolName, addend, elfRelocationContext.getLog());
applyComponentOffsetPointer(program, relocationAddress, addend);
}
byteLength = 8;
break;
}
@@ -99,6 +105,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
case AARCH64_ElfRelocationConstants.R_AARCH64_P32_ABS16: {
newValue = (symbolValue + addend);
memory.setShort(relocationAddress, (short) (newValue & 0xffff));
byteLength = 2;
break;
}
@@ -107,6 +114,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
newValue = (symbolValue + addend);
newValue -= (offset); // PC relative
memory.setLong(relocationAddress, newValue);
byteLength = 8;
break;
}
@@ -125,6 +133,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
newValue = (symbolValue + addend);
newValue -= (offset); // PC relative
memory.setShort(relocationAddress, (short) (newValue & 0xffff));
byteLength = 2;
break;
}
@@ -238,7 +247,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
addend = getValue(memory, relocationAddress, is64bit);
}
newValue = symbolValue + addend;
setValue(memory, relocationAddress, newValue, is64bit);
byteLength = setValue(memory, relocationAddress, newValue, is64bit);
break;
}
@@ -251,11 +260,13 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
// GOT entry bytes if it refers to .plt block
Address symAddress = elfRelocationContext.getSymbolAddress(sym);
MemoryBlock block = memory.getBlock(symAddress);
// TODO: jump slots are always in GOT - not sure why PLT check is done
boolean isPltSym = block != null && block.getName().startsWith(".plt");
boolean isExternalSym =
block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName());
if (!isPltSym) {
setValue(memory, relocationAddress, symAddress.getOffset(), is64bit);
byteLength =
setValue(memory, relocationAddress, symAddress.getOffset(), is64bit);
}
if ((isPltSym || isExternalSym) && !StringUtils.isBlank(symbolName)) {
Function extFunction =
@@ -265,7 +276,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
markAsError(program, relocationAddress, "R_AARCH64_JUMP_SLOT", symbolName,
"Failed to create R_AARCH64_JUMP_SLOT external function",
elfRelocationContext.getLog());
return;
// relocation already applied above
}
}
break;
@@ -278,7 +289,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
addend = getValue(memory, relocationAddress, is64bit);
}
newValue = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend;
setValue(memory, relocationAddress, newValue, is64bit);
byteLength = setValue(memory, relocationAddress, newValue, is64bit);
break;
}
@@ -286,14 +297,16 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
case AARCH64_ElfRelocationConstants.R_AARCH64_COPY: {
markAsWarning(program, relocationAddress, "R_AARCH64_COPY", symbolName, symbolIndex,
"Runtime copy not supported", elfRelocationContext.getLog());
return RelocationResult.UNSUPPORTED;
}
default: {
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
elfRelocationContext.getLog());
break;
return RelocationResult.UNSUPPORTED;
}
}
return new RelocationResult(Status.APPLIED, byteLength);
}
/**
@@ -302,15 +315,18 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
* @param addr address to set new value
* @param value value
* @param is64bit true if value is 64, false if 32bit
* return value byte-length
* @throws MemoryAccessException on set of value
*/
private void setValue(Memory memory, Address addr, long value, boolean is64bit)
private int setValue(Memory memory, Address addr, long value, boolean is64bit)
throws MemoryAccessException {
if (is64bit) {
memory.setLong(addr, value);
} else {
memory.setInt(addr, (int) value);
return 8;
}
memory.setInt(addr, (int) value);
return 4;
}
/**
@@ -17,11 +17,13 @@ package ghidra.app.util.bin.format.macho.relocation;
import static ghidra.app.util.bin.format.macho.relocation.AARCH64_MachoRelocationConstants.*;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.macho.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.util.Conv;
import ghidra.util.exception.NotFoundException;
/**
* A {@link MachoRelocationHandler} for AARCH64
@@ -42,11 +44,11 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
}
@Override
public void relocate(MachoRelocation relocation)
throws MemoryAccessException, NotFoundException {
public RelocationResult relocate(MachoRelocation relocation)
throws MemoryAccessException, RelocationException {
if (!relocation.requiresRelocation()) {
return;
return RelocationResult.SKIPPED;
}
RelocationInfo relocationInfo = relocation.getRelocationInfo();
@@ -69,21 +71,22 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
}
long orig = read(relocation);
int byteLength;
switch (relocationInfo.getType()) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_POINTER_TO_GOT: {
long addend = orig;
long value = targetAddr.getOffset() + addend;
write(relocation, value);
byteLength = write(relocation, value);
break;
}
case ARM64_RELOC_SUBTRACTOR: {
Address targetAddrExtra = relocation.getTargetAddressExtra();
if (orig > 0) {
write(relocation, targetAddrExtra.add(orig).subtract(targetAddr));
byteLength = write(relocation, targetAddrExtra.add(orig).subtract(targetAddr));
}
else {
write(relocation, targetAddr.add(orig).subtract(targetAddrExtra));
byteLength = write(relocation, targetAddr.add(orig).subtract(targetAddrExtra));
}
break;
}
@@ -91,7 +94,7 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
long addend = orig & 0x3ffffff;
long value = (targetAddr.subtract(relocAddr) >> 2) + addend;
long instr = orig | (value & 0x3ffffff);
write(relocation, instr);
byteLength = write(relocation, instr);
break;
}
case ARM64_RELOC_PAGE21:
@@ -106,7 +109,7 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
long value = ((pageTarget - pageReloc) >> 12) & 0x1fffff;
long instr =
(orig & 0x9f00001f) | ((value << 3) & 0x7ffffe0) | ((value & 0x3) << 29);
write(relocation, instr);
byteLength = write(relocation, instr);
break;
}
case ARM64_RELOC_PAGEOFF12:
@@ -126,13 +129,13 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
long value = (targetAddr.getOffset() + addend) & 0xfff;
instr = orig | (value << 10);
}
write(relocation, instr);
byteLength = write(relocation, instr);
break;
}
case ARM64_RELOC_AUTHENTICATED_POINTER: {
long addend = orig & Conv.INT_MASK;
long value = targetAddr.getOffset() + addend;
write(relocation, value);
byteLength = write(relocation, value);
break;
}
@@ -140,8 +143,9 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: // not seen yet
case ARM64_RELOC_ADDEND: // should never see on its own here
default:
throw new NotFoundException("Unimplemented relocation");
return RelocationResult.UNSUPPORTED;
}
return new RelocationResult(Status.APPLIED, byteLength);
}
/**
@@ -22,6 +22,8 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.NotFoundException;
public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
@@ -43,13 +45,13 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
}
@Override
public void relocate(ElfRelocationContext context, ElfRelocation relocation,
public RelocationResult relocate(ElfRelocationContext context, ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException {
ElfHeader elf = context.getElfHeader();
if (elf.e_machine() != ElfConstants.EM_ARM ||
!(context instanceof ARM_ElfRelocationContext)) {
return;
return RelocationResult.FAILURE;
}
ARM_ElfRelocationContext elfRelocationContext = (ARM_ElfRelocationContext) context;
@@ -62,7 +64,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
int type = relocation.getType();
if (type == ARM_ElfRelocationConstants.R_ARM_NONE) {
return;
return RelocationResult.SKIPPED;
}
int symbolIndex = relocation.getSymbolIndex();
@@ -80,6 +82,8 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
int newValue = 0;
int byteLength = 4; // most relocations affect 4-bytes (change if different)
switch (type) {
case ARM_ElfRelocationConstants.R_ARM_PC24: { // Target class: ARM Instruction
int oldValue = memory.getInt(relocationAddress, instructionBigEndian);
@@ -154,6 +158,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
case ARM_ElfRelocationConstants.R_ARM_ABS16: { // Target class: Data
short sValue = (short) (symbolValue + addend);
memory.setShort(relocationAddress, sValue);
byteLength = 2;
break;
}
case ARM_ElfRelocationConstants.R_ARM_ABS12: { // Target class: ARM Instruction
@@ -171,6 +176,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
case ARM_ElfRelocationConstants.R_ARM_ABS_8: { // Target class: Data
byte bValue = (byte) (symbolValue + addend);
memory.setByte(relocationAddress, bValue);
byteLength = 1;
break;
}
/*
@@ -221,6 +227,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
newValue = newValue >> 1;
short sValue = (short) ((oldValue & 0xff00) | (newValue & 0x00ff));
memory.setShort(relocationAddress, sValue, instructionBigEndian);
byteLength = 2;
break;
}
/*
@@ -270,6 +277,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
// GOT entry bytes if it refers to .plt block
Address symAddress = elfRelocationContext.getSymbolAddress(sym);
MemoryBlock block = memory.getBlock(symAddress);
// TODO: jump slots are always in GOT - not sure why PLT check is done
boolean isPltSym = block != null && block.getName().startsWith(".plt");
boolean isExternalSym =
block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName());
@@ -284,7 +292,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
markAsError(program, relocationAddress, "R_ARM_JUMP_SLOT", symbolName,
"Failed to create R_ARM_JUMP_SLOT external function",
elfRelocationContext.getLog());
return;
// relocation already applied above
}
}
break;
@@ -592,6 +600,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
newValue -= (offset + elfRelocationContext.getPcBias(true)); // PC relative
newValue = (oldValue & 0x0000f800) | ((newValue >> 1) & 0x000007ff);
memory.setShort(relocationAddress, (short) newValue, instructionBigEndian);
byteLength = 2;
break;
}
case ARM_ElfRelocationConstants.R_ARM_THM_JUMP8: {
@@ -603,6 +612,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
newValue -= (offset + elfRelocationContext.getPcBias(true)); // PC relative
newValue = (oldValue & 0x0000ff00) | ((newValue >> 1) & 0x000000ff);
memory.setShort(relocationAddress, (short) newValue, instructionBigEndian);
byteLength = 2;
break;
}
/*
@@ -689,15 +699,16 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
case ARM_ElfRelocationConstants.R_ARM_COPY: {
markAsWarning(program, relocationAddress, "R_ARM_COPY", symbolName, symbolIndex,
"Runtime copy not supported", elfRelocationContext.getLog());
break;
return RelocationResult.UNSUPPORTED;
}
default: {
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
elfRelocationContext.getLog());
break;
return RelocationResult.UNSUPPORTED;
}
}
return new RelocationResult(Status.APPLIED, byteLength);
}
private boolean isThumb(ElfSymbol symbol) {
@@ -17,10 +17,12 @@ package ghidra.app.util.bin.format.macho.relocation;
import static ghidra.app.util.bin.format.macho.relocation.ARM_MachoRelocationConstants.*;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.macho.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.NotFoundException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult;
/**
* A {@link MachoRelocationHandler} for ARM
@@ -43,24 +45,25 @@ public class ARM_MachoRelocationHandler extends MachoRelocationHandler {
}
@Override
public void relocate(MachoRelocation relocation)
throws MemoryAccessException, NotFoundException {
public RelocationResult relocate(MachoRelocation relocation)
throws MemoryAccessException, RelocationException {
if (!relocation.requiresRelocation()) {
return;
return RelocationResult.SKIPPED;
}
RelocationInfo relocationInfo = relocation.getRelocationInfo();
Address targetAddr = relocation.getTargetAddress();
long orig = read(relocation);
int byteLength;
switch (relocationInfo.getType()) {
case ARM_RELOC_VANILLA:
if (!relocationInfo.isPcRelocated()) {
write(relocation, targetAddr.getOffset());
byteLength = write(relocation, targetAddr.getOffset());
}
else {
throw new NotFoundException("Unimplemented relocation");
return RelocationResult.UNSUPPORTED;
}
break;
case ARM_THUMB_RELOC_BR22: {
@@ -86,7 +89,7 @@ public class ARM_MachoRelocationHandler extends MachoRelocationHandler {
imm11 = (value >> 1) & 0x7ff;
long instr = orig & (blx ? 0xc000f800 : 0xd000f800);
instr |= (j1 << 29) | (j2 << 27) | (imm11 << 16) | (s << 10) | imm10;
write(relocation, instr);
byteLength = write(relocation, instr);
break;
}
@@ -99,7 +102,8 @@ public class ARM_MachoRelocationHandler extends MachoRelocationHandler {
case ARM_RELOC_HALF: // relocation not required (scattered)
case ARM_RELOC_HALF_SECTDIFF: // relocation not required (scattered)
default:
throw new NotFoundException("Unimplemented relocation");
return RelocationResult.UNSUPPORTED;
}
return new RelocationResult(Status.APPLIED, byteLength);
}
}
@@ -24,6 +24,8 @@ import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.NotFoundException;
@@ -35,7 +37,8 @@ public class AVR32_ElfRelocationHandler extends ElfRelocationHandler {
}
@Override
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation,
public RelocationResult relocate(ElfRelocationContext elfRelocationContext,
ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException {
Program program = elfRelocationContext.getProgram();
@@ -57,11 +60,13 @@ public class AVR32_ElfRelocationHandler extends ElfRelocationHandler {
int oldValue = memory.getInt(relocationAddress);
int byteLength = 4; // most relocations affect 4-bytes (change if different)
if (elf.e_machine() == ElfConstants.EM_AVR32) {
int newValueShiftToAligntoUpper = 0;
switch (type) {
case AVR32_ElfRelocationConstants.R_AVR32_NONE:
break;
return RelocationResult.SKIPPED;
case AVR32_ElfRelocationConstants.R_AVR32_32:
int newValue = (((int) symbolValue + (int) addend) & 0xffffffff);
memory.setInt(relocationAddress, newValue);
@@ -188,7 +193,7 @@ public class AVR32_ElfRelocationHandler extends ElfRelocationHandler {
System.out.println(" HANDLED AVR relocation: R_AVR32_ALIGN at "+relocationAddress + ", New = " + newValue);
}*/
//System.out.println(" HANDLED AVR relocation: R_AVR32_ALIGN at "+relocationAddress + ", OldValue = " + Integer.toHexString(oldValue));
break;
return RelocationResult.SKIPPED;
//TODO: THE FOLLOWING:
/*case AVR32_ElfRelocationConstants.R_AVR32_16_CP:
@@ -316,9 +321,10 @@ public class AVR32_ElfRelocationHandler extends ElfRelocationHandler {
markAsUnhandled(program, relocationAddress, type, symbolIndex,
elfRelocationContext.getSymbolName(symbolIndex),
elfRelocationContext.getLog());
break;
return RelocationResult.UNSUPPORTED;
}
}
return new RelocationResult(Status.APPLIED, byteLength);
}
}
@@ -20,6 +20,8 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.NotFoundException;
public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
@@ -30,7 +32,8 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
}
@Override
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation,
public RelocationResult relocate(ElfRelocationContext elfRelocationContext,
ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException {
Program program = elfRelocationContext.getProgram();
@@ -57,14 +60,15 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
int oldValue = memory.getShort(relocationAddress);
if (elf.e_machine() != ElfConstants.EM_AVR) {
return;
return RelocationResult.FAILURE;
}
int newValue = 0;
int byteLength = 2; // most relocations affect 2-bytes (change if different)
switch (type) {
case AVR8_ElfRelocationConstants.R_AVR_NONE:
break;
return RelocationResult.SKIPPED;
case AVR8_ElfRelocationConstants.R_AVR_32:
newValue = (((int) symbolValue + (int) addend) & 0xffffffff);
@@ -78,12 +82,12 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
if (newValue > ((1 << 7) - 1) || (newValue < -(1 << 7))) {
markAsError(program, relocationAddress, type, symbolName, "relocation overflow",
elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue = (oldValue & 0xfc07) | (((newValue >> 1) << 3) & 0x3f8);
memory.setShort(relocationAddress, (short) newValue);
@@ -96,7 +100,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue >>= 1;
@@ -168,7 +172,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue >>= 1;
newValue = (oldValue & 0xf0f0) | (newValue & 0xf) | ((newValue << 4) & 0xf00);
@@ -180,7 +184,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue >>= 1;
newValue = (newValue >> 8) & 0xff;
@@ -193,7 +197,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue >>= 1;
newValue = (newValue >> 16) & 0xff;
@@ -207,7 +211,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue >>= 1;
newValue = (oldValue & 0xf0f0) | (newValue & 0xf) | ((newValue << 4) & 0xf00);
@@ -220,7 +224,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue >>= 1;
newValue = (newValue >> 8) & 0xff;
@@ -234,7 +238,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue >>= 1;
newValue = (newValue >> 16) & 0xff;
@@ -248,7 +252,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
return;
return RelocationResult.FAILURE;
}
newValue >>= 1;
@@ -256,6 +260,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
oldValue | ((newValue & 0x10000) | ((newValue << 3) & 0x1f00000)) >> 16;
memory.setShort(relocationAddress, (short) (hiValue & 0xffff));
memory.setShort(relocationAddress.add(2), (short) (newValue & 0xffff));
byteLength = 4;
break;
case AVR8_ElfRelocationConstants.R_AVR_LDI: /* data/eeprom */
@@ -264,6 +269,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 0xffff) > 255) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
// continue to apply
}
newValue = (newValue >> 8) & 0xff;
@@ -277,6 +283,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if (((newValue & 0xffff) > 63) || (newValue < 0)) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
// continue to apply
}
newValue = (oldValue & 0xd3f8) | (newValue & 7) | ((newValue & (3 << 3)) << 7) |
@@ -290,6 +297,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if (((newValue & 0xffff) > 63) || (newValue < 0)) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
// continue to apply
}
newValue = (oldValue & 0xff30) | (newValue & 0xF) | ((newValue & 0x30) << 2);
@@ -309,6 +317,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if (((newValue & 0xffff) < 0x40) || (newValue & 0xFFFF) > 0xbf) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
// continue to apply
}
newValue = newValue & 0x7f;
@@ -322,6 +331,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 0xffff) > 0x3f) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
// continue to apply
}
newValue = (oldValue & 0xf9f0) | ((newValue & 0x30) << 5) | (newValue & 0x0f);
@@ -334,6 +344,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 0xffff) > 0x1f) {
markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog());
// continue to apply
}
newValue = (oldValue & 0xff07) | ((newValue & 0x1f) << 3);
@@ -351,8 +362,9 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
default:
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
elfRelocationContext.getLog());
break;
return RelocationResult.UNSUPPORTED;
}
return new RelocationResult(Status.APPLIED, byteLength);
}
}
@@ -801,7 +801,7 @@ public class MIPS_ElfExtension extends ElfExtension {
catch (NotFoundException e) {
throw new AssertException("unexpected", e);
}
catch (MemoryAccessException | AddressOverflowException e) {
catch (MemoryAccessException e) {
elfLoadHelper.log("Failed to adjust GOT: " + e.getMessage());
}
}
@@ -855,14 +855,14 @@ public class MIPS_ElfExtension extends ElfExtension {
catch (NotFoundException e) {
throw new AssertException("unexpected", e);
}
catch (MemoryAccessException | AddressOverflowException e) {
catch (MemoryAccessException e) {
elfLoadHelper.log("Failed to adjust MIPS GOT: " + e.getMessage());
}
}
private Address adjustTableEntryIfNonZero(Address tableBaseAddr, int entryIndex,
long adjustment, ElfLoadHelper elfLoadHelper)
throws MemoryAccessException, AddressOverflowException {
throws MemoryAccessException {
boolean is64Bit = elfLoadHelper.getElfHeader().is64Bit();
Memory memory = elfLoadHelper.getProgram().getMemory();
Address tableEntryAddr;
@@ -871,7 +871,7 @@ public class MIPS_ElfExtension extends ElfExtension {
if (adjustment != 0) {
long offset = memory.getLong(tableEntryAddr);
if (offset != 0) {
elfLoadHelper.addFakeRelocTableEntry(tableEntryAddr, 8);
elfLoadHelper.addArtificialRelocTableEntry(tableEntryAddr, 8);
memory.setLong(tableEntryAddr, offset + adjustment);
}
}
@@ -881,7 +881,7 @@ public class MIPS_ElfExtension extends ElfExtension {
if (adjustment != 0) {
int offset = memory.getInt(tableEntryAddr);
if (offset != 0) {
elfLoadHelper.addFakeRelocTableEntry(tableEntryAddr, 4);
elfLoadHelper.addArtificialRelocTableEntry(tableEntryAddr, 4);
memory.setInt(tableEntryAddr, (int) (offset + adjustment));
}
}
@@ -898,7 +898,7 @@ public class MIPS_ElfExtension extends ElfExtension {
tableEntryAddr = tableBaseAddr.add(entryIndex * 8);
long offset = memory.getLong(tableEntryAddr);
if (offset == 0) {
elfLoadHelper.addFakeRelocTableEntry(tableEntryAddr, 8);
elfLoadHelper.addArtificialRelocTableEntry(tableEntryAddr, 8);
memory.setLong(tableEntryAddr, value);
}
}
@@ -906,7 +906,7 @@ public class MIPS_ElfExtension extends ElfExtension {
tableEntryAddr = tableBaseAddr.add(entryIndex * 4);
int offset = memory.getInt(tableEntryAddr);
if (offset == 0) {
elfLoadHelper.addFakeRelocTableEntry(tableEntryAddr, 4);
elfLoadHelper.addArtificialRelocTableEntry(tableEntryAddr, 4);
memory.setInt(tableEntryAddr, (int) value);
}
}
@@ -0,0 +1,323 @@
/* ###
* 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.bin.format.elf.relocation;
import java.util.*;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.format.elf.*;
import ghidra.app.util.bin.format.elf.extend.MIPS_ElfExtension;
import ghidra.app.util.bin.format.elf.relocation.MIPS_ElfRelocationHandler.MIPS_DeferredRelocation;
import ghidra.program.model.address.*;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.*;
import ghidra.util.exception.AssertException;
/**
* {@link MIPS_ElfRelocationContext} provides extended relocation context with the ability
* to retain deferred relocation lists. In addition, the ability to generate a section GOT
* table is provided to facilitate relocations encountered within object modules.
*/
class MIPS_ElfRelocationContext extends ElfRelocationContext {
private LinkedList<MIPS_DeferredRelocation> hi16list = new LinkedList<>();
private LinkedList<MIPS_DeferredRelocation> got16list = new LinkedList<>();
private AddressRange sectionGotLimits;
private Address sectionGotAddress;
private Address lastSectionGotEntryAddress;
private Address nextSectionGotEntryAddress;
private Map<Long, Address> gotMap;
boolean useSavedAddend = false;
boolean savedAddendHasError = false;
long savedAddend;
Address lastSymbolAddr;
MIPS_ElfRelocationContext(MIPS_ElfRelocationHandler handler, ElfLoadHelper loadHelper,
Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, symbolMap);
}
@Override
public void endRelocationTableProcessing() {
// Mark all deferred relocations which were never processed
for (MIPS_DeferredRelocation reloc : hi16list) {
reloc.markUnprocessed(this, "LO16 Relocation");
}
hi16list.clear();
for (MIPS_DeferredRelocation reloc : got16list) {
reloc.markUnprocessed(this, "LO16 Relocation");
}
got16list.clear();
// Generate the section GOT table if required
createGot();
sectionGotLimits = null;
sectionGotAddress = null;
lastSectionGotEntryAddress = null;
nextSectionGotEntryAddress = null;
gotMap = null;
useSavedAddend = false;
savedAddendHasError = false;
lastSymbolAddr = null;
super.endRelocationTableProcessing();
}
private void allocateSectionGot() {
int alignment = getLoadAdapter().getLinkageBlockAlignment();
sectionGotLimits =
getLoadHelper().allocateLinkageBlock(alignment, 0x10000, getSectionGotName());
sectionGotAddress =
sectionGotLimits != null ? sectionGotLimits.getMinAddress() : Address.NO_ADDRESS;
nextSectionGotEntryAddress = sectionGotAddress;
if (sectionGotLimits == null) {
loadHelper.log("Failed to allocate " + getSectionGotName() +
" block required for relocation processing");
}
else {
loadHelper.log("Created " + getSectionGotName() +
" block required for relocation processing (gp=0x" +
Long.toHexString(getGPValue()) + ")");
}
}
/**
* Allocate the next section GOT entry location.
* @return Address of GOT entry or null if unable to allocate.
*/
private Address getNextSectionGotEntryAddress() {
if (nextSectionGotEntryAddress == null) {
allocateSectionGot();
}
Address addr = nextSectionGotEntryAddress;
if (addr != Address.NO_ADDRESS) {
try {
int pointerSize = loadHelper.getProgram().getDefaultPointerSize();
Address lastAddr = nextSectionGotEntryAddress.addNoWrap(pointerSize - 1);
if (sectionGotLimits.contains(lastAddr)) {
lastSectionGotEntryAddress = lastAddr;
nextSectionGotEntryAddress = lastSectionGotEntryAddress.addNoWrap(1);
if (!sectionGotLimits.contains(nextSectionGotEntryAddress)) {
nextSectionGotEntryAddress = Address.NO_ADDRESS;
}
}
else {
// unable to allocation entry size
nextSectionGotEntryAddress = Address.NO_ADDRESS;
return Address.NO_ADDRESS;
}
}
catch (AddressOverflowException e) {
nextSectionGotEntryAddress = Address.NO_ADDRESS;
}
}
return addr != Address.NO_ADDRESS ? addr : null;
}
/**
* Get the preferred GP.
* NOTE: This needs work to properly handle the use of multiple GP's
* @return preferred GP value or -1 if unable to determine GP
*/
public long getGPValue() {
long gp = getAdjustedGPValue();
if (gp == -1) {
// TODO: we should probably not resort to assuming use of fabricated got so easily
// since getAdjustedGPValue has rather limited capability at present
// assume GP relative to fabricated GOT
if (sectionGotAddress == null) {
allocateSectionGot();
}
if (sectionGotAddress == Address.NO_ADDRESS) {
return -1;
}
// gp is defined as 0x7ff0 byte offset into the global offset table
return sectionGotAddress.getOffset() + 0x7ff0;
}
return gp;
}
@Override
public boolean extractAddend() {
return !relocationTable.hasAddendRelocations() && !useSavedAddend;
}
/**
* Determine if the next relocation has the same offset.
* If true, the computed value should be stored to savedAddend and
* useSaveAddend set true.
* @param relocation current relocation
* @return true if next relocation has same offset
*/
boolean nextRelocationHasSameOffset(ElfRelocation relocation) {
ElfRelocation[] relocations = relocationTable.getRelocations();
int relocIndex = relocation.getRelocationIndex();
if (relocIndex < 0 || relocIndex >= (relocations.length - 1)) {
return false;
}
return relocations[relocIndex].getOffset() == relocations[relocIndex + 1].getOffset() &&
relocations[relocIndex + 1].getType() != MIPS_ElfRelocationConstants.R_MIPS_NONE;
}
/**
* Get or allocate a GOT entry for the specified symbolValue
* @param symbolValue symbol value to be added to GOT
* @return GOT entry address or null if unable to allocate
*/
public Address getSectionGotAddress(long symbolValue) {
Address addr = null;
if (gotMap == null) {
gotMap = new HashMap<>();
}
else {
addr = gotMap.get(symbolValue);
}
if (addr == null) {
addr = getNextSectionGotEntryAddress();
if (addr == null) {
return null;
}
gotMap.put(symbolValue, addr);
}
return addr;
}
private String getSectionGotName() {
String sectionName = relocationTable.getSectionToBeRelocated().getNameAsString();
return ElfRelocationHandler.GOT_BLOCK_NAME + sectionName;
}
/**
* Flush the section GOT table to a new %got memory block
*/
private void createGot() {
if (lastSectionGotEntryAddress == null) {
return;
}
int size = (int) lastSectionGotEntryAddress.subtract(sectionGotAddress) + 1;
String blockName = getSectionGotName();
try {
MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false,
blockName, sectionGotAddress, size,
"NOTE: This block is artificial and allows ELF Relocations to work correctly",
"Elf Loader", true, false, false, loadHelper.getLog());
DataConverter converter =
program.getMemory().isBigEndian() ? BigEndianDataConverter.INSTANCE
: LittleEndianDataConverter.INSTANCE;
for (long symbolValue : gotMap.keySet()) {
Address addr = gotMap.get(symbolValue);
byte[] bytes;
if (program.getDefaultPointerSize() == 4) {
bytes = converter.getBytes((int) symbolValue);
}
else {
bytes = converter.getBytes(symbolValue);
}
block.putBytes(addr, bytes);
loadHelper.createData(addr, PointerDataType.dataType);
}
}
catch (MemoryAccessException e) {
throw new AssertException(e); // unexpected
}
}
/**
* Get the GP value
* @return adjusted GP value or -1 if _mips_gp_value symbol not defined.
*/
long getAdjustedGPValue() {
// TODO: this is a simplified use of GP and could be incorrect when multiple GPs exist
Symbol symbol = SymbolUtilities.getLabelOrFunctionSymbol(program,
MIPS_ElfExtension.MIPS_GP_VALUE_SYMBOL, err -> getLog().error("MIPS_ELF", err));
if (symbol == null) {
return -1;
}
return symbol.getAddress().getOffset();
}
/**
* Get the GP0 value (from .reginfo and generated symbol)
* @param mipsRelocationContext
* @return adjusted GP0 value or -1 if _mips_gp0_value symbol not defined.
*/
long getGP0Value() {
Symbol symbol = SymbolUtilities.getLabelOrFunctionSymbol(program,
MIPS_ElfExtension.MIPS_GP0_VALUE_SYMBOL, err -> getLog().error("MIPS_ELF", err));
if (symbol == null) {
return -1;
}
return symbol.getAddress().getOffset();
}
@Override
public long getSymbolValue(ElfSymbol symbol) {
if ("__gnu_local_gp".equals(symbol.getNameAsString())) {
return getAdjustedGPValue(); // TODO: need to verify this case still
}
return super.getSymbolValue(symbol);
}
/**
* Iterate over deferred HI16 relocations. Iterator may be used to remove
* entries as they are processed.
* @return HI16 relocation iterator
*/
Iterator<MIPS_DeferredRelocation> iterateHi16() {
return hi16list.iterator();
}
/**
* Add HI16 relocation for deferred processing
* @param hi16reloc HI16 relocation
*/
void addHI16Relocation(MIPS_DeferredRelocation hi16reloc) {
hi16list.add(hi16reloc);
}
/**
* Iterate over deferred GOT16 relocations. Iterator may be used to remove
* entries as they are processed.
* @return GOT16 relocation iterator
*/
Iterator<MIPS_DeferredRelocation> iterateGot16() {
return got16list.iterator();
}
/**
* Add HI16 relocation for deferred processing
* @param hi16reloc HI16 relocation
*/
void addGOT16Relocation(MIPS_DeferredRelocation got16reloc) {
got16list.add(got16reloc);
}
}

Some files were not shown because too many files have changed in this diff Show More