GP-2984 modified ELF relocation processing to use single ElfRelocationContext instance. Modified X86-64 relocation processing to handle GOTPCREL for object modules.

This commit is contained in:
ghidra1 2023-01-19 09:12:36 -05:00
parent acd80575c1
commit 6e1ad5578f
15 changed files with 597 additions and 176 deletions

View File

@ -20,6 +20,8 @@ package ghidra.app.util.bin.format.elf;
*/
public interface ElfConstants {
public static final String GOT_SYMBOL_NAME = "_GLOBAL_OFFSET_TABLE_";
// ELF Identification Area Indexes
/**Length of the File ID*/

View File

@ -17,7 +17,8 @@ package ghidra.app.util.bin.format.elf;
import ghidra.app.util.bin.format.MemoryLoadable;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
@ -175,7 +176,7 @@ public interface ElfLoadHelper {
* Returns the appropriate .got (Global Offset Table) section address using the
* DT_PLTGOT value defined in the .dynamic section.
* If the dynamic value is not defined, the symbol offset for _GLOBAL_OFFSET_TABLE_
* will be used, otherwise null will be returned.
* will be used, otherwise null will be returned. See {@link ElfConstants#GOT_SYMBOL_NAME}.
* @return the .got section address offset
*/
public Long getGOTValue();
@ -215,14 +216,15 @@ public interface ElfLoadHelper {
* Add a fake relocation table entry if none previously existed for the specified address.
* This is intended to record original file bytes when forced modifications have been
* performed during the ELF import processing. A relocation type of 0 will be specified for
* fake entry.
* fake entry.
* NOTE: The number of recorded original FileBytes currently ignores the specified length.
* However, the length is still used to verify that that the intended modification region
* dose not intersect another relocation.
* @param address relocation address
* @param length number of bytes affected
* @return true if recorded successfully, or false if conflict with existing relocation entry occurs
* @throws MemoryAccessException if unable to read bytes from memory
* @throws AddressOverflowException if range address wrap occurs
* @return true if recorded successfully, or false if conflict with existing relocation
* entry and memory addressing error occurs
*/
public boolean addFakeRelocTableEntry(Address address, int length)
throws MemoryAccessException, AddressOverflowException;
public boolean addFakeRelocTableEntry(Address address, int length);
}

View File

@ -36,30 +36,48 @@ public class ElfRelocationContext {
protected final ElfRelocationHandler handler;
protected final ElfLoadHelper loadHelper;
protected final ElfRelocationTable relocationTable;
private ElfSymbolTable symbolTable; // may be null
private ElfSymbol nullSymbol; // corresponds to symbolIndex==0 when no symbolTable
protected final Map<ElfSymbol, Address> symbolMap;
protected final Program program;
protected ElfRelocationTable relocationTable;
protected ElfSymbolTable symbolTable; // may be null
private ElfSymbol nullSymbol; // corresponds to symbolIndex==0 when no symbolTable
/**
* Relocation context for a specific Elf image and relocation table
* @param handler relocation handler or null if not available
* @param loadHelper the elf load helper
* @param relocationTable Elf relocation table
* @param symbolMap Elf symbol placement map
*/
protected ElfRelocationContext(ElfRelocationHandler handler, ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
Map<ElfSymbol, Address> symbolMap) {
this.handler = handler;
this.loadHelper = loadHelper;
this.relocationTable = relocationTable;
symbolTable = relocationTable.getAssociatedSymbolTable();
this.symbolMap = symbolMap;
this.program = loadHelper.getProgram();
}
/**
* Invoked at start of relocation processing for specified table.
* The method {@link #endRelocationTableProcessing()} will be invoked after last relocation
* is processed.
* @param relocTable relocation table
*/
public void startRelocationTableProcessing(ElfRelocationTable relocTable) {
this.relocationTable = relocTable;
symbolTable = relocTable.getAssociatedSymbolTable();
if (symbolTable == null) {
nullSymbol = ElfSymbol.createNullSymbol(loadHelper.getElfHeader());
}
this.symbolMap = symbolMap;
this.program = loadHelper.getProgram();
}
/**
* Invoked at end of relocation processing for current relocation table.
* See {@link #startRelocationTableProcessing(ElfRelocationTable)}.
*/
public void endRelocationTableProcessing() {
this.relocationTable = null;
}
/**
@ -130,20 +148,19 @@ public class ElfRelocationContext {
/**
* Get a relocation context for a specfic Elf image and relocation table
* @param loadHelper Elf load helper
* @param relocationTable Elf relocation table
* @param symbolMap Elf symbol placement map
* @return relocation context or null
*/
public static ElfRelocationContext getRelocationContext(ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
Map<ElfSymbol, Address> symbolMap) {
ElfHeader elf = loadHelper.getElfHeader();
ElfRelocationContext context = null;
ElfRelocationHandler handler = ElfRelocationHandlerFactory.getHandler(elf);
if (handler != null) {
context = handler.createRelocationContext(loadHelper, relocationTable, symbolMap);
context = handler.createRelocationContext(loadHelper, symbolMap);
}
if (context == null) {
context = new ElfRelocationContext(handler, loadHelper, relocationTable, symbolMap);
context = new ElfRelocationContext(handler, loadHelper, symbolMap);
}
return context;
}

View File

@ -38,6 +38,12 @@ import ghidra.util.exception.NotFoundException;
*/
abstract public class ElfRelocationHandler implements ExtensionPoint {
/**
* Fabricated Global Offset Table (GOT) name/prefix to be used when processing an object module
* and a GOT must be fabricated to allow relocation processing.
*/
public static final String GOT_BLOCK_NAME = "%got";
abstract public boolean canRelocate(ElfHeader elf);
/**
@ -55,12 +61,11 @@ abstract public class ElfRelocationHandler implements ExtensionPoint {
* Relocation context for a specific Elf image and relocation table. The relocation context
* is used to process relocations and manage any data required to process relocations.
* @param loadHelper Elf load helper
* @param relocationTable Elf relocation table
* @param symbolMap Elf symbol placement map
* @return relocation context or null if unsupported
*/
public ElfRelocationContext createRelocationContext(ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
Map<ElfSymbol, Address> symbolMap) {
return null;
}

View File

@ -794,89 +794,96 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
log("ELF relocation handler extension not found! Unable to process relocations.");
}
Address defaultBase = getDefaultAddress(elf.adjustAddressForPrelink(0));
AddressSpace defaultSpace = defaultBase.getAddressSpace();
long defaultBaseOffset = defaultBase.getAddressableWordOffset();
int totalCount = 0;
for (ElfRelocationTable relocationTable : relocationTables) {
totalCount += relocationTable.getRelocationCount();
}
monitor.initialize(totalCount);
for (ElfRelocationTable relocationTable : relocationTables) {
monitor.checkCanceled();
Address relocTableAddr = null;
ElfSectionHeader section = relocationTable.getTableSectionHeader();
if (section != null) {
relocTableAddr = findLoadAddress(section, 0);
ElfRelocationContext context = null;
if (processRelocations) {
context = ElfRelocationContext.getRelocationContext(this, symbolMap);
}
try {
for (ElfRelocationTable relocationTable : relocationTables) {
monitor.checkCanceled();
processRelocationTable(relocationTable, context, monitor);
}
else {
relocTableAddr = findLoadAddress(relocationTable.getFileOffset(), 1);
}
/**
* Cases:
* 1. elf.isRelocatable()
* a) sectionToBeRelocated null (may be NULL section?)
* b) sectionToBeRelocated known - offset relative to section load address
*
* 2. !elf.isRelocatable()
* a) sectionToBeRelocated null (may be NULL section?)
* b) sectionToBeRelocated known - offset relative to image base
*/
AddressSpace relocationSpace = defaultSpace;
long baseOffset = defaultBaseOffset;
ElfSectionHeader sectionToBeRelocated = relocationTable.getSectionToBeRelocated();
if (sectionToBeRelocated != null) {
// relocation offsets are relative to start of section load address
Address sectionLoadAddr = findLoadAddress(sectionToBeRelocated, 0);
if (sectionLoadAddr == null) {
log("Failed to identify relocation base address for relocation table 0x" +
relocationTable.getAddressOffset() + " [section: " +
sectionToBeRelocated.getNameAsString() + "]");
monitor.incrementProgress(relocationTable.getRelocationCount());
continue;
}
relocationSpace = sectionLoadAddr.getAddressSpace();
if (elf.isRelocatable()) {
baseOffset = sectionLoadAddr.getAddressableWordOffset();
}
else if (relocationSpace != defaultSpace) {
baseOffset = 0;
}
}
if (relocTableAddr != null) {
markupRelocationTable(relocTableAddr, relocationTable, monitor);
}
ElfRelocationContext context = null;
if (processRelocations) {
context =
ElfRelocationContext.getRelocationContext(this, relocationTable, symbolMap);
}
try {
processRelocationTable(relocationTable, context, relocationSpace, baseOffset,
monitor);
}
finally {
if (context != null) {
context.dispose();
}
}
finally {
if (context != null) {
context.dispose();
}
}
}
private void processRelocationTable(ElfRelocationTable relocationTable,
ElfRelocationContext context, TaskMonitor monitor) throws CancelledException {
Address defaultBase = getDefaultAddress(elf.adjustAddressForPrelink(0));
AddressSpace defaultSpace = defaultBase.getAddressSpace();
long defaultBaseOffset = defaultBase.getAddressableWordOffset();
Address relocTableAddr = null;
ElfSectionHeader section = relocationTable.getTableSectionHeader();
if (section != null) {
relocTableAddr = findLoadAddress(section, 0);
}
else {
relocTableAddr = findLoadAddress(relocationTable.getFileOffset(), 1);
}
/**
* Cases:
* 1. elf.isRelocatable()
* a) sectionToBeRelocated null (may be NULL section?)
* b) sectionToBeRelocated known - offset relative to section load address
*
* 2. !elf.isRelocatable()
* a) sectionToBeRelocated null (may be NULL section?)
* b) sectionToBeRelocated known - offset relative to image base
*/
AddressSpace relocationSpace = defaultSpace;
long baseOffset = defaultBaseOffset;
ElfSectionHeader sectionToBeRelocated = relocationTable.getSectionToBeRelocated();
if (sectionToBeRelocated != null) {
// relocation offsets are relative to start of section load address
Address sectionLoadAddr = findLoadAddress(sectionToBeRelocated, 0);
if (sectionLoadAddr == null) {
log("Failed to identify relocation base address for relocation table 0x" +
relocationTable.getAddressOffset() + " [section: " +
sectionToBeRelocated.getNameAsString() + "]");
monitor.incrementProgress(relocationTable.getRelocationCount());
return;
}
relocationSpace = sectionLoadAddr.getAddressSpace();
if (elf.isRelocatable()) {
baseOffset = sectionLoadAddr.getAddressableWordOffset();
}
else if (relocationSpace != defaultSpace) {
baseOffset = 0;
}
}
if (relocTableAddr != null) {
markupRelocationTable(relocTableAddr, relocationTable, monitor);
}
processRelocationTableEntries(relocationTable, context, relocationSpace, baseOffset,
monitor);
}
private void processRelocationTableEntries(ElfRelocationTable relocationTable,
ElfRelocationContext context, AddressSpace relocationSpace, long baseWordOffset,
TaskMonitor monitor) throws CancelledException {
if (context != null) {
context.startRelocationTableProcessing(relocationTable);
}
ElfSymbolTable symbolTable = relocationTable.getAssociatedSymbolTable();
ElfRelocation[] relocs = relocationTable.getRelocations();
@ -970,11 +977,15 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
}
finally {
// Save relocation data
// Save relocation data - uses original FileBytes
program.getRelocationTable()
.add(relocAddr, reloc.getType(), values, null, symbolName);
}
}
if (context != null) {
context.endRelocationTableProcessing();
}
}
@Override
@ -996,20 +1007,24 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
@Override
public boolean addFakeRelocTableEntry(Address address, int length)
throws AddressOverflowException {
Address maxAddr = address.addNoWrap(length - 1);
RelocationTable relocationTable = program.getRelocationTable();
List<Relocation> relocations = relocationTable.getRelocations(address);
if (!relocations.isEmpty()) {
return false;
public boolean addFakeRelocTableEntry(Address address, int length) {
try {
Address maxAddr = address.addNoWrap(length - 1);
RelocationTable relocationTable = program.getRelocationTable();
List<Relocation> relocations = relocationTable.getRelocations(address);
if (!relocations.isEmpty()) {
return false;
}
Address nextRelocAddr = relocationTable.getRelocationAddressAfter(address);
if (nextRelocAddr == null || nextRelocAddr.compareTo(maxAddr) > 0) {
relocationTable.add(address, 0, new long[0], null, null);
return true;
}
}
Address nextRelocAddr = relocationTable.getRelocationAddressAfter(address);
if (nextRelocAddr != null && nextRelocAddr.compareTo(maxAddr) <= 0) {
return false;
catch (AddressOverflowException e) {
Msg.error(this, "Failed to generate fake relocation data at " + address, e);
}
relocationTable.add(address, 0, new long[0], null, null);
return true;
return false;
}
/**
@ -1420,7 +1435,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
block.setWrite(true);
block.setSourceName(BLOCK_SOURCE_NAME);
block.setComment(
"NOTE: This block is artificial and is used to make relocations work correctly");
"NOTE: This block is artificial and allows ELF Relocations to work correctly");
}
catch (Exception e) {
log("Error creating external memory block: " + " - " + getMessage(e));
@ -1547,6 +1562,14 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
boolean usingFakeExternal = false;
if (address == Address.NO_ADDRESS) {
if (ElfConstants.GOT_SYMBOL_NAME.equals(symName)) {
// Do not assign GOT symbol to the EXTERNAL block.
// This is likely an object module which is not fully linked.
// It is very likely relocation handler will need to allocate a GOT
// for any GOT-based relocations.
continue;
}
if (StringUtils.isBlank(symName)) {
continue;
}
@ -3200,7 +3223,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
throw new AssertException("unexpected", e);
}
}
Symbol gotSym = SymbolUtilities.getLabelOrFunctionSymbol(program, "_GLOBAL_OFFSET_TABLE_",
Symbol gotSym =
SymbolUtilities.getLabelOrFunctionSymbol(program, ElfConstants.GOT_SYMBOL_NAME,
err -> log(err));
if (gotSym != null) {
return gotSym.getAddress().getAddressableWordOffset();

View File

@ -17,7 +17,8 @@ package ghidra.app.util.bin.format.elf.relocation;
import java.util.Map;
import ghidra.app.util.bin.format.elf.*;
import ghidra.app.util.bin.format.elf.ElfLoadHelper;
import ghidra.app.util.bin.format.elf.ElfSymbol;
import ghidra.app.util.bin.format.elf.extend.ARM_ElfExtension;
import ghidra.program.model.address.Address;
@ -26,9 +27,8 @@ class ARM_ElfRelocationContext extends ElfRelocationContext {
private final boolean applyPcBiasToRelativeRelocations;
protected ARM_ElfRelocationContext(ElfRelocationHandler handler, ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable,
Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, relocationTable, symbolMap);
super(handler, loadHelper, symbolMap);
applyPcBiasToRelativeRelocations =
loadHelper.getOption(ARM_ElfExtension.APPLY_PC_BIAS_TO_RELATIVE_RELOCATIONS_OPTION_NAME,

View File

@ -28,8 +28,8 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
@Override
public ARM_ElfRelocationContext createRelocationContext(ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
return new ARM_ElfRelocationContext(this, loadHelper, relocationTable, symbolMap);
Map<ElfSymbol, Address> symbolMap) {
return new ARM_ElfRelocationContext(this, loadHelper, symbolMap);
}
@Override

View File

@ -36,8 +36,6 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
// private static final int TP_OFFSET = 0x7000;
// private static final int DTP_OFFSET = 0x8000;
// private static final String GOT_SYMBOL_NAME = "_GLOBAL_OFFSET_TABLE_";
@Override
public boolean canRelocate(ElfHeader elf) {
return elf.e_machine() == ElfConstants.EM_MIPS;
@ -45,8 +43,8 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
@Override
public MIPS_ElfRelocationContext createRelocationContext(ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
return new MIPS_ElfRelocationContext(this, loadHelper, relocationTable, symbolMap);
Map<ElfSymbol, Address> symbolMap) {
return new MIPS_ElfRelocationContext(this, loadHelper, symbolMap);
}
@Override
@ -1061,12 +1059,37 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
private Address lastSymbolAddr;
MIPS_ElfRelocationContext(MIPS_ElfRelocationHandler handler, ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, relocationTable, symbolMap);
Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, symbolMap);
}
// TODO: move section GOT creation into ElfRelocationContext to make it
// available to other relocation handlers
@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();
@ -1155,7 +1178,7 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
* Determine if the next relocation has the same offset.
* If true, the computed value should be stored to savedAddend and
* useSaveAddend set true.
* @param relocIndex current relocation index
* @param relocation current relocation
* @return true if next relocation has same offset
*/
boolean nextRelocationHasSameOffset(ElfRelocation relocation) {
@ -1193,7 +1216,7 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
private String getSectionGotName() {
String sectionName = relocationTable.getSectionToBeRelocated().getNameAsString();
return "%got" + sectionName;
return ElfRelocationHandler.GOT_BLOCK_NAME + sectionName;
}
/**
@ -1204,12 +1227,12 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
return;
}
int size = (int) lastSectionGotEntryAddress.subtract(sectionGotAddress) + 1;
String sectionName = relocationTable.getSectionToBeRelocated().getNameAsString();
String blockName = getSectionGotName();
try {
MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false,
blockName, sectionGotAddress, size, "GOT for " + sectionName + " section",
"MIPS-Elf Loader", true, false, false, loadHelper.getLog());
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;
@ -1302,24 +1325,6 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
void addGOT16Relocation(MIPS_DeferredRelocation got16reloc) {
got16list.add(got16reloc);
}
@Override
public void dispose() {
// 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();
super.dispose();
}
}
/**

View File

@ -18,7 +18,6 @@ package ghidra.app.util.bin.format.elf.relocation;
import java.util.Map;
import ghidra.app.util.bin.format.elf.ElfLoadHelper;
import ghidra.app.util.bin.format.elf.ElfRelocationTable;
import ghidra.app.util.bin.format.elf.ElfSymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
@ -26,8 +25,8 @@ import ghidra.program.model.address.AddressSpace;
class PIC30_ElfRelocationContext extends ElfRelocationContext {
protected PIC30_ElfRelocationContext(ElfRelocationHandler handler, ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, relocationTable, symbolMap);
Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, symbolMap);
}
private boolean isDebugSection(AddressSpace overlaySpace) {

View File

@ -107,8 +107,8 @@ public class PIC30_ElfRelocationHandler extends ElfRelocationHandler {
@Override
public PIC30_ElfRelocationContext createRelocationContext(ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
return new PIC30_ElfRelocationContext(this, loadHelper, relocationTable, symbolMap);
Map<ElfSymbol, Address> symbolMap) {
return new PIC30_ElfRelocationContext(this, loadHelper, symbolMap);
}
private boolean isEDSVariant(ElfRelocationContext elfRelocationContext) {

View File

@ -121,7 +121,7 @@ public class PowerPC_ElfExtension extends ElfExtension {
elfLoadHelper.addFakeRelocTableEntry(gotAddr, 4);
memory.setInt(gotAddr, dynamicOffset);
}
catch (MemoryAccessException | AddressOverflowException e) {
catch (MemoryAccessException e) {
elfLoadHelper.log(e);
}
}

View File

@ -41,8 +41,8 @@ public class PowerPC_ElfRelocationHandler extends ElfRelocationHandler {
@Override
public PowerPC_ElfRelocationContext createRelocationContext(ElfLoadHelper loadHelper,
ElfRelocationTable relocationTable, Map<ElfSymbol, Address> symbolMap) {
return new PowerPC_ElfRelocationContext(this, loadHelper, relocationTable, symbolMap);
Map<ElfSymbol, Address> symbolMap) {
return new PowerPC_ElfRelocationContext(this, loadHelper, symbolMap);
}
@Override
@ -286,9 +286,8 @@ public class PowerPC_ElfRelocationHandler extends ElfRelocationHandler {
private Integer sda2Base;
protected PowerPC_ElfRelocationContext(ElfRelocationHandler handler,
ElfLoadHelper loadHelper, ElfRelocationTable relocationTable,
Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, relocationTable, symbolMap);
ElfLoadHelper loadHelper, Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, symbolMap);
}
/**

View File

@ -95,9 +95,15 @@ public class X86_32_ElfRelocationHandler extends ElfRelocationHandler {
memory.setInt(relocationAddress, value);
break;
case X86_32_ElfRelocationConstants.R_386_GOTOFF:
long dotgot = elfRelocationContext.getGOTValue();
value = (int) symbolValue + (int) addend - (int) dotgot;
memory.setInt(relocationAddress, value);
try {
long dotgot = elfRelocationContext.getGOTValue();
value = (int) symbolValue + (int) addend - (int) dotgot;
memory.setInt(relocationAddress, value);
}
catch (NotFoundException e) {
markAsError(program, relocationAddress, "R_386_GOTOFF", symbolName,
e.getMessage(), elfRelocationContext.getLog());
}
break;
case X86_32_ElfRelocationConstants.R_386_COPY:
markAsWarning(program, relocationAddress, "R_386_COPY", symbolName, symbolIndex,
@ -106,22 +112,22 @@ public class X86_32_ElfRelocationHandler extends ElfRelocationHandler {
// Thread Local Symbol relocations (unimplemented concept)
case X86_32_ElfRelocationConstants.R_386_TLS_DTPMOD32:
markAsWarning(program, relocationAddress, "R_386_TLS_DTPMOD32", symbolName,
symbolIndex, "Thread Local Symbol relocation not support",
symbolIndex, "Thread Local Symbol relocation not supported",
elfRelocationContext.getLog());
break;
case X86_32_ElfRelocationConstants.R_386_TLS_DTPOFF32:
markAsWarning(program, relocationAddress, "R_386_TLS_DTPOFF32", symbolName,
symbolIndex, "Thread Local Symbol relocation not support",
symbolIndex, "Thread Local Symbol relocation not supported",
elfRelocationContext.getLog());
break;
case X86_32_ElfRelocationConstants.R_386_TLS_TPOFF32:
markAsWarning(program, relocationAddress, "R_386_TLS_TPOFF32", symbolName,
symbolIndex, "Thread Local Symbol relocation not support",
symbolIndex, "Thread Local Symbol relocation not supported",
elfRelocationContext.getLog());
break;
case X86_32_ElfRelocationConstants.R_386_TLS_TPOFF:
markAsWarning(program, relocationAddress, "R_386_TLS_TPOFF", symbolName,
symbolIndex, "Thread Local Symbol relocation not support",
symbolIndex, "Thread Local Symbol relocation not supported",
elfRelocationContext.getLog());
break;
@ -149,9 +155,15 @@ public class X86_32_ElfRelocationHandler extends ElfRelocationHandler {
case X86_32_ElfRelocationConstants.R_386_GOTPC:
// similar to R_386_PC32 but uses .got address instead of symbol address
dotgot = elfRelocationContext.getGOTValue();
value = (int) (dotgot + addend - offset);
memory.setInt(relocationAddress, value);
try {
long dotgot = elfRelocationContext.getGOTValue();
value = (int) (dotgot + addend - offset);
memory.setInt(relocationAddress, value);
}
catch (NotFoundException e) {
markAsError(program, relocationAddress, "R_386_GOTPC", symbolName,
e.getMessage(), elfRelocationContext.getLog());
}
break;
// TODO: Cases not yet examined

View File

@ -0,0 +1,263 @@
/* ###
* 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.program.model.address.*;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.NotFoundException;
/**
* <code>X86_64_ElfRelocationContext</code> provides ability to generate a
* Global Offset Table (GOT) to facilitate GOT related relocations encountered within
* object modules.
*/
class X86_64_ElfRelocationContext extends ElfRelocationContext {
private AddressRange allocatedGotLimits;
private Address allocatedGotAddress;
private Address lastAllocatedGotEntryAddress;
private Address nextAllocatedGotEntryAddress;
private Map<Long, Address> gotMap;
X86_64_ElfRelocationContext(X86_64_ElfRelocationHandler handler, ElfLoadHelper loadHelper,
Map<ElfSymbol, Address> symbolMap) {
super(handler, loadHelper, symbolMap);
}
@Override
public long getSymbolValue(ElfSymbol symbol) {
long symbolValue = super.getSymbolValue(symbol);
if (symbolValue == 0 && ElfConstants.GOT_SYMBOL_NAME.equals(symbol.getNameAsString())) {
Address gotAddr = allocateGot();
if (gotAddr != null) {
return gotAddr.getOffset();
}
}
return symbolValue;
}
@Override
public long getGOTValue() throws NotFoundException {
try {
return super.getGOTValue();
}
catch (NotFoundException e) {
Address gotAddr = allocateGot();
if (gotAddr != null) {
return gotAddr.getOffset();
}
throw e;
}
}
private ElfSymbol findGotElfSymbol() {
for (ElfSymbolTable st : getElfHeader().getSymbolTables()) {
for (ElfSymbol s : st.getSymbols()) {
if (ElfConstants.GOT_SYMBOL_NAME.equals(s.getNameAsString())) {
return s;
}
}
}
return null;
}
private int computeRequiredGotSize() {
// NOTE: GOT allocation calculation assumes all GOT entries correspond to a specific
// symbol and not a computed offset. This assumption may need to be revised based upon
// uses of getGotEntryAddress method
Set<Long> uniqueSymbolValues = new HashSet<>();
for (ElfRelocationTable rt : getElfHeader().getRelocationTables()) {
ElfSymbolTable st = rt.getAssociatedSymbolTable();
if (st == null) {
continue;
}
for (ElfRelocation r : rt.getRelocations()) {
int symbolIndex = r.getSymbolIndex();
if (!requiresGotEntry(r) || symbolIndex == 0) {
continue;
}
ElfSymbol elfSymbol = st.getSymbol(symbolIndex);
if (elfSymbol == null) {
continue;
}
uniqueSymbolValues.add(elfSymbol.getValue());
}
}
return Math.max(8, uniqueSymbolValues.size() * 8);
}
private boolean requiresGotEntry(ElfRelocation r) {
switch (r.getType()) {
case X86_64_ElfRelocationConstants.R_X86_64_GOTPCREL:
case X86_64_ElfRelocationConstants.R_X86_64_GOTOFF64:
case X86_64_ElfRelocationConstants.R_X86_64_GOTPC32:
case X86_64_ElfRelocationConstants.R_X86_64_GOT64:
case X86_64_ElfRelocationConstants.R_X86_64_GOTPCREL64:
case X86_64_ElfRelocationConstants.R_X86_64_GOTPC64:
case X86_64_ElfRelocationConstants.R_X86_64_GOTPCRELX:
case X86_64_ElfRelocationConstants.R_X86_64_REX_GOTPCRELX:
return true;
default:
return false;
}
}
private Address allocateGot() {
allocatedGotAddress = Address.NO_ADDRESS;
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
ElfSymbol gotElfSymbol = findGotElfSymbol();
if (gotElfSymbol == null) {
// TODO: may need to support cases where GOT symbol not defined
loadHelper.log(
"GOT allocatiom failed. " + ElfConstants.GOT_SYMBOL_NAME + " not defined");
return null;
}
if (getSymbolAddress(gotElfSymbol) != null) {
throw new AssertException(ElfConstants.GOT_SYMBOL_NAME + " already allocated");
}
int alignment = getLoadAdapter().getLinkageBlockAlignment();
allocatedGotLimits =
getLoadHelper().allocateLinkageBlock(alignment, computeRequiredGotSize(),
ElfRelocationHandler.GOT_BLOCK_NAME);
if (allocatedGotLimits != null &&
allocatedGotLimits.getMinAddress().getOffset() < Integer.MAX_VALUE) {
// GOT must fall within first 32-bit segment
symbolMap.put(gotElfSymbol, allocatedGotLimits.getMinAddress());
allocatedGotAddress = allocatedGotLimits.getMinAddress();
nextAllocatedGotEntryAddress = allocatedGotAddress;
gotMap = new HashMap<>();
loadHelper.log("Created " + ElfRelocationHandler.GOT_BLOCK_NAME +
" block required for GOT relocation processing");
return allocatedGotAddress;
}
loadHelper.log("Failed to allocate " + ElfRelocationHandler.GOT_BLOCK_NAME +
" block required for relocation processing");
return null;
}
/**
* Allocate the next section GOT entry location. If GOT has not been allocated an attempt
* will be made to create one. If allocated gotMap will also be established.
* @return Address of GOT entry or {@link Address#NO_ADDRESS} if unable to allocate.
*/
private Address getNextAllocatedGotEntryAddress() {
if (nextAllocatedGotEntryAddress == null) {
allocateGot();
}
Address addr = nextAllocatedGotEntryAddress;
if (addr == Address.NO_ADDRESS) {
return Address.NO_ADDRESS; // insufficient space in got
}
try {
// verify that entry fits in got
int pointerSize = loadHelper.getProgram().getDefaultPointerSize();
Address lastAddr = nextAllocatedGotEntryAddress.addNoWrap(pointerSize - 1);
if (allocatedGotLimits.contains(lastAddr)) {
// entry fits in got - update and return entry address
lastAllocatedGotEntryAddress = lastAddr;
nextAllocatedGotEntryAddress = lastAllocatedGotEntryAddress.addNoWrap(1);
if (!allocatedGotLimits.contains(nextAllocatedGotEntryAddress)) {
// allocated got space fully consumed
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
}
return addr;
}
}
catch (AddressOverflowException e) {
// ignore
}
// insufficient space in got - fail future allocation attempts
nextAllocatedGotEntryAddress = Address.NO_ADDRESS;
return Address.NO_ADDRESS;
}
/**
* Get or allocate a GOT entry for the specified symbolValue.
* NOTE: This is restricted to object modules only which do not of a GOT.
* @param symbolValue symbol value
* @return GOT entry address or null if unable to allocate
*/
public Address getGotEntryAddress(long symbolValue) {
Address addr = null;
if (gotMap != null) {
addr = gotMap.get(symbolValue);
}
if (addr == null) {
addr = getNextAllocatedGotEntryAddress();
gotMap.put(symbolValue, addr);
}
return addr == Address.NO_ADDRESS ? null : addr;
}
/**
* Flush the section GOT table to a new %got memory block
*/
private void createGot() {
if (lastAllocatedGotEntryAddress == null) {
return;
}
int size = (int) lastAllocatedGotEntryAddress.subtract(allocatedGotAddress) + 1;
try {
MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false,
ElfRelocationHandler.GOT_BLOCK_NAME, allocatedGotAddress, 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
}
}
@Override
public void dispose() {
// Generate the object module GOT table if required
createGot();
super.dispose();
}
}

View File

@ -15,6 +15,8 @@
*/
package ghidra.app.util.bin.format.elf.relocation;
import java.util.Map;
import ghidra.app.util.bin.format.elf.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
@ -34,6 +36,12 @@ public class X86_64_ElfRelocationHandler extends ElfRelocationHandler {
return X86_64_ElfRelocationConstants.R_X86_64_RELATIVE;
}
@Override
public X86_64_ElfRelocationContext createRelocationContext(ElfLoadHelper loadHelper,
Map<ElfSymbol, Address> symbolMap) {
return new X86_64_ElfRelocationContext(this, loadHelper, symbolMap);
}
@Override
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation,
Address relocationAddress) throws MemoryAccessException, NotFoundException {
@ -46,6 +54,9 @@ public class X86_64_ElfRelocationHandler extends ElfRelocationHandler {
Program program = elfRelocationContext.getProgram();
Memory memory = program.getMemory();
X86_64_ElfRelocationContext x86RelocationContext =
(X86_64_ElfRelocationContext) elfRelocationContext;
int type = relocation.getType();
if (type == X86_64_ElfRelocationConstants.R_X86_64_NONE) {
return;
@ -120,9 +131,15 @@ public class X86_64_ElfRelocationHandler extends ElfRelocationHandler {
memory.setLong(relocationAddress, value);
break;
case X86_64_ElfRelocationConstants.R_X86_64_GOTOFF64:
long dotgot = elfRelocationContext.getGOTValue();
value = symbolValue + addend - dotgot;
memory.setLong(relocationAddress, value);
try {
long dotgot = elfRelocationContext.getGOTValue();
value = symbolValue + addend - dotgot;
memory.setLong(relocationAddress, value);
}
catch (NotFoundException e) {
markAsError(program, relocationAddress, "R_X86_64_GOTOFF64", symbolName,
e.getMessage(), elfRelocationContext.getLog());
}
break;
case X86_64_ElfRelocationConstants.R_X86_64_32: // this one complains for unsigned overflow
case X86_64_ElfRelocationConstants.R_X86_64_32S: // this one complains for signed overflow
@ -143,36 +160,112 @@ public class X86_64_ElfRelocationHandler extends ElfRelocationHandler {
// Thread Local Symbol relocations (unimplemented concept)
case X86_64_ElfRelocationConstants.R_X86_64_DTPMOD64:
markAsWarning(program, relocationAddress, "R_X86_64_DTPMOD64", symbolName,
symbolIndex, "Thread Local Symbol relocation not support",
symbolIndex, "Thread Local Symbol relocation not supported",
elfRelocationContext.getLog());
break;
case X86_64_ElfRelocationConstants.R_X86_64_DTPOFF64:
markAsWarning(program, relocationAddress, "R_X86_64_DTPOFF64", symbolName,
symbolIndex, "Thread Local Symbol relocation not support",
symbolIndex, "Thread Local Symbol relocation not supported",
elfRelocationContext.getLog());
break;
case X86_64_ElfRelocationConstants.R_X86_64_TPOFF64:
markAsWarning(program, relocationAddress, "R_X86_64_TPOFF64", symbolName,
symbolIndex, "Thread Local Symbol relocation not support",
symbolIndex, "Thread Local Symbol relocation not supported",
elfRelocationContext.getLog());
break;
case X86_64_ElfRelocationConstants.R_X86_64_TLSDESC:
markAsWarning(program, relocationAddress, "R_X86_64_TLSDESC", symbolName,
symbolIndex, "Thread Local Symbol relocation not support",
symbolIndex, "Thread Local Symbol relocation not supported",
elfRelocationContext.getLog());
break;
// cases which do not use symbol value
case X86_64_ElfRelocationConstants.R_X86_64_GOTPC32:
dotgot = elfRelocationContext.getGOTValue();
value = dotgot + addend - offset;
try {
long dotgot = elfRelocationContext.getGOTValue();
value = dotgot + addend - offset;
memory.setInt(relocationAddress, (int) value);
}
catch (NotFoundException e) {
markAsError(program, relocationAddress, "R_X86_64_GOTPC32", symbolName,
e.getMessage(), elfRelocationContext.getLog());
}
break;
case X86_64_ElfRelocationConstants.R_X86_64_GOTPCRELX:
case X86_64_ElfRelocationConstants.R_X86_64_REX_GOTPCRELX:
// Check for supported Relax cases (assumes non-PIC)
// Assumes non-PIC treatment is OK and attempts
// indirect-to-direct instruction transformation
Address opAddr = relocationAddress.subtract(2);
Address modRMAddr = relocationAddress.subtract(1);
Address directValueAddr = null;
byte op = memory.getByte(opAddr);
byte modRM = memory.getByte(modRMAddr);
byte symbolType = sym.getType();
if (symbolType < ElfSymbol.STT_NOTYPE || symbolType > ElfSymbol.STT_COMMON) {
// do not transform instruction for OS-specific symbol types
}
else if (op == (byte) 0x8b) { // check for MOV op
// convert to LEA op
elfRelocationContext.getLoadHelper().addFakeRelocTableEntry(opAddr, 2);
memory.setByte(opAddr, (byte) 0x8d); // direct LEA op
directValueAddr = relocationAddress;
}
else if (op == (byte) 0xff) { // check for possible JMP/CALL op
if (modRM == (byte) 0x25) { // check for indirect JMP op
// convert to direct JMP op
// must compensate for shorter instruction by appending NOP
elfRelocationContext.getLoadHelper().addFakeRelocTableEntry(opAddr, 2);
memory.setByte(opAddr, (byte) 0xe9); // direct JMP op
memory.setByte(relocationAddress.add(3), (byte) 0x90); // append NOP
directValueAddr = modRMAddr;
addend += 1;
}
else if (modRM == (byte) 0x15) { // check for indirect CALL instruction
// convert to direct CALL instruction
// use of addr32 prefix allows use of single instruction
elfRelocationContext.getLoadHelper().addFakeRelocTableEntry(opAddr, 2);
memory.setByte(opAddr, (byte) 0x67); // addr32 prefix
memory.setByte(modRMAddr, (byte) 0xe8); // direct CALL op
directValueAddr = relocationAddress;
}
}
if (directValueAddr != null) {
value = symbolValue + addend - offset;
memory.setInt(directValueAddr, (int) value);
break;
}
// If instruction not handled as relaxed instruction
// Let R_X86_64_GOTPCREL case handle as simple GOTPCREL relocation.
case X86_64_ElfRelocationConstants.R_X86_64_GOTPCREL:
Address symbolGotAddress = x86RelocationContext.getGotEntryAddress(symbolValue);
if (symbolGotAddress == null) {
markAsError(program, relocationAddress, type, symbolName,
"GOT allocation failure", elfRelocationContext.getLog());
break;
}
value = symbolGotAddress.getOffset() + addend - offset;
memory.setInt(relocationAddress, (int) value);
break;
case X86_64_ElfRelocationConstants.R_X86_64_GOTPCREL:
dotgot = elfRelocationContext.getGOTValue();
value = symbolValue + dotgot + addend - offset;
memory.setInt(relocationAddress, (int) value);
case X86_64_ElfRelocationConstants.R_X86_64_GOTPCREL64:
symbolGotAddress = x86RelocationContext.getGotEntryAddress(symbolValue);
if (symbolGotAddress == null) {
markAsError(program, relocationAddress, "R_X86_64_GOTPCREL64", symbolName,
"GOT allocation failure", elfRelocationContext.getLog());
break;
}
value = symbolGotAddress.getOffset() + addend - offset;
memory.setLong(relocationAddress, value);
break;
case X86_64_ElfRelocationConstants.R_X86_64_RELATIVE: