mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-700: Improved support for Mach-O MH_OBJECT files
This commit is contained in:
parent
92b7728cd7
commit
c2f60b15d3
27 changed files with 1542 additions and 653 deletions
|
@ -11,6 +11,7 @@ ScriptProvider
|
|||
Recognizer
|
||||
BinaryAnalysisCommand
|
||||
CoffRelocationHandler
|
||||
MachoRelocationHandler
|
||||
ElfRelocationHandler
|
||||
ElfExtension
|
||||
RelocationFixupHandler
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue