diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/RelocationException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/RelocationException.java
new file mode 100644
index 0000000000..fca2923092
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/RelocationException.java
@@ -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;
+
+/**
+ * RelocationException 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.
Note that the detail message associated with
+ * {@code cause} is not 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);
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/coff/relocation/CoffRelocationContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/coff/relocation/CoffRelocationContext.java
new file mode 100644
index 0000000000..0f2787c471
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/coff/relocation/CoffRelocationContext.java
@@ -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;
+
+/**
+ * CoffRelocationContext 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 symbolsMap;
+
+ private final Map 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 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 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);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/coff/relocation/CoffRelocationHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/coff/relocation/CoffRelocationHandler.java
index 53f6a308a3..d00f2b18f2 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/coff/relocation/CoffRelocationHandler.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/coff/relocation/CoffRelocationHandler.java
@@ -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;
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java
index 4601c1da8e..a9072e0d32 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java
@@ -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 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() : "");
}
}
+
+ 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
diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/coff/relocation/X86_32_CoffRelocationHandler.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/coff/relocation/X86_32_CoffRelocationHandler.java
index 710268731b..c45687b182 100644
--- a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/coff/relocation/X86_32_CoffRelocationHandler.java
+++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/coff/relocation/X86_32_CoffRelocationHandler.java
@@ -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;
}
diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/coff/relocation/X86_64_CoffRelocationHandler.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/coff/relocation/X86_64_CoffRelocationHandler.java
index 9b5ffc9f1e..891d8b371f 100644
--- a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/coff/relocation/X86_64_CoffRelocationHandler.java
+++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/coff/relocation/X86_64_CoffRelocationHandler.java
@@ -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;
}