Merge remote-tracking branch 'origin/GP-2081_ghidra1_TICoffRelocHandler_REBASED--SQUASHED'

This commit is contained in:
Ryan Kurtz 2022-06-22 00:32:40 -04:00
commit d67bc4508c
6 changed files with 349 additions and 68 deletions

View File

@ -0,0 +1,52 @@
/* ###
* 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;
import java.util.Objects;
/**
* <code>RelocationException</code> thrown when a supported relocation encounters an
* unexpected error during processing.
*/
public class RelocationException extends Exception {
/**
* Constructs a new exception with the specified detail message.
*
* @param message the detail message (required).
*/
public RelocationException(String message) {
super(message);
Objects.requireNonNull(message);
}
/**
* Constructs a new exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this exception's detail message.
*
* @param message the detail message (required).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
*/
RelocationException(String message, Exception cause) {
super(message, cause);
Objects.requireNonNull(message);
}
}

View File

@ -0,0 +1,135 @@
/* ###
* 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.coff.relocation;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.coff.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
/**
* <code>CoffRelocationContext</code> provide COFF relocation context data to be used by
* {@link CoffRelocationHandler} during processing of relocations.
*/
public class CoffRelocationContext {
private final Program program;
private final CoffFileHeader header;
private final Map<CoffSymbol, Symbol> symbolsMap;
private final Map<String, Object> contextMap = new HashMap<>();
private CoffSectionHeader section;
/**
* Construct COFF relocation context
* @param program program to which relocations are applied
* @param header COFF file header
* @param symbolsMap symbol lookup map
*/
public CoffRelocationContext(Program program, CoffFileHeader header,
Map<CoffSymbol, Symbol> symbolsMap) {
this.program = program;
this.header = header;
this.symbolsMap = symbolsMap;
}
/**
* Reset context at start of COFF section relocation processing
* @param coffSection COFF section
*/
public void resetContext(CoffSectionHeader coffSection) {
this.section = coffSection;
contextMap.clear();
}
/**
* Get program to which relocations are being applied
* @return program
*/
public Program getProgram() {
return program;
}
/**
* Get COFF section to which relocations are being applied
* @return COFF section
*/
public CoffSectionHeader getSection() {
return section;
}
/**
* Get symbol required to process a relocation. Method should only be invoked
* when a symbol is required since some relocations may not require a symbol.
* @param relocation relocation whose related symbol should be returned
* @return relocation symbol
* @throws RelocationException if symbol not found
*/
public Symbol getSymbol(CoffRelocation relocation) throws RelocationException {
Symbol symbol =
symbolsMap.get(header.getSymbolAtIndex(relocation.getSymbolIndex()));
if (symbol == null) {
throw new RelocationException("missing required symbol");
}
return symbol;
}
/**
* Get address of symbol required to process a relocation. Method should only be invoked
* when a symbol is required since some relocations may not require a symbol.
* @param relocation relocation whose related symbol should be returned
* @return relocation symbol
* @throws RelocationException if symbol not found
*/
public Address getSymbolAddress(CoffRelocation relocation) throws RelocationException {
return getSymbol(relocation).getAddress();
}
/**
* Get and optionally compute context value for specified key
* @param key extension-specific context key
* @param mappingFunction function used to compute value if absent
* @return context value
*/
public Object computeContextValueIfAbsent(String key,
Function<String, Object> mappingFunction) {
return contextMap.computeIfAbsent(key, mappingFunction);
}
/**
* Store context value for specified key
* @param key extension-specific context key
* @param value context value
*/
public void putContextValue(String key, Object value) {
contextMap.put(key, value);
}
/**
* Get context value for specified key
* @param key extension-specific key
* @return context value or null if absent
*/
public Object getContextValue(String key) {
return contextMap.get(key);
}
}

View File

