From c2f60b15d362f26bcc0ee7d58eb1f3018cd16975 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Tue, 23 Feb 2021 13:40:32 -0500 Subject: [PATCH] GP-700: Improved support for Mach-O MH_OBJECT files --- .../Base/data/ExtensionPoint.manifest | 1 + .../bin/format/macho/RelocationFactory.java | 104 ------- .../util/bin/format/macho/RelocationInfo.java | 189 ++++++++---- .../bin/format/macho/RelocationTypeARM.java | 32 -- .../bin/format/macho/RelocationTypeARM64.java | 54 ---- .../format/macho/RelocationTypeGeneric.java | 26 -- .../bin/format/macho/RelocationTypePPC.java | 36 --- .../format/macho/RelocationTypeX86_32.java | 20 -- .../format/macho/RelocationTypeX86_64.java | 60 ---- .../format/macho/ScatteredRelocationInfo.java | 116 ------- .../app/util/bin/format/macho/Section.java | 2 +- .../commands/DynamicSymbolTableCommand.java | 4 +- .../macho/commands/LinkerOptionCommand.java | 110 +++++++ .../macho/commands/LoadCommandTypes.java | 6 +- .../commands/dyld/ClassicBindProcessor.java | 6 +- .../macho/relocation/MachoRelocation.java | 290 ++++++++++++++++++ .../relocation/MachoRelocationHandler.java | 112 +++++++ .../MachoRelocationHandlerFactory.java | 42 +++ .../app/util/opinion/MachoProgramBuilder.java | 273 ++++++++--------- .../AARCH64_MachoRelocationConstants.java | 84 +++++ .../AARCH64_MachoRelocationHandler.java | 156 ++++++++++ .../ARM_MachoRelocationConstants.java | 82 +++++ .../ARM_MachoRelocationHandler.java | 105 +++++++ .../X86_32_MachoRelocationConstants.java | 54 ++++ .../X86_32_MachoRelocationHandler.java | 74 +++++ .../X86_64_MachoRelocationConstants.java | 74 +++++ .../X86_64_MachoRelocationHandler.java | 83 +++++ 27 files changed, 1542 insertions(+), 653 deletions(-) delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationFactory.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeARM.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeARM64.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeGeneric.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypePPC.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeX86_32.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeX86_64.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/ScatteredRelocationInfo.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LinkerOptionCommand.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocation.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocationHandler.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocationHandlerFactory.java create mode 100644 Ghidra/Processors/AARCH64/src/main/java/ghidra/app/util/bin/format/macho/relocation/AARCH64_MachoRelocationConstants.java create mode 100644 Ghidra/Processors/AARCH64/src/main/java/ghidra/app/util/bin/format/macho/relocation/AARCH64_MachoRelocationHandler.java create mode 100644 Ghidra/Processors/ARM/src/main/java/ghidra/app/util/bin/format/macho/relocation/ARM_MachoRelocationConstants.java create mode 100644 Ghidra/Processors/ARM/src/main/java/ghidra/app/util/bin/format/macho/relocation/ARM_MachoRelocationHandler.java create mode 100644 Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_32_MachoRelocationConstants.java create mode 100644 Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_32_MachoRelocationHandler.java create mode 100644 Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_64_MachoRelocationConstants.java create mode 100644 Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_64_MachoRelocationHandler.java diff --git a/Ghidra/Features/Base/data/ExtensionPoint.manifest b/Ghidra/Features/Base/data/ExtensionPoint.manifest index a13556031b..32324026b7 100644 --- a/Ghidra/Features/Base/data/ExtensionPoint.manifest +++ b/Ghidra/Features/Base/data/ExtensionPoint.manifest @@ -11,6 +11,7 @@ ScriptProvider Recognizer BinaryAnalysisCommand CoffRelocationHandler +MachoRelocationHandler ElfRelocationHandler ElfExtension RelocationFixupHandler diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationFactory.java deleted file mode 100644 index 89d74fb53e..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationFactory.java +++ /dev/null @@ -1,104 +0,0 @@ -/* ### - * 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.macho; - -import ghidra.app.util.bin.format.*; - -import java.io.*; - -public final class RelocationFactory { - - public final static int SECTION = 0xaaa; - public final static int EXTERNAL = 0xbbb; - public final static int LOCAL = 0xccc; - - public final static RelocationInfo readRelocation(FactoryBundledWithBinaryReader reader, boolean is32bit) throws IOException { - long relocIndex = reader.getPointerIndex(); - RelocationInfo info = RelocationInfo.createRelocationInfo(reader); - if ((info.getAddress() & ScatteredRelocationInfo.R_SCATTERED) == 0) { - return info; - } - reader.setPointerIndex(relocIndex); - return ScatteredRelocationInfo.createScatteredRelocationInfo(reader); - } - - public final static String getRelocationDescription(MachHeader header, RelocationInfo relocation) { - switch (header.getCpuType()) { - case CpuTypes.CPU_TYPE_POWERPC: - case CpuTypes.CPU_TYPE_POWERPC64: - { - RelocationTypePPC [] values = RelocationTypePPC.values(); - for (RelocationTypePPC value : values) { - if (value.ordinal() == relocation.getType()) { - return value.name(); - } - } - break; - } - case CpuTypes.CPU_TYPE_X86: - { - RelocationTypeX86_32 [] values = RelocationTypeX86_32.values(); - for (RelocationTypeX86_32 value : values) { - if (value.ordinal() == relocation.getType()) { - return value.name(); - } - } - break; - } - case CpuTypes.CPU_TYPE_X86_64: - { - RelocationTypeX86_64 [] values = RelocationTypeX86_64.values(); - for (RelocationTypeX86_64 value : values) { - if (value.ordinal() == relocation.getType()) { - return value.name(); - } - } - break; - } - case CpuTypes.CPU_TYPE_ARM: - { - RelocationTypeARM [] values = RelocationTypeARM.values(); - for (RelocationTypeARM value : values) { - if (value.ordinal() == relocation.getType()) { - return value.name(); - } - } - break; - } - case CpuTypes.CPU_TYPE_ARM_64: - { - RelocationTypeARM64 [] values = RelocationTypeARM64.values(); - for (RelocationTypeARM64 value : values) { - if (value.ordinal() == relocation.getType()) { - return value.name(); - } - } - break; - } - default: - { - RelocationTypeGeneric [] values = RelocationTypeGeneric.values(); - for (RelocationTypeGeneric value : values) { - if (value.ordinal() == relocation.getType()) { - return value.name(); - } - } - break; - } - } - return "Unknown Relocation Type: 0x"+Integer.toHexString(relocation.getType()); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationInfo.java index d6125189e0..aac0ad7936 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationInfo.java @@ -23,69 +23,115 @@ import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; /** - * Represents a relocation_info structure. + * Represents a relocation_info and scattered_relocation_info structure. * - * @see mach-o/reloc.h + * @see mach-o/reloc.h */ public class RelocationInfo implements StructConverter { - protected int r_address = -1; - protected int r_symbolnum = -1; - protected int r_pcrel = -1; - protected int r_length = -1; - protected int r_extern = -1; - protected int r_type = -1; - public static RelocationInfo createRelocationInfo( - FactoryBundledWithBinaryReader reader) throws IOException { - RelocationInfo relocationInfo = (RelocationInfo) reader.getFactory().create(RelocationInfo.class); - relocationInfo.initRelocationInfo(reader); - return relocationInfo; - } + /** + * Mask to be applied to the r_address field of a relocation_info structure to tell that it is + * really a scattered_relocation_info structure + */ + private static int R_SCATTERED = 0x80000000; - public RelocationInfo() { + /** + * 1=scattered, 0=non-scattered + */ + private int r_scattered; + + /** + * Offset in the section to what is being relocated. The r_address is not really the address + * as its name indicates but an offset. + */ + private int r_address; + + /** + * Symbol index if r_extern == 1 or section ordinal if r_extern == 0 + */ + private int r_value; + + /** + * Was relocated PC relative already + */ + private int r_pcrel; + + /** + * 0=byte, 1=word, 2=long, 3=quad + */ + private int r_length; + + /** + * If r_extern is zero then r_symbolnum is an ordinal for the segment the symbol being relocated + * is in + */ + private int r_extern; + + /** + * If not 0, machine specific relocation type + */ + private int r_type; + + public static RelocationInfo createRelocationInfo(FactoryBundledWithBinaryReader reader) + throws IOException { + RelocationInfo relocationInfo = + (RelocationInfo) reader.getFactory().create(RelocationInfo.class); + relocationInfo.initRelocationInfo(reader); + return relocationInfo; + } + + public RelocationInfo() { } private void initRelocationInfo(FactoryBundledWithBinaryReader reader) throws IOException { - r_address = reader.readNextInt(); - int value = reader.readNextInt(); + int i1 = reader.readNextInt(); + int i2 = reader.readNextInt(); - if (reader.isLittleEndian()) { - r_symbolnum = (value & 0x00ffffff); - r_pcrel = (value & 0x01000000) >> 24; - r_length = (value & 0x06000000) >> 25; - r_extern = (value & 0x08000000) >> 27; - r_type = (value & 0xf0000000) >> 28; + if ((i1 & R_SCATTERED) != 0) { + r_scattered = 1; + r_extern = 1; + r_address = i1 & 0xffffff; + r_type = (i1 >> 24) & 0xf; + r_length = (i1 >> 28) & 0x3; + r_pcrel = (i1 >> 30) & 0x1; + r_value = i2; } else { - r_symbolnum = (value & 0xffffff00) >> 8; - r_pcrel = (value & 0x00000080) >> 7; - r_length = (value & 0x00000060) >> 5; - r_extern = (value & 0x00000010) >> 4; - r_type = (value & 0x0000000f); + r_scattered = 0; + r_address = i1; + r_value = i2 & 0xffffff; + r_pcrel = (i2 >> 24) & 0x1; + r_length = (i2 >> 25) & 0x3; + r_extern = (i2 >> 27) & 0x1; + r_type = (i2 >> 28) & 0xf; } } public int getAddress() { return r_address; } - - public int getSymbolIndex() { - return r_symbolnum; + + public int getValue() { + return r_value; } - + public boolean isPcRelocated() { return r_pcrel == 1; } - + public int getLength() { return r_length; } - + public boolean isExternal() { return r_extern == 1; } + public boolean isScattered() { + return r_scattered == 1; + } + public int getType() { return r_type; } @@ -94,51 +140,78 @@ public class RelocationInfo implements StructConverter { * Returns the values array for storage into the program's relocation table. * @return the values array for storage into the program's relocation table */ - public long [] toValues() { + public long[] toValues() { return new long[] { 0,//zero indicates that it is not a scattered relocation - r_address & 0xffffffffL, - r_symbolnum & 0xffffffffL, - r_pcrel & 0xffffffffL, - r_length & 0xffffffffL, - r_extern & 0xffffffffL, - r_type & 0xffffffffL}; + r_address & 0xffffffffL, r_value & 0xffffffffL, r_pcrel & 0xffffffffL, + r_length & 0xffffffffL, r_extern & 0xffffffffL, r_type & 0xffffffffL }; } @Override public String toString() { StringBuffer buffer = new StringBuffer(); - buffer.append("Address: "+Long.toHexString(r_address)); + buffer.append("Address: " + Long.toHexString(r_address)); buffer.append('\n'); - buffer.append("Symbol Index: "+Integer.toHexString(r_symbolnum)); + buffer.append("Value: " + Integer.toHexString(r_value)); buffer.append('\n'); - buffer.append("PC Relocated: "+isPcRelocated()); + buffer.append("Scattered: " + isScattered()); buffer.append('\n'); - buffer.append("Length: "+Integer.toHexString(r_length)+getLengthInBytes()); + buffer.append("PC Relocated: " + isPcRelocated()); buffer.append('\n'); - buffer.append("External: "+isExternal()); + buffer.append("Length: " + Integer.toHexString(r_length) + getLengthString()); buffer.append('\n'); - buffer.append("Type: "+Integer.toHexString(r_type)); + buffer.append("External: " + isExternal()); + buffer.append('\n'); + buffer.append("Type: " + Integer.toHexString(r_type)); buffer.append('\n'); return buffer.toString(); } - protected String getLengthInBytes() { + private String getLengthString() { switch (r_length) { - case 0 : return " (1 byte)"; - case 1 : return " (2 bytes)"; - case 2 : return " (3 bytes)"; - case 3 : return " (4 bytes)"; + case 0: + return " (1 byte)"; + case 1: + return " (2 bytes)"; + case 2: + return " (4 bytes)"; + case 3: + return " (8 bytes)"; } return ""; } @Override public DataType toDataType() throws DuplicateNameException, IOException { - StructureDataType struct = new StructureDataType("relocation_info", 0); - struct.add(DWORD, "r_address", null); - struct.add(DWORD, "r_mask", "{r_symbolnum,r_pcrel,r_length,r_extern,r_type}"); - struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY)); - return struct; + StructureDataType struct; + if (isScattered()) { + struct = new StructureDataType("scattered_relocation_info", 0); + try { + struct.insertBitFieldAt(0, DWORD.getLength(), 0, DWORD, 24, "r_address", ""); + struct.insertBitFieldAt(0, DWORD.getLength(), 24, DWORD, 4, "r_type", ""); + struct.insertBitFieldAt(0, DWORD.getLength(), 28, DWORD, 2, "r_length", ""); + struct.insertBitFieldAt(0, DWORD.getLength(), 30, DWORD, 1, "r_pcrel", ""); + struct.insertBitFieldAt(0, DWORD.getLength(), 31, DWORD, 1, "r_scattered", ""); + } + catch (InvalidDataTypeException e) { + struct.add(DWORD, "r_mask", "{r_address,r_type,r_length,r_pcrel,r_scattered}"); + } + struct.add(DWORD, "r_value", null); + } + else { + struct = new StructureDataType("relocation_info", 0); + struct.add(DWORD, "r_address", null); + try { + struct.insertBitFieldAt(4, DWORD.getLength(), 0, DWORD, 24, "r_symbolnum", ""); + struct.insertBitFieldAt(4, DWORD.getLength(), 24, DWORD, 1, "r_pcrel", ""); + struct.insertBitFieldAt(4, DWORD.getLength(), 25, DWORD, 2, "r_length", ""); + struct.insertBitFieldAt(4, DWORD.getLength(), 27, DWORD, 1, "r_extern", ""); + struct.insertBitFieldAt(4, DWORD.getLength(), 28, DWORD, 4, "r_type", ""); + } + catch (InvalidDataTypeException e) { + struct.add(DWORD, "r_mask", "{r_symbolnum,r_pcrel,r_length,r_extern,r_type}"); + } + } + struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY)); + return struct; } } - diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeARM.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeARM.java deleted file mode 100644 index bc5c36de22..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeARM.java +++ /dev/null @@ -1,32 +0,0 @@ -/* ### - * 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. - * 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.macho; - -public enum RelocationTypeARM { - - ARM_RELOC_VANILLA, - ARM_RELOC_PAIR, - ARM_RELOC_SECTDIFF, - ARM_RELOC_LOCAL_SECTDIFF, - ARM_RELOC_PB_LA_PTR, - ARM_RELOC_BR24, - ARM_THUMB_RELOC_BR22, - ARM_THUMB_32_BRANCH,//obsolete - ARM_RELOC_HALF, - ARM_RELOC_HALF_SECTDIFF; - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeARM64.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeARM64.java deleted file mode 100644 index 931f407b07..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeARM64.java +++ /dev/null @@ -1,54 +0,0 @@ -/* ### - * 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. - * 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.macho; - -public enum RelocationTypeARM64 { - - /** for pointers */ - ARM64_RELOC_UNSIGNED, - - /** must be followed but an ARM64_RELOC_UNSIGNED */ - ARM64_RELOC_SUBTRACTOR, - - /** b/bl instruction with 26-bit displacement */ - ARM64_RELOC_BRANCH26, - - /** PC-rel distance to page of target */ - ARM64_RELOC_PAGE21, - - /** offset within page, scaled by r_length */ - ARM64_RELOC_PAGEOFF12, - - /** PC-rel distance to page of GOT slot */ - ARM64_RELOC_GOT_LOAD_PAGE21, - - /** offset within page of GOT slot, scaled by r_length */ - ARM64_RELOC_GOT_LOAD_PAGEOFF12, - - /** for pointers to GOT slots*/ - ARM64_RELOC_POINTER_TO_GOT, - - /** PC-rel distance to page of TLVP slot */ - ARM64_RELOC_TLVP_LOAD_PAGE21, - - /** offset within page of TLVP slot, scaled by r_length */ - ARM64_RELOC_TLVP_LOAD_PAGEOFF12, - - /** must be followed by ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12 */ - ARM64_RELOC_ADDEND; - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeGeneric.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeGeneric.java deleted file mode 100644 index 379cf90411..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeGeneric.java +++ /dev/null @@ -1,26 +0,0 @@ -/* ### - * 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.macho; - -public enum RelocationTypeGeneric { - - GENERIC_RELOC_VANILLA, - GENERIC_RELOC_PAIR, - GENERIC_RELOC_SECTDIFF, - GENERIC_RELOC_PB_LA_PTR, - GENERIC_RELOC_LOCAL_SECTDIFF, - GENERIC_RELOC_TLV; -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypePPC.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypePPC.java deleted file mode 100644 index d0e3fb9060..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypePPC.java +++ /dev/null @@ -1,36 +0,0 @@ -/* ### - * 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.macho; - -public enum RelocationTypePPC { - PPC_RELOC_VANILLA, - PPC_RELOC_PAIR, - PPC_RELOC_BR14, - PPC_RELOC_BR24, - PPC_RELOC_HI16, - PPC_RELOC_LO16, - PPC_RELOC_HA16, - PPC_RELOC_LO14, - PPC_RELOC_SECTDIFF, - PPC_RELOC_PB_LA_PTR, - PPC_RELOC_HI16_SECTDIFF, - PPC_RELOC_LO16_SECTDIFF, - PPC_RELOC_HA16_SECTDIFF, - PPC_RELOC_JBSR, - PPC_RELOC_LO14_SECTDIFF, - PPC_RELOC_LOCAL_SECTDIFF; - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeX86_32.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeX86_32.java deleted file mode 100644 index bd40beb143..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeX86_32.java +++ /dev/null @@ -1,20 +0,0 @@ -/* ### - * 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. - * 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.macho; - -public enum RelocationTypeX86_32 { -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeX86_64.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeX86_64.java deleted file mode 100644 index 27b0f8ba88..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/RelocationTypeX86_64.java +++ /dev/null @@ -1,60 +0,0 @@ -/* ### - * 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. - * 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.macho; - -public enum RelocationTypeX86_64 { - /** - * for absolute addresses - */ - X86_64_RELOC_UNSIGNED, - /** - * for signed 32-bit displacement - */ - X86_64_RELOC_SIGNED, - /** - * a CALL/JMP instruction with 32-bit displacement - */ - X86_64_RELOC_BRANCH, - /** - * a MOVQ load of a GOT entry - */ - X86_64_RELOC_GOT_LOAD, - /** - * other GOT references - */ - X86_64_RELOC_GOT, - /** - * must be followed by a X86_64_RELOC_UNSIGNED - */ - X86_64_RELOC_SUBTRACTOR, - /** - * for signed 32-bit displacement with a -1 addend - */ - X86_64_RELOC_SIGNED_1, - /** - * for signed 32-bit displacement with a -2 addend - */ - X86_64_RELOC_SIGNED_2, - /** - * for signed 32-bit displacement with a -4 addend - */ - X86_64_RELOC_SIGNED_4, - /** - * for thread local variables - */ - X86_64_RELOC_TLV -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/ScatteredRelocationInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/ScatteredRelocationInfo.java deleted file mode 100644 index a36953a43f..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/ScatteredRelocationInfo.java +++ /dev/null @@ -1,116 +0,0 @@ -/* ### - * 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.macho; - -import java.io.IOException; - -import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; - -/** - * Represents a scattered_relocation_info structure. - * - * @see mach-o/reloc.h - */ -public class ScatteredRelocationInfo extends RelocationInfo { - - public final static int R_SCATTERED = 0x80000000; - - private int r_scattered; - private int r_value; - - public static ScatteredRelocationInfo createScatteredRelocationInfo( - FactoryBundledWithBinaryReader reader) throws IOException { - ScatteredRelocationInfo scatteredRelocationInfo = - (ScatteredRelocationInfo) reader.getFactory().create(ScatteredRelocationInfo.class); - scatteredRelocationInfo.initScatteredRelocationInfo(reader); - return scatteredRelocationInfo; - } - - /** - * DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD. - */ - public ScatteredRelocationInfo() { - } - - private void initScatteredRelocationInfo(FactoryBundledWithBinaryReader reader) - throws IOException { - int mask = reader.readNextInt(); - - r_scattered = ((mask & 0x80000000) >> 31) & 0x1; - r_pcrel = ((mask & 0x40000000) >> 30); - r_length = ((mask & 0x30000000) >> 28); - r_type = ((mask & 0x0f000000) >> 24); - r_address = ((mask & 0x00ffffff)); - - r_value = reader.readNextInt(); - } - - /** - * Returns true this is a scattered relocation. - * @return true this is a scattered relocation - */ - public boolean isScattered() { - return r_scattered == 1; - } - - /** - * The address of the relocatable expression for the item in the file that - * needs to be updated if the address is changed. For relocatable - * expressions with the difference of two section addresses, the address - * from which to subtract (in mathematical terms, the minuend) is - * contained in the first relocation entry and the address to subtract (the - * subtrahend) is contained in the second relocation entry. - * @return - */ - public int getValue() { - return r_value; - } - - @Override - public long[] toValues() { - return new long[] { r_scattered, r_pcrel, r_length, r_type, r_address, - r_value & 0xffffffffL }; - } - - @Override - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append("Scattered: " + isScattered()); - buffer.append('\n'); - buffer.append("PC Relocated: " + isPcRelocated()); - buffer.append('\n'); - buffer.append("Length: " + Integer.toHexString(r_length) + getLengthInBytes()); - buffer.append('\n'); - buffer.append("Type: " + Integer.toHexString(r_type)); - buffer.append('\n'); - buffer.append("Address: " + Long.toHexString(r_address)); - buffer.append('\n'); - buffer.append("Value: " + Integer.toHexString(r_value)); - buffer.append('\n'); - return buffer.toString(); - } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - StructureDataType struct = new StructureDataType("scattered_relocation_info", 0); - struct.add(DWORD, "r_mask", "{r_scattered,r_pcrel,r_length,r_type,r_address}"); - struct.add(DWORD, "r_value", null); - struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY)); - return struct; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/Section.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/Section.java index ff3f664815..7a2ee4efe9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/Section.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/Section.java @@ -93,7 +93,7 @@ public class Section implements StructConverter { long index = reader.getPointerIndex(); reader.setPointerIndex(reloff); for (int i = 0; i < nrelocs; ++i) { - relocations.add(RelocationFactory.readRelocation(reader, is32bit)); + relocations.add(RelocationInfo.createRelocationInfo(reader)); } reader.setPointerIndex(index); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java index aee689cb19..f7264c6d04 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java @@ -132,13 +132,13 @@ public class DynamicSymbolTableCommand extends LoadCommand { if (extreloff > 0) { reader.setPointerIndex(header.getStartIndex() + extreloff); for (int i = 0; i < nextrel; ++i) { - externalRelocations.add(RelocationFactory.readRelocation(reader, header.is32bit())); + externalRelocations.add(RelocationInfo.createRelocationInfo(reader)); } } if (locreloff > 0) { reader.setPointerIndex(header.getStartIndex() + locreloff); for (int i = 0; i < nlocrel; ++i) { - localRelocations.add(RelocationFactory.readRelocation(reader, header.is32bit())); + localRelocations.add(RelocationInfo.createRelocationInfo(reader)); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LinkerOptionCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LinkerOptionCommand.java new file mode 100644 index 0000000000..cfa0faea58 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LinkerOptionCommand.java @@ -0,0 +1,110 @@ +/* ### + * 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.macho.commands; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader; +import ghidra.app.util.bin.format.macho.MachHeader; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.flatapi.FlatProgramAPI; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.StructureDataType; +import ghidra.program.model.listing.ProgramModule; +import ghidra.util.exception.DuplicateNameException; +import ghidra.util.task.TaskMonitor; + +/** + * Represents a linker_option_command structure + * + * @see mach-o/loader.h + */ +public class LinkerOptionCommand extends LoadCommand { + + private int count; + private List linkerOptions; + + static LinkerOptionCommand createLinkerOptionCommand(FactoryBundledWithBinaryReader reader) + throws IOException { + + LinkerOptionCommand command = + (LinkerOptionCommand) reader.getFactory().create(LinkerOptionCommand.class); + command.initLinkerOptionCommand(reader); + return command; + } + + /** + * DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD. + */ + public LinkerOptionCommand() { + } + + private void initLinkerOptionCommand(FactoryBundledWithBinaryReader reader) throws IOException { + initLoadCommand(reader); + count = reader.readNextInt(); + linkerOptions = new ArrayList<>(count); + long readerIndex = reader.getPointerIndex(); + for (int i = 0; i < count; i++) { + String str = reader.readTerminatedString(readerIndex, '\0'); + linkerOptions.add(str); + readerIndex += str.length() + 1; + } + } + + /** + * Gets this {@link LinkerOptionCommand}'s linker options + * + * @return This {@link LinkerOptionCommand}'s linker options + */ + public List getLinkerOptions() { + return linkerOptions; + } + + @Override + public String getCommandName() { + return "linker_option_command"; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(getCommandName(), 0); + struct.add(DWORD, "cmd", null); + struct.add(DWORD, "cmdsize", null); + struct.add(DWORD, "count", null); + return struct; + } + + @Override + public void markup(MachHeader header, FlatProgramAPI api, Address baseAddress, boolean isBinary, + ProgramModule parentModule, TaskMonitor monitor, MessageLog log) { + updateMonitor(monitor); + + try { + if (isBinary) { + createFragment(api, baseAddress, parentModule); + Address address = baseAddress.getNewAddress(getStartIndex()); + api.createData(address, toDataType()); + } + } + catch (Exception e) { + log.appendMsg("Unable to create " + getCommandName()); + } + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandTypes.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandTypes.java index 18dd600979..0589739abf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandTypes.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandTypes.java @@ -113,7 +113,8 @@ public final class LoadCommandTypes { case LC_CODE_SIGNATURE: case LC_SEGMENT_SPLIT_INFO: case LC_FUNCTION_STARTS: - case LC_DATA_IN_CODE: + case LC_DATA_IN_CODE: + case LC_OPTIMIZATION_HINT: case LC_DYLIB_CODE_SIGN_DRS: { return LinkEditDataCommand.createLinkEditDataCommand(reader); } @@ -145,6 +146,9 @@ public final class LoadCommandTypes { case LC_BUILD_VERSION: { return BuildVersionCommand.createBuildVersionCommand(reader); } + case LC_LINKER_OPTIONS: { + return LinkerOptionCommand.createLinkerOptionCommand(reader); + } default: { return UnsupportedLoadCommand.createUnsupportedLoadCommand(reader, type); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/ClassicBindProcessor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/ClassicBindProcessor.java index cf60b3324b..4a594fa8a6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/ClassicBindProcessor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/ClassicBindProcessor.java @@ -15,13 +15,13 @@ */ package ghidra.app.util.bin.format.macho.commands.dyld; +import java.util.List; + import ghidra.app.util.bin.format.macho.*; import ghidra.app.util.bin.format.macho.commands.*; import ghidra.program.model.listing.Program; import ghidra.util.task.TaskMonitor; -import java.util.List; - public class ClassicBindProcessor extends AbstractClassicProcessor { public ClassicBindProcessor(MachHeader header, Program program) { @@ -44,7 +44,7 @@ public class ClassicBindProcessor extends AbstractClassicProcessor { break; } long address = relocation.getAddress() + getRelocationBase(); - int symbolIndex = relocation.getSymbolIndex(); + int symbolIndex = relocation.getValue(); NList nList = symbolTableCommand.getSymbolAt(symbolIndex); boolean isWeak = (nList.getDescription() & NListConstants.DESC_N_WEAK_REF) != 0; String fromDylib = getClassicOrdinalName(nList.getLibraryOrdinal()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocation.java new file mode 100644 index 0000000000..a7a439610e --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocation.java @@ -0,0 +1,290 @@ +/* ### + * 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.macho.relocation; + +import ghidra.app.util.bin.format.macho.*; +import ghidra.app.util.bin.format.macho.commands.NList; +import ghidra.app.util.bin.format.macho.commands.SymbolTableCommand; +import ghidra.app.util.opinion.MachoLoader; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.listing.Program; +import ghidra.program.model.symbol.Symbol; +import ghidra.program.model.symbol.SymbolUtilities; +import ghidra.util.NumericUtilities; +import ghidra.util.exception.NotFoundException; + +/** + * A representation of a single Mach-O relocation that the {@link MachoRelocationHandler} will use + * to perform the relocation. In Mach-O, some relocations may be "paired," so an instance of this + * class may contain 2 {@link RelocationInfo}s. + */ +public class MachoRelocation { + + private Program program; + private AddressSpace space; + private MachHeader machoHeader; + + private Address relocationAddress; + private RelocationInfo relocationInfo; + private Symbol targetSymbol; + private Section targetSection; + private Address targetPointer; + + private RelocationInfo relocationInfoExtra; + private Symbol targetSymbolExtra; + private Section targetSectionExtra; + private Address targetPointerExtra; + + /** + * Creates a new unpaired {@link MachoRelocation} object + * + * @param program The program + * @param machoHeader The Mach-O header + * @param relocationAddress The {@link Address} the relocation takes place at + * @param relocationInfo The lower-level {@link RelocationInfo} that describes the relocation + */ + public MachoRelocation(Program program, MachHeader machoHeader, Address relocationAddress, + RelocationInfo relocationInfo) { + this.program = program; + this.space = program.getAddressFactory().getDefaultAddressSpace(); + this.machoHeader = machoHeader; + this.relocationAddress = relocationAddress; + this.relocationInfo = relocationInfo; + if (relocationInfo.isScattered()) { + this.targetPointer = space.getAddress(relocationInfo.getValue()); + } + else if (relocationInfo.isExternal()) { + this.targetSymbol = findTargetSymbol(relocationInfo); + } + else { + this.targetSection = findTargetSection(relocationInfo); + } + } + + /** + * Creates a new paired {@link MachoRelocation} object + * + * @param program The program + * @param machoHeader The Mach-O header + * @param relocationAddress The {@link Address} the relocation takes place at + * @param relocationInfo The lower-level {@link RelocationInfo} that describes the first part + * of the relocation + * @param relocationInfoExtra The lower-level {@link RelocationInfo} that describes the second + * part of the relocation + */ + public MachoRelocation(Program program, MachHeader machoHeader, Address relocationAddress, + RelocationInfo relocationInfo, RelocationInfo relocationInfoExtra) { + this(program, machoHeader, relocationAddress, relocationInfo); + this.relocationInfoExtra = relocationInfoExtra; + if (relocationInfoExtra.isScattered()) { + this.targetPointerExtra = space.getAddress(relocationInfoExtra.getValue()); + } + else if (relocationInfoExtra.isExternal()) { + this.targetSymbolExtra = findTargetSymbol(relocationInfoExtra); + } + else { + this.targetSectionExtra = findTargetSection(relocationInfoExtra); + } + } + + /** + * Gets the {@link Program} associated with this relocation + * + * + * @return The {@link Program} associated with this relocation + */ + public Program getProgram() { + return program; + } + + /** + * Gets the {@link Address} the relocation takes place at + * + * @return The {@link Address} the relocation takes place at + */ + public Address getRelocationAddress() { + return relocationAddress; + } + + /** + * Gets the lower-level {@link RelocationInfo} that describes the relocation + * + * @return The lower-level {@link RelocationInfo} that describes the relocation + */ + public RelocationInfo getRelocationInfo() { + return relocationInfo; + } + + /** + * Gets the lower-level {@link RelocationInfo} that describes the second part of the paired + * relocation. This could be null if the relocation is not paired. + * + * @return The lower-level {@link RelocationInfo} that describes the second part of the paired + * relocation, or null if the relocation is not paired + */ + public RelocationInfo getRelocationInfoExtra() { + return relocationInfoExtra; + } + + /** + * Gets the {@link Address} of the relocation target + * + * @return The {@link Address} of the relocation target + * @throws NotFoundException If the {@link Address} of the relocation target could not be found + */ + public Address getTargetAddress() throws NotFoundException { + if (targetSymbol != null) { + return targetSymbol.getAddress(); + } + if (targetSection != null) { + return space.getAddress(targetSection.getAddress()); + } + if (targetPointer != null) { + return targetPointer; + } + throw new NotFoundException("Relocation target not found"); + } + + /** + * Gets the {@link Address} of the extra relocation target + * + * @return The {@link Address} of the extra relocation target + * @throws NotFoundException If the {@link Address} of the extra relocation target could not be + * found (of if there wasn't an extra relocation target). + */ + public Address getTargetAddressExtra() throws NotFoundException { + if (targetSymbolExtra != null) { + return targetSymbolExtra.getAddress(); + } + if (targetSectionExtra != null) { + return space.getAddress(targetSectionExtra.getAddress()); + } + if (targetPointerExtra != null) { + return targetPointerExtra; + } + throw new NotFoundException("Extra relocation target not found"); + } + + /** + * Checks to see if this relocation requires work to be done on it. Since our + * {@link MachoLoader loader} does not allow non-default image bases, it is unnecessary to + * perform relocations under certain conditions. + * + * @return True if relocation steps are needed; otherwise, false + */ + public boolean requiresRelocation() { + boolean requires = relocationInfo.isExternal() && !relocationInfo.isScattered(); + if (relocationInfoExtra != null) { + requires = requires || + (relocationInfoExtra.isExternal() && !relocationInfoExtra.isScattered()); + } + return requires; + } + + /** + * Gets a short description of the target of the relocation + * + * @return A short description of the target of the relocation + */ + public String getTargetDescription() { + StringBuilder sb = new StringBuilder(); + + if (targetPointer != null) { + sb.append(targetPointer); + } + else if (targetSymbol != null) { + sb.append(targetSymbol.getName()); + } + else if (targetSection != null) { + sb.append(targetSection.getSectionName()); + } + else { + sb.append(NumericUtilities.toHexString(relocationInfo.getValue())); + } + + if (relocationInfoExtra != null) { + sb.append(" / "); + if (targetPointerExtra != null) { + sb.append(targetPointerExtra); + } + else if (targetSymbolExtra != null) { + sb.append(targetSymbolExtra.getName()); + } + else if (targetSectionExtra != null) { + sb.append(targetSectionExtra.getSectionName()); + } + else { + sb.append(NumericUtilities.toHexString(relocationInfoExtra.getValue())); + } + } + return sb.toString(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("Symbol: %s, Section: %s\n", targetSymbol, targetSection)); + sb.append(relocationInfo + "\n"); + + if (relocationInfoExtra != null) { + sb.append( + String.format("Symbol: %s, Section: %s\n", targetSymbolExtra, targetSectionExtra)); + sb.append(relocationInfoExtra); + } + return sb.toString(); + } + + /** + * Attempts to find the target {@link Symbol} associated with the given lower-level + * {@link RelocationInfo}. This method is only useful when the given {@link RelocationInfo} is + * marked as "external". + * + * @param relocInfo The lower-level {@link RelocationInfo} that describes the relocation + * @return The relocation's target {@link Symbol}, or null if one was not found + */ + private Symbol findTargetSymbol(RelocationInfo relocInfo) { + Symbol sym = null; + NList nlist = machoHeader.getFirstLoadCommand(SymbolTableCommand.class) + .getSymbolAt(relocInfo.getValue()); + Address addr = space.getAddress(nlist.getValue()); + sym = program.getSymbolTable() + .getSymbol(SymbolUtilities.replaceInvalidChars(nlist.getString(), true), addr, + null); + if (sym == null) { + sym = SymbolUtilities.getLabelOrFunctionSymbol(program, nlist.getString(), err -> { + // no logging + }); + } + return sym; + } + + /** + * Attempts to find the target {@link Section} associated with the given lower-level + * {@link RelocationInfo}. This method is only useful when the given {@link RelocationInfo} is + * NOT marked as "external". + * + * @param relocInfo The lower-level {@link RelocationInfo} that describes the relocation + * @return The relocation's target {@link Section}, or null if one was not found + */ + private Section findTargetSection(RelocationInfo relocInfo) { + int index = relocInfo.getValue() - 1; + if (index >= 0 && index < machoHeader.getAllSections().size()) { + return machoHeader.getAllSections().get(index); + } + return null; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocationHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocationHandler.java new file mode 100644 index 0000000000..d705eba0c7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocationHandler.java @@ -0,0 +1,112 @@ +/* ### + * 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.macho.relocation; + +import ghidra.app.util.bin.format.macho.MachHeader; +import ghidra.app.util.bin.format.macho.RelocationInfo; +import ghidra.program.model.address.Address; +import ghidra.program.model.mem.Memory; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.util.classfinder.ExtensionPoint; +import ghidra.util.exception.NotFoundException; + +/** + * An abstract class used to perform Mach-O relocations. Classes should extend this class to + * provide relocations in a machine/processor specific way. + * + * @see mach-o/reloc.h + */ +abstract public class MachoRelocationHandler implements ExtensionPoint { + + /** + * Checks to see whether or not an instance of this Mach-O relocation handler can handle + * relocating the Mach-O defined by the provided file header + * + * @param header The header associated with the Mach-O to relocate + * @return True if this relocation handler can do the relocation; otherwise, false + */ + abstract public boolean canRelocate(MachHeader header); + + /** + * Checks to see if the given relocation is a "paired" relocation. A paired relocation has a + * certain expectation from the relocation that follows it. + * + * @param relocation The relocation to check + * @return True if the given relocation is a "paired" relocation; otherwise, false + */ + abstract public boolean isPairedRelocation(RelocationInfo relocation); + + /** + * Performs a relocation + + * @param relocation The relocation to perform + * @throws MemoryAccessException If there is a problem accessing memory during the relocation + * @throws NotFoundException If this handler didn't find a way to perform the relocation + */ + abstract public void relocate(MachoRelocation relocation) + throws MemoryAccessException, NotFoundException; + + /** + * Reads bytes at the given address. The size of the read is determined by the length of the + * relocation info. + * + * @param relocation The relocation to read + * @return The read bytes + * @throws MemoryAccessException If there is a problem accessing memory during the read + */ + public static long read(MachoRelocation relocation) + throws MemoryAccessException { + Memory mem = relocation.getProgram().getMemory(); + int len = relocation.getRelocationInfo().getLength(); + Address addr = relocation.getRelocationAddress(); + if (len == 3) { + return mem.getLong(addr); + } + if (len == 2) { + return mem.getInt(addr); + } + if (len == 1) { + return mem.getShort(addr); + } + return mem.getByte(addr); + } + + /** + * Writes bytes at the given address. The size of the write is determined by the length of the + * relocation info. + * + * @param relocation The relocation to write + * @param value The value to write + * @throws MemoryAccessException If there is a problem accessing memory during the write + */ + public static void write(MachoRelocation relocation, long value) throws MemoryAccessException { + Memory mem = relocation.getProgram().getMemory(); + int len = relocation.getRelocationInfo().getLength(); + Address addr = relocation.getRelocationAddress(); + if (len == 3) { + mem.setLong(addr, value); + } + else if (len == 2) { + mem.setInt(addr, (int) value); + } + else if (len == 1) { + mem.setShort(addr, (short) value); + } + else { + mem.setByte(addr, (byte) value); + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocationHandlerFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocationHandlerFactory.java new file mode 100644 index 0000000000..423d0c35a7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/relocation/MachoRelocationHandlerFactory.java @@ -0,0 +1,42 @@ +/* ### + * 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.macho.relocation; + +import ghidra.app.util.bin.format.macho.MachHeader; +import ghidra.util.classfinder.ClassSearcher; + +/** + * A class that gets the appropriate Mach-O relocation handler for a specific Mach-O file + */ +public final class MachoRelocationHandlerFactory { + + /** + * Gets the appropriate Mach-O relocation handler that is capable of relocating the Mach-O that + * is defined by the given Mach-O header + * + * @param header The header associated with the Mach-O to relocate + * @return The appropriate Mach-O relocation handler that is capable of relocating the Mach-O + * that is defined by the given Mach-O header. Could return null if no such handler was + * found. + */ + public final static MachoRelocationHandler getHandler(MachHeader header) { + return ClassSearcher.getInstances(MachoRelocationHandler.class) + .stream() + .filter(h -> h.canRelocate(header)) + .findFirst() + .orElse(null); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java index 8bd52e562d..4211c4f41a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java @@ -24,6 +24,7 @@ import ghidra.app.util.bin.StructConverter; import ghidra.app.util.bin.format.macho.*; import ghidra.app.util.bin.format.macho.commands.*; import ghidra.app.util.bin.format.macho.commands.dyld.*; +import ghidra.app.util.bin.format.macho.relocation.*; import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand; import ghidra.app.util.bin.format.objectiveC.ObjectiveC1_Constants; import ghidra.app.util.importer.MessageLog; @@ -40,10 +41,9 @@ import ghidra.program.model.mem.*; import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.symbol.*; import ghidra.program.model.util.CodeUnitInsertionException; -import ghidra.util.DataConverter; import ghidra.util.Msg; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.InvalidInputException; +import ghidra.util.NumericUtilities; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; /** @@ -108,13 +108,6 @@ public class MachoProgramBuilder { monitor.setCancelEnabled(false); machoHeader = MachHeader.createMachHeader(MessageLogContinuesFactory.create(log), provider); machoHeader.parse(); - Address headerAddr = null; - for (SegmentCommand segment : machoHeader.getAllSegments()) { - if (segment.getFileOffset() == 0 && segment.getFileSize() > 0) { - headerAddr = space.getAddress(segment.getVMaddress()); - break; - } - } monitor.setCancelEnabled(true); setImageBase(); @@ -130,7 +123,7 @@ public class MachoProgramBuilder { processUndefinedSymbols(); processAbsoluteSymbols(); processDyldInfo(); - markupHeaders(machoHeader, headerAddr); + markupHeaders(machoHeader, setupHeaderAddr(machoHeader.getAllSegments())); markupSections(); processProgramVars(); loadSectionRelocations(); @@ -490,8 +483,8 @@ public class MachoProgramBuilder { String name = generateValidName(symbol.getString()); if (name != null && name.length() > 0) { try { - program.getSymbolTable().createLabel(startAddr, name, namespace, - SourceType.IMPORTED); + program.getSymbolTable() + .createLabel(startAddr, name, namespace, SourceType.IMPORTED); } catch (Exception e) { log.appendMsg("Unable to create indirect symbol " + name); @@ -572,8 +565,9 @@ public class MachoProgramBuilder { symbol.setName(ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME, SourceType.IMPORTED); } else { - program.getSymbolTable().createLabel(address, - ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME, SourceType.IMPORTED); + program.getSymbolTable() + .createLabel(address, ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME, + SourceType.IMPORTED); } } @@ -599,8 +593,8 @@ public class MachoProgramBuilder { continue; } if (symbol.isTypeUndefined()) { - List globalSymbols = program.getSymbolTable().getLabelOrFunctionSymbols( - symbol.getString(), null); + List globalSymbols = program.getSymbolTable() + .getLabelOrFunctionSymbols(symbol.getString(), null); if (globalSymbols.isEmpty()) {//IF IT DOES NOT ALREADY EXIST... undefinedSymbols.add(symbol); } @@ -824,6 +818,22 @@ public class MachoProgramBuilder { StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } + else if (loadCommand instanceof LinkerOptionCommand) { + LinkerOptionCommand linkerOptionCommand = (LinkerOptionCommand) loadCommand; + List linkerOptions = linkerOptionCommand.getLinkerOptions(); + int offset = linkerOptionCommand.toDataType().getLength(); + for (int i = 0; i < linkerOptions.size(); i++) { + Address addr = loadCommandAddr.add(offset); + int len = linkerOptions.get(i).length() + 1; + if (i == linkerOptions.size() - 1) { + len = (int) (NumericUtilities.getUnsignedAlignedValue( + addr.add(len).getOffset(), 4) - addr.getOffset()); + } + DataUtilities.createData(program, addr, StructConverter.STRING, len, false, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + offset += len; + } + } } } catch (CodeUnitInsertionException e) { @@ -832,6 +842,36 @@ public class MachoProgramBuilder { } + /** + * Sets up the {@link MachHeader} in memory and returns its address. If the header was not + * intended to reside in memory (like for Mach-O object files}, then this method will create an + * area in the "OTHER" address space for the header to live in. + * + * @param segments A {@link Collection} of {@link SegmentCommand Mach-O segments} + * @return The {@link Address} of {@link MachHeader} in memory + */ + private Address setupHeaderAddr(Collection segments) + throws AddressOverflowException { + Address headerAddr = null; + long lowestFileOffset = Long.MAX_VALUE; + + // Check to see if the header resides in an existing segment. If it does, we know its + // address and we are done. Keep track of the lowest file offset of later use. + for (SegmentCommand segment : segments) { + if (segment.getFileOffset() == 0 && segment.getFileSize() > 0) { + return space.getAddress(segment.getVMaddress()); + } + lowestFileOffset = Math.min(lowestFileOffset, segment.getFileOffset()); + } + + // The header did not live in a defined segment. Create a memory region in the OTHER space + // and copy the header there. + headerAddr = AddressSpace.OTHER_SPACE.getAddress(0); + MemoryBlock headerBlock = MemoryBlockUtils.createInitializedBlock(program, true, "HEADER", + headerAddr, fileBytes, 0, lowestFileOffset, "Header", "", false, false, false, log); + return headerBlock.getStart(); + } + private void markupSections() throws Exception { monitor.setMessage("Processing section markup..."); @@ -906,8 +946,8 @@ public class MachoProgramBuilder { * See crt.c from opensource.apple.com */ private void processProgramVars() { - if (program.getLanguage().getProcessor() == Processor.findOrPossiblyCreateProcessor( - "PowerPC")) { + if (program.getLanguage().getProcessor() == Processor + .findOrPossiblyCreateProcessor("PowerPC")) { return; } @@ -983,40 +1023,80 @@ public class MachoProgramBuilder { monitor.setMessage("Processing relocation table..."); + MachoRelocationHandler handler = MachoRelocationHandlerFactory.getHandler(machoHeader); + List
sections = machoHeader.getAllSections(); for (Section section : sections) { if (monitor.isCancelled()) { return; } - List relocations = section.getRelocations(); - for (RelocationInfo relocation : relocations) { + + MemoryBlock sectionMemoryBlock = getMemoryBlock(section); + if (sectionMemoryBlock == null) { + if (section.getNumberOfRelocations() > 0) { + log.appendMsg("Unable to process relocations for " + section.getSectionName() + + ". No memory block was created."); + } + continue; + } + + Iterator iter = section.getRelocations().iterator(); + while (iter.hasNext()) { if (monitor.isCancelled()) { return; } - boolean handled = false; - try { - if (machoHeader.getCpuType() == CpuTypes.CPU_TYPE_X86 || - machoHeader.getCpuType() == CpuTypes.CPU_TYPE_X86_64) { - processSectionRelocation_x86(section, relocation); - handled = true; - } - else if (machoHeader.getCpuType() == CpuTypes.CPU_TYPE_POWERPC) { - //PPC RELOCATIONS ARE GOOD TO GO, AS IS - handled = true; - } - else if (machoHeader.getCpuType() == CpuTypes.CPU_TYPE_ARM) {//TODO + RelocationInfo relocationInfo = iter.next(); + Address address = sectionMemoryBlock.getStart().add(relocationInfo.getAddress()); + byte[] origBytes = getOriginalRelocationBytes(relocationInfo, address); + MachoRelocation relocation = null; + if (handler == null) { + handleRelocationError(address, String.format( + "No relocation handler for machine type 0x%x to process relocation at %s with type 0x%x", + machoHeader.getCpuType(), address, relocationInfo.getType())); + } + else { + try { + relocation = handler.isPairedRelocation(relocationInfo) + ? new MachoRelocation(program, machoHeader, address, relocationInfo, + iter.next()) + : new MachoRelocation(program, machoHeader, address, + relocationInfo); + handler.relocate(relocation); + } + catch (MemoryAccessException e) { + handleRelocationError(address, String.format( + "Error accessing memory at address %s. Relocation failed.", address)); + } + catch (NotFoundException e) { + handleRelocationError(address, + String.format("Relocation type 0x%x at address %s is not supported: %s", + relocationInfo.getType(), address, e.getMessage())); + } + catch (AddressOutOfBoundsException e) { + handleRelocationError(address, + String.format("Error computing relocation at address %s.", address)); } } - catch (Exception e) { - log.appendMsg("Error loading section relocations: " + e.getMessage()); - } - addToRelocationTable(section, relocation, handled); + + program.getRelocationTable() + .add(address, relocationInfo.getType(), + new long[] { relocationInfo.getValue(), relocationInfo.getLength(), + relocationInfo.isPcRelocated() ? 1 : 0, + relocationInfo.isExternal() ? 1 : 0, + relocationInfo.isScattered() ? 1 : 0}, + origBytes, relocation.getTargetDescription()); } } } + private void handleRelocationError(Address address, String message) { + program.getBookmarkManager() + .setBookmark(address, BookmarkType.ERROR, "Relocations", message); + log.appendMsg(message); + } + private void loadExternalRelocations() { monitor.setMessage("Processing external relocations..."); @@ -1033,7 +1113,7 @@ public class MachoProgramBuilder { if (monitor.isCancelled()) { break; } - loadRelocation(RelocationFactory.EXTERNAL, reloc); + loadRelocation(reloc); } } } @@ -1054,7 +1134,7 @@ public class MachoProgramBuilder { if (monitor.isCancelled()) { return; } - loadRelocation(RelocationFactory.LOCAL, reloc); + loadRelocation(reloc); } } } @@ -1086,7 +1166,7 @@ public class MachoProgramBuilder { return space.getAddress(relocBase); } - private void loadRelocation(int relocationType, RelocationInfo relocation) { + private void loadRelocation(RelocationInfo relocation) { Address baseAddr = getRelocationBaseAddress(); @@ -1100,8 +1180,9 @@ public class MachoProgramBuilder { byte[] originalRelocationBytes = getOriginalRelocationBytes(relocation, relocationAddress); - program.getRelocationTable().add(relocationAddress, relocationType, relocation.toValues(), - originalRelocationBytes, null); + program.getRelocationTable() + .add(relocationAddress, relocation.getType(), relocation.toValues(), + originalRelocationBytes, null); } private void addLibrary(String library) { @@ -1216,8 +1297,9 @@ public class MachoProgramBuilder { private void markAsThumb(Address address) throws ContextChangeException, AddressOverflowException { - if (!program.getLanguage().getProcessor().equals( - Processor.findOrPossiblyCreateProcessor("ARM"))) { + if (!program.getLanguage() + .getProcessor() + .equals(Processor.findOrPossiblyCreateProcessor("ARM"))) { return; } if ((address.getOffset() & 1) == 1) { @@ -1245,8 +1327,9 @@ public class MachoProgramBuilder { try { MemoryBlock memoryBlock = memory.getBlock(reference.getToAddress()); Namespace namespace = createNamespace(memoryBlock.getName()); - program.getSymbolTable().createLabel(reference.getToAddress(), - fromSymbol.getName(), namespace, SourceType.IMPORTED); + program.getSymbolTable() + .createLabel(reference.getToAddress(), fromSymbol.getName(), namespace, + SourceType.IMPORTED); } catch (Exception e) { //log.appendMsg("Unable to create lazy pointer symbol " + fromSymbol.getName() + " at " + reference.getToAddress()); @@ -1262,8 +1345,9 @@ public class MachoProgramBuilder { private Namespace createNamespace(String namespaceName) { try { - return program.getSymbolTable().createNameSpace(program.getGlobalNamespace(), - namespaceName, SourceType.IMPORTED); + return program.getSymbolTable() + .createNameSpace(program.getGlobalNamespace(), namespaceName, + SourceType.IMPORTED); } catch (DuplicateNameException | InvalidInputException e) { Namespace namespace = @@ -1323,101 +1407,10 @@ public class MachoProgramBuilder { } } - private void processSectionRelocation_x86(Section relocationSection, - RelocationInfo relocation) { - - DataConverter dc = getDataConverter(); - - MemoryBlock memoryBlock = getMemoryBlock(relocationSection); - - Address relocationAddress = memoryBlock.getStart().add(relocation.getAddress()); - - int relocationSize = (int) Math.pow(2, Math.min(2, relocation.getLength())); - - Address destinationAddress = memoryBlock.getStart().getNewAddress(0); - - byte[] originalRelocationBytes = getOriginalRelocationBytes(relocation, relocationAddress); - - if (relocation instanceof ScatteredRelocationInfo) { - Address relocationBase = getRelocationBaseAddress(); - relocationAddress = relocationBase.add(relocation.getAddress()); - } - else { - if (relocation.isExternal()) { - int symbolIndex = relocation.getSymbolIndex(); - NList nList = machoHeader.getFirstLoadCommand(SymbolTableCommand.class).getSymbolAt( - symbolIndex); - Symbol symbol = SymbolUtilities.getLabelOrFunctionSymbol(program, nList.getString(), - err -> log.appendMsg("Macho", err)); - if (relocation.isPcRelocated()) { - - destinationAddress = symbol.getAddress().subtractWrap( - relocationAddress.getOffset()).subtractWrap(4); - /** for x86_64 relocations, the original code contains the offset into the symbol. - * usually it is just 00 00 00 00, but in some cases there is a value there which - * tells us to offset. _foo+4 would be 04 00 00 00. confirmed in x86_64/reloc.h for Mach-O. - */ - if (machoHeader.getCpuType() == CpuTypes.CPU_TYPE_X86_64) { - destinationAddress = - destinationAddress.add(dc.getInt(originalRelocationBytes)); - } - } - else { - destinationAddress = symbol.getAddress(); - } - } - else { - int originalRelocationValue = dc.getInt(originalRelocationBytes); - int sectionIndex = relocation.getSymbolIndex(); - Section section = machoHeader.getAllSections().get(sectionIndex - 1); - long difference = originalRelocationValue - section.getAddress(); - destinationAddress = space.getAddress(section.getAddress() + difference); - } - } - - byte[] destinationBytes = new byte[originalRelocationBytes.length]; - try { - dc.getBytes((int) destinationAddress.getOffset(), destinationBytes);// TODO - // just - // truncate - // to - // INT?? - - memory.setBytes(relocationAddress, destinationBytes, 0, relocationSize); - } - catch (Exception e) { - log.appendMsg( - "Unable to process relocation at " + relocationAddress + ": " + e.getMessage()); - } - } - - private DataConverter getDataConverter() { - DataConverter dc = DataConverter.getInstance(program.getLanguage().isBigEndian()); - return dc; - } - - private void addToRelocationTable(Section relocationSection, RelocationInfo relocation, - boolean handled) { - MemoryBlock memoryBlock = getMemoryBlock(relocationSection); - - Address relocationAddress = memoryBlock.getStart().add(relocation.getAddress()); - - byte[] originalRelocationBytes = getOriginalRelocationBytes(relocation, relocationAddress); - - program.getRelocationTable().add(relocationAddress, RelocationFactory.SECTION, - relocation.toValues(), originalRelocationBytes, null); - - if (!handled) { - BookmarkManager bookmarkManager = program.getBookmarkManager(); - bookmarkManager.setBookmark(relocationAddress, BookmarkType.ERROR, "Relocations", - "Unhandled relocation"); - } - } - private byte[] getOriginalRelocationBytes(RelocationInfo relocation, Address relocationAddress) { - int relocationSize = (int) Math.pow(2, Math.min(2, relocation.getLength())); + int relocationSize = (int) Math.pow(2, relocation.getLength()); byte[] originalRelocationBytes = new byte[relocationSize]; try { memory.getBytes(relocationAddress, originalRelocationBytes); diff --git a/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/util/bin/format/macho/relocation/AARCH64_MachoRelocationConstants.java b/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/util/bin/format/macho/relocation/AARCH64_MachoRelocationConstants.java new file mode 100644 index 0000000000..5726b88095 --- /dev/null +++ b/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/util/bin/format/macho/relocation/AARCH64_MachoRelocationConstants.java @@ -0,0 +1,84 @@ +/* ### + * 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.macho.relocation; + +/** + * {@link AARCH64_MachoRelocationHandler} constants + * + * @see mach-o/arm64/reloc.h + */ +public class AARCH64_MachoRelocationConstants { + + /** + * For pointers + */ + public final static int ARM64_RELOC_UNSIGNED = 0; + + /** + * Must be followed by a ARM64_RELOC_UNSIGNED + */ + public final static int ARM64_RELOC_SUBTRACTOR = 1; + + /** + * A B/BL instruction with 26-bit displacement + */ + public final static int ARM64_RELOC_BRANCH26 = 2; + + /** + * PC-rel distance to page of target + */ + public final static int ARM64_RELOC_PAGE21 = 3; + + /** + * Offset within page, scaled by r_length + */ + public final static int ARM64_RELOC_PAGEOFF12 = 4; + + /** + * PC-rel distance to page of GOT slot + */ + public final static int ARM64_RELOC_GOT_LOAD_PAGE21 = 5; + + /** + * Offset within page of GOT slot, scaled by r_length + */ + public final static int ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6; + + /** + * For pointers to GOT slots + */ + public final static int ARM64_RELOC_POINTER_TO_GOT = 7; + + /** + * PC-rel distance to page of TLVP slot + */ + public final static int ARM64_RELOC_TLVP_LOAD_PAGE21 = 8; + + /** + * Offset within page of TLVP slot, scaled by r_length + */ + public final static int ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9; + + /** + * Must be followed by PAGE21 or PAGEOFF12 + */ + public final static int ARM64_RELOC_ADDEND = 10; + + /** + * Like ARM64_RELOC_UNSIGNED, but addend in lower 32-bits + */ + public final static int ARM64_RELOC_AUTHENTICATED_POINTER = 11; +} diff --git a/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/util/bin/format/macho/relocation/AARCH64_MachoRelocationHandler.java b/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/util/bin/format/macho/relocation/AARCH64_MachoRelocationHandler.java new file mode 100644 index 0000000000..d0489dc699 --- /dev/null +++ b/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/util/bin/format/macho/relocation/AARCH64_MachoRelocationHandler.java @@ -0,0 +1,156 @@ +/* ### + * 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.macho.relocation; + +import static ghidra.app.util.bin.format.macho.relocation.AARCH64_MachoRelocationConstants.*; + +import ghidra.app.util.bin.format.macho.*; +import ghidra.program.model.address.Address; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.util.Conv; +import ghidra.util.exception.NotFoundException; + +/** + * A {@link MachoRelocationHandler} for AARCH64 + * + * @see mach-o/arm64/reloc.h + */ +public class AARCH64_MachoRelocationHandler extends MachoRelocationHandler { + + @Override + public boolean canRelocate(MachHeader header) { + return header.getCpuType() == CpuTypes.CPU_TYPE_ARM_64; + } + + @Override + public boolean isPairedRelocation(RelocationInfo relocation) { + return relocation.getType() == ARM64_RELOC_SUBTRACTOR || + relocation.getType() == ARM64_RELOC_ADDEND; + } + + @Override + public void relocate(MachoRelocation relocation) + throws MemoryAccessException, NotFoundException { + + if (!relocation.requiresRelocation()) { + return; + } + + RelocationInfo relocationInfo = relocation.getRelocationInfo(); + Address relocAddr = relocation.getRelocationAddress(); + Address targetAddr; + long addendFromReloc; + if (relocationInfo.getType() == ARM64_RELOC_ADDEND) { + // ARM64_RELOC_ADDEND is a paired relocation, but it's a bit unique because it doesn't + // define its own relocation target...simply an addend value to be applied to the 2nd + // part of the relocation. We'll just save off the addend value and proceed as if the + // "extra" part of the relocation pair is a normal unpaired relocation. + targetAddr = relocation.getTargetAddressExtra(); + addendFromReloc = relocationInfo.getValue(); + relocationInfo = relocation.getRelocationInfoExtra(); + } + else { + targetAddr = relocation.getTargetAddress(); + addendFromReloc = 0; + + } + long orig = read(relocation); + + switch (relocationInfo.getType()) { + case ARM64_RELOC_UNSIGNED: + case ARM64_RELOC_POINTER_TO_GOT: { + long addend = orig; + long value = targetAddr.getOffset() + addend; + write(relocation, value); + break; + } + case ARM64_RELOC_SUBTRACTOR: { + Address targetAddrExtra = relocation.getTargetAddressExtra(); + if (orig > 0) { + write(relocation, targetAddrExtra.add(orig).subtract(targetAddr)); + } + else { + write(relocation, targetAddr.add(orig).subtract(targetAddrExtra)); + } + break; + } + case ARM64_RELOC_BRANCH26: { + long addend = orig & 0x3ffffff; + long value = (targetAddr.subtract(relocAddr) >> 2) + addend; + long instr = orig | (value & 0x3ffffff); + write(relocation, instr); + break; + } + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_GOT_LOAD_PAGE21: { + // ADRP + long immlo = (orig >> 29) & 0x3; + long immhi = (orig >> 5) & 0x7ffff; + long addend = ((immhi << 2) | immlo) << 12; + addend += addendFromReloc; + long pageTarget = PG(targetAddr.getOffset() + addend); + long pageReloc = PG(relocAddr.getOffset()); + long value = ((pageTarget - pageReloc) >> 12) & 0x1fffff; + long instr = + (orig & 0x9f00001f) | ((value << 3) & 0x7ffffe0) | ((value & 0x3) << 29); + write(relocation, instr); + break; + } + case ARM64_RELOC_PAGEOFF12: + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: { + long instr; + long addend = addendFromReloc; + if ((orig & 0x08000000) > 0) { + // LDR/STR + long size = (orig >> 30) & 0x3; + addend += (orig >> 10) & 0xfff; + long value = ((targetAddr.getOffset() + addend) & 0xfff) >> size; + instr = orig | (value << 10); + } + else { + // ADD + addend += (orig >> 10) & 0xfff; + long value = (targetAddr.getOffset() + addend) & 0xfff; + instr = orig | (value << 10); + } + write(relocation, instr); + break; + } + case ARM64_RELOC_AUTHENTICATED_POINTER: { + long addend = orig & Conv.INT_MASK; + long value = targetAddr.getOffset() + addend; + write(relocation, value); + break; + } + + case ARM64_RELOC_TLVP_LOAD_PAGE21: // not seen yet + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: // not seen yet + case ARM64_RELOC_ADDEND: // should never see on its own here + default: + throw new NotFoundException("Unimplemented relocation"); + } + } + + /** + * Returns the page address of the given address (assumes 4KB page) + * + * @param addr The address to get the page of + * @return The page address of the given address + */ + private long PG(long addr) { + return addr & (~0xfff); + } +} diff --git a/Ghidra/Processors/ARM/src/main/java/ghidra/app/util/bin/format/macho/relocation/ARM_MachoRelocationConstants.java b/Ghidra/Processors/ARM/src/main/java/ghidra/app/util/bin/format/macho/relocation/ARM_MachoRelocationConstants.java new file mode 100644 index 0000000000..b8728fbad7 --- /dev/null +++ b/Ghidra/Processors/ARM/src/main/java/ghidra/app/util/bin/format/macho/relocation/ARM_MachoRelocationConstants.java @@ -0,0 +1,82 @@ +/* ### + * 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.macho.relocation; + +/** + * {@link ARM_MachoRelocationHandler} constants + * + * @see mach-o/arm/reloc.h + */ +public class ARM_MachoRelocationConstants { + + /** + * Generic relocation as described above + */ + public final static int ARM_RELOC_VANILLA = 0; + + /** + * The second relocation entry of a pair + */ + public final static int ARM_RELOC_PAIR = 1; + + /** + * A PAIR follows with subtract symbol value + */ + public final static int ARM_RELOC_SECTDIFF = 2; + + /** + * Like ARM_RELOC_SECTDIFF, but the symbol referenced was local + */ + public final static int ARM_RELOC_LOCAL_SECTDIFF = 3; + + /** + * Pre-bound lazy pointer + */ + public final static int ARM_RELOC_PB_LA_PTR = 4; + + /** + * 24 bit branch displacement (to a word address) + */ + public final static int ARM_RELOC_BR24 = 5; + + /** + * 22 bit branch displacement (to a half-word address) + */ + public final static int ARM_THUMB_RELOC_BR22 = 6; + + /** + * Obsolete - a thumb 32-bit branch instruction possibly needing page-spanning branch workaround + */ + public final static int ARM_THUMB_32BIT_BRANCH = 7; + + /** + * For these two r_type relocations they always have a pair following them and the r_length bits + * are used differently. The encoding of the r_length is as follows: + * + * low bit of r_length: + * 0 - :lower16: for movw instructions + * 1 - :upper16: for movt instructions + * + * high bit of r_length: + * 0 - arm instructions + * 1 - thumb instructions + * + * The other half of the relocated expression is in the following pair relocation entry in the + * low 16 bits of r_address field. + */ + public final static int ARM_RELOC_HALF = 8; + public final static int ARM_RELOC_HALF_SECTDIFF = 9; +} diff --git a/Ghidra/Processors/ARM/src/main/java/ghidra/app/util/bin/format/macho/relocation/ARM_MachoRelocationHandler.java b/Ghidra/Processors/ARM/src/main/java/ghidra/app/util/bin/format/macho/relocation/ARM_MachoRelocationHandler.java new file mode 100644 index 0000000000..005c3c1191 --- /dev/null +++ b/Ghidra/Processors/ARM/src/main/java/ghidra/app/util/bin/format/macho/relocation/ARM_MachoRelocationHandler.java @@ -0,0 +1,105 @@ +/* ### + * 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.macho.relocation; + +import static ghidra.app.util.bin.format.macho.relocation.ARM_MachoRelocationConstants.*; + +import ghidra.app.util.bin.format.macho.*; +import ghidra.program.model.address.Address; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.util.exception.NotFoundException; + +/** + * A {@link MachoRelocationHandler} for ARM + * + * @see mach-o/arm/reloc.h + */ +public class ARM_MachoRelocationHandler extends MachoRelocationHandler { + + @Override + public boolean canRelocate(MachHeader header) { + return header.getCpuType() == CpuTypes.CPU_TYPE_ARM; + } + + @Override + public boolean isPairedRelocation(RelocationInfo relocation) { + return relocation.getType() == ARM_RELOC_SECTDIFF || + relocation.getType() == ARM_RELOC_LOCAL_SECTDIFF || + relocation.getType() == ARM_RELOC_HALF || + relocation.getType() == ARM_RELOC_HALF_SECTDIFF; + } + + @Override + public void relocate(MachoRelocation relocation) + throws MemoryAccessException, NotFoundException { + + if (!relocation.requiresRelocation()) { + return; + } + + RelocationInfo relocationInfo = relocation.getRelocationInfo(); + Address targetAddr = relocation.getTargetAddress(); + long orig = read(relocation); + + switch (relocationInfo.getType()) { + case ARM_RELOC_VANILLA: + if (!relocationInfo.isPcRelocated()) { + write(relocation, targetAddr.getOffset()); + } + else { + throw new NotFoundException("Unimplemented relocation"); + } + break; + case ARM_THUMB_RELOC_BR22: { + // BL and BLX + boolean blx = (orig & 0xd000f800) == 0xc000f000; + long s = (orig >> 10) & 0x1; + long j1 = (orig >> 29) & 0x1; + long j2 = (orig >> 27) & 0x1; + long i1 = ~(j1 ^ s) & 0x1; + long i2 = ~(j2 ^ s) & 0x1; + long imm10 = orig & 0x3ff; + long imm11 = (orig >> 16) & 0x7ff; + long addend = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + addend |= s == 1 ? 0xfe000000 : 0; // sign extend + addend &= blx ? ~0x3 : ~0; // 4-byte align BLX + long value = targetAddr.getOffset() + addend; + s = (value >> 24) & 0x1; + i1 = (value >> 23) & 0x1; + i2 = (value >> 22) & 0x1; + j1 = ~(i1 ^ s) & 0x1; + j2 = ~(i2 ^ s) & 0x1; + imm10 = (value >> 12) & 0x3ff; + imm11 = (value >> 1) & 0x7ff; + long instr = orig & (blx ? 0xc000f800 : 0xd000f800); + instr |= (j1 << 29) | (j2 << 27) | (imm11 << 16) | (s << 10) | imm10; + write(relocation, instr); + break; + } + + case ARM_RELOC_PAIR: // should never see on its own here + case ARM_RELOC_SECTDIFF: // relocation not required (scattered) + case ARM_RELOC_LOCAL_SECTDIFF: // relocation not required (scattered) + case ARM_RELOC_PB_LA_PTR: // not seen yet + case ARM_RELOC_BR24: // not seen yet + case ARM_THUMB_32BIT_BRANCH: // not seen yet + case ARM_RELOC_HALF: // relocation not required (scattered) + case ARM_RELOC_HALF_SECTDIFF: // relocation not required (scattered) + default: + throw new NotFoundException("Unimplemented relocation"); + } + } +} diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_32_MachoRelocationConstants.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_32_MachoRelocationConstants.java new file mode 100644 index 0000000000..507b462b67 --- /dev/null +++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_32_MachoRelocationConstants.java @@ -0,0 +1,54 @@ +/* ### + * 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.macho.relocation; + +/** + * {@link X86_32_MachoRelocationHandler} constants + * + * @see mach-o/reloc.h + */ +public class X86_32_MachoRelocationConstants { + + /** + * Generic relocation + */ + public final static int GENERIC_RELOC_VANILLA = 0; + + /** + * Only follows a GENERIC_RELOC_SECTDIFF + */ + public final static int GENERIC_RELOC_PAIR = 1; + + /** + * The difference of two symbols defined in two different sections + */ + public final static int GENERIC_RELOC_SECTDIFF = 2; + + /** + * Pre-bound lazy pointer + */ + public final static int GENERIC_RELOC_PB_LA_PTR = 3; + + /** + * The difference of two symbols defined in two different sections + */ + public final static int GENERIC_RELOC_LOCAL_SECTDIFF = 4; + + /** + * Thread local variables + */ + public final static int GENERIC_RELOC_TLV = 5; +} diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_32_MachoRelocationHandler.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_32_MachoRelocationHandler.java new file mode 100644 index 0000000000..49bf72d6c0 --- /dev/null +++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_32_MachoRelocationHandler.java @@ -0,0 +1,74 @@ +/* ### + * 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.macho.relocation; + +import static ghidra.app.util.bin.format.macho.relocation.X86_32_MachoRelocationConstants.*; + +import ghidra.app.util.bin.format.macho.*; +import ghidra.program.model.address.Address; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.util.exception.NotFoundException; + +/** + * A {@link MachoRelocationHandler} for x86 32-bit + * + * @see mach-o/reloc.h + */ +public class X86_32_MachoRelocationHandler extends MachoRelocationHandler { + + @Override + public boolean canRelocate(MachHeader header) { + return header.getCpuType() == CpuTypes.CPU_TYPE_X86; + } + + @Override + public boolean isPairedRelocation(RelocationInfo relocation) { + return relocation.getType() == GENERIC_RELOC_SECTDIFF || + relocation.getType() == GENERIC_RELOC_LOCAL_SECTDIFF; + } + + @Override + public void relocate(MachoRelocation relocation) + throws MemoryAccessException, NotFoundException { + + if (!relocation.requiresRelocation()) { + return; + } + + RelocationInfo relocationInfo = relocation.getRelocationInfo(); + Address relocAddr = relocation.getRelocationAddress(); + Address targetAddr = relocation.getTargetAddress(); + + switch (relocationInfo.getType()) { + case GENERIC_RELOC_VANILLA: + if (relocationInfo.isPcRelocated()) { + write(relocation, targetAddr.subtract(relocAddr) - 4); + } + else { + write(relocation, targetAddr.getOffset()); + } + break; + + case GENERIC_RELOC_PAIR: // should never see on its own here + case GENERIC_RELOC_SECTDIFF: // relocation not required (scattered) + case GENERIC_RELOC_PB_LA_PTR: // not seen yet + case GENERIC_RELOC_LOCAL_SECTDIFF: // relocation not required (scattered) + case GENERIC_RELOC_TLV: // not seen yet + default: + throw new NotFoundException("Unimplemented relocation"); + } + } +} diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_64_MachoRelocationConstants.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_64_MachoRelocationConstants.java new file mode 100644 index 0000000000..5918ac8d53 --- /dev/null +++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_64_MachoRelocationConstants.java @@ -0,0 +1,74 @@ +/* ### + * 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.macho.relocation; + +/** + * {@link X86_64_MachoRelocationHandler} constants + * + * @see mach-o/x86_64/reloc.h + */ +public class X86_64_MachoRelocationConstants { + + /** + * For absolute addresses + */ + public final static int X86_64_RELOC_UNSIGNED = 0; + + /** + * For signed 32-bit displacement + */ + public final static int X86_64_RELOC_SIGNED = 1; + + /** + * A CALL/JMP instruction with 32-bit displacement + */ + public final static int X86_64_RELOC_BRANCH = 2; + + /** + * A MOVQ load of a GOT entry + */ + public final static int X86_64_RELOC_GOT_LOAD = 3; + + /** + * Other GOT references + */ + public final static int X86_64_RELOC_GOT = 4; + + /** + * Must be followed by a X86_64_RELOC_UNSIGNED + */ + public final static int X86_64_RELOC_SUBTRACTOR = 5; + + /** + * For signed 32-bit displacement with a -1 addend + */ + public final static int X86_64_RELOC_SIGNED_1 = 6; + + /** + * For signed 32-bit displacement with a -2 addend + */ + public final static int X86_64_RELOC_SIGNED_2 = 7; + + /** + * For signed 32-bit displacement with a -4 addend + */ + public final static int X86_64_RELOC_SIGNED_4 = 8; + + /** + * For thread local variables + */ + public final static int X86_64_RELOC_TLV = 9; +} diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_64_MachoRelocationHandler.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_64_MachoRelocationHandler.java new file mode 100644 index 0000000000..e3b8681d0b --- /dev/null +++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/macho/relocation/X86_64_MachoRelocationHandler.java @@ -0,0 +1,83 @@ +/* ### + * 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.macho.relocation; + +import static ghidra.app.util.bin.format.macho.relocation.X86_64_MachoRelocationConstants.*; + +import ghidra.app.util.bin.format.macho.*; +import ghidra.program.model.address.Address; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.util.exception.NotFoundException; + +/** + * A {@link MachoRelocationHandler} for x86 64-bit + * + * @see mach-o/x86_64/reloc.h + */ +public class X86_64_MachoRelocationHandler extends MachoRelocationHandler { + + @Override + public boolean canRelocate(MachHeader header) { + return header.getCpuType() == CpuTypes.CPU_TYPE_X86_64; + } + + @Override + public boolean isPairedRelocation(RelocationInfo relocation) { + return relocation.getType() == X86_64_RELOC_SUBTRACTOR; + } + + @Override + public void relocate(MachoRelocation relocation) + throws MemoryAccessException, NotFoundException { + + if (!relocation.requiresRelocation()) { + return; + } + + RelocationInfo relocationInfo = relocation.getRelocationInfo(); + Address relocAddr = relocation.getRelocationAddress(); + Address targetAddr = relocation.getTargetAddress(); + long addend = read(relocation); + + switch (relocationInfo.getType()) { + case X86_64_RELOC_UNSIGNED: + write(relocation, targetAddr.add(addend).getOffset()); + break; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_BRANCH: + case X86_64_RELOC_GOT_LOAD: + case X86_64_RELOC_GOT: + case X86_64_RELOC_SIGNED_1: // addend should already be -1 + case X86_64_RELOC_SIGNED_2: // addend should already be -2 + case X86_64_RELOC_SIGNED_4: // addend should already be -4 + write(relocation, targetAddr.add(addend).subtract(relocAddr) - 4); + break; + case X86_64_RELOC_SUBTRACTOR: + Address targetAddrExtra = relocation.getTargetAddressExtra(); + if (addend > 0) { + write(relocation, targetAddrExtra.add(addend).subtract(targetAddr)); + } + else { + write(relocation, targetAddr.add(addend).subtract(targetAddrExtra)); + } + break; + + case X86_64_RELOC_TLV: // not seen yet + default: + throw new NotFoundException("Unimplemented relocation"); + } + } +}