GP-3091 ppc64 ELF improvements for 32-bit addressing. Fixed default ELF

GOT markup boundary condition.  Fixed improper EXTERNAL symbols with
.pltgot. prefix and duplication.
This commit is contained in:
ghidra1 2025-07-21 18:13:24 -04:00
parent 0ddd0d7533
commit 4a0e95ecd3
7 changed files with 375 additions and 176 deletions

View file

@ -216,9 +216,13 @@ public class ClearFlowAndRepairCmd extends BackgroundCommand<Program> {
monitor.checkCancelled();
Symbol[] syms = symTable.getSymbols(addr);
for (Symbol sym : syms) {
// TODO: GP-5872 This code is suspect - should it be restricted to LABELS only.
// Why would we remove a non-default function that had references?
if (sym.getSource() == SourceType.DEFAULT) {
break;
}
// TODO: GP-5872 If one of many labels at a location has a direct reference why
// would we bail when we may have already removed a few that did not.
if (sym.hasReferences()) {
continue;
}
@ -718,6 +722,11 @@ public class ClearFlowAndRepairCmd extends BackgroundCommand<Program> {
if (source == SourceType.USER_DEFINED || source == SourceType.IMPORTED) {
continue; // keep imported or user-defined function
}
// TODO: GP-5872 Clearing thunks explicitly created by loader or pattern
// generally have default SourceType and may not have references
// to them. We need to prevent these thunks from getting cleared.
// PowerPC64 ELF extension was forced to rename thunks it created to
// avoid this where the thunk rename has its own set of issues.
}
if (clearOffcut && !destAddrs.contains(blockAddr)) {

View file

@ -167,8 +167,7 @@ public class ElfDefaultGotPltMarkup {
// PLT head is unknown. If the binary has not placed external symbols within the PLT
// processing and disassembly of the PLT may be skipped.
long pltgot = elf.adjustAddressForPrelink(
dynamicTable.getDynamicValue(pltGotType));
long pltgot = elf.adjustAddressForPrelink(dynamicTable.getDynamicValue(pltGotType));
Address gotStart = defaultSpace.getAddress(pltgot + imageBaseAdj);
ElfRelocation[] relocations = relocationTable.getRelocations();
@ -354,7 +353,7 @@ public class ElfDefaultGotPltMarkup {
* Mark-up all GOT entries as pointers within the memory range gotStart to
* gotEnd.
* @param gotStart address for start of GOT
* @param gotEnd address for end of GOT
* @param gotEnd address for end of GOT (inclusive)
* @param monitor task monitor
* @throws CancelledException thrown if task cancelled
*/
@ -402,21 +401,17 @@ public class ElfDefaultGotPltMarkup {
try {
int pointerSize = program.getDataTypeManager().getDataOrganization().getPointerSize();
long gotSizeRemaining = gotEnd.subtract(gotStart) + 1;
long entryOffset = 0;
Address newImageBase = null;
Address nextGotAddr = gotStart;
while (gotEnd.subtract(nextGotAddr) >= pointerSize) {
while (gotSizeRemaining >= pointerSize) {
Address nextGotAddr = gotStart.addNoWrap(entryOffset);
data = createPointer(nextGotAddr, true);
if (data == null) {
break;
}
try {
nextGotAddr = data.getMaxAddress().add(1);
}
catch (AddressOutOfBoundsException e) {
break; // no more room
}
gotSizeRemaining -= pointerSize;
entryOffset += pointerSize;
newImageBase = UglyImageBaseCheck(data, newImageBase);
}
if (newImageBase != null) {
@ -573,12 +568,6 @@ public class ElfDefaultGotPltMarkup {
}
int pointerSize = program.getDataTypeManager().getDataOrganization().getPointerSize();
Pointer pointer = PointerDataType.dataType.clone(program.getDataTypeManager());
if (elf.is32Bit() && pointerSize != 4) {
pointer = Pointer32DataType.dataType;
}
else if (elf.is64Bit() && pointerSize != 8) {
pointer = Pointer64DataType.dataType;
}
Data data = listing.getDataAt(addr);
if (data == null || !pointer.isEquivalent(data.getDataType())) {
if (data != null) {
@ -623,7 +612,10 @@ public class ElfDefaultGotPltMarkup {
Program program = pointerData.getProgram();
Memory memory = program.getMemory();
Address refAddr = (Address) pointerData.getValue();
if (memory.contains(refAddr)) {
if (refAddr == null) {
return false;
}
else if (memory.contains(refAddr)) {
return true;
}
Symbol primary = program.getSymbolTable().getPrimarySymbol(refAddr);

View file

@ -20,6 +20,8 @@ import static ghidra.program.util.FunctionChangeRecord.FunctionChangeType.*;
import java.io.IOException;
import java.util.*;
import javax.help.UnsupportedOperationException;
import db.DBRecord;
import ghidra.program.database.*;
import ghidra.program.database.data.DataTypeManagerDB;
@ -140,6 +142,9 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public void setThunkedFunction(Function referencedFunction) {
if (isExternal()) {
throw new UnsupportedOperationException("External functions may not be a thunk");
}
if ((referencedFunction != null) && !(referencedFunction instanceof FunctionDB)) {
throw new IllegalArgumentException("FunctionDB expected for referenced function");
}

View file

@ -702,6 +702,7 @@ public interface Function extends Namespace {
* @throws IllegalArgumentException if an attempt is made to thunk a function or another
* thunk which would result in a loop back to this function or if this function is an external
* function, or specified function is from a different program instance.
* @throws UnsupportedOperationException if this method is invoked on an external function.
*/
public void setThunkedFunction(Function thunkedFunction) throws IllegalArgumentException;

View file

@ -113,6 +113,8 @@ public interface FunctionManager extends ManagerDB {
* @return new function or null if one or more functions overlap the specified body address set.
* @throws OverlappingFunctionException if the address set of the body overlaps an existing
* function
* @throws UnsupportedOperationException if this method is invoked on an external entryPoint
* address.
*/
public Function createThunkFunction(String name, Namespace nameSpace, Address entryPoint,
AddressSetView body, Function thunkedFunction, SourceType source)

View file

@ -24,13 +24,11 @@ import ghidra.app.util.bin.format.elf.*;
import ghidra.app.util.bin.format.elf.ElfDynamicType.ElfDynamicValueType;
import ghidra.app.util.bin.format.elf.relocation.PowerPC64_ElfRelocationType;
import ghidra.app.util.opinion.ElfLoader;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.address.*;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.QWordDataType;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.*;
import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
@ -66,19 +64,24 @@ public class PowerPC64_ElfExtension extends ElfExtension {
private static final int STO_PPC64_LOCAL_BIT = 5;
private static final int STO_PPC64_LOCAL_MASK = 0xE0;
// Use equate for ELFv1 to remember if function descriptors use 2 or 3 pointers.
// An equate value of 0 indicates 3-pointers, while 1 indicates 2-pointers
private static final String FN_DESCR_TYPE_NAME = "_ELFv1_PPC64_FN_DESCR_TYPE_";
public static final String TOC_BASE = "TOC_BASE"; // injected symbol to mark global TOC_BASE
public static final String OPD_SECTION_NAME = ".opd";
@Override
public boolean canHandle(ElfHeader elf) {
return elf.e_machine() == ElfConstants.EM_PPC64 && elf.is64Bit();
return elf.e_machine() == ElfConstants.EM_PPC64;
}
@Override
public boolean canHandle(ElfLoadHelper elfLoadHelper) {
Language language = elfLoadHelper.getProgram().getLanguage();
return canHandle(elfLoadHelper.getElfHeader()) &&
"PowerPC".equals(language.getProcessor().toString()) &&
language.getLanguageDescription().getSize() == 64;
"PowerPC".equals(language.getProcessor().toString());
}
@Override
@ -107,12 +110,104 @@ public class PowerPC64_ElfExtension extends ElfExtension {
setEntryPointContext(elfLoadHelper, monitor);
processOPDSection(elfLoadHelper, monitor);
if (getPpc64ElfABIVersion(elfLoadHelper.getElfHeader()) != 2) {
processOPDSection(elfLoadHelper, monitor); // establishes OPD_SIZE equate
}
// Super handles conventional GOT and executable PLT
super.processGotPlt(elfLoadHelper, monitor);
processPpc64v2PltPointerTable(elfLoadHelper, monitor);
processPpc64PltGotPointerTable(elfLoadHelper, monitor);
// ppc64 extension only handles non-execute PLT containing pointers or function descriptors
ElfHeader elf = elfLoadHelper.getElfHeader();
if (getPpc64ElfABIVersion(elf) == 2) {
setPpc64ELFv2TocBase(elfLoadHelper, monitor);
}
if (!processPpc64DynamicPltPointerTable(elfLoadHelper, monitor)) {
processPpc64PltSectionPointerTable(elfLoadHelper, monitor);
}
}
private void setPpc64ELFv2TocBase(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) {
// paint TOC_BASE value as r2 across executable blocks since r2
// is needed to resolve call stubs
Symbol tocSymbol = SymbolUtilities.getLabelOrFunctionSymbol(elfLoadHelper.getProgram(),
TOC_BASE, err -> elfLoadHelper.getLog().appendMsg("PowerPC64_ELF", err));
if (tocSymbol != null) {
paintTocAsR2value(tocSymbol.getAddress().getOffset(), elfLoadHelper, monitor);
}
}
private void rememberELFv1FunctionDescriptorSize(ElfLoadHelper elfLoadHelper,
boolean hasThreePointerFnDescriptor) {
EquateTable equateTable = elfLoadHelper.getProgram().getEquateTable();
try {
equateTable.createEquate(FN_DESCR_TYPE_NAME, hasThreePointerFnDescriptor ? 0 : 1);
}
catch (DuplicateNameException | InvalidInputException e) {
Msg.error(this, "Unexpected exception", e);
}
}
/**
* Recall the determination of function desciptors using 2 or 3 pointers for ELFv1.
* The {@link #rememberELFv1FunctionDescriptorSize(ElfLoadHelper, boolean)} must have been
* previously invoked to store this via an Equate use {@link #FN_DESCR_TYPE_NAME}.
* @param elfLoadHelper ELF load helper
* @return true if 3-pointer or false if 2-pointer function descriptor, null if never stored
*/
private Boolean hasELFv1ThreePointerFnDescriptor(ElfLoadHelper elfLoadHelper) {
EquateTable equateTable = elfLoadHelper.getProgram().getEquateTable();
Equate value = equateTable.getEquate(FN_DESCR_TYPE_NAME);
if (value == null) {
return null;
}
return value.getValue() == 0;
}
/**
* Determine the ELFv1 Function Descriptor size/type by checking TOC_Base entry for first
* two .opd entries.
* @param opdAddr entry address of within .opd (generally, should be first entry)
* @param uses32bitPtr true if 32-bit pointers are used, false if 64-bit
* @param mem program memory
* @param isFirstEntry true if opdAddr corresponds to first entry
* @return true if .opd entried consist of three pointers, false if two pointers
* @throws MemoryAccessException if memory access error occurs while checking .opd entries
* @throws AddressOverflowException if address error occurs while checking .opd entries
*/
private boolean hasELFv1ThreePointerFnDescriptor(Address opdAddr, boolean uses32bitPtr,
Memory mem, boolean isFirstEntry)
throws MemoryAccessException, AddressOverflowException {
// NOTE: This method assumes the first two entries within the .opd region will have the same
// TOC value (2nd pointer). I'm sure this is over simplified and could fail for some larger
// binaries. If a better test is devised it could replace the use of this method.
int pointerSize = uses32bitPtr ? 4 : 8;
long tocValue = getUnsignedValue(opdAddr.addNoWrap(pointerSize), uses32bitPtr, mem);
// look forward assuming 2-pointer descriptor size
try {
long nextTocValue =
getUnsignedValue(opdAddr.addNoWrap(3 * pointerSize), uses32bitPtr, mem);
if (nextTocValue == tocValue) {
return false;
}
if (!isFirstEntry) {
long prevTocValue =
getUnsignedValue(opdAddr.subtractNoWrap(pointerSize), uses32bitPtr, mem);
if (prevTocValue == tocValue) {
return false;
}
}
}
catch (AddressOverflowException | MemoryAccessException e) {
// ignore error from peeking around
}
return true; // assume 3-pointer descriptor use
}
private void findTocBase(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) {
@ -147,27 +242,33 @@ public class PowerPC64_ElfExtension extends ElfExtension {
}
}
private void processPpc64PltGotPointerTable(ElfLoadHelper elfLoadHelper, TaskMonitor monitor)
throws CancelledException {
private void processPpc64PltSectionPointerTable(ElfLoadHelper elfLoadHelper,
TaskMonitor monitor) throws CancelledException {
MemoryBlock pltBlock = getPltSectionBlockSetReadOnly(elfLoadHelper);
if (pltBlock == null) {
return; // .plt not found or is executable
}
ElfHeader elf = elfLoadHelper.getElfHeader();
if (getPpc64ABIVersion(elf) == 2) {
// paint TOC_BASE value as r2 across executable blocks since r2
// is needed to resolve call stubs
Symbol tocSymbol = SymbolUtilities.getLabelOrFunctionSymbol(elfLoadHelper.getProgram(),
TOC_BASE, err -> elfLoadHelper.getLog().appendMsg("PowerPC64_ELF", err));
if (tocSymbol != null) {
paintTocAsR2value(tocSymbol.getAddress().getOffset(), elfLoadHelper, monitor);
if (getPpc64ElfABIVersion(elf) == 2) {
markupELFv2PltGot(elfLoadHelper, pltBlock.getStart(), -1, monitor);
}
// TODO: verify ABI detection
return;
else {
markupELFv1PltPointerTable(elfLoadHelper, pltBlock.getStart(), -1, monitor);
}
}
private boolean processPpc64DynamicPltPointerTable(ElfLoadHelper elfLoadHelper,
TaskMonitor monitor) throws CancelledException {
ElfHeader elf = elfLoadHelper.getElfHeader();
ElfDynamicTable dynamicTable = elf.getDynamicTable();
if (dynamicTable == null || !dynamicTable.containsDynamicValue(ElfDynamicType.DT_PLTGOT) ||
!dynamicTable.containsDynamicValue(ElfDynamicType.DT_PLTRELSZ) ||
!dynamicTable.containsDynamicValue(ElfDynamicType.DT_PLTREL)) {
return;
return false;
}
try {
@ -175,27 +276,119 @@ public class PowerPC64_ElfExtension extends ElfExtension {
elf.adjustAddressForPrelink(dynamicTable.getDynamicValue(ElfDynamicType.DT_PLTGOT));
Address pltAddr = elfLoadHelper.getDefaultAddress(pltgotOffset);
Program program = elfLoadHelper.getProgram();
MemoryBlock pltBlock = program.getMemory().getBlock(pltAddr);
if (pltBlock == null || pltBlock.isExecute()) {
return;
MemoryBlock block = program.getMemory().getBlock(pltAddr);
if (block == null || block.isExecute()) {
return false; // PLT block not found or is executable
}
// Compute number of entries in .pltgot based upon number of associated dynamic relocations
int relEntrySize = (dynamicTable
.getDynamicValue(ElfDynamicType.DT_PLTREL) == ElfDynamicType.DT_RELA.value) ? 24
: 16;
long pltEntryCount =
dynamicTable.getDynamicValue(ElfDynamicType.DT_PLTRELSZ) / relEntrySize;
if (getPpc64ElfABIVersion(elf) == 2) {
markupELFv2PltGot(elfLoadHelper, pltAddr, pltEntryCount, monitor);
}
else {
markupELFv1PltPointerTable(elfLoadHelper, pltAddr, pltEntryCount, monitor);
}
}
catch (NotFoundException e) {
throw new AssertException("Unexpected Error", e);
}
return true;
}
private void markupELFv2PltGot(ElfLoadHelper elfLoadHelper, Address pltAddr, long pltEntryCount,
TaskMonitor monitor) throws CancelledException {
int pointerSize = elfLoadHelper.getProgram().getDefaultPointerSize();
if (pltEntryCount <= 0) {
// compute based upon block size
MemoryBlock pltBlock = elfLoadHelper.getProgram().getMemory().getBlock(pltAddr);
pltEntryCount = (pltBlock.getSize() - PLT_HEAD_SIZE) / pointerSize;
}
Address addr = pltAddr.add(PLT_HEAD_SIZE);
try {
monitor.setShowProgressValue(true);
monitor.setProgress(0);
monitor.setMaximum(pltEntryCount);
int count = 0;
for (int i = 0; i < pltEntryCount; i++) {
monitor.checkCancelled();
pltAddr = pltAddr.addNoWrap(24);
Symbol refSymbol = markupDescriptorEntry(pltAddr, false, elfLoadHelper);
if (refSymbol != null && refSymbol.getSymbolType() == SymbolType.FUNCTION &&
refSymbol.getSource() == SourceType.DEFAULT) {
monitor.setProgress(++count);
if (elfLoadHelper.createData(addr, PointerDataType.dataType) == null) {
break; // stop early if failed to create a pointer
}
addr = addr.addNoWrap(pointerSize);
}
}
catch (AddressOverflowException e) {
// ignore
}
}
private void markupELFv1PltPointerTable(ElfLoadHelper elfLoadHelper, Address pltAddr,
long pltEntryCount, TaskMonitor monitor) throws CancelledException {
// Recover ELFv1 function descriptor type from stored Equate
Boolean hasThreePointerFnDescriptor = hasELFv1ThreePointerFnDescriptor(elfLoadHelper);
if (hasThreePointerFnDescriptor == null) {
return;
}
// NOTE: There are different conventions for the PLT based upon specific ABI
// conventions. Dectecting what convention applies can be very tricky
// and may requiring applying some hueristic. For now we will avoid
// marking up this section and rely on other analysis.
//
// Case observations:
// - Shared library for ELFv1 treated .plt section similar to .opd with
// the placement of function descriptors (3 pointers).
Program program = elfLoadHelper.getProgram();
Memory mem = program.getMemory();
int fnDescriptorSize = hasThreePointerFnDescriptor ? 3 : 2;
int pointerByteSize = program.getDefaultPointerSize();
int opdEntryByteSize = fnDescriptorSize * pointerByteSize;
if (pltEntryCount <= 0) {
// compute based upon block size
MemoryBlock pltBlock = elfLoadHelper.getProgram().getMemory().getBlock(pltAddr);
pltEntryCount = pltBlock.getSize() / opdEntryByteSize;
}
try {
// Force source type on function to prevent potential removal by clear-flow
refSymbol.setName(".pltgot." + refSymbol.getName(), SourceType.IMPORTED);
monitor.setShowProgressValue(true);
monitor.setProgress(0);
monitor.setMaximum(pltEntryCount);
int count = 0;
for (int i = 0; i < pltEntryCount; i++) {
monitor.checkCancelled();
monitor.setProgress(++count);
// NOTE: First entry is skipped intentionally
pltAddr = pltAddr.addNoWrap(opdEntryByteSize);
Symbol refSymbol = markupDescriptorEntry(pltAddr, false,
hasThreePointerFnDescriptor, elfLoadHelper);
if (refSymbol != null && refSymbol.getSymbolType() == SymbolType.FUNCTION &&
refSymbol.getSource() == SourceType.DEFAULT &&
!mem.isExternalBlockAddress(refSymbol.getAddress())) {
// TODO: Rename of DEFAULT thunk should always be avoided (see GP-5872)
try {
// Rename default symbol with non-default source to prevent potential removal
// by clear-flow.
refSymbol.setName(".plt." + refSymbol.getName(), SourceType.IMPORTED);
}
catch (DuplicateNameException | InvalidInputException e) {
// ignore
@ -203,9 +396,6 @@ public class PowerPC64_ElfExtension extends ElfExtension {
}
}
}
catch (NotFoundException e) {
throw new AssertException("unexpected", e);
}
catch (AddressOverflowException e) {
elfLoadHelper.log("Failed to process PltGot entries: " + e.getMessage());
}
@ -234,91 +424,120 @@ public class PowerPC64_ElfExtension extends ElfExtension {
}
private void processPpc64v2PltPointerTable(ElfLoadHelper elfLoadHelper, TaskMonitor monitor)
throws CancelledException {
private MemoryBlock getPltSectionBlockSetReadOnly(ElfLoadHelper elfLoadHelper) {
ElfHeader elf = elfLoadHelper.getElfHeader();
ElfSectionHeader pltSection = elf.getSection(ElfSectionHeaderConstants.dot_plt);
if (pltSection == null) {
return;
return null;
}
Program program = elfLoadHelper.getProgram();
MemoryBlock pltBlock = program.getMemory().getBlock(pltSection.getNameAsString());
if (pltBlock == null) {
return;
return null;
}
if (pltSection.isExecutable()) {
return;
return null;
}
// set pltBlock read-only to permit decompiler simplification
pltBlock.setWrite(false);
if (getPpc64ABIVersion(elf) != 2) {
// TODO: add support for other PLT implementations
return;
}
// TODO: Uncertain
Address addr = pltBlock.getStart().add(PLT_HEAD_SIZE);
try {
while (addr.compareTo(pltBlock.getEnd()) < 0) {
monitor.checkCancelled();
if (elfLoadHelper.createData(addr, PointerDataType.dataType) == null) {
break; // stop early if failed to create a pointer
}
addr = addr.addNoWrap(PLT_ENTRY_SIZE);
}
}
catch (AddressOverflowException e) {
// ignore
}
return pltBlock;
}
private void processOPDSection(ElfLoadHelper elfLoadHelper, TaskMonitor monitor)
throws CancelledException {
MemoryBlock opdBlock = elfLoadHelper.getProgram().getMemory().getBlock(".opd");
boolean makeSymbol = false;
Program program = elfLoadHelper.getProgram();
Memory mem = program.getMemory();
MemoryBlock opdBlock = mem.getBlock(OPD_SECTION_NAME);
if (opdBlock == null) {
// Handle case where section names have been stripped - find .opd section
ElfHeader elf = elfLoadHelper.getElfHeader();
if (elf.getSectionHeaderCount() == 0 || !(elf.isExecutable() || elf.isSharedObject())) {
return;
}
// Determine entry address which should point into .opd block
long entry = elf.e_entry();
if (entry == 0) {
return;
}
AddressFactory addrFactory = program.getAddressFactory();
entry += elfLoadHelper.getImageBaseWordAdjustmentOffset();
Address entryAddress =
addrFactory.getDefaultAddressSpace().getTruncatedAddress(entry, true);
opdBlock = mem.getBlock(entryAddress);
makeSymbol = true;
}
if (opdBlock == null || !opdBlock.isInitialized() || opdBlock.isExecute()) {
return;
}
if (makeSymbol) {
try {
// Create .opd symbol if section lacked this name
elfLoadHelper.createSymbol(opdBlock.getStart(), OPD_SECTION_NAME, false, false,
null);
}
catch (InvalidInputException e) {
// ignore - unexpected
}
}
monitor.setMessage("Processing Function Descriptor Symbols...");
Address addr = opdBlock.getStart();
Address endAddr = opdBlock.getEnd();
try {
int pointerByteSize = program.getDefaultPointerSize();
boolean hasThreePointerFnDescriptor =
hasELFv1ThreePointerFnDescriptor(addr, pointerByteSize == 4, mem, true);
int fnDescriptorSize = hasThreePointerFnDescriptor ? 3 : 2;
int opdEntryByteSize = fnDescriptorSize * pointerByteSize;
rememberELFv1FunctionDescriptorSize(elfLoadHelper, hasThreePointerFnDescriptor); // remember as equate
monitor.setShowProgressValue(true);
monitor.setProgress(0);
monitor.setMaximum((endAddr.subtract(addr) + 1) / 24);
monitor.setMaximum((endAddr.subtract(addr) + 1) / opdEntryByteSize);
int count = 0;
try {
while (addr.compareTo(endAddr) < 0) {
monitor.checkCancelled();
monitor.setProgress(++count);
processOPDEntry(elfLoadHelper, addr);
addr = addr.addNoWrap(24);
processOPDEntry(elfLoadHelper, addr, hasThreePointerFnDescriptor);
addr = addr.addNoWrap(opdEntryByteSize);
}
}
catch (AddressOverflowException e) {
// ignore end of space
catch (MemoryAccessException | AddressOverflowException e) {
// ignore end of space or failure to detect descriptor size
}
// allow .opd section contents to be treated as constant values
opdBlock.setWrite(false);
}
private void processOPDEntry(ElfLoadHelper elfLoadHelper, Address opdAddr) {
private long getUnsignedValue(Address addr, boolean uses32bitPtr, Memory mem)
throws MemoryAccessException {
return uses32bitPtr ? Integer.toUnsignedLong(mem.getInt(addr)) : mem.getLong(addr);
}
private void processOPDEntry(ElfLoadHelper elfLoadHelper, Address opdAddr,
boolean fnDescriptorHasEnvPtr) {
Program program = elfLoadHelper.getProgram();
SymbolTable symbolTable = program.getSymbolTable();
boolean isGlobal = symbolTable.isExternalEntryPoint(opdAddr);
Symbol refSymbol = markupDescriptorEntry(opdAddr, isGlobal, elfLoadHelper);
Symbol refSymbol =
markupDescriptorEntry(opdAddr, isGlobal, fnDescriptorHasEnvPtr, elfLoadHelper);
if (refSymbol == null) {
return;
}
@ -334,7 +553,8 @@ public class PowerPC64_ElfExtension extends ElfExtension {
refSymbol.getSource() == SourceType.DEFAULT) {
try {
// Force source type on function to prevent potential removal by clear-flow
refSymbol.setName(".opd." + refSymbol.getName(), SourceType.IMPORTED);
refSymbol.setName(OPD_SECTION_NAME + "." + refSymbol.getName(),
SourceType.IMPORTED);
}
catch (DuplicateNameException | InvalidInputException e) {
// ignore
@ -365,24 +585,35 @@ public class PowerPC64_ElfExtension extends ElfExtension {
}
private Symbol markupDescriptorEntry(Address entryAddr, boolean isGlobal,
ElfLoadHelper elfLoadHelper) {
boolean hasThreePointerFnDescriptor, ElfLoadHelper elfLoadHelper) {
Program program = elfLoadHelper.getProgram();
// markup function descriptor (3 elements, 24-bytes)
Data refPtr = elfLoadHelper.createData(entryAddr, PointerDataType.dataType);
// markup function descriptor (two or three pointers)
Data refPtr = elfLoadHelper.createData(entryAddr, PointerDataType.dataType); // function *
Data tocPtr = elfLoadHelper.createData(entryAddr.add(program.getDefaultPointerSize()),
PointerDataType.dataType);
// TODO: uncertain what 3rd procedure descriptor element represents
elfLoadHelper.createData(entryAddr.add(2 * program.getDefaultPointerSize()),
QWordDataType.dataType);
PointerDataType.dataType); // toc *
if (refPtr == null || tocPtr == null) {
Msg.error(this, "Failed to process PPC64 descriptor at " + entryAddr);
Msg.error(this, "Failed to process PPC64 function descriptor at " + entryAddr);
return null;
}
// FIXME! How do we determine if an env* is present - descriptor may only have two pointers
// instead of three.
if (hasThreePointerFnDescriptor) {
elfLoadHelper.createData(entryAddr.add(2 * program.getDefaultPointerSize()),
PointerDataType.dataType); // env *
}
Address refAddr = (Address) refPtr.getValue();
if (refAddr == null || program.getMemory().getBlock(refAddr) == null) {
if (refAddr == null || refAddr.getOffset() == 0 ||
program.getMemory().getBlock(refAddr) == null) {
return null;
}
Address tocAddr = (Address) tocPtr.getValue();
if (tocAddr == null || tocAddr.getOffset() == 0) {
return null;
}
@ -403,8 +634,6 @@ public class PowerPC64_ElfExtension extends ElfExtension {
}
// set r2 to TOC base for each function
Address tocAddr = (Address) tocPtr.getValue();
if (tocAddr != null) {
Register r2reg = program.getRegister("r2");
RegisterValue tocValue = new RegisterValue(r2reg, tocAddr.getOffsetAsBigInteger());
try {
@ -413,7 +642,7 @@ public class PowerPC64_ElfExtension extends ElfExtension {
catch (ContextChangeException e) {
throw new AssertException(e);
}
}
return function.getSymbol();
}
@ -440,7 +669,7 @@ public class PowerPC64_ElfExtension extends ElfExtension {
throws CancelledException {
Program program = elfLoadHelper.getProgram();
if (getPpc64ABIVersion(elfLoadHelper.getElfHeader()) == 2) {
if (getPpc64ElfABIVersion(elfLoadHelper.getElfHeader()) == 2) {
monitor.setMessage("Assuming r12 for global functions...");
@ -476,11 +705,11 @@ public class PowerPC64_ElfExtension extends ElfExtension {
// Check for V2 ABI
if (isExternal || elfSymbol.getType() != ElfSymbol.STT_FUNC ||
getPpc64ABIVersion(elfHeader) != 2) {
getPpc64ElfABIVersion(elfHeader) != 2) {
return address;
}
// NOTE: I don't think the ABI supports little-endian
// NOTE: I don't think the ELFv1 ABI supports little-endian
Language language = elfLoadHelper.getProgram().getLanguage();
if (!canHandle(elfLoadHelper) || elfHeader.e_machine() != ElfConstants.EM_PPC64 ||
language.getLanguageDescription().getSize() != 64) {
@ -530,22 +759,22 @@ public class PowerPC64_ElfExtension extends ElfExtension {
}
/**
* Get the PPC64 ABI version specified within the ELF header.
* Get the PPC64 ELF ABI version specified within the ELF header.
* Expected values include:
* <ul>
* <li> 1 for original function descriptor using ABI </li>
* <li> 1 for original function descriptor use (i.e., .opd) </li>
* <li> 2 for revised ABI without function descriptors </li>
* <li> 0 for unspecified or not using any features affected by the differences </li>
* <li> 0 for unspecified or not using any features affected by the differences (ELFv1 assumed)</li>
* </ul>
* @param elf ELF header
* @return ABI version
* @return ELF ABI version
*/
public static int getPpc64ABIVersion(ElfHeader elf) {
public static int getPpc64ElfABIVersion(ElfHeader elf) {
if (elf.e_machine() != ElfConstants.EM_PPC64) {
return 0;
}
if (elf.getSection(".opd") != null) {
if (elf.getSection(OPD_SECTION_NAME) != null) {
return 1;
}

View file

@ -25,7 +25,6 @@ import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.*;
public class PowerPC64_ElfRelocationHandler
extends AbstractElfRelocationHandler<PowerPC64_ElfRelocationType, ElfRelocationContext<?>> {
@ -46,7 +45,7 @@ public class PowerPC64_ElfRelocationHandler
@Override
public boolean canRelocate(ElfHeader elf) {
return elf.e_machine() == ElfConstants.EM_PPC64 && elf.is64Bit();
return elf.e_machine() == ElfConstants.EM_PPC64;
}
@Override
@ -194,10 +193,6 @@ public class PowerPC64_ElfRelocationHandler
memory.setInt(relocationAddress, newValue);
break;
case R_PPC64_REL24:
// attempt to handle Object module case where referenced symbol resides within .opd
symbolValue = fixupOPDSymbolValue(elfRelocationContext, sym);
newValue = (int) ((symbolValue + addend - offset) >> 2);
newValue = ((newValue << 2) & PPC64_LOW24);
newValue = (oldValue & ~PPC64_LOW24) | newValue;
@ -230,7 +225,7 @@ public class PowerPC64_ElfRelocationHandler
}
if (PowerPC64_ElfExtension
.getPpc64ABIVersion(elfRelocationContext.getElfHeader()) == 1) {
.getPpc64ElfABIVersion(elfRelocationContext.getElfHeader()) == 1) {
// ABI ELFv1 (used by big-endian PPC64) expected to copy full function descriptor
// into .got.plt section where symbolAddr refers to function descriptor
// Copy function descriptor data
@ -273,38 +268,4 @@ public class PowerPC64_ElfRelocationHandler
return new RelocationResult(Status.APPLIED, byteLength);
}
/**
* This method generates a symbol value with possible substitution for those
* symbols residing within the .opd to refer to the real function instead.
* Care must be taken not to invoke this method for relocations which may be
* applied to call stubs. It is also important that relocations have already
* been applied to the .opd section since we will be using its data for
* locating the real function.
* @param elfRelocationContext ELF relocation context
* @param sym ELF relocation symbol
* @return symbol value
* @throws MemoryAccessException if memory access error occurs
*/
private long fixupOPDSymbolValue(ElfRelocationContext<?> elfRelocationContext, ElfSymbol sym)
throws MemoryAccessException {
Address addr = elfRelocationContext.getSymbolAddress(sym);
if (addr == null) {
return 0;
}
Program program = elfRelocationContext.getProgram();
MemoryBlock block = program.getMemory().getBlock(addr);
if (block == null || !".opd".equals(block.getName())) {
return addr.getOffset();
}
// .opd symbols will get moved to the real function by the extension (see processFunctionDescriptors)
// Call stubs should always use the .opd symbol value and not the function address so we can - this
// distinction can only be made using the relocation type.
byte[] bytes = new byte[8];
block.getBytes(addr, bytes);
boolean bigEndian = elfRelocationContext.getElfHeader().isBigEndian();
DataConverter dataConverter =
bigEndian ? BigEndianDataConverter.INSTANCE : LittleEndianDataConverter.INSTANCE;
return dataConverter.getLong(bytes);
}
}