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");
+ }
+ }
+}