diff --git a/Ghidra/Features/Base/ghidra_scripts/FixElfExternalOffsetDataRelocationScript.java b/Ghidra/Features/Base/ghidra_scripts/FixElfExternalOffsetDataRelocationScript.java new file mode 100644 index 0000000000..3f198e7e8c --- /dev/null +++ b/Ghidra/Features/Base/ghidra_scripts/FixElfExternalOffsetDataRelocationScript.java @@ -0,0 +1,206 @@ +/* ### + * 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. + */ +// +// With ELF imports performed with Ghidra 10.0 and 10.1 certain data relocations +// which corresponded to symbol pointers with an additional offset produced an +// ERROR bookmark and was not applied. With Ghidra 10.2 such locations now +// get the relocation applied and should utilize a Pointer-Typedef with an +// pointer offset setting. Use of a normal pointer will produce an invalid +// reference which was the original reason we avoided applying the relocation. +// +// This script applies the correct relocaton by modifying the memory bytes +// at all bookmarked locations and applies a WARNING bookmark. If data has been +// applied script will attempt to correct using an offset pointer-typedef. +// +// Script may be constrained by a selection. +// +//@category ELF Relocations +import java.util.Iterator; + +import ghidra.app.script.GhidraScript; +import ghidra.app.util.bin.format.elf.relocation.ElfRelocationHandler; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.program.model.data.DataUtilities.ClearDataMode; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.DumbMemBufferImpl; +import ghidra.program.model.mem.Memory; +import ghidra.program.model.symbol.*; +import ghidra.util.GhidraDataConverter; +import ghidra.util.Msg; + +public class FixElfExternalOffsetDataRelocationScript extends GhidraScript { + + private static final String EXT_RELO_BOOKMARK_CATEGORY = "EXTERNAL Relocation"; + private static final String EXT_RELO_BOOKMARK_TEXT_PREFIX = + "Unsupported EXTERNAL Data Elf Relocation: External Location ="; + + @Override + protected void run() throws Exception { + + if (currentProgram == null) { + popup("No active program"); + return; + } + + MessageLog log = new MessageLog(); // throw-away log + + Iterator errorBookmarks = + currentProgram.getBookmarkManager().getBookmarksIterator(BookmarkType.ERROR); + while (errorBookmarks.hasNext()) { + Bookmark b = errorBookmarks.next(); + if (EXT_RELO_BOOKMARK_CATEGORY.equals(b.getCategory()) && + b.getComment().startsWith(EXT_RELO_BOOKMARK_TEXT_PREFIX)) { + if (currentSelection != null && !currentSelection.contains(b.getAddress())) { + continue; + } + try { + if (!updateExternalDataRelocation(b, log)) { + Msg.error(this, + "Failed to update EXTERNAL relocation at " + b.getAddress()); + } + } + catch (Exception e) { + Msg.error(this, "Error occured while updating EXTERNAL relocation at " + + b.getAddress() + ": " + e.getMessage()); + } + } + } + } + + private boolean updateExternalDataRelocation(Bookmark relocErrorBookmark, MessageLog log) throws Exception { + + Address address = relocErrorBookmark.getAddress(); + String bookmarkComment = relocErrorBookmark.getComment(); + + int byteSize = address.getAddressSpace().getPointerSize(); + + int index = bookmarkComment.lastIndexOf("0x"); + if (index < 0) { + return false; + } + + char signChar = bookmarkComment.charAt(index - 1); + int offset; + try { + offset = Integer.parseInt(bookmarkComment.substring(index + 2), 16); + } + catch (NumberFormatException e) { + return false; + } + if (signChar == '-') { + offset = -offset; + } + else if (signChar != '+') { + return false; + } + + Memory memory = currentProgram.getMemory(); + DumbMemBufferImpl buf = new DumbMemBufferImpl(memory, address); + + Address symbolAddr = PointerDataType.getAddressValue(buf, byteSize, address.getAddressSpace()); + if (symbolAddr == null) { + return false; // invalid pointer data + } + + String symbolName = bookmarkComment.substring(EXT_RELO_BOOKMARK_TEXT_PREFIX.length(), index - 1).trim(); + if (currentProgram.getSymbolTable().getSymbol(symbolName, symbolAddr, null) == null) { + return false; // EXTERNAL block symbol not found at stored address + } + + Listing listing = currentProgram.getListing(); + Data data = listing.getDataContaining(address); + if (data == null) { + return false; // possible instruction applied + } + + DataType dt = data.getDataType(); + boolean isDefaultTypeApplied = Undefined.isUndefined(dt); + int componentOffset = (int) address.subtract(data.getAddress()); + if (!isDefaultTypeApplied && + !canFixupStructure(dt, componentOffset, address.getPointerSize())) { + return false; // unsupported datatype applied + } + + long newValue = symbolAddr.getOffset() + offset; + + GhidraDataConverter converter = GhidraDataConverter.getInstance(buf.isBigEndian()); + byte[] bytes = new byte[byteSize]; + converter.putValue(newValue, byteSize, bytes, 0); + memory.setBytes(address, bytes); + + currentProgram.getBookmarkManager().removeBookmark(relocErrorBookmark); + + ElfRelocationHandler.warnExternalOffsetRelocation(currentProgram, address, symbolAddr, symbolName, offset, log); + + DataType offsetPtrDt = + currentProgram.getDataTypeManager() + .resolve(new PointerTypedef(null, null, -1, currentProgram.getDataTypeManager(), + offset), null); + + if (isDefaultTypeApplied) { + // Replace undefined/default data with offset-pointer + DataUtilities.createData(currentProgram, address, offsetPtrDt, -1, false, + ClearDataMode.CLEAR_ALL_CONFLICT_DATA); + } + else { + Structure s = (Structure) dt; + DataTypeComponent dtc = s.getComponentAt(componentOffset); + if (!offsetPtrDt.isEquivalent(dtc.getDataType())) { + s.replace(dtc.getOrdinal(), offsetPtrDt, -1); + } + ReferenceManager refMgr = currentProgram.getReferenceManager(); + Reference ref = refMgr.getPrimaryReferenceFrom(address, 0); + if (ref != null && symbolAddr.equals(ref.getToAddress())) { + // Replace reference with default offset reference + refMgr.delete(ref); + refMgr.addOffsetMemReference(address, symbolAddr, true, offset, RefType.DATA, + SourceType.DEFAULT, 0); + } + } + return true; + } + + private boolean canFixupStructure(DataType dt, int componentOffset, int pointerLength) { + if (!(dt instanceof Structure)) { + return false; + } + Structure s = (Structure) dt; + DataTypeComponent dtc = s.getComponentAt(componentOffset); + if (dtc.getLength() != pointerLength) { + return false; + } + DataType cdt = dtc.getDataType(); + if (cdt instanceof Pointer) { + return true; + } + + // Check for case where structure may already have been modified + if (cdt instanceof TypeDef) { + TypeDef td = (TypeDef) cdt; + if (!(td.getDataType() instanceof Pointer)) { + return false; + } + if (!td.isAutoNamed()) { + return false; + } + return true; + } + return false; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/OffsetReferenceDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/OffsetReferenceDB.java index 40bdb129d6..5dfad00b32 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/OffsetReferenceDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/OffsetReferenceDB.java @@ -56,4 +56,9 @@ class OffsetReferenceDB extends MemReferenceDB implements OffsetReference { return offsetOrShift == ref.getOffset(); } + @Override + public String toString() { + return super.toString() + " Offset: 0x" + Long.toHexString(offsetOrShift); + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ShiftedReferenceDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ShiftedReferenceDB.java index 3e5daef1eb..5b07e4f3c6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ShiftedReferenceDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ShiftedReferenceDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,4 +55,9 @@ class ShiftedReferenceDB extends MemReferenceDB implements ShiftedReference { ShiftedReference ref = (ShiftedReference) obj; return offsetOrShift == ref.getShift(); } + + @Override + public String toString() { + return super.toString() + " Shift: 0x" + Long.toHexString(offsetOrShift); + } }