diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clear/ClearFlowAndRepairCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clear/ClearFlowAndRepairCmd.java index b263b48e6a..8b0a1d3a29 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clear/ClearFlowAndRepairCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clear/ClearFlowAndRepairCmd.java @@ -216,9 +216,13 @@ public class ClearFlowAndRepairCmd extends BackgroundCommand { 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 { 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)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java index 004895fc5b..1f2f48aff9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java @@ -4,9 +4,9 @@ * 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. @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java index c0eb241518..44421edeca 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java @@ -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"); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Function.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Function.java index 0d248c678c..3987240ae0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Function.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Function.java @@ -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; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java index 5194a53efe..f70442ca70 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java @@ -4,9 +4,9 @@ * 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. @@ -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) diff --git a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC64_ElfExtension.java b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC64_ElfExtension.java index 94dcb78471..6a08022f42 100644 --- a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC64_ElfExtension.java +++ b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC64_ElfExtension.java @@ -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); - } - // TODO: verify ABI detection - return; + if (getPpc64ElfABIVersion(elf) == 2) { + markupELFv2PltGot(elfLoadHelper, pltBlock.getStart(), -1, monitor); } + 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); + 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 { + 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) { + refSymbol.getSource() == SourceType.DEFAULT && + !mem.isExternalBlockAddress(refSymbol.getAddress())) { + + // TODO: Rename of DEFAULT thunk should always be avoided (see GP-5872) + try { - // Force source type on function to prevent potential removal by clear-flow - refSymbol.setName(".pltgot." + refSymbol.getName(), SourceType.IMPORTED); + // 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(); - monitor.setShowProgressValue(true); - monitor.setProgress(0); - monitor.setMaximum((endAddr.subtract(addr) + 1) / 24); - int count = 0; - 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) / opdEntryByteSize); + int count = 0; + 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,17 +634,15 @@ 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 { - program.getProgramContext().setRegisterValue(refAddr, refAddr, tocValue); - } - catch (ContextChangeException e) { - throw new AssertException(e); - } + Register r2reg = program.getRegister("r2"); + RegisterValue tocValue = new RegisterValue(r2reg, tocAddr.getOffsetAsBigInteger()); + try { + program.getProgramContext().setRegisterValue(refAddr, refAddr, tocValue); } + 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: * * @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; } diff --git a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/relocation/PowerPC64_ElfRelocationHandler.java b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/relocation/PowerPC64_ElfRelocationHandler.java index 9b640d7fe9..a719a75614 100644 --- a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/relocation/PowerPC64_ElfRelocationHandler.java +++ b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/relocation/PowerPC64_ElfRelocationHandler.java @@ -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> { @@ -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 @@ -62,7 +61,7 @@ public class PowerPC64_ElfRelocationHandler Program program = elfRelocationContext.getProgram(); Memory memory = program.getMemory(); - + // NOTE: Based upon glibc source it appears that PowerPC only uses RELA relocations long addend = relocation.getAddend(); long offset = relocationAddress.getOffset(); @@ -98,7 +97,7 @@ public class PowerPC64_ElfRelocationHandler break; default: } - + // Handle relative relocations that do not require symbolAddr or symbolValue switch (type) { @@ -106,32 +105,32 @@ public class PowerPC64_ElfRelocationHandler long value64 = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend; memory.setLong(relocationAddress, value64); return new RelocationResult(Status.APPLIED, 8); - + case R_PPC64_TOC: memory.setLong(relocationAddress, toc); return new RelocationResult(Status.APPLIED, 8); - + case R_PPC64_COPY: markAsUnsupportedCopy(program, relocationAddress, type, symbolName, symbolIndex, sym.getSize(), elfRelocationContext.getLog()); return RelocationResult.UNSUPPORTED; - + default: break; } - + // Check for unresolved symbolAddr and symbolValue required by remaining relocation types handled below if (handleUnresolvedSymbol(elfRelocationContext, relocation, relocationAddress)) { return RelocationResult.FAILURE; - } + } int oldValue = memory.getInt(relocationAddress); int newValue = 0; - + int byteLength = 4; // most relocations affect 4-bytes (change if different) switch (type) { - + case R_PPC64_ADDR32: newValue = (int) (symbolValue + addend); memory.setInt(relocationAddress, newValue); @@ -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); - } - }