GP-700: Improved support for Mach-O MH_OBJECT files

This commit is contained in:
Ryan Kurtz 2021-02-23 13:40:32 -05:00
parent 92b7728cd7
commit c2f60b15d3
27 changed files with 1542 additions and 653 deletions

View file

@ -11,6 +11,7 @@ ScriptProvider
Recognizer
BinaryAnalysisCommand
CoffRelocationHandler
MachoRelocationHandler
ElfRelocationHandler
ElfExtension
RelocationFixupHandler

View file

@ -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());
}
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/reloc.h.auto.html">mach-o/reloc.h</a>
* @see <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/reloc.h.auto.html">mach-o/reloc.h</a>
*/
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;
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 {
}

View file

@ -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
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/reloc.h.auto.html">mach-o/reloc.h</a>
*/
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;
}
}

View file

@ -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);
}

View file

@ -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));
}
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
*/
public class LinkerOptionCommand extends LoadCommand {
private int count;
private List<String> 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<String> 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());
}
}
}

View file

@ -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);
}

View file

@ -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());

View file

@ -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;
}
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/reloc.h.auto.html">mach-o/reloc.h</a>
*/
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);
}
}
}

View file

@ -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);
}
}

View file

@ -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<Symbol> globalSymbols = program.getSymbolTable().getLabelOrFunctionSymbols(
symbol.getString(), null);
List<Symbol> 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<String> 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<SegmentCommand> 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<Section> sections = machoHeader.getAllSections();
for (Section section : sections) {
if (monitor.isCancelled()) {
return;
}
List<RelocationInfo> 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<RelocationInfo> 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);

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/arm64/reloc.h.auto.html">mach-o/arm64/reloc.h</a>
*/
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;
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/arm64/reloc.h.auto.html">mach-o/arm64/reloc.h</a>
*/
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);
}
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/arm/reloc.h.auto.html">mach-o/arm/reloc.h</a>
*/
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;
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/arm/reloc.h.auto.html">mach-o/arm/reloc.h</a>
*/
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");
}
}
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/reloc.h.auto.html">mach-o/reloc.h</a>
*/
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;
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/reloc.h.auto.html">mach-o/reloc.h</a>
*/
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");
}
}
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/x86_64/reloc.h.auto.html">mach-o/x86_64/reloc.h</a>
*/
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;
}

View file

@ -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 <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/x86_64/reloc.h.auto.html">mach-o/x86_64/reloc.h</a>
*/
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");
}
}
}