@ -15,12 +15,11 @@
*/
package ghidra.app.util.bin.format.coff.relocation;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.coff.CoffFileHeader;
import ghidra.app.util.bin.format.coff.CoffRelocation;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.NotFoundException;
@ -28,7 +27,7 @@ import ghidra.util.exception.NotFoundException;
* An abstract class used to perform COFF relocations. Classes should extend this class to
* provide relocations in a machine/processor specific way.
*/
abstract public class CoffRelocationHandler implements ExtensionPoint {
public interface CoffRelocationHandler extends ExtensionPoint {
/**
* Checks to see whether or not an instance of this COFF relocation hander can handle
@ -37,18 +36,20 @@ abstract public class CoffRelocationHandler implements ExtensionPoint {
* @param fileHeader The file header associated with the COFF to relocate.
* @return True if this relocation handler can do the relocation; otherwise, false.
*/
abstract public boolean canRelocate(CoffFileHeader fileHeader);
public boolean canRelocate(CoffFileHeader fileHeader);
/**
* Performs a relocation.
* Performs a relocation at the specified address.
*
* @param program The program to relocate.
* @param address The address at which to perform the relocation.
* @param symbol The symbol used during relocation.
* @param relocation The relocation information to use to perform the relocation.
* @param relocationContext relocation context data
* @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.
*/
abstract public void relocate(Program program, Address address, Symbol symbol,
CoffRelocation relocation) throws MemoryAccessException, NotFoundException;
public void relocate(Address address, CoffRelocation relocation,
CoffRelocationContext relocationContext)
throws MemoryAccessException, NotFoundException, RelocationException;
}

View File

@ -23,9 +23,9 @@ import java.util.Map.Entry;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.coff.*;
import ghidra.app.util.bin.format.coff.relocation.CoffRelocationHandler;
import ghidra.app.util.bin.format.coff.relocation.CoffRelocationHandlerFactory;
import ghidra.app.util.bin.format.coff.relocation.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.mem.FileBytes;
@ -128,7 +128,7 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
// Only one of the CoffLoader/MSCoffLoader will survive this check
return loadSpecs;
}
String secondary = isCLI(header) ? "cli" : null;
String secondary = isCLI(header) ? "cli" : Integer.toString(header.getFlags() & 0xffff);
List<QueryResult> results =
QueryOpinionService.query(getName(), header.getMachineName(), secondary);
for (QueryResult result : results) {
@ -645,6 +645,16 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
MessageLog log, TaskMonitor monitor) {
CoffRelocationHandler handler = CoffRelocationHandlerFactory.getHandler(header);
if (handler == null) {
String msg = String.format("No COFF relocation handler for machine type 0x%x",
(Short) header.getMachine());
log.appendMsg(msg);
Msg.error(this, program.getName() + ": " + msg);
}
CoffRelocationContext relocationContext =
new CoffRelocationContext(program, header, symbolsMap);
int failureCount = 0;
for (CoffSectionHeader section : header.getSections()) {
if (monitor.isCancelled()) {
@ -653,69 +663,123 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
Address sectionStartAddr = sectionsMap.get(section);
if (sectionStartAddr == null) {
if (section.getRelocationCount() > 0) {
log.appendMsg("Unable to process relocations for " + section.getName() +
". No memory block was created.");
int relocCount = section.getRelocationCount();
if (relocCount > 0) {
failureCount += relocCount;
String msg = "Unable to process " + relocCount + " relocations for section " +
section.getName() + ". No memory block was created.";
log.appendMsg(msg);
Msg.error(this, program.getName() + ": " + msg);
}
continue;
}
relocationContext.resetContext(section);
Address failedAddr = null;
for (CoffRelocation relocation : section.getRelocations()) {
if (monitor.isCancelled()) {
break;
}
Address address = sectionStartAddr.add(relocation.getAddress()); // assuming it's always a byte-offset
// Sections are defined with physical address while relocations use virtual address.
// Must adjust relocation address to physical.
// NOTE: Relocation address offset assumed to always be a byte-offset
Address address =
sectionStartAddr.add(relocation.getAddress() - section.getVirtualAddress());
short relocationType = relocation.getType();
byte[] origBytes = new byte[0];
Symbol symbol =
symbolsMap.get(header.getSymbolAtIndex(relocation.getSymbolIndex()));
if (handler == null) {
handleRelocationError(program, log, address, String.format(
"No relocation handler for machine type 0x%x to process relocation at %s with type 0x%x",
header.getMachine(), address, relocation.getType()));
}
else if (symbol == null) {
handleRelocationError(program, log, address,
String.format("No symbol to process relocation at %s with type 0x%x",
address, relocation.getType()));
++failureCount;
handleRelocationError(program, address, relocationType,
"No COFF relocation handler", null);
}
else {
try {
origBytes = new byte[4];
program.getMemory().getBytes(address, origBytes);
handler.relocate(program, address, symbol, relocation);
if (address.equals(failedAddr)) {
// skip relocation if previous failed relocation was at the same address
// since it is likely dependent on the previous failed relocation result
++failureCount;
String logMessage =
String.format("Skipped dependent COFF Relocation type 0x%x at %s",
relocationType, address.toString());
Msg.error(this, program.getName() + ": " + logMessage);
// TODO: once RelocationTable can retain all relocations at the same address
// this continue statement should be removed (see GP-2128)
continue;
}
//else {
program.getMemory().getBytes(address, origBytes);
handler.relocate(address, relocation, relocationContext);
//}
}
catch (MemoryAccessException e) {
handleRelocationError(program, log, address, String.format(
"Error accessing memory at address %s. Relocation failed.", address));
++failureCount;
failedAddr = address;
handleRelocationError(program, address, relocationType,
"Error accessing memory", null);
}
catch (NotFoundException e) {
handleRelocationError(program, log, address,
String.format("Relocation type 0x%x at address %s is not supported.",
relocation.getType(), address));
++failureCount;
failedAddr = address;
handleRelocationError(program, address, relocationType,
"Unsupported COFF relocation type", null);
}
catch (AddressOutOfBoundsException e) {
handleRelocationError(program, log, address,
String.format("Error computing relocation at address %s.", address));
catch (RelocationException e) {
++failureCount;
failedAddr = address;
handleRelocationError(program, address, relocationType, e.getMessage(),
null);
}
catch (Exception e) {
++failureCount;
failedAddr = address;
String msg = e.getMessage();
if (msg == null) {
msg = e.toString();
}
handleRelocationError(program, address, relocationType, msg, e);
}
}
// The relocation symbol may be null when either not required by a relocation or
// not found with symbol index
Symbol symbol =
symbolsMap.get(header.getSymbolAtIndex(relocation.getSymbolIndex()));
// TODO: There may be multiple relocations at the same address.
// The RelocationTable for retaining relocations needs to be revised to handle
// this. At present only the last one will remain in the DB-backed address-based
// table. (see GP-2128)
program.getRelocationTable()
.add(address, relocation.getType(),
new long[] { relocation.getSymbolIndex() }, origBytes,
symbol != null ? symbol.getName() : "<null>");
}
}
if (failureCount != 0) {
String msg = "Failed to process a total of " + failureCount +
" relocations. See log and error bookmarks for details.";
log.appendMsg(msg);
Msg.error(this, program.getName() + ": " + msg);
}
}
private void handleRelocationError(Program program, MessageLog log, Address address,
String message) {
private void handleRelocationError(Program program, Address address,
Short relocationType, String message, Exception causeToReport) {
String bookmarkMessage =
String.format("Failed to apply COFF Relocation type 0x%x: %s", relocationType, message);
program.getBookmarkManager()
.setBookmark(address, BookmarkType.ERROR, "Relocations", message);
log.appendMsg(message);
.setBookmark(address, BookmarkType.ERROR, "Relocations", bookmarkMessage);
String logMessage = String.format("Failed to apply COFF Relocation type 0x%x at %s: %s",
relocationType, address.toString(), message);
Msg.error(this, program.getName() + ": " + logMessage, causeToReport);
}
@Override

View File

@ -15,14 +15,15 @@
*/
package ghidra.app.util.bin.format.coff.relocation;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.coff.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.exception.NotFoundException;
public class X86_32_CoffRelocationHandler extends CoffRelocationHandler {
public class X86_32_CoffRelocationHandler implements CoffRelocationHandler {
@Override
public boolean canRelocate(CoffFileHeader fileHeader) {
@ -30,27 +31,38 @@ public class X86_32_CoffRelocationHandler extends CoffRelocationHandler {
}
@Override
public void relocate(Program program, Address address, Symbol symbol,
CoffRelocation relocation) throws MemoryAccessException, NotFoundException {
public void relocate(Address address, CoffRelocation relocation,
CoffRelocationContext relocationContext)
throws MemoryAccessException, NotFoundException, RelocationException {
int addend = program.getMemory().getInt(address);
Program program = relocationContext.getProgram();
Memory mem = program.getMemory();
int addend = mem.getInt(address);
switch (relocation.getType()) {
// We are implementing these types:
case IMAGE_REL_I386_DIR32: {
program.getMemory().setInt(address,
(int) symbol.getAddress().add(addend).getOffset());
int value = (int) relocationContext.getSymbolAddress(relocation)
.add(addend)
.getOffset();
program.getMemory().setInt(address, value);
break;
}
case IMAGE_REL_I386_DIR32NB: {
program.getMemory().setInt(address,
(int) symbol.getAddress().add(addend).subtract(program.getImageBase()));
int value = (int) relocationContext.getSymbolAddress(relocation)
.add(addend)
.subtract(program.getImageBase());
mem.setInt(address, value);
break;
}
case IMAGE_REL_I386_REL32: {
program.getMemory().setInt(address,
(int) symbol.getAddress().add(addend).subtract(address) - 4);
int value = (int) relocationContext.getSymbolAddress(relocation)
.add(addend)
.subtract(address);
value -= 4;
mem.setInt(address, value);
break;
}

View File

@ -15,41 +15,55 @@
*/
package ghidra.app.util.bin.format.coff.relocation;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.coff.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.exception.NotFoundException;
public class X86_64_CoffRelocationHandler extends CoffRelocationHandler {
public class X86_64_CoffRelocationHandler implements CoffRelocationHandler {
@Override
public boolean canRelocate(CoffFileHeader fileHeader) {
return fileHeader.getMachine() == CoffMachineType.IMAGE_FILE_MACHINE_AMD64;
}
@Override
public void relocate(Program program, Address address, Symbol symbol,
CoffRelocation relocation) throws MemoryAccessException, NotFoundException {
public void relocate(Address address, CoffRelocation relocation,
CoffRelocationContext relocationContext)
throws MemoryAccessException, NotFoundException, RelocationException {
Program program = relocationContext.getProgram();
Memory mem = program.getMemory();
int distance = 0;
long addend = program.getMemory().getInt(address);
long addend = mem.getInt(address);
switch (relocation.getType()) {
// We are implementing these types:
case IMAGE_REL_AMD64_ADDR64:
addend = program.getMemory().getLong(address); // overwrite default 4-byte addend
program.getMemory().setLong(address, symbol.getAddress().add(addend).getOffset());
case IMAGE_REL_AMD64_ADDR64: {
addend = mem.getLong(address); // overwrite default 4-byte addend
long value = relocationContext.getSymbolAddress(relocation)
.add(addend)
.getOffset();
mem.setLong(address, value);
break;
case IMAGE_REL_AMD64_ADDR32:
program.getMemory().setInt(address,
(int) symbol.getAddress().add(addend).getOffset());
}
case IMAGE_REL_AMD64_ADDR32: {
int value = (int) relocationContext.getSymbolAddress(relocation)
.add(addend)
.getOffset();
mem.setInt(address, value);
break;
}
case IMAGE_REL_AMD64_ADDR32NB: {
program.getMemory().setInt(address,
(int) symbol.getAddress().add(addend).subtract(program.getImageBase()));
int value = (int) relocationContext.getSymbolAddress(relocation)
.add(addend)
.subtract(program.getImageBase());
mem.setInt(address, value);
break;
}
case IMAGE_REL_AMD64_REL32_5: { // fallthrough to IMAGE_REL_AMD64_REL32 to get correct 'distance'
@ -68,8 +82,11 @@ public class X86_64_CoffRelocationHandler extends CoffRelocationHandler {
distance++;
}
case IMAGE_REL_AMD64_REL32: {
program.getMemory().setInt(address,
(int) symbol.getAddress().add(addend).subtract(address) - 4 - distance);
int value = (int) relocationContext.getSymbolAddress(relocation)
.add(addend)
.subtract(address);
value -= (distance + 4);
mem.setInt(address, value);
break;
}