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.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation; import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.reloc.RelocationTable;
import ghidra.util.NumericUtilities; import ghidra.util.NumericUtilities;
@@ -51,15 +52,19 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
}; };
static final int ADDRESS_COL = 0; static final int ADDRESS_COL = 0;
static final int TYPE_COL = 1; static final int STATUS_COL = 1;
static final int VALUE_COL = 2; static final int TYPE_COL = 2;
static final int BYTES_COL = 3; static final int VALUE_COL = 3;
static final int NAME_COL = 4; 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_ADDRESS = "Address";
static final String RELOCATION_STATUS = "Status";
static final String RELOCATION_TYPE = "Type"; static final String RELOCATION_TYPE = "Type";
static final String RELOCATION_VALUE = "Values"; 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"; static final String RELOCATION_NAME = "Name";
public RelocationTableModel(ServiceProvider serviceProvider, Program program, public RelocationTableModel(ServiceProvider serviceProvider, Program program,
@@ -73,9 +78,11 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
descriptor.addVisibleColumn( descriptor.addVisibleColumn(
DiscoverableTableUtils.adaptColumForModel(this, new AddressTableColumn()), 1, true); DiscoverableTableUtils.adaptColumForModel(this, new AddressTableColumn()), 1, true);
descriptor.addVisibleColumn(new RelocationStatusColumn());
descriptor.addVisibleColumn(new RelocationTypeColumn()); descriptor.addVisibleColumn(new RelocationTypeColumn());
descriptor.addVisibleColumn(new RelocationValueColumn()); descriptor.addVisibleColumn(new RelocationValueColumn());
descriptor.addVisibleColumn(new RelocationBytesColumn()); descriptor.addVisibleColumn(new RelocationOriginalBytesColumn());
descriptor.addHiddenColumn(new RelocationCurrentBytesColumn());
descriptor.addVisibleColumn(new RelocationNameColumn()); descriptor.addVisibleColumn(new RelocationNameColumn());
return descriptor; return descriptor;
@@ -118,6 +125,24 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
return rowObject.relocation.getAddress(); 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 // 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 private static class RelocationTypeColumn extends
AbstractProgramBasedDynamicTableColumn<RelocationRowObject, String> { 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> { AbstractProgramBasedDynamicTableColumn<RelocationRowObject, String> {
@Override @Override
public String getColumnName() { public String getColumnName() {
return RELOCATION_BYTES; return RELOCATION_ORIGINAL_BYTES;
} }
@Override @Override
@@ -195,23 +236,33 @@ class RelocationTableModel extends AddressBasedTableModel<RelocationRowObject> {
ServiceProvider serviceProvider) throws IllegalArgumentException { ServiceProvider serviceProvider) throws IllegalArgumentException {
return packBytes(rowObject.relocation.getBytes()); return packBytes(rowObject.relocation.getBytes());
} }
}
private String packBytes(byte[] bytes) { private static class RelocationCurrentBytesColumn extends
if (bytes == null) { AbstractProgramBasedDynamicTableColumn<RelocationRowObject, String> {
return "";
@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(); byte[] bytes = new byte[originalBytes.length];
for (long b : bytes) { try {
if (buf.length() != 0) { program.getMemory().getBytes(relocation.getAddress(), bytes);
buf.append(' ');
}
String byteStr = Long.toHexString(b & 0xff);
if (byteStr.length() == 1) {
buf.append('0');
}
buf.append(byteStr);
} }
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.app.util.bin.format.coff.CoffRelocation;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryAccessException; 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.classfinder.ExtensionPoint;
import ghidra.util.exception.NotFoundException;
/** /**
* An abstract class used to perform COFF relocations. Classes should extend this class to * 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 address The address at which to perform the relocation.
* @param relocation The relocation information to use to perform the relocation. * @param relocation The relocation information to use to perform the relocation.
* @param relocationContext relocation context data * @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 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. * @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) 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 // TODO: record artificial relative relocation for reversion/export concerns
entry1Value += imageBaseAdj; // adjust first entry value entry1Value += imageBaseAdj; // adjust first entry value
if (elf.is64Bit()) { if (elf.is64Bit()) {
elfLoadHelper.addFakeRelocTableEntry(gotStart, 8); elfLoadHelper.addArtificialRelocTableEntry(gotStart, 8);
memory.setLong(gotStart, entry1Value); memory.setLong(gotStart, entry1Value);
} }
else { else {
elfLoadHelper.addFakeRelocTableEntry(gotStart, 4); elfLoadHelper.addArtificialRelocTableEntry(gotStart, 4);
memory.setInt(gotStart, (int) entry1Value); 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.data.DataType;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
@@ -213,10 +214,10 @@ public interface ElfLoadHelper {
throws MemoryAccessException; 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 * 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 * performed during the ELF import processing. A relocation type of 0 and a status of
* fake entry. * {@link Status#APPLIED_OTHER} will be applied to the relocation entry.
* NOTE: The number of recorded original FileBytes currently ignores the specified length. * 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 * However, the length is still used to verify that that the intended modification region
* dose not intersect another relocation. * dose not intersect another relocation.
@@ -225,6 +226,6 @@ public interface ElfLoadHelper {
* @return true if recorded successfully, or false if conflict with existing relocation * @return true if recorded successfully, or false if conflict with existing relocation
* entry and memory addressing error occurs * 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.listing.*;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.util.exception.*; 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. * All relocation entries must be processed in the order they appear within the table.
* @param relocation relocation to be processed * @param relocation relocation to be processed
* @param relocationAddress relocation address where it should be applied * @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) { if (handler == null) {
handleNoHandlerError(relocation, relocationAddress); handleNoHandlerError(relocation, relocationAddress);
return; return RelocationResult.FAILURE;
} }
int symbolIndex = relocation.getSymbolIndex(); int symbolIndex = relocation.getSymbolIndex();
@@ -98,21 +101,22 @@ public class ElfRelocationContext {
if (sym == null) { if (sym == null) {
ElfRelocationHandler.markAsUnhandled(program, relocationAddress, relocation.getType(), ElfRelocationHandler.markAsUnhandled(program, relocationAddress, relocation.getType(),
symbolIndex, "index " + symbolIndex, getLog()); symbolIndex, "index " + symbolIndex, getLog());
return; return RelocationResult.FAILURE;
} }
if (sym.isTLS()) { if (sym.isTLS()) {
handleUnsupportedTLSRelocation(relocation, relocationAddress, sym); handleUnsupportedTLSRelocation(relocation, relocationAddress, sym);
return; return RelocationResult.FAILURE;
} }
try { try {
handler.relocate(this, relocation, relocationAddress); return handler.relocate(this, relocation, relocationAddress);
} }
catch (MemoryAccessException | NotFoundException e) { catch (MemoryAccessException | NotFoundException e) {
loadHelper.log(e); loadHelper.log(e);
ElfRelocationHandler.markAsUnhandled(program, relocationAddress, relocation.getType(), ElfRelocationHandler.markAsUnhandled(program, relocationAddress, relocation.getType(),
symbolIndex, sym.getNameAsString(), getLog()); 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.data.PointerTypedef;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.classfinder.ExtensionPoint; import ghidra.util.classfinder.ExtensionPoint;
@@ -74,10 +75,11 @@ abstract public class ElfRelocationHandler implements ExtensionPoint {
* @param elfRelocationContext relocation context * @param elfRelocationContext relocation context
* @param relocation ELF relocation * @param relocation ELF relocation
* @param relocationAddress relocation target address (fixup location) * @param relocationAddress relocation target address (fixup location)
* @return applied relocation result (conveys status and applied byte-length)
* @throws MemoryAccessException memory access failure * @throws MemoryAccessException memory access failure
* @throws NotFoundException required relocation data not found * @throws NotFoundException required relocation data not found
*/ */
abstract public void relocate(ElfRelocationContext elfRelocationContext, abstract public RelocationResult relocate(ElfRelocationContext elfRelocationContext,
ElfRelocation relocation, Address relocationAddress) ElfRelocation relocation, Address relocationAddress)
throws MemoryAccessException, NotFoundException; throws MemoryAccessException, NotFoundException;
@@ -25,8 +25,10 @@ import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.util.*; import ghidra.util.DataConverter;
import ghidra.util.SystemUtilities;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public abstract class AbstractClassicProcessor { public abstract class AbstractClassicProcessor {
@@ -70,12 +72,9 @@ public abstract class AbstractClassicProcessor {
long offset = symbol.getAddress().getOffset(); long offset = symbol.getAddress().getOffset();
boolean handled = false;
int fileType = header.getFileType(); int fileType = header.getFileType();
byte originalBytes[] = new byte[0]; int byteLength = 0;
switch (fileType) { switch (fileType) {
case MachHeaderFileTypes.MH_EXECUTE: case MachHeaderFileTypes.MH_EXECUTE:
@@ -85,13 +84,8 @@ public abstract class AbstractClassicProcessor {
byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset) byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset)
: converter.getBytes((int) offset); : converter.getBytes((int) offset);
originalBytes = new byte[bytes.length];
memory.getBytes(address, originalBytes);
memory.setBytes(address, bytes); memory.setBytes(address, bytes);
byteLength = bytes.length;
handled = true;
break; break;
} }
case MachHeaderFileTypes.MH_KEXT_BUNDLE: { case MachHeaderFileTypes.MH_KEXT_BUNDLE: {
@@ -108,20 +102,15 @@ public abstract class AbstractClassicProcessor {
long difference = offset - addressValue - 4; long difference = offset - addressValue - 4;
byte[] bytes = converter.getBytes((int) difference); byte[] bytes = converter.getBytes((int) difference);
originalBytes = new byte[bytes.length];
memory.getBytes(address, originalBytes);
memory.setBytes(address, bytes); memory.setBytes(address, bytes);
handled = true; byteLength = bytes.length;
} }
} }
else { else {
byte[] bytes = (program.getDefaultPointerSize() == 8) byte[] bytes = (program.getDefaultPointerSize() == 8)
? converter.getBytes(offset) : converter.getBytes((int) offset); ? converter.getBytes(offset) : converter.getBytes((int) offset);
originalBytes = new byte[bytes.length];
memory.getBytes(address, originalBytes);
memory.setBytes(address, bytes); memory.setBytes(address, bytes);
handled = true; byteLength = bytes.length;
} }
} }
else if (header.getCpuType() == CpuTypes.CPU_TYPE_POWERPC) {//TODO powerpc kext files else if (header.getCpuType() == CpuTypes.CPU_TYPE_POWERPC) {//TODO powerpc kext files
@@ -140,11 +129,8 @@ public abstract class AbstractClassicProcessor {
case MachHeaderFileTypes.MH_OBJECT: { case MachHeaderFileTypes.MH_OBJECT: {
byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset) byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset)
: converter.getBytes((int) offset); : converter.getBytes((int) offset);
originalBytes = new byte[bytes.length];
memory.getBytes(address, originalBytes);
memory.setBytes(address, bytes); memory.setBytes(address, bytes);
handled = true; byteLength = bytes.length;
break; break;
} }
default: { default: {
@@ -152,15 +138,21 @@ public abstract class AbstractClassicProcessor {
} }
} }
// put an entry in the relocation table, handled or not Status status;
String symbolName = symbol.getName(); if (byteLength <= 0) {
program.getRelocationTable().add(address, fileType, new long[0], originalBytes, symbolName);
if (!handled) {
program.getBookmarkManager().setBookmark(address, BookmarkType.ERROR, program.getBookmarkManager().setBookmark(address, BookmarkType.ERROR,
"Unhandled Classic Binding", "Unable to fixup classic binding. " + "Unhandled Classic Binding", "Unable to fixup classic binding. " +
"This instruction will contain an invalid destination / fixup."); "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); List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
monitor.setMessage("Fixing V1 chained data page pointers..."); monitor.setMessage("Fixing V1 chained data page pointers...");
monitor.setMaximum(entries_count); 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 // not actually changing bytes, so not really a relocation, but a relocate-able place
if (addRelocations) { if (addRelocations) {
addRelocationTableEntry(program, addr, 0x1000, value, origBytes, addRelocationTableEntry(program, addr, 0x1000, value, 8, null);
null);
} }
//memory.setLong(addr, value); //memory.setLong(addr, value);
@@ -212,8 +212,6 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
Memory memory = program.getMemory(); Memory memory = program.getMemory();
List<Address> unchainedLocList = new ArrayList<>(1024); List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
long valueMask = 0xffffffffffffffffL >>> (64 - deltaShift); long valueMask = 0xffffffffffffffffL >>> (64 - deltaShift);
long delta = -1; long delta = -1;
@@ -230,7 +228,7 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
// chainValue += slideAmount - if we were sliding // chainValue += slideAmount - if we were sliding
} }
if (addRelocations) { if (addRelocations) {
addRelocationTableEntry(program, chainLoc, 2, chainValue, origBytes, null); addRelocationTableEntry(program, chainLoc, 2, chainValue, 8, null);
} }
memory.setLong(chainLoc, chainValue); memory.setLong(chainLoc, chainValue);
@@ -172,8 +172,6 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
List<Address> unchainedLocList = new ArrayList<>(1024); List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
long delta = -1; long delta = -1;
while (delta != 0) { while (delta != 0) {
monitor.checkCanceled(); monitor.checkCanceled();
@@ -201,7 +199,7 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
if (addRelocation) { if (addRelocation) {
addRelocationTableEntry(program, chainLoc, 3 * (isAuthenticated ? -1 : 1), addRelocationTableEntry(program, chainLoc, 3 * (isAuthenticated ? -1 : 1),
chainValue, origBytes, null); chainValue, 8, null);
} }
memory.setLong(chainLoc, chainValue); memory.setLong(chainLoc, chainValue);
@@ -225,8 +225,6 @@ public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
List<Address> unchainedLocList = new ArrayList<Address>(1024); List<Address> unchainedLocList = new ArrayList<Address>(1024);
byte origBytes[] = new byte[4];
int valueMask = 0xffffffff >>> (32 - deltaShift); int valueMask = 0xffffffff >>> (32 - deltaShift);
long delta = -1; long delta = -1;
@@ -250,7 +248,7 @@ public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
} }
if (addRelocations) { if (addRelocations) {
addRelocationTableEntry(program, chainLoc, 4, chainValue, origBytes, null); addRelocationTableEntry(program, chainLoc, 4, chainValue, 4, null);
} }
memory.setInt(chainLoc, chainValue); memory.setInt(chainLoc, chainValue);
@@ -26,6 +26,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -129,11 +130,11 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
throws MemoryAccessException, CancelledException; throws MemoryAccessException, CancelledException;
protected void addRelocationTableEntry(Program program, Address chainLoc, int type, 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 // Add entry to relocation table for the pointer fixup
program.getMemory().getBytes(chainLoc, origBytes);
program.getRelocationTable() 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.address.Address;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException; 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> * @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 { long value) throws MemoryAccessException {
int byteLength;
switch (ptrFormat) { switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E: case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND: case DYLD_CHAINED_PTR_ARM64E_USERLAND:
@@ -125,16 +129,20 @@ public class DyldChainedPtr {
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
memory.setLong(chainLoc, value); memory.setLong(chainLoc, value);
byteLength = 8;
break; break;
case DYLD_CHAINED_PTR_32: case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE: case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE: case DYLD_CHAINED_PTR_32_FIRMWARE:
memory.setInt(chainLoc, (int) (value & 0xFFFFFFFFL)); memory.setInt(chainLoc, (int) (value & 0xFFFFFFFFL));
byteLength = 4;
break; break;
default: default:
break; return RelocationResult.UNSUPPORTED;
} }
return new RelocationResult(Status.APPLIED_OTHER, byteLength);
} }
public static long getChainValue(Memory memory, Address chainLoc, DyldChainType ptrFormat) public static long getChainValue(Memory memory, Address chainLoc, DyldChainType ptrFormat)
@@ -15,6 +15,7 @@
*/ */
package ghidra.app.util.bin.format.macho.relocation; 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.*;
import ghidra.app.util.bin.format.macho.commands.NList; import ghidra.app.util.bin.format.macho.commands.NList;
import ghidra.app.util.bin.format.macho.commands.SymbolTableCommand; 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.Symbol;
import ghidra.program.model.symbol.SymbolUtilities; import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.NumericUtilities; import ghidra.util.NumericUtilities;
import ghidra.util.exception.NotFoundException;
/** /**
* A representation of a single Mach-O relocation that the {@link MachoRelocationHandler} will use * 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 * Gets the {@link Address} of the relocation target
* *
* @return 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) { if (targetSymbol != null) {
return targetSymbol.getAddress(); return targetSymbol.getAddress();
} }
@@ -156,17 +156,17 @@ public class MachoRelocation {
if (targetPointer != null) { if (targetPointer != null) {
return targetPointer; 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 * Gets the {@link Address} of the extra relocation target
* *
* @return 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). * found (of if there wasn't an extra relocation target).
*/ */
public Address getTargetAddressExtra() throws NotFoundException { public Address getTargetAddressExtra() throws RelocationException {
if (targetSymbolExtra != null) { if (targetSymbolExtra != null) {
return targetSymbolExtra.getAddress(); return targetSymbolExtra.getAddress();
} }
@@ -176,7 +176,7 @@ public class MachoRelocation {
if (targetPointerExtra != null) { if (targetPointerExtra != null) {
return targetPointerExtra; 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; 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.MachHeader;
import ghidra.app.util.bin.format.macho.RelocationInfo; import ghidra.app.util.bin.format.macho.RelocationInfo;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException; 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.classfinder.ExtensionPoint;
import ghidra.util.exception.NotFoundException;
/** /**
* An abstract class used to perform Mach-O relocations. Classes should extend this class to * 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 * Performs a relocation
* @param relocation The relocation to perform * @param relocation The relocation to perform
* @return applied relocation result
* @throws MemoryAccessException If there is a problem accessing memory during the relocation * @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) abstract public RelocationResult relocate(MachoRelocation relocation)
throws MemoryAccessException, NotFoundException; throws MemoryAccessException, RelocationException;
/** /**
* Reads bytes at the given address. The size of the read is determined by the length of the * 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 relocation The relocation to write
* @param value The value 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 * @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(); Memory mem = relocation.getProgram().getMemory();
int len = relocation.getRelocationInfo().getLength();
Address addr = relocation.getRelocationAddress(); Address addr = relocation.getRelocationAddress();
if (len == 3) { switch (relocation.getRelocationInfo().getLength()) {
mem.setLong(addr, value); case 3:
} mem.setLong(addr, value);
else if (len == 2) { return 8;
mem.setInt(addr, (int) value); case 2:
} mem.setInt(addr, (int) value);
else if (len == 1) { return 4;
mem.setShort(addr, (short) value); case 1:
} mem.setShort(addr, (short) value);
else { return 2;
mem.setByte(addr, (byte) value); 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.data.PointerDataType;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
/** /**
* This class maintains the running state while * This class maintains the running state while
@@ -200,23 +201,21 @@ public class RelocationState {
if (block == null || !block.isInitialized()) { if (block == null || !block.isInitialized()) {
return; return;
} }
boolean success = false;
try { try {
int value = memory.getInt(address); 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; value += addend;
memory.setInt(address, value); memory.setInt(address, value);
success = true;
} }
catch (MemoryAccessException e) { catch (MemoryAccessException e) {
log.appendMsg("Unable to perform change memory at " + address); 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) { 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.lang.Language;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*; 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.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.*; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class CoffLoader extends AbstractLibrarySupportLoader { public class CoffLoader extends AbstractLibrarySupportLoader {
@@ -666,6 +669,9 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
sectionStartAddr.add(relocation.getAddress() - section.getVirtualAddress()); sectionStartAddr.add(relocation.getAddress() - section.getVirtualAddress());
short relocationType = relocation.getType(); short relocationType = relocation.getType();
Status status = Status.FAILURE;
int byteLength = 0;
if (handler == null) { if (handler == null) {
++failureCount; ++failureCount;
handleRelocationError(program, address, relocationType, handleRelocationError(program, address, relocationType,
@@ -677,6 +683,7 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
// skip relocation if previous failed relocation was at the same address // skip relocation if previous failed relocation was at the same address
// since it is likely dependent on the previous failed relocation result // since it is likely dependent on the previous failed relocation result
++failureCount; ++failureCount;
status = Status.SKIPPED;
String logMessage = String logMessage =
String.format("Skipped dependent COFF Relocation type 0x%x at %s", 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); Msg.error(this, program.getName() + ": " + logMessage);
} }
else { 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) { catch (MemoryAccessException e) {
++failureCount; ++failureCount;
failedAddr = address; failedAddr = address;
handleRelocationError(program, address, relocationType, handleRelocationError(program, address, relocationType,
"Error accessing memory", null); "error accessing memory", null);
}
catch (NotFoundException e) {
++failureCount;
failedAddr = address;
handleRelocationError(program, address, relocationType,
"Unsupported COFF relocation type", null);
} }
catch (RelocationException e) { catch (RelocationException e) {
++failureCount; ++failureCount;
@@ -705,7 +716,7 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
handleRelocationError(program, address, relocationType, e.getMessage(), handleRelocationError(program, address, relocationType, e.getMessage(),
null); null);
} }
catch (Exception e) { catch (Exception e) { // handle unexpected exceptions
++failureCount; ++failureCount;
failedAddr = address; failedAddr = address;
String msg = e.getMessage(); String msg = e.getMessage();
@@ -721,13 +732,9 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
Symbol symbol = Symbol symbol =
symbolsMap.get(header.getSymbolAtIndex(relocation.getSymbolIndex())); 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() program.getRelocationTable()
.add(address, relocation.getType(), .add(address, status, relocation.getType(),
new long[] { relocation.getSymbolIndex() }, null, new long[] { relocation.getSymbolIndex() }, byteLength,
symbol != null ? symbol.getName() : "<null>"); 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.lang.Register;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation; import ghidra.program.model.reloc.*;
import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.scalar.Scalar; import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap; import ghidra.program.model.util.AddressSetPropertyMap;
@@ -992,8 +992,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
reloc.setType(relrRelocationType); reloc.setType(relrRelocationType);
} }
Status status = Status.SKIPPED;
int byteLength = 0;
try { try {
if (unableToApplyRelocs) { if (unableToApplyRelocs) {
status = Status.FAILURE;
ElfRelocationHandler.markAsError(program, relocAddr, type, symbolName, ElfRelocationHandler.markAsError(program, relocAddr, type, symbolName,
"missing symbol table", log); "missing symbol table", log);
continue; continue;
@@ -1008,6 +1011,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
memory.convertToInitialized(relocBlock, (byte) 0); memory.convertToInitialized(relocBlock, (byte) 0);
} }
catch (Exception e) { catch (Exception e) {
status = Status.FAILURE;
Msg.error(this, Msg.error(this,
"Unexpected exception while converting block to initialized", e); "Unexpected exception while converting block to initialized", e);
ElfRelocationHandler.markAsUninitializedMemory(program, relocAddr, type, ElfRelocationHandler.markAsUninitializedMemory(program, relocAddr, type,
@@ -1018,15 +1022,19 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
if (context != null) { if (context != null) {
if (relrTypeUnknown) { if (relrTypeUnknown) {
status = Status.UNSUPPORTED;
ElfRelocationHandler.markAsUnsupportedRelr(program, relocAddr); ElfRelocationHandler.markAsUnsupportedRelr(program, relocAddr);
} }
else { else {
context.processRelocation(reloc, relocAddr); RelocationResult result = context.processRelocation(reloc, relocAddr);
byteLength = result.byteLength();
status = result.status();
} }
} }
} }
catch (MemoryAccessException e) { catch (MemoryAccessException e) {
if (type != 0) { // ignore if type 0 which is always NONE (no relocation performed) 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" + log("Unable to perform relocation: Type = " + type + " (0x" +
Long.toHexString(type) + ") at " + relocAddr + " (Symbol = " + symbolName + Long.toHexString(type) + ") at " + relocAddr + " (Symbol = " + symbolName +
") - " + getMessage(e)); ") - " + getMessage(e));
@@ -1035,7 +1043,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
finally { finally {
// Save relocation data - uses original FileBytes // Save relocation data - uses original FileBytes
program.getRelocationTable() 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 @Override
public boolean addFakeRelocTableEntry(Address address, int length) { public boolean addArtificialRelocTableEntry(Address address, int length) {
try { try {
Address maxAddr = address.addNoWrap(length - 1); Address maxAddr = address.addNoWrap(length - 1);
RelocationTable relocationTable = program.getRelocationTable(); RelocationTable relocationTable = program.getRelocationTable();
List<Relocation> relocations = relocationTable.getRelocations(address); List<Relocation> relocations = relocationTable.getRelocations(address);
if (!relocations.isEmpty()) { if (!relocations.isEmpty()) {
return false; Msg.warn(this, "Artificial relocation at " + address +
" conflicts with a previous relocation");
} }
Address nextRelocAddr = relocationTable.getRelocationAddressAfter(address); Address nextRelocAddr = relocationTable.getRelocationAddressAfter(address);
if (nextRelocAddr == null || nextRelocAddr.compareTo(maxAddr) > 0) { if (nextRelocAddr != null && nextRelocAddr.compareTo(maxAddr) <= 0) {
relocationTable.add(address, 0, new long[0], null, null); Msg.warn(this,
return true; "Artificial relocation at " + address + " overlaps a previous relocation");
} }
relocationTable.add(address, Status.APPLIED_OTHER, 0, null, length, null);
return true;
} }
catch (AddressOverflowException e) { catch (AddressOverflowException e) {
Msg.error(this, "Failed to generate fake relocation data at " + address, 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.MemoryBlockUtils;
import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.StructConverter; 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.*;
import ghidra.app.util.bin.format.macho.commands.*; import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.commands.ExportTrie.ExportEntry; 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.lang.Register;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*; 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.reloc.RelocationTable;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
@@ -1284,38 +1287,50 @@ public class MachoProgramBuilder {
Address address = relocationMap.get(relocationInfo); Address address = relocationMap.get(relocationInfo);
MachoRelocation relocation = null; MachoRelocation relocation = null;
RelocationResult result = RelocationResult.FAILURE;
if (handler == null) { if (handler == null) {
handleRelocationError(address, String.format( handleRelocationError(address, String.format(
"No relocation handler for machine type 0x%x to process relocation at %s with type 0x%x", "No relocation handler for machine type 0x%x to process relocation at %s with type 0x%x",
machoHeader.getCpuType(), address, relocationInfo.getType())); machoHeader.getCpuType(), address, relocationInfo.getType()));
} }
else { else {
relocation = handler.isPairedRelocation(relocationInfo)
? new MachoRelocation(program, machoHeader, address, relocationInfo,
iter.next())
: new MachoRelocation(program, machoHeader, address, relocationInfo);
try { try {
relocation = handler.isPairedRelocation(relocationInfo) result = handler.relocate(relocation);
? new MachoRelocation(program, machoHeader, address, relocationInfo,
iter.next()) if (result.status() == Status.UNSUPPORTED) {
: new MachoRelocation(program, machoHeader, address, relocationInfo); handleRelocationError(address,
handler.relocate(relocation); String.format("Relocation type 0x%x at address %s is not supported",
relocationInfo.getType(), address));
}
} }
catch (MemoryAccessException e) { catch (MemoryAccessException e) {
handleRelocationError(address, String.format( 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) { catch (RelocationException e) {
handleRelocationError(address, handleRelocationError(address, String.format(
String.format("Relocation type 0x%x at address %s is not supported: %s", "Relocation failure at address %s: %s", address, e.getMessage()));
relocationInfo.getType(), address, e.getMessage())); }
} catch (Exception e) { // handle unexpected exceptions
catch (AddressOutOfBoundsException e) { String msg = e.getMessage();
handleRelocationError(address, if (msg == null) {
String.format("Error computing relocation at address %s.", address)); msg = e.toString();
}
msg = String.format("Relocation failure at address %s: %s", address, msg);
handleRelocationError(address, msg);
Msg.error(this, msg, e);
} }
} }
program.getRelocationTable() 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.getLength(), relocationInfo.isPcRelocated() ? 1 : 0,
relocationInfo.isExternal() ? 1 : 0, relocationInfo.isScattered() ? 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 address The address of the relocation error
* @param message The error message * @param message The error message
*/ */
private void handleRelocationError(Address address, String message) { private void handleRelocationError(Address address, String message) {
program.getBookmarkManager() program.getBookmarkManager()
.setBookmark(address, BookmarkType.ERROR, "Relocations", message); .setBookmark(address, BookmarkType.ERROR, "Relocations", message);
log.appendMsg(message); log.appendMsg(message);
@@ -1783,10 +1798,21 @@ public class MachoProgramBuilder {
} }
if (!start || !program.getRelocationTable().hasRelocation(chainLoc)) { if (!start || !program.getRelocationTable().hasRelocation(chainLoc)) {
addRelocationTableEntry(chainLoc, int byteLength = 0;
(start ? 0x8000 : 0x4000) | (isAuthenticated ? 4 : 0) | (isBound ? 2 : 0) | 1, Status status = Status.FAILURE;
newChainValue, symName); try {
DyldChainedPtr.setChainValue(memory, chainLoc, pointerFormat, newChainValue); 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 // delay creating data until after memory has been changed
unchainedLocList.add(chainLoc); 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) { if (shouldAddChainedFixupsRelocations) {
// Add entry to relocation table for the pointer fixup // Add entry to relocation table for the pointer fixup
program.getRelocationTable() 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 // Add entry to relocation table for the pointer fixup
byte origBytes[] = new byte[8]; byte origBytes[] = new byte[8];
memory.getBytes(pointerAddr, origBytes); memory.getBytes(pointerAddr, origBytes);
program.getRelocationTable()
.add(pointerAddr, (int) fixedPointerType, new long[] { fixedPointerValue },
origBytes, null);
// Fixup the pointer boolean success = false;
memory.setLong(pointerAddr, fixedPointerValue); 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.lang.Register;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.util.DataConverter; import ghidra.util.DataConverter;
import ghidra.util.LittleEndianDataConverter; import ghidra.util.LittleEndianDataConverter;
@@ -302,20 +303,20 @@ public class MzLoader extends AbstractLibrarySupportLoader {
for (RelocationFixup relocationFixup : relocationFixups) { for (RelocationFixup relocationFixup : relocationFixups) {
SegmentedAddress relocationAddress = relocationFixup.address(); SegmentedAddress relocationAddress = relocationFixup.address();
Status status = Status.FAILURE;
try { try {
byte[] origBytes = new byte[2];
memory.getBytes(relocationAddress, origBytes);
memory.setShort(relocationAddress, (short) relocationFixup.segment()); memory.setShort(relocationAddress, (short) relocationFixup.segment());
status = Status.APPLIED;
// Add to relocation table
program.getRelocationTable()
.add(relocationAddress, 0, new long[] { relocationAddress.getSegment(),
relocationAddress.getSegmentOffset() }, origBytes, null);
} }
catch (AddressOutOfBoundsException | MemoryAccessException e) { catch (MemoryAccessException e) {
log.appendMsg(String.format("Failed to apply relocation: %s (%s)", log.appendMsg(String.format("Failed to apply relocation: %s (%s)",
relocationAddress, e.getMessage())); 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.lang.Register;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
@@ -655,15 +656,15 @@ public class NeLoader extends AbstractOrdinalSupportLoader {
} }
int relocType = reloc.getType(); int relocType = reloc.getType();
int byteLength = SegmentRelocation.TYPE_LENGTHS[relocType];
do { do {
// TODO: it appears that offset may be a far-offset and not always a segment-offset
SegmentedAddress address = space.getAddress(segment, offset); SegmentedAddress address = space.getAddress(segment, offset);
try { 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); offset = relocate(memory, reloc, address, relocAddr);
relocTable.add(address, Status.APPLIED, relocType, reloc.getValues(),
byteLength, null);
} }
catch (MemoryAccessException e) { catch (MemoryAccessException e) {
log.appendMsg("Relocation does not exist in memory: " + relocAddr); 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.lang.Language;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.DataConverter; import ghidra.util.DataConverter;
@@ -268,8 +269,9 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
} }
long[] values = new long[1]; long[] values = new long[1];
values[0] = finalvalue; values[0] = finalvalue;
program.getRelocationTable().add(state.locAddress, state.locationType, values, program.getRelocationTable()
origbytes, null); .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.data.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap; import ghidra.program.model.util.AddressSetPropertyMap;
@@ -305,7 +306,8 @@ public class PeLoader extends AbstractPeDebugLoader {
int baseAddr = reloc.getVirtualAddress(); int baseAddr = reloc.getVirtualAddress();
for (int i = 0; i < reloc.getCount(); ++i) { for (int i = 0; i < reloc.getCount(); ++i) {
long addr = optionalHeader.getImageBase() + baseAddr + reloc.getOffset(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 java.util.StringTokenizer;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXParseException;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.reloc.Relocation; import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.reloc.RelocationTable;
import ghidra.util.XmlProgramUtilities; import ghidra.util.XmlProgramUtilities;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@@ -42,15 +44,14 @@ class RelocationTableXmlMgr {
this.log = log; this.log = log;
} }
void read(XmlPullParser parser, TaskMonitor monitor) throws CancelledException { void read(XmlPullParser parser, TaskMonitor monitor)
throws SAXParseException, CancelledException {
RelocationTable relocTable = program.getRelocationTable(); RelocationTable relocTable = program.getRelocationTable();
AddressFactory factory = program.getAddressFactory(); AddressFactory factory = program.getAddressFactory();
XmlElement element = parser.next(); XmlElement element = parser.next();
while (true) { while (true) {
if (monitor.isCancelled()) { monitor.checkCanceled();
throw new CancelledException();
}
element = parser.next(); element = parser.next();
if (!element.getName().equals("RELOCATION")) { if (!element.getName().equals("RELOCATION")) {
break; break;
@@ -70,7 +71,29 @@ class RelocationTableXmlMgr {
byte[] bytes = unpackBytes(element.getAttribute("BYTES")); // optional byte[] bytes = unpackBytes(element.getAttribute("BYTES")); // optional
String symbolName = element.getAttribute("SYMBOL_NAME"); // 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) { catch (Exception e) {
log.appendException(e); log.appendException(e);
@@ -83,6 +106,9 @@ class RelocationTableXmlMgr {
} }
private long[] unpackLongs(String attrValue) { private long[] unpackLongs(String attrValue) {
if (attrValue == null) {
return null;
}
StringTokenizer st = new StringTokenizer(attrValue, ","); StringTokenizer st = new StringTokenizer(attrValue, ",");
long[] values = new long[st.countTokens()]; long[] values = new long[st.countTokens()];
int index = 0; int index = 0;
@@ -1704,9 +1704,10 @@ public class FlatProgramAPI {
* @param address the address at which to create a new Data object. * @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. * @param datatype the Data Type that describes the type of Data object to create.
* @return the newly created Data object * @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(); Listing listing = currentProgram.getListing();
Data d = listing.getDefinedDataAt(address); Data d = listing.getDefinedDataAt(address);
if (d != null) { if (d != null) {
@@ -160,8 +160,12 @@
<!ELEMENT RELOCATION EMPTY> <!ELEMENT RELOCATION EMPTY>
<!ATTLIST RELOCATION ADDRESS CDATA #REQUIRED> <!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 TYPE CDATA #REQUIRED>
<!ATTLIST RELOCATION VALUE CDATA #REQUIRED> <!ATTLIST RELOCATION VALUE CDATA #IMPLIED>
<!ATTLIST RELOCATION BYTES CDATA #IMPLIED> <!ATTLIST RELOCATION BYTES CDATA #IMPLIED>
<!ATTLIST RELOCATION SYMBOL_NAME CDATA #IMPLIED> <!ATTLIST RELOCATION SYMBOL_NAME CDATA #IMPLIED>
@@ -974,42 +974,27 @@ public class DexHeaderFormatMarkup {
} }
String string = stringDataItem.getString(); 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 { createStringSymbol(stringDataAddress, string, "strings");
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);
}
// markup string Id items // markup string Id items
DataType dataType = item.toDataType(); 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) + Data data = api.createData(address, dataType);
"\nString: " + string.trim() + "\nString Data Address: " + stringDataAddress); fragmentManager.stringsDataSet.add(address, address.add(dataType.getLength() - 1));
createStringSymbol(address, string, "string_data");
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; ++index;
address = address.add(dataType.getLength()); 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.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128; import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.file.formats.android.dex.util.DexUtil; import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.data.ArrayDataType; import ghidra.program.model.data.*;
import ghidra.program.model.data.CategoryPath; import ghidra.util.exception.AssertException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
/** /**
@@ -73,11 +70,17 @@ public class StringDataItem implements StructConverter {
} }
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() {
Structure structure = new StructureDataType("string_data_item_" + actualLength, 0); Structure structure = new StructureDataType("string_data_item_" + actualLength, 0);
structure.add(new ArrayDataType(BYTE, lebLength, BYTE.getLength()), "utf16_size", null); structure.add(new ArrayDataType(BYTE, lebLength, BYTE.getLength()), "utf16_size", null);
structure.add(UTF8, actualLength, "data", 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; return structure;
} }
@@ -107,9 +107,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* TypeDefSettingsDefinition. Added DataTypeManager versioning * TypeDefSettingsDefinition. Added DataTypeManager versioning
* support. Added support for auto-named TypeDef datatypes. * support. Added support for auto-named TypeDef datatypes.
* Transitioned string data translation storage to use address-based * 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 * 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 EXTERNAL_FUNCTIONS_ADDED_VERSION = 17;
public static final int COMPOUND_VARIABLE_STORAGE_ADDED_VERSION = 18; public static final int COMPOUND_VARIABLE_STORAGE_ADDED_VERSION = 18;
public static final int AUTO_PARAMETERS_ADDED_VERSION = 19; 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"; private static final String DATA_MAP_TABLE_NAME = "Program";
@@ -16,12 +16,19 @@
package ghidra.program.database.reloc; package ghidra.program.database.reloc;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import db.*; import db.*;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.*;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.Program;
import ghidra.util.exception.VersionException; 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; import ghidra.util.task.TaskMonitor;
abstract class RelocationDBAdapter { abstract class RelocationDBAdapter {
@@ -32,30 +39,46 @@ abstract class RelocationDBAdapter {
// V3 - added Bytes // V3 - added Bytes
// V4 - added Name, switched Value to binary coded long[] from long // V4 - added Name, switched Value to binary coded long[] from long
// V5 - moved Addr key to column and indexed, use one-up key // 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 ADDR_COL = 0; // indexed
final static int TYPE_COL = 1; final static int FLAGS_COL = 1; // includes status enum and optional length when bytes==null
final static int VALUE_COL = 2; final static int TYPE_COL = 2;
final static int BYTES_COL = 3; final static int VALUE_COL = 3; // binary coded long[]
final static int SYMBOL_NAME_COL = 4; 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 String TABLE_NAME = "Relocations";
final static Schema SCHEMA = new Schema( 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 }, 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, static RelocationDBAdapter getAdapter(DBHandle dbHandle, int openMode, AddressMap addrMap,
TaskMonitor monitor) throws VersionException, IOException { TaskMonitor monitor) throws VersionException, IOException {
try { try {
return new RelocationDBAdapterV5(dbHandle, addrMap, openMode == DBConstants.CREATE); return new RelocationDBAdapterV6(dbHandle, addrMap, openMode == DBConstants.CREATE);
} }
catch (VersionException e) { catch (VersionException e) {
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
throw e; throw e;
} }
RelocationDBAdapter adapter = findReadOnlyAdapter(dbHandle, addrMap); RelocationDBAdapter adapter =
findReadOnlyAdapter(dbHandle, addrMap);
if (openMode == DBConstants.UPGRADE) { if (openMode == DBConstants.UPGRADE) {
adapter = upgrade(dbHandle, addrMap, adapter, monitor); adapter = upgrade(dbHandle, addrMap, adapter, monitor);
} }
@@ -65,6 +88,12 @@ abstract class RelocationDBAdapter {
private static RelocationDBAdapter findReadOnlyAdapter(DBHandle handle, AddressMap addrMap) private static RelocationDBAdapter findReadOnlyAdapter(DBHandle handle, AddressMap addrMap)
throws IOException, VersionException { throws IOException, VersionException {
try {
return new RelocationDBAdapterV5(handle, addrMap);
}
catch (VersionException e) {
// try the next version
}
try { try {
return new RelocationDBAdapterV4(handle, addrMap); return new RelocationDBAdapterV4(handle, addrMap);
} }
@@ -102,7 +131,8 @@ abstract class RelocationDBAdapter {
try { try {
tmpHandle.startTransaction(); tmpHandle.startTransaction();
RelocationDBAdapter tmpAdapter = new RelocationDBAdapterV5(tmpHandle, addrMap, true); RelocationDBAdapter tmpAdapter =
new RelocationDBAdapterV6(tmpHandle, addrMap, true);
RecordIterator iter = oldAdapter.iterator(); RecordIterator iter = oldAdapter.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
DBRecord rec = iter.next(); DBRecord rec = iter.next();
@@ -110,19 +140,20 @@ abstract class RelocationDBAdapter {
Address addr = oldAddrMap.decodeAddress(rec.getLongValue(ADDR_COL)); Address addr = oldAddrMap.decodeAddress(rec.getLongValue(ADDR_COL));
BinaryCodedField values = BinaryCodedField values =
new BinaryCodedField((BinaryField) rec.getFieldValue(VALUE_COL)); 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), values.getLongArray(), rec.getBinaryData(BYTES_COL),
rec.getString(SYMBOL_NAME_COL)); rec.getString(SYMBOL_NAME_COL));
} }
dbHandle.deleteTable(TABLE_NAME); dbHandle.deleteTable(TABLE_NAME);
RelocationDBAdapterV5 newAdapter = new RelocationDBAdapterV5(dbHandle, addrMap, true); RelocationDBAdapterV6 newAdapter =
new RelocationDBAdapterV6(dbHandle, addrMap, true);
iter = tmpAdapter.iterator(); iter = tmpAdapter.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
DBRecord rec = iter.next(); DBRecord rec = iter.next();
newAdapter.add(rec); newAdapter.put(rec);
} }
return newAdapter; 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 * Add new relocation record
* @param addr relocation address * @param addr relocation address
* @param flags encoded flags (status, length), see {@link #getFlags(Status, int)}.
* @param type relocation type * @param type relocation type
* @param values relocation value (e.g., symbol index) * @param values relocation value (e.g., symbol index)
* @param bytes original memory bytes * @param bytes original memory bytes
* @param symbolName symbol name * @param symbolName symbol name
* @throws IOException if a database error occurs * @throws IOException if a database error occurs
*/ */
abstract void add(Address addr, int type, long[] values, byte[] bytes, String symbolName) abstract void add(Address addr, byte flags, int type, long[] values, byte[] bytes,
throws IOException; String symbolName) throws IOException;
/** /**
* Iterator over all records in address order. * Iterator over all records in address order.
@@ -183,9 +254,151 @@ abstract class RelocationDBAdapter {
*/ */
abstract DBRecord adaptRecord(DBRecord rec); 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 { class RecordIteratorAdapter implements RecordIterator {
RecordIterator it; RecordIterator it;
@@ -43,7 +43,8 @@ class RelocationDBAdapterNoTable extends RelocationDBAdapter {
} }
@Override @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(); throw new UnsupportedOperationException();
} }
@@ -22,6 +22,7 @@ import ghidra.program.database.map.AddressKeyRecordIterator;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException; import ghidra.util.exception.VersionException;
class RelocationDBAdapterV1 extends RelocationDBAdapter { class RelocationDBAdapterV1 extends RelocationDBAdapter {
@@ -29,6 +30,7 @@ class RelocationDBAdapterV1 extends RelocationDBAdapter {
private final static int V1_TYPE_COL = 0; 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( // final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV1.VERSION, "Address", new Field[] { IntField.INSTANCE }, // RelocationDBAdapterV1.VERSION, "Address", new Field[] { IntField.INSTANCE },
// new String[] { "Type" }); // new String[] { "Type" });
@@ -43,8 +45,8 @@ class RelocationDBAdapterV1 extends RelocationDBAdapter {
* @throws IOException if database IO error occurs * @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V1 * @throws VersionException throw if table schema is not V1
*/ */
RelocationDBAdapterV1(DBHandle handle, AddressMap addrMap) throws IOException, RelocationDBAdapterV1(DBHandle handle, AddressMap addrMap)
VersionException { throws IOException, VersionException {
this.addrMap = addrMap; this.addrMap = addrMap;
relocTable = handle.getTable(TABLE_NAME); relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) { if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
@@ -53,7 +55,8 @@ class RelocationDBAdapterV1 extends RelocationDBAdapter {
} }
@Override @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(); throw new UnsupportedOperationException();
} }
@@ -88,6 +91,7 @@ class RelocationDBAdapterV1 extends RelocationDBAdapter {
} }
DBRecord newRec = SCHEMA.createRecord(rec.getKey()); DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getKey()); // key was encoded address 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)); newRec.setIntValue(TYPE_COL, rec.getIntValue(V1_TYPE_COL));
return newRec; return newRec;
} }
@@ -22,6 +22,7 @@ import ghidra.program.database.map.AddressKeyRecordIterator;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException; import ghidra.util.exception.VersionException;
class RelocationDBAdapterV2 extends RelocationDBAdapter { 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_TYPE_COL = 0;
private final static int V2_VALUE_COL = 1; 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( // final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV2.VERSION, "Address", new Field[] { IntField.INSTANCE, LongField.INSTANCE }, // RelocationDBAdapterV2.VERSION, "Address", new Field[] { IntField.INSTANCE, LongField.INSTANCE },
// new String[] { "Type", "Values" }); // new String[] { "Type", "Values" });
@@ -44,8 +46,8 @@ class RelocationDBAdapterV2 extends RelocationDBAdapter {
* @throws IOException if database IO error occurs * @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V2 * @throws VersionException throw if table schema is not V2
*/ */
RelocationDBAdapterV2(DBHandle handle, AddressMap addrMap) throws IOException, RelocationDBAdapterV2(DBHandle handle, AddressMap addrMap)
VersionException { throws IOException, VersionException {
this.addrMap = addrMap; this.addrMap = addrMap;
relocTable = handle.getTable(TABLE_NAME); relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) { if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
@@ -54,7 +56,8 @@ class RelocationDBAdapterV2 extends RelocationDBAdapter {
} }
@Override @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(); throw new UnsupportedOperationException();
} }
@@ -89,6 +92,7 @@ class RelocationDBAdapterV2 extends RelocationDBAdapter {
} }
DBRecord newRec = SCHEMA.createRecord(rec.getKey()); DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getKey()); // key was encoded address 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)); newRec.setIntValue(TYPE_COL, rec.getIntValue(V2_TYPE_COL));
long[] values = new long[] { rec.getLongValue(V2_VALUE_COL) }; long[] values = new long[] { rec.getLongValue(V2_VALUE_COL) };
newRec.setField(VALUE_COL, new BinaryCodedField(values)); 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.database.map.AddressMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException; import ghidra.util.exception.VersionException;
class RelocationDBAdapterV3 extends RelocationDBAdapter { 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_VALUE_COL = 1;
private final static int V3_BYTES_COL = 2; 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( // final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV3.VERSION, "Address", new Field[] { IntField.INSTANCE, // RelocationDBAdapterV3.VERSION, "Address", new Field[] { IntField.INSTANCE,
// BinaryField.INSTANCE, BinaryField.INSTANCE }, // BinaryField.INSTANCE, BinaryField.INSTANCE },
@@ -47,8 +49,8 @@ class RelocationDBAdapterV3 extends RelocationDBAdapter {
* @throws IOException if database IO error occurs * @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V3 * @throws VersionException throw if table schema is not V3
*/ */
RelocationDBAdapterV3(DBHandle handle, AddressMap addrMap) throws IOException, RelocationDBAdapterV3(DBHandle handle, AddressMap addrMap)
VersionException { throws IOException, VersionException {
this.addrMap = addrMap; this.addrMap = addrMap;
relocTable = handle.getTable(TABLE_NAME); relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) { if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
@@ -57,7 +59,7 @@ class RelocationDBAdapterV3 extends RelocationDBAdapter {
} }
@Override @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 { throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@@ -93,6 +95,7 @@ class RelocationDBAdapterV3 extends RelocationDBAdapter {
} }
DBRecord newRec = SCHEMA.createRecord(rec.getKey()); DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getKey()); // key was encoded address 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.setIntValue(TYPE_COL, rec.getIntValue(V3_TYPE_COL));
newRec.setBinaryData(VALUE_COL, rec.getBinaryData(V3_VALUE_COL)); newRec.setBinaryData(VALUE_COL, rec.getBinaryData(V3_VALUE_COL));
newRec.setBinaryData(BYTES_COL, rec.getBinaryData(V3_BYTES_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.database.map.AddressMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException; import ghidra.util.exception.VersionException;
public class RelocationDBAdapterV4 extends RelocationDBAdapter { 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_BYTES_COL = 2;
private final static int V4_SYMBOL_NAME_COL = 3; 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( // final static Schema SCHEMA = new Schema(
// RelocationDBAdapterV4.VERSION, "Address", new Field[] { IntField.INSTANCE, // RelocationDBAdapterV4.VERSION, "Address", new Field[] { IntField.INSTANCE,
// BinaryField.INSTANCE, BinaryField.INSTANCE, StringField.INSTANCE }, // BinaryField.INSTANCE, BinaryField.INSTANCE, StringField.INSTANCE },
@@ -47,8 +49,8 @@ public class RelocationDBAdapterV4 extends RelocationDBAdapter {
* @throws IOException if database IO error occurs * @throws IOException if database IO error occurs
* @throws VersionException throw if table schema is not V4 * @throws VersionException throw if table schema is not V4
*/ */
RelocationDBAdapterV4(DBHandle handle, AddressMap addrMap) throws IOException, RelocationDBAdapterV4(DBHandle handle, AddressMap addrMap)
VersionException { throws IOException, VersionException {
this.addrMap = addrMap; this.addrMap = addrMap;
relocTable = handle.getTable(TABLE_NAME); relocTable = handle.getTable(TABLE_NAME);
if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) { if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
@@ -57,7 +59,7 @@ public class RelocationDBAdapterV4 extends RelocationDBAdapter {
} }
@Override @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 { throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@@ -93,6 +95,7 @@ public class RelocationDBAdapterV4 extends RelocationDBAdapter {
} }
DBRecord newRec = SCHEMA.createRecord(rec.getKey()); DBRecord newRec = SCHEMA.createRecord(rec.getKey());
newRec.setLongValue(ADDR_COL, rec.getKey()); // key was encoded address 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.setIntValue(TYPE_COL, rec.getIntValue(V4_TYPE_COL));
newRec.setBinaryData(VALUE_COL, rec.getBinaryData(V4_VALUE_COL)); // binary coded long[] newRec.setBinaryData(VALUE_COL, rec.getBinaryData(V4_VALUE_COL)); // binary coded long[]
newRec.setBinaryData(BYTES_COL, rec.getBinaryData(V4_BYTES_COL)); 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.database.map.AddressMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.VersionException; import ghidra.util.exception.VersionException;
public class RelocationDBAdapterV5 extends RelocationDBAdapter { public class RelocationDBAdapterV5 extends RelocationDBAdapter {
final static int VERSION = 5; 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 Table relocTable;
private AddressMap addrMap; 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; this.addrMap = addrMap;
if (create) { relocTable = handle.getTable(TABLE_NAME);
relocTable = handle.createTable(TABLE_NAME, SCHEMA, new int[] { ADDR_COL }); if (relocTable == null || relocTable.getSchema().getVersion() != VERSION) {
} throw new VersionException();
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 @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 { throws IOException {
long key = relocTable.getKey(); throw new UnsupportedOperationException();
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);
} }
@Override @Override
@@ -67,35 +72,41 @@ public class RelocationDBAdapterV5 extends RelocationDBAdapter {
@Override @Override
RecordIterator iterator() throws IOException { RecordIterator iterator() throws IOException {
return new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable, RecordIterator recIter =
new KeyToRecordIterator(relocTable, new AddressIndexPrimaryKeyIterator(relocTable,
ADDR_COL, addrMap, true)); ADDR_COL, addrMap, true));
return new RecordIteratorAdapter(recIter);
} }
@Override @Override
RecordIterator iterator(AddressSetView set) throws IOException { 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)); ADDR_COL, addrMap, set, true));
return new RecordIteratorAdapter(recIter);
} }
@Override @Override
RecordIterator iterator(Address start) throws IOException { 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)); ADDR_COL, addrMap, start, true));
return new RecordIteratorAdapter(recIter);
} }
@Override @Override
DBRecord adaptRecord(DBRecord rec) { DBRecord adaptRecord(DBRecord rec) {
// my guess is that we don't need to do this until there is a version newer than us if (rec == null) {
throw new UnsupportedOperationException("Don't know how to adapt to the new version"); return null;
} }
DBRecord newRec = SCHEMA.createRecord(rec.getKey());
/** newRec.setLongValue(ADDR_COL, rec.getLongValue(V5_ADDR_COL));
* Add V5 relocation record to table. newRec.setByteValue(FLAGS_COL, getFlags(Status.UNKNOWN, 0));
* @param rec relocation record newRec.setIntValue(TYPE_COL, rec.getIntValue(V5_TYPE_COL));
* @throws IOException if database IO error occurs newRec.setBinaryData(VALUE_COL, rec.getBinaryData(V5_VALUE_COL)); // binary coded long[]
*/ newRec.setBinaryData(BYTES_COL, rec.getBinaryData(V5_BYTES_COL));
void add(DBRecord rec) throws IOException { newRec.setString(SYMBOL_NAME_COL, rec.getString(V5_SYMBOL_NAME_COL));
relocTable.putRecord(rec); 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.ProgramDB;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.database.mem.AddressSourceInfo; import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program; 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;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.reloc.RelocationTable;
import ghidra.util.Lock; import ghidra.util.Lock;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@@ -84,39 +84,91 @@ public class RelocationManager implements RelocationTable, ManagerDB {
@Override @Override
public void programReady(int openMode, int currentRevision, TaskMonitor monitor) public void programReady(int openMode, int currentRevision, TaskMonitor monitor)
throws IOException, CancelledException { 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) { * Get default byte length when unknown
return bytes; * @param program program containing relocation
} * @return default byte length
int byteCount = program.getDefaultPointerSize() > 4 ? 8 : 4; */
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]; byte[] originalBytes = new byte[byteCount];
AddressSourceInfo addressSourceInfo = program.getMemory().getAddressSourceInfo(addr); // must get one byte at a time due to the possibility of byte-mapped memory use
if (addressSourceInfo == null) { for (int i = 0; i < byteCount; i++) {
return null; if (i != 0) {
} addr = addr.next();
MemoryBlockSourceInfo memoryBlockSourceInfo = addressSourceInfo.getMemoryBlockSourceInfo(); }
Optional<FileBytes> optional = memoryBlockSourceInfo.getFileBytes(); if (addr == null) {
if (!optional.isEmpty()) { break;
FileBytes fileBytes = optional.get(); }
long fileBytesOffset = addressSourceInfo.getFileOffset(); AddressSourceInfo addressSourceInfo = memory.getAddressSourceInfo(addr);
long offsetIntoSourceRange = if (addressSourceInfo == null) {
fileBytesOffset - memoryBlockSourceInfo.getFileBytesOffset(); return originalBytes;
long available = memoryBlockSourceInfo.getLength() - offsetIntoSourceRange; }
int readSize = (int) Math.min(available, byteCount); originalBytes[i] = addressSourceInfo.getOriginalValue();
fileBytes.getOriginalBytes(fileBytesOffset, originalBytes, 0, readSize);
} }
return originalBytes; 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 @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(); lock.acquire();
try { try {
adapter.add(addr, type, values, bytes, symbolName); byte flags = RelocationDBAdapter.getFlags(status, 0);
return new Relocation(addr, type, values, getOriginalBytes(addr, bytes), symbolName); 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) { catch (IOException e) {
program.dbError(e); program.dbError(e);
@@ -178,11 +230,15 @@ public class RelocationManager implements RelocationTable, ManagerDB {
private Relocation getRelocation(DBRecord rec) throws IOException { private Relocation getRelocation(DBRecord rec) throws IOException {
Address addr = addrMap.decodeAddress(rec.getLongValue(RelocationDBAdapter.ADDR_COL)); 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 = BinaryCodedField valuesField =
new BinaryCodedField((BinaryField) rec.getFieldValue(RelocationDBAdapter.VALUE_COL)); new BinaryCodedField((BinaryField) rec.getFieldValue(RelocationDBAdapter.VALUE_COL));
byte[] originalBytes = byte[] originalBytes =
getOriginalBytes(addr, rec.getBinaryData(RelocationDBAdapter.BYTES_COL)); getOriginalBytes(addr, status, rec.getBinaryData(RelocationDBAdapter.BYTES_COL),
return new Relocation(addr, rec.getIntValue(RelocationDBAdapter.TYPE_COL), length);
return new Relocation(addr, status, rec.getIntValue(RelocationDBAdapter.TYPE_COL),
valuesField.getLongArray(), valuesField.getLongArray(),
originalBytes, rec.getString(RelocationDBAdapter.SYMBOL_NAME_COL)); originalBytes, rec.getString(RelocationDBAdapter.SYMBOL_NAME_COL));
} }
@@ -22,7 +22,96 @@ import ghidra.program.model.address.Address;
* program relocation. * program relocation.
*/ */
public class 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 Address addr;
private Status status;
private int type; private int type;
private long[] values; private long[] values;
private byte[] bytes; private byte[] bytes;
@@ -32,14 +121,17 @@ public class Relocation {
* Constructs a new relocation. * Constructs a new relocation.
* *
* @param addr the address where the relocation is required * @param addr the address where the relocation is required
* @param status relocation status
* @param type the type of relocation to perform * @param type the type of relocation to perform
* @param values the values needed when performing the relocation. Definition of values is * @param values the values needed when performing the relocation. Definition of values is
* specific to loader used and relocation type. * specific to loader used and relocation type.
* @param bytes original instruction bytes affected by relocation * @param bytes original instruction bytes affected by relocation
* @param symbolName the name of the symbol being relocated * @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.addr = addr;
this.status = status;
this.type = type; this.type = type;
this.values = values; this.values = values;
this.bytes = bytes; this.bytes = bytes;
@@ -55,6 +147,15 @@ public class Relocation {
return addr; 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. * 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.database.mem.FileBytes;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.reloc.Relocation.Status;
/** /**
* An interface for storing the relocations defined in a program. * 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"; public static final String RELOCATABLE_PROP_NAME = "Relocatable";
/** /**
* Creates and adds a new relocation with the specified * Adds a new relocation entry when the original bytes being replaced are to be specified.
* address, type, and value.
* *
* @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 type the type of relocation to perform
* @param values the values needed when performing the relocation. Definition of values is * @param values relocation-specific values which may be useful in diagnosing relocation;
* specific to loader used and relocation type. * may be null.
* @param bytes original instruction bytes affected by relocation. A null value should be * @param bytes original memory bytes affected by relocation. A null value may be
* passed to rely on original underlying {@link FileBytes}. * 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 * @param symbolName the name of the symbol being relocated; may be null
* @return the newly added relocation object * @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. * 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.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.NotFoundException; import ghidra.util.exception.NotFoundException;
public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler { public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
@@ -37,12 +39,13 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
} }
@Override @Override
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation, public RelocationResult relocate(ElfRelocationContext elfRelocationContext,
ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException { Address relocationAddress) throws MemoryAccessException, NotFoundException {
ElfHeader elf = elfRelocationContext.getElfHeader(); ElfHeader elf = elfRelocationContext.getElfHeader();
if (elf.e_machine() != ElfConstants.EM_AARCH64) { if (elf.e_machine() != ElfConstants.EM_AARCH64) {
return; return RelocationResult.FAILURE;
} }
Program program = elfRelocationContext.getProgram(); Program program = elfRelocationContext.getProgram();
@@ -50,7 +53,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
int type = relocation.getType(); int type = relocation.getType();
if (type == AARCH64_ElfRelocationConstants.R_AARCH64_NONE) { if (type == AARCH64_ElfRelocationConstants.R_AARCH64_NONE) {
return; return RelocationResult.SKIPPED;
} }
int symbolIndex = relocation.getSymbolIndex(); int symbolIndex = relocation.getSymbolIndex();
@@ -72,6 +75,8 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
long symbolValue = elfRelocationContext.getSymbolValue(sym); long symbolValue = elfRelocationContext.getSymbolValue(sym);
long newValue = 0; long newValue = 0;
int byteLength = 4; // most relocations affect 4-bytes (change if different)
switch (type) { switch (type) {
// .xword: (S+A) // .xword: (S+A)
case AARCH64_ElfRelocationConstants.R_AARCH64_ABS64: { case AARCH64_ElfRelocationConstants.R_AARCH64_ABS64: {
@@ -82,6 +87,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
symbolAddr, symbolName, addend, elfRelocationContext.getLog()); symbolAddr, symbolName, addend, elfRelocationContext.getLog());
applyComponentOffsetPointer(program, relocationAddress, addend); applyComponentOffsetPointer(program, relocationAddress, addend);
} }
byteLength = 8;
break; break;
} }
@@ -99,6 +105,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
case AARCH64_ElfRelocationConstants.R_AARCH64_P32_ABS16: { case AARCH64_ElfRelocationConstants.R_AARCH64_P32_ABS16: {
newValue = (symbolValue + addend); newValue = (symbolValue + addend);
memory.setShort(relocationAddress, (short) (newValue & 0xffff)); memory.setShort(relocationAddress, (short) (newValue & 0xffff));
byteLength = 2;
break; break;
} }
@@ -107,6 +114,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
newValue = (symbolValue + addend); newValue = (symbolValue + addend);
newValue -= (offset); // PC relative newValue -= (offset); // PC relative
memory.setLong(relocationAddress, newValue); memory.setLong(relocationAddress, newValue);
byteLength = 8;
break; break;
} }
@@ -125,6 +133,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
newValue = (symbolValue + addend); newValue = (symbolValue + addend);
newValue -= (offset); // PC relative newValue -= (offset); // PC relative
memory.setShort(relocationAddress, (short) (newValue & 0xffff)); memory.setShort(relocationAddress, (short) (newValue & 0xffff));
byteLength = 2;
break; break;
} }
@@ -238,7 +247,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
addend = getValue(memory, relocationAddress, is64bit); addend = getValue(memory, relocationAddress, is64bit);
} }
newValue = symbolValue + addend; newValue = symbolValue + addend;
setValue(memory, relocationAddress, newValue, is64bit); byteLength = setValue(memory, relocationAddress, newValue, is64bit);
break; break;
} }
@@ -251,11 +260,13 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
// GOT entry bytes if it refers to .plt block // GOT entry bytes if it refers to .plt block
Address symAddress = elfRelocationContext.getSymbolAddress(sym); Address symAddress = elfRelocationContext.getSymbolAddress(sym);
MemoryBlock block = memory.getBlock(symAddress); 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 isPltSym = block != null && block.getName().startsWith(".plt");
boolean isExternalSym = boolean isExternalSym =
block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName()); block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName());
if (!isPltSym) { if (!isPltSym) {
setValue(memory, relocationAddress, symAddress.getOffset(), is64bit); byteLength =
setValue(memory, relocationAddress, symAddress.getOffset(), is64bit);
} }
if ((isPltSym || isExternalSym) && !StringUtils.isBlank(symbolName)) { if ((isPltSym || isExternalSym) && !StringUtils.isBlank(symbolName)) {
Function extFunction = Function extFunction =
@@ -265,7 +276,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
markAsError(program, relocationAddress, "R_AARCH64_JUMP_SLOT", symbolName, markAsError(program, relocationAddress, "R_AARCH64_JUMP_SLOT", symbolName,
"Failed to create R_AARCH64_JUMP_SLOT external function", "Failed to create R_AARCH64_JUMP_SLOT external function",
elfRelocationContext.getLog()); elfRelocationContext.getLog());
return; // relocation already applied above
} }
} }
break; break;
@@ -278,7 +289,7 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
addend = getValue(memory, relocationAddress, is64bit); addend = getValue(memory, relocationAddress, is64bit);
} }
newValue = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend; newValue = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend;
setValue(memory, relocationAddress, newValue, is64bit); byteLength = setValue(memory, relocationAddress, newValue, is64bit);
break; break;
} }
@@ -286,14 +297,16 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
case AARCH64_ElfRelocationConstants.R_AARCH64_COPY: { case AARCH64_ElfRelocationConstants.R_AARCH64_COPY: {
markAsWarning(program, relocationAddress, "R_AARCH64_COPY", symbolName, symbolIndex, markAsWarning(program, relocationAddress, "R_AARCH64_COPY", symbolName, symbolIndex,
"Runtime copy not supported", elfRelocationContext.getLog()); "Runtime copy not supported", elfRelocationContext.getLog());
return RelocationResult.UNSUPPORTED;
} }
default: { default: {
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName, markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
elfRelocationContext.getLog()); 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 addr address to set new value
* @param value value * @param value value
* @param is64bit true if value is 64, false if 32bit * @param is64bit true if value is 64, false if 32bit
* return value byte-length
* @throws MemoryAccessException on set of value * @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 { throws MemoryAccessException {
if (is64bit) { if (is64bit) {
memory.setLong(addr, value); memory.setLong(addr, value);
} else { return 8;
memory.setInt(addr, (int) value);
} }
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 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.app.util.bin.format.macho.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryAccessException; 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.Conv;
import ghidra.util.exception.NotFoundException;
/** /**
* A {@link MachoRelocationHandler} for AARCH64 * A {@link MachoRelocationHandler} for AARCH64
@@ -42,11 +44,11 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
} }
@Override @Override
public void relocate(MachoRelocation relocation) public RelocationResult relocate(MachoRelocation relocation)
throws MemoryAccessException, NotFoundException { throws MemoryAccessException, RelocationException {
if (!relocation.requiresRelocation()) { if (!relocation.requiresRelocation()) {
return; return RelocationResult.SKIPPED;
} }
RelocationInfo relocationInfo = relocation.getRelocationInfo(); RelocationInfo relocationInfo = relocation.getRelocationInfo();
@@ -69,21 +71,22 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
} }
long orig = read(relocation); long orig = read(relocation);
int byteLength;
switch (relocationInfo.getType()) { switch (relocationInfo.getType()) {
case ARM64_RELOC_UNSIGNED: case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_POINTER_TO_GOT: { case ARM64_RELOC_POINTER_TO_GOT: {
long addend = orig; long addend = orig;
long value = targetAddr.getOffset() + addend; long value = targetAddr.getOffset() + addend;
write(relocation, value); byteLength = write(relocation, value);
break; break;
} }
case ARM64_RELOC_SUBTRACTOR: { case ARM64_RELOC_SUBTRACTOR: {
Address targetAddrExtra = relocation.getTargetAddressExtra(); Address targetAddrExtra = relocation.getTargetAddressExtra();
if (orig > 0) { if (orig > 0) {
write(relocation, targetAddrExtra.add(orig).subtract(targetAddr)); byteLength = write(relocation, targetAddrExtra.add(orig).subtract(targetAddr));
} }
else { else {
write(relocation, targetAddr.add(orig).subtract(targetAddrExtra)); byteLength = write(relocation, targetAddr.add(orig).subtract(targetAddrExtra));
} }
break; break;
} }
@@ -91,7 +94,7 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
long addend = orig & 0x3ffffff; long addend = orig & 0x3ffffff;
long value = (targetAddr.subtract(relocAddr) >> 2) + addend; long value = (targetAddr.subtract(relocAddr) >> 2) + addend;
long instr = orig | (value & 0x3ffffff); long instr = orig | (value & 0x3ffffff);
write(relocation, instr); byteLength = write(relocation, instr);
break; break;
} }
case ARM64_RELOC_PAGE21: case ARM64_RELOC_PAGE21:
@@ -106,7 +109,7 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
long value = ((pageTarget - pageReloc) >> 12) & 0x1fffff; long value = ((pageTarget - pageReloc) >> 12) & 0x1fffff;
long instr = long instr =
(orig & 0x9f00001f) | ((value << 3) & 0x7ffffe0) | ((value & 0x3) << 29); (orig & 0x9f00001f) | ((value << 3) & 0x7ffffe0) | ((value & 0x3) << 29);
write(relocation, instr); byteLength = write(relocation, instr);
break; break;
} }
case ARM64_RELOC_PAGEOFF12: case ARM64_RELOC_PAGEOFF12:
@@ -126,13 +129,13 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
long value = (targetAddr.getOffset() + addend) & 0xfff; long value = (targetAddr.getOffset() + addend) & 0xfff;
instr = orig | (value << 10); instr = orig | (value << 10);
} }
write(relocation, instr); byteLength = write(relocation, instr);
break; break;
} }
case ARM64_RELOC_AUTHENTICATED_POINTER: { case ARM64_RELOC_AUTHENTICATED_POINTER: {
long addend = orig & Conv.INT_MASK; long addend = orig & Conv.INT_MASK;
long value = targetAddr.getOffset() + addend; long value = targetAddr.getOffset() + addend;
write(relocation, value); byteLength = write(relocation, value);
break; break;
} }
@@ -140,8 +143,9 @@ public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler {
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: // not seen yet case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: // not seen yet
case ARM64_RELOC_ADDEND: // should never see on its own here case ARM64_RELOC_ADDEND: // should never see on its own here
default: 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.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.NotFoundException; import ghidra.util.exception.NotFoundException;
public class ARM_ElfRelocationHandler extends ElfRelocationHandler { public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
@@ -43,13 +45,13 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
} }
@Override @Override
public void relocate(ElfRelocationContext context, ElfRelocation relocation, public RelocationResult relocate(ElfRelocationContext context, ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException { Address relocationAddress) throws MemoryAccessException, NotFoundException {
ElfHeader elf = context.getElfHeader(); ElfHeader elf = context.getElfHeader();
if (elf.e_machine() != ElfConstants.EM_ARM || if (elf.e_machine() != ElfConstants.EM_ARM ||
!(context instanceof ARM_ElfRelocationContext)) { !(context instanceof ARM_ElfRelocationContext)) {
return; return RelocationResult.FAILURE;
} }
ARM_ElfRelocationContext elfRelocationContext = (ARM_ElfRelocationContext) context; ARM_ElfRelocationContext elfRelocationContext = (ARM_ElfRelocationContext) context;
@@ -62,7 +64,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
int type = relocation.getType(); int type = relocation.getType();
if (type == ARM_ElfRelocationConstants.R_ARM_NONE) { if (type == ARM_ElfRelocationConstants.R_ARM_NONE) {
return; return RelocationResult.SKIPPED;
} }
int symbolIndex = relocation.getSymbolIndex(); int symbolIndex = relocation.getSymbolIndex();
@@ -80,6 +82,8 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
int newValue = 0; int newValue = 0;
int byteLength = 4; // most relocations affect 4-bytes (change if different)
switch (type) { switch (type) {
case ARM_ElfRelocationConstants.R_ARM_PC24: { // Target class: ARM Instruction case ARM_ElfRelocationConstants.R_ARM_PC24: { // Target class: ARM Instruction
int oldValue = memory.getInt(relocationAddress, instructionBigEndian); 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 case ARM_ElfRelocationConstants.R_ARM_ABS16: { // Target class: Data
short sValue = (short) (symbolValue + addend); short sValue = (short) (symbolValue + addend);
memory.setShort(relocationAddress, sValue); memory.setShort(relocationAddress, sValue);
byteLength = 2;
break; break;
} }
case ARM_ElfRelocationConstants.R_ARM_ABS12: { // Target class: ARM Instruction 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 case ARM_ElfRelocationConstants.R_ARM_ABS_8: { // Target class: Data
byte bValue = (byte) (symbolValue + addend); byte bValue = (byte) (symbolValue + addend);
memory.setByte(relocationAddress, bValue); memory.setByte(relocationAddress, bValue);
byteLength = 1;
break; break;
} }
/* /*
@@ -221,6 +227,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
newValue = newValue >> 1; newValue = newValue >> 1;
short sValue = (short) ((oldValue & 0xff00) | (newValue & 0x00ff)); short sValue = (short) ((oldValue & 0xff00) | (newValue & 0x00ff));
memory.setShort(relocationAddress, sValue, instructionBigEndian); memory.setShort(relocationAddress, sValue, instructionBigEndian);
byteLength = 2;
break; break;
} }
/* /*
@@ -270,6 +277,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
// GOT entry bytes if it refers to .plt block // GOT entry bytes if it refers to .plt block
Address symAddress = elfRelocationContext.getSymbolAddress(sym); Address symAddress = elfRelocationContext.getSymbolAddress(sym);
MemoryBlock block = memory.getBlock(symAddress); 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 isPltSym = block != null && block.getName().startsWith(".plt");
boolean isExternalSym = boolean isExternalSym =
block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName()); 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, markAsError(program, relocationAddress, "R_ARM_JUMP_SLOT", symbolName,
"Failed to create R_ARM_JUMP_SLOT external function", "Failed to create R_ARM_JUMP_SLOT external function",
elfRelocationContext.getLog()); elfRelocationContext.getLog());
return; // relocation already applied above
} }
} }
break; break;
@@ -592,6 +600,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
newValue -= (offset + elfRelocationContext.getPcBias(true)); // PC relative newValue -= (offset + elfRelocationContext.getPcBias(true)); // PC relative
newValue = (oldValue & 0x0000f800) | ((newValue >> 1) & 0x000007ff); newValue = (oldValue & 0x0000f800) | ((newValue >> 1) & 0x000007ff);
memory.setShort(relocationAddress, (short) newValue, instructionBigEndian); memory.setShort(relocationAddress, (short) newValue, instructionBigEndian);
byteLength = 2;
break; break;
} }
case ARM_ElfRelocationConstants.R_ARM_THM_JUMP8: { 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 -= (offset + elfRelocationContext.getPcBias(true)); // PC relative
newValue = (oldValue & 0x0000ff00) | ((newValue >> 1) & 0x000000ff); newValue = (oldValue & 0x0000ff00) | ((newValue >> 1) & 0x000000ff);
memory.setShort(relocationAddress, (short) newValue, instructionBigEndian); memory.setShort(relocationAddress, (short) newValue, instructionBigEndian);
byteLength = 2;
break; break;
} }
/* /*
@@ -689,15 +699,16 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
case ARM_ElfRelocationConstants.R_ARM_COPY: { case ARM_ElfRelocationConstants.R_ARM_COPY: {
markAsWarning(program, relocationAddress, "R_ARM_COPY", symbolName, symbolIndex, markAsWarning(program, relocationAddress, "R_ARM_COPY", symbolName, symbolIndex,
"Runtime copy not supported", elfRelocationContext.getLog()); "Runtime copy not supported", elfRelocationContext.getLog());
break; return RelocationResult.UNSUPPORTED;
} }
default: { default: {
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName, markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
elfRelocationContext.getLog()); elfRelocationContext.getLog());
break; return RelocationResult.UNSUPPORTED;
} }
} }
return new RelocationResult(Status.APPLIED, byteLength);
} }
private boolean isThumb(ElfSymbol symbol) { 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 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.app.util.bin.format.macho.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryAccessException; 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 * A {@link MachoRelocationHandler} for ARM
@@ -43,24 +45,25 @@ public class ARM_MachoRelocationHandler extends MachoRelocationHandler {
} }
@Override @Override
public void relocate(MachoRelocation relocation) public RelocationResult relocate(MachoRelocation relocation)
throws MemoryAccessException, NotFoundException { throws MemoryAccessException, RelocationException {
if (!relocation.requiresRelocation()) { if (!relocation.requiresRelocation()) {
return; return RelocationResult.SKIPPED;
} }
RelocationInfo relocationInfo = relocation.getRelocationInfo(); RelocationInfo relocationInfo = relocation.getRelocationInfo();
Address targetAddr = relocation.getTargetAddress(); Address targetAddr = relocation.getTargetAddress();
long orig = read(relocation); long orig = read(relocation);
int byteLength;
switch (relocationInfo.getType()) { switch (relocationInfo.getType()) {
case ARM_RELOC_VANILLA: case ARM_RELOC_VANILLA:
if (!relocationInfo.isPcRelocated()) { if (!relocationInfo.isPcRelocated()) {
write(relocation, targetAddr.getOffset()); byteLength = write(relocation, targetAddr.getOffset());
} }
else { else {
throw new NotFoundException("Unimplemented relocation"); return RelocationResult.UNSUPPORTED;
} }
break; break;
case ARM_THUMB_RELOC_BR22: { case ARM_THUMB_RELOC_BR22: {
@@ -86,7 +89,7 @@ public class ARM_MachoRelocationHandler extends MachoRelocationHandler {
imm11 = (value >> 1) & 0x7ff; imm11 = (value >> 1) & 0x7ff;
long instr = orig & (blx ? 0xc000f800 : 0xd000f800); long instr = orig & (blx ? 0xc000f800 : 0xd000f800);
instr |= (j1 << 29) | (j2 << 27) | (imm11 << 16) | (s << 10) | imm10; instr |= (j1 << 29) | (j2 << 27) | (imm11 << 16) | (s << 10) | imm10;
write(relocation, instr); byteLength = write(relocation, instr);
break; break;
} }
@@ -99,7 +102,8 @@ public class ARM_MachoRelocationHandler extends MachoRelocationHandler {
case ARM_RELOC_HALF: // relocation not required (scattered) case ARM_RELOC_HALF: // relocation not required (scattered)
case ARM_RELOC_HALF_SECTDIFF: // relocation not required (scattered) case ARM_RELOC_HALF_SECTDIFF: // relocation not required (scattered)
default: 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.listing.Program;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException; 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.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.NotFoundException; import ghidra.util.exception.NotFoundException;
@@ -35,7 +37,8 @@ public class AVR32_ElfRelocationHandler extends ElfRelocationHandler {
} }
@Override @Override
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation, public RelocationResult relocate(ElfRelocationContext elfRelocationContext,
ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException { Address relocationAddress) throws MemoryAccessException, NotFoundException {
Program program = elfRelocationContext.getProgram(); Program program = elfRelocationContext.getProgram();
@@ -57,11 +60,13 @@ public class AVR32_ElfRelocationHandler extends ElfRelocationHandler {
int oldValue = memory.getInt(relocationAddress); int oldValue = memory.getInt(relocationAddress);
int byteLength = 4; // most relocations affect 4-bytes (change if different)
if (elf.e_machine() == ElfConstants.EM_AVR32) { if (elf.e_machine() == ElfConstants.EM_AVR32) {
int newValueShiftToAligntoUpper = 0; int newValueShiftToAligntoUpper = 0;
switch (type) { switch (type) {
case AVR32_ElfRelocationConstants.R_AVR32_NONE: case AVR32_ElfRelocationConstants.R_AVR32_NONE:
break; return RelocationResult.SKIPPED;
case AVR32_ElfRelocationConstants.R_AVR32_32: case AVR32_ElfRelocationConstants.R_AVR32_32:
int newValue = (((int) symbolValue + (int) addend) & 0xffffffff); int newValue = (((int) symbolValue + (int) addend) & 0xffffffff);
memory.setInt(relocationAddress, newValue); 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 + ", New = " + newValue);
}*/ }*/
//System.out.println(" HANDLED AVR relocation: R_AVR32_ALIGN at "+relocationAddress + ", OldValue = " + Integer.toHexString(oldValue)); //System.out.println(" HANDLED AVR relocation: R_AVR32_ALIGN at "+relocationAddress + ", OldValue = " + Integer.toHexString(oldValue));
break; return RelocationResult.SKIPPED;
//TODO: THE FOLLOWING: //TODO: THE FOLLOWING:
/*case AVR32_ElfRelocationConstants.R_AVR32_16_CP: /*case AVR32_ElfRelocationConstants.R_AVR32_16_CP:
@@ -316,9 +321,10 @@ public class AVR32_ElfRelocationHandler extends ElfRelocationHandler {
markAsUnhandled(program, relocationAddress, type, symbolIndex, markAsUnhandled(program, relocationAddress, type, symbolIndex,
elfRelocationContext.getSymbolName(symbolIndex), elfRelocationContext.getSymbolName(symbolIndex),
elfRelocationContext.getLog()); 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.listing.Program;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.util.exception.NotFoundException; import ghidra.util.exception.NotFoundException;
public class AVR8_ElfRelocationHandler extends ElfRelocationHandler { public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
@@ -30,7 +32,8 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
} }
@Override @Override
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation, public RelocationResult relocate(ElfRelocationContext elfRelocationContext,
ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException { Address relocationAddress) throws MemoryAccessException, NotFoundException {
Program program = elfRelocationContext.getProgram(); Program program = elfRelocationContext.getProgram();
@@ -57,14 +60,15 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
int oldValue = memory.getShort(relocationAddress); int oldValue = memory.getShort(relocationAddress);
if (elf.e_machine() != ElfConstants.EM_AVR) { if (elf.e_machine() != ElfConstants.EM_AVR) {
return; return RelocationResult.FAILURE;
} }
int newValue = 0; int newValue = 0;
int byteLength = 2; // most relocations affect 2-bytes (change if different)
switch (type) { switch (type) {
case AVR8_ElfRelocationConstants.R_AVR_NONE: case AVR8_ElfRelocationConstants.R_AVR_NONE:
break; return RelocationResult.SKIPPED;
case AVR8_ElfRelocationConstants.R_AVR_32: case AVR8_ElfRelocationConstants.R_AVR_32:
newValue = (((int) symbolValue + (int) addend) & 0xffffffff); newValue = (((int) symbolValue + (int) addend) & 0xffffffff);
@@ -78,12 +82,12 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
if (newValue > ((1 << 7) - 1) || (newValue < -(1 << 7))) { if (newValue > ((1 << 7) - 1) || (newValue < -(1 << 7))) {
markAsError(program, relocationAddress, type, symbolName, "relocation overflow", markAsError(program, relocationAddress, type, symbolName, "relocation overflow",
elfRelocationContext.getLog()); elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue = (oldValue & 0xfc07) | (((newValue >> 1) << 3) & 0x3f8); newValue = (oldValue & 0xfc07) | (((newValue >> 1) << 3) & 0x3f8);
memory.setShort(relocationAddress, (short) newValue); memory.setShort(relocationAddress, (short) newValue);
@@ -96,7 +100,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue >>= 1; newValue >>= 1;
@@ -168,7 +172,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue >>= 1; newValue >>= 1;
newValue = (oldValue & 0xf0f0) | (newValue & 0xf) | ((newValue << 4) & 0xf00); newValue = (oldValue & 0xf0f0) | (newValue & 0xf) | ((newValue << 4) & 0xf00);
@@ -180,7 +184,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue >>= 1; newValue >>= 1;
newValue = (newValue >> 8) & 0xff; newValue = (newValue >> 8) & 0xff;
@@ -193,7 +197,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue >>= 1; newValue >>= 1;
newValue = (newValue >> 16) & 0xff; newValue = (newValue >> 16) & 0xff;
@@ -207,7 +211,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue >>= 1; newValue >>= 1;
newValue = (oldValue & 0xf0f0) | (newValue & 0xf) | ((newValue << 4) & 0xf00); newValue = (oldValue & 0xf0f0) | (newValue & 0xf) | ((newValue << 4) & 0xf00);
@@ -220,7 +224,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue >>= 1; newValue >>= 1;
newValue = (newValue >> 8) & 0xff; newValue = (newValue >> 8) & 0xff;
@@ -234,7 +238,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue >>= 1; newValue >>= 1;
newValue = (newValue >> 16) & 0xff; newValue = (newValue >> 16) & 0xff;
@@ -248,7 +252,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 1) == 1) { if ((newValue & 1) == 1) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
return; return RelocationResult.FAILURE;
} }
newValue >>= 1; newValue >>= 1;
@@ -256,6 +260,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
oldValue | ((newValue & 0x10000) | ((newValue << 3) & 0x1f00000)) >> 16; oldValue | ((newValue & 0x10000) | ((newValue << 3) & 0x1f00000)) >> 16;
memory.setShort(relocationAddress, (short) (hiValue & 0xffff)); memory.setShort(relocationAddress, (short) (hiValue & 0xffff));
memory.setShort(relocationAddress.add(2), (short) (newValue & 0xffff)); memory.setShort(relocationAddress.add(2), (short) (newValue & 0xffff));
byteLength = 4;
break; break;
case AVR8_ElfRelocationConstants.R_AVR_LDI: /* data/eeprom */ case AVR8_ElfRelocationConstants.R_AVR_LDI: /* data/eeprom */
@@ -264,6 +269,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 0xffff) > 255) { if ((newValue & 0xffff) > 255) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
// continue to apply
} }
newValue = (newValue >> 8) & 0xff; newValue = (newValue >> 8) & 0xff;
@@ -277,6 +283,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if (((newValue & 0xffff) > 63) || (newValue < 0)) { if (((newValue & 0xffff) > 63) || (newValue < 0)) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
// continue to apply
} }
newValue = (oldValue & 0xd3f8) | (newValue & 7) | ((newValue & (3 << 3)) << 7) | 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)) { if (((newValue & 0xffff) > 63) || (newValue < 0)) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
// continue to apply
} }
newValue = (oldValue & 0xff30) | (newValue & 0xF) | ((newValue & 0x30) << 2); 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) { if (((newValue & 0xffff) < 0x40) || (newValue & 0xFFFF) > 0xbf) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
// continue to apply
} }
newValue = newValue & 0x7f; newValue = newValue & 0x7f;
@@ -322,6 +331,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 0xffff) > 0x3f) { if ((newValue & 0xffff) > 0x3f) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
// continue to apply
} }
newValue = (oldValue & 0xf9f0) | ((newValue & 0x30) << 5) | (newValue & 0x0f); newValue = (oldValue & 0xf9f0) | ((newValue & 0x30) << 5) | (newValue & 0x0f);
@@ -334,6 +344,7 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
if ((newValue & 0xffff) > 0x1f) { if ((newValue & 0xffff) > 0x1f) {
markAsError(program, relocationAddress, type, symbolName, markAsError(program, relocationAddress, type, symbolName,
"relocation out of range", elfRelocationContext.getLog()); "relocation out of range", elfRelocationContext.getLog());
// continue to apply
} }
newValue = (oldValue & 0xff07) | ((newValue & 0x1f) << 3); newValue = (oldValue & 0xff07) | ((newValue & 0x1f) << 3);
@@ -351,8 +362,9 @@ public class AVR8_ElfRelocationHandler extends ElfRelocationHandler {
default: default:
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName, markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
elfRelocationContext.getLog()); 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) { catch (NotFoundException e) {
throw new AssertException("unexpected", e); throw new AssertException("unexpected", e);
} }
catch (MemoryAccessException | AddressOverflowException e) { catch (MemoryAccessException e) {
elfLoadHelper.log("Failed to adjust GOT: " + e.getMessage()); elfLoadHelper.log("Failed to adjust GOT: " + e.getMessage());
} }
} }
@@ -855,14 +855,14 @@ public class MIPS_ElfExtension extends ElfExtension {
catch (NotFoundException e) { catch (NotFoundException e) {
throw new AssertException("unexpected", e); throw new AssertException("unexpected", e);
} }
catch (MemoryAccessException | AddressOverflowException e) { catch (MemoryAccessException e) {
elfLoadHelper.log("Failed to adjust MIPS GOT: " + e.getMessage()); elfLoadHelper.log("Failed to adjust MIPS GOT: " + e.getMessage());
} }
} }
private Address adjustTableEntryIfNonZero(Address tableBaseAddr, int entryIndex, private Address adjustTableEntryIfNonZero(Address tableBaseAddr, int entryIndex,
long adjustment, ElfLoadHelper elfLoadHelper) long adjustment, ElfLoadHelper elfLoadHelper)
throws MemoryAccessException, AddressOverflowException { throws MemoryAccessException {
boolean is64Bit = elfLoadHelper.getElfHeader().is64Bit(); boolean is64Bit = elfLoadHelper.getElfHeader().is64Bit();
Memory memory = elfLoadHelper.getProgram().getMemory(); Memory memory = elfLoadHelper.getProgram().getMemory();
Address tableEntryAddr; Address tableEntryAddr;
@@ -871,7 +871,7 @@ public class MIPS_ElfExtension extends ElfExtension {
if (adjustment != 0) { if (adjustment != 0) {
long offset = memory.getLong(tableEntryAddr); long offset = memory.getLong(tableEntryAddr);
if (offset != 0) { if (offset != 0) {
elfLoadHelper.addFakeRelocTableEntry(tableEntryAddr, 8); elfLoadHelper.addArtificialRelocTableEntry(tableEntryAddr, 8);
memory.setLong(tableEntryAddr, offset + adjustment); memory.setLong(tableEntryAddr, offset + adjustment);
} }
} }
@@ -881,7 +881,7 @@ public class MIPS_ElfExtension extends ElfExtension {
if (adjustment != 0) { if (adjustment != 0) {
int offset = memory.getInt(tableEntryAddr); int offset = memory.getInt(tableEntryAddr);
if (offset != 0) { if (offset != 0) {
elfLoadHelper.addFakeRelocTableEntry(tableEntryAddr, 4); elfLoadHelper.addArtificialRelocTableEntry(tableEntryAddr, 4);
memory.setInt(tableEntryAddr, (int) (offset + adjustment)); memory.setInt(tableEntryAddr, (int) (offset + adjustment));
} }
} }
@@ -898,7 +898,7 @@ public class MIPS_ElfExtension extends ElfExtension {
tableEntryAddr = tableBaseAddr.add(entryIndex * 8); tableEntryAddr = tableBaseAddr.add(entryIndex * 8);
long offset = memory.getLong(tableEntryAddr); long offset = memory.getLong(tableEntryAddr);
if (offset == 0) { if (offset == 0) {
elfLoadHelper.addFakeRelocTableEntry(tableEntryAddr, 8); elfLoadHelper.addArtificialRelocTableEntry(tableEntryAddr, 8);
memory.setLong(tableEntryAddr, value); memory.setLong(tableEntryAddr, value);
} }
} }
@@ -906,7 +906,7 @@ public class MIPS_ElfExtension extends ElfExtension {
tableEntryAddr = tableBaseAddr.add(entryIndex * 4); tableEntryAddr = tableBaseAddr.add(entryIndex * 4);
int offset = memory.getInt(tableEntryAddr); int offset = memory.getInt(tableEntryAddr);
if (offset == 0) { if (offset == 0) {
elfLoadHelper.addFakeRelocTableEntry(tableEntryAddr, 4); elfLoadHelper.addArtificialRelocTableEntry(tableEntryAddr, 4);
memory.setInt(tableEntryAddr, (int) value); 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