mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GT-3320 changed manner in which Android ELF APS2 relocation table
is represented within listing. Corrected Android relocation handling when sections are not present.
This commit is contained in:
parent
3f6ca95996
commit
d68f3697e3
13 changed files with 924 additions and 302 deletions
|
@ -0,0 +1,157 @@
|
||||||
|
/* ###
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>MemBufferByteProvider</code> provide a {@link ByteProvider} backed by
|
||||||
|
* a {@link MemBuffer}.
|
||||||
|
*/
|
||||||
|
public class MemBufferByteProvider implements ByteProvider {
|
||||||
|
|
||||||
|
private MemBuffer buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param buffer memory buffer
|
||||||
|
*/
|
||||||
|
public MemBufferByteProvider(MemBuffer buffer) {
|
||||||
|
this.buffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getFile() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAbsolutePath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return maximum length since actual length is unknown
|
||||||
|
* @return maximum possible length
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long length() {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidIndex(long index) {
|
||||||
|
if (index < 0 || index > Integer.MAX_VALUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.getByte((int) index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (MemoryAccessException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
// not applicable
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte readByte(long index) throws IOException {
|
||||||
|
if (index < 0 || index > Integer.MAX_VALUE) {
|
||||||
|
throw new IOException("index out of range");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return buffer.getByte((int) index);
|
||||||
|
}
|
||||||
|
catch (MemoryAccessException e) {
|
||||||
|
throw new IOException("index out of range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] readBytes(long index, long length) throws IOException {
|
||||||
|
if (index < 0 || (index + length - 1) > Integer.MAX_VALUE) {
|
||||||
|
throw new IOException("index/length of range");
|
||||||
|
}
|
||||||
|
int len = (int) length;
|
||||||
|
byte[] bytes = new byte[len];
|
||||||
|
if (buffer.getBytes(bytes, (int) index) != len) {
|
||||||
|
throw new IOException("index/length of range");
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream(long index) throws IOException {
|
||||||
|
if (index < 0 || index > Integer.MAX_VALUE) {
|
||||||
|
throw new IOException("index out of range");
|
||||||
|
}
|
||||||
|
return new MemBufferProviderInputStream((int) index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MemBufferProviderInputStream extends InputStream {
|
||||||
|
|
||||||
|
private int initialOffset;
|
||||||
|
private int offset;
|
||||||
|
|
||||||
|
MemBufferProviderInputStream(int offset) {
|
||||||
|
this.offset = offset;
|
||||||
|
this.initialOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
byte b = readByte(offset++);
|
||||||
|
return b & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) {
|
||||||
|
byte[] bytes = new byte[len];
|
||||||
|
int count = buffer.getBytes(bytes, offset);
|
||||||
|
System.arraycopy(bytes, 0, b, off, count);
|
||||||
|
offset += count;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() {
|
||||||
|
return (int) length() - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() throws IOException {
|
||||||
|
offset = initialOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
// not applicable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/* ###
|
||||||
|
* 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.elf;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.AbstractLeb128DataType;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.docking.settings.SettingsDefinition;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.data.DataType;
|
||||||
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>AndroidElfRelocationData</code> provides a dynamic LEB128 data
|
||||||
|
* component for packed Android ELF Relocation Table.
|
||||||
|
* See {@link AndroidElfRelocationTableDataType}.
|
||||||
|
* <br>
|
||||||
|
* Secondary purpose is to retain the relocation offset associated with a
|
||||||
|
* component instance. This functionality relies on the 1:1 relationship
|
||||||
|
* between this dynamic datatype and the single component which references it.
|
||||||
|
*/
|
||||||
|
class AndroidElfRelocationData extends AbstractLeb128DataType {
|
||||||
|
|
||||||
|
private final long relocationOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packed relocation offset data type based upon a signed LEB128
|
||||||
|
* value.
|
||||||
|
* @param dtm the data type manager to associate with this data type.
|
||||||
|
* @param relocationOffset relocation offset associated with component.
|
||||||
|
*/
|
||||||
|
AndroidElfRelocationData(DataTypeManager dtm, long relocationOffset) {
|
||||||
|
super("sleb128", true, dtm);
|
||||||
|
this.relocationOffset = relocationOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataType clone(DataTypeManager dtm) {
|
||||||
|
if (dtm == getDataTypeManager()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new AndroidElfRelocationData(dtm, relocationOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMnemonic(Settings settings) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Android Packed Relocation Data for ELF";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultLabelPrefix() {
|
||||||
|
return "sleb128";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SettingsDefinition[] getBuiltInSettingsDefinitions() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getValueClass(Settings settings) {
|
||||||
|
return Address.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the relocation offset associated with this data item
|
||||||
|
* @return the relocation offset associated with this data item
|
||||||
|
*/
|
||||||
|
long getRelocationOffset() {
|
||||||
|
return relocationOffset;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
/* ###
|
||||||
|
* 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.elf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.*;
|
||||||
|
import ghidra.app.util.bin.format.elf.AndroidElfRelocationTableDataType.LEB128Info;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
import ghidra.program.model.mem.WrappedMemBuffer;
|
||||||
|
import ghidra.program.model.scalar.Scalar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>AndroidElfRelocationGroup</code> provides a dynamic substructure
|
||||||
|
* component for relocation groups within a packed Android ELF Relocation Table.
|
||||||
|
* See {@link AndroidElfRelocationTableDataType}.
|
||||||
|
*/
|
||||||
|
class AndroidElfRelocationGroup extends DynamicDataType {
|
||||||
|
|
||||||
|
// Packed Android APS2 relocation group flags
|
||||||
|
static final long RELOCATION_GROUPED_BY_INFO_FLAG = 1;
|
||||||
|
static final long RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
|
||||||
|
static final long RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
|
||||||
|
static final long RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
|
||||||
|
|
||||||
|
private final long baseRelocOffset;
|
||||||
|
|
||||||
|
AndroidElfRelocationGroup(DataTypeManager dtm, long baseRelocOffset) {
|
||||||
|
super(CategoryPath.ROOT, "AndroidElfRelocationGroup", dtm);
|
||||||
|
this.baseRelocOffset = baseRelocOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataType clone(DataTypeManager dtm) {
|
||||||
|
if (dtm == dataMgr) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new AndroidElfRelocationGroup(dtm, baseRelocOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Android Packed Relocation Entry Group for ELF";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(MemBuffer buf, Settings settings, int length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DataTypeComponent[] getAllComponents(MemBuffer buf) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteProvider provider = new MemBufferByteProvider(buf);
|
||||||
|
BinaryReader reader = new BinaryReader(provider, false);
|
||||||
|
|
||||||
|
ArrayList<DataTypeComponent> list = new ArrayList<>();
|
||||||
|
|
||||||
|
LEB128Info sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
long groupSize = sleb128.value;
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "group_size", null));
|
||||||
|
|
||||||
|
sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
long groupFlags = sleb128.value;
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "group_flags", null));
|
||||||
|
|
||||||
|
boolean groupedByInfo = (groupFlags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
|
||||||
|
boolean groupedByDelta = (groupFlags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
|
||||||
|
boolean groupedByAddend = (groupFlags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
|
||||||
|
boolean groupHasAddend = (groupFlags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
|
||||||
|
|
||||||
|
long groupOffsetDelta = 0;
|
||||||
|
if (groupedByDelta) {
|
||||||
|
sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
groupOffsetDelta = sleb128.value;
|
||||||
|
|
||||||
|
long minOffset = baseRelocOffset + groupOffsetDelta;
|
||||||
|
String rangeStr = "First relocation offset: 0x" + Long.toHexString(minOffset);
|
||||||
|
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "group_offsetDelta", rangeStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupedByInfo) {
|
||||||
|
sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "group_info", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupedByAddend && groupHasAddend) {
|
||||||
|
sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "group_addend", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
long relocOffset = baseRelocOffset;
|
||||||
|
|
||||||
|
if (groupedByDelta && groupedByInfo && (!groupHasAddend || groupedByAddend)) {
|
||||||
|
// no individual relocation entry data
|
||||||
|
relocOffset += (groupSize - 1) * groupOffsetDelta;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < groupSize; i++) {
|
||||||
|
if (groupedByDelta) {
|
||||||
|
relocOffset += groupOffsetDelta;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
relocOffset += sleb128.value;
|
||||||
|
DataTypeComponent dtc = new ReadOnlyDataTypeComponent(
|
||||||
|
new AndroidElfRelocationOffset(dataMgr, relocOffset), this,
|
||||||
|
sleb128.byteLength, list.size(), sleb128.offset, "reloc_offset_" + i,
|
||||||
|
null);
|
||||||
|
list.add(dtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!groupedByInfo) {
|
||||||
|
sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "reloc_info_" + i, null,
|
||||||
|
relocOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupHasAddend && !groupedByAddend) {
|
||||||
|
sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "reloc_addend_" + i, null,
|
||||||
|
relocOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent[] comps = new DataTypeComponent[list.size()];
|
||||||
|
return list.toArray(comps);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long getLastRelocationOffset(WrappedMemBuffer buf) {
|
||||||
|
DataTypeComponent[] comps = getComps(buf);
|
||||||
|
if ((comps == null) || (comps.length < 3)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scalar s = (Scalar) comps[0].getDataType().getValue(buf, null, comps[0].getLength());
|
||||||
|
int groupSize = (int) s.getValue();
|
||||||
|
|
||||||
|
DataTypeComponent lastDtc = comps[comps.length - 1];
|
||||||
|
|
||||||
|
if ("group_offsetDelta".equals(comps[2].getFieldName())) {
|
||||||
|
WrappedMemBuffer cbuf = new WrappedMemBuffer(buf, comps[2].getOffset());
|
||||||
|
s = (Scalar) comps[2].getDataType().getValue(cbuf, null, comps[2].getLength());
|
||||||
|
long groupOffsetDelta = s.getValue();
|
||||||
|
if (lastDtc.getFieldName().startsWith("group_")) {
|
||||||
|
// must compute final offset for group
|
||||||
|
return baseRelocOffset + (groupSize * groupOffsetDelta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastDtc.getFieldName().startsWith("group_")) {
|
||||||
|
return -1; // unexpected
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType dt = lastDtc.getDataType();
|
||||||
|
if (dt instanceof AndroidElfRelocationOffset) {
|
||||||
|
AndroidElfRelocationOffset d = (AndroidElfRelocationOffset) dt;
|
||||||
|
return d.getRelocationOffset(); // return stashed offset
|
||||||
|
}
|
||||||
|
else if (dt instanceof AndroidElfRelocationData) {
|
||||||
|
AndroidElfRelocationData d = (AndroidElfRelocationData) dt;
|
||||||
|
return d.getRelocationOffset(); // return stashed offset
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1; // unexpected
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
/* ###
|
||||||
|
* 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.elf;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.AbstractLeb128DataType;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.docking.settings.SettingsDefinition;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
import ghidra.program.model.data.DataType;
|
||||||
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
import ghidra.program.model.scalar.Scalar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>AndroidElfRelocationOffset</code> provides a dynamic LEB128 relocation
|
||||||
|
* offset adjustment component for packed Android ELF Relocation Table groups.
|
||||||
|
* See {@link AndroidElfRelocationGroup}. The offset adjustment provided
|
||||||
|
* by the LEB128 memory data is added to the associated baseOffset to obtain
|
||||||
|
* the corresponding relocation offset/address.
|
||||||
|
* <br>
|
||||||
|
* Secondary purpose is to retain the relocation offset associated with a
|
||||||
|
* component instance. This functionality relies on the 1:1 relationship
|
||||||
|
* between this dynamic datatype and the single component which references it.
|
||||||
|
*/
|
||||||
|
class AndroidElfRelocationOffset extends AbstractLeb128DataType {
|
||||||
|
|
||||||
|
private final long baseOffset;
|
||||||
|
private long relocationOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packed relocation offset data type based upon a signed LEB128
|
||||||
|
* value adjusted by baseOffset.
|
||||||
|
* @param dtm the data type manager to associate with this data type.
|
||||||
|
* @param baseOffset base offset to which LEB128 offset data should be added
|
||||||
|
*/
|
||||||
|
AndroidElfRelocationOffset(DataTypeManager dtm, long baseOffset) {
|
||||||
|
super("sleb128_offset", true, dtm);
|
||||||
|
this.baseOffset = baseOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataType clone(DataTypeManager dtm) {
|
||||||
|
if (dtm == getDataTypeManager()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new AndroidElfRelocationOffset(dtm, baseOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMnemonic(Settings settings) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Android Packed Relocation Offset for ELF";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultLabelPrefix() {
|
||||||
|
return "sleb128";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SettingsDefinition[] getBuiltInSettingsDefinitions() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getValueClass(Settings settings) {
|
||||||
|
return Address.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(MemBuffer buf, Settings settings, int length) {
|
||||||
|
Scalar s = (Scalar) super.getValue(buf, settings, length);
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// assume pointer into physical space associated with buf
|
||||||
|
AddressSpace space = buf.getAddress().getAddressSpace().getPhysicalSpace();
|
||||||
|
return space.getAddress(s.getUnsignedValue() + baseOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
|
||||||
|
Scalar s = (Scalar) super.getValue(buf, settings, length);
|
||||||
|
if (s == null) {
|
||||||
|
return "??";
|
||||||
|
}
|
||||||
|
// TODO: not sure what representation to use
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
if (baseOffset != 0) {
|
||||||
|
b.append("0x");
|
||||||
|
b.append(Long.toHexString(baseOffset));
|
||||||
|
b.append(" + ");
|
||||||
|
}
|
||||||
|
b.append("0x");
|
||||||
|
b.append(Long.toHexString(s.getUnsignedValue()));
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the stashed relocation offset associated with this data item
|
||||||
|
* @return the relocation offset associated with this data item
|
||||||
|
*/
|
||||||
|
long getRelocationOffset() {
|
||||||
|
return relocationOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the computed relocation offset location associated with this
|
||||||
|
* component.
|
||||||
|
* @param relocationOffset stashed relocation offset
|
||||||
|
*/
|
||||||
|
void setRelocationOffset(long relocationOffset) {
|
||||||
|
this.relocationOffset = relocationOffset;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
/* ###
|
||||||
|
* 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.elf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.SignedLeb128DataType;
|
||||||
|
import ghidra.app.util.bin.*;
|
||||||
|
import ghidra.app.util.bin.format.dwarf4.LEB128;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
import ghidra.program.model.mem.WrappedMemBuffer;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>AndroidElfRelocationTableDataType</code> provides an implementation of
|
||||||
|
* an Android APS2 packed ELF relocation table.
|
||||||
|
*/
|
||||||
|
public class AndroidElfRelocationTableDataType extends DynamicDataType {
|
||||||
|
|
||||||
|
public AndroidElfRelocationTableDataType() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndroidElfRelocationTableDataType(DataTypeManager dtm) {
|
||||||
|
super(CategoryPath.ROOT, "AndroidElfRelocationTable", dtm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataType clone(DataTypeManager dtm) {
|
||||||
|
if (dtm == dataMgr) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new AndroidElfRelocationTableDataType(dtm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Android Packed Relocation Table for ELF";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(MemBuffer buf, Settings settings, int length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static class LEB128Info {
|
||||||
|
|
||||||
|
final int offset;
|
||||||
|
final long value;
|
||||||
|
final int byteLength;
|
||||||
|
|
||||||
|
private LEB128Info(int offset, long value, int byteLength) {
|
||||||
|
this.offset = offset;
|
||||||
|
this.value = value;
|
||||||
|
this.byteLength = byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent getComponent(DynamicDataType parent, int ordinal, String name,
|
||||||
|
String comment, long relocOffset) {
|
||||||
|
return new ReadOnlyDataTypeComponent(
|
||||||
|
new AndroidElfRelocationData(parent.getDataTypeManager(), relocOffset), parent,
|
||||||
|
byteLength, ordinal, offset, name, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent getComponent(DynamicDataType parent, int ordinal, String name,
|
||||||
|
String comment) {
|
||||||
|
return new ReadOnlyDataTypeComponent(
|
||||||
|
new SignedLeb128DataType(parent.getDataTypeManager()), parent, byteLength, ordinal,
|
||||||
|
offset, name, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LEB128Info parse(BinaryReader reader, boolean signed) throws IOException {
|
||||||
|
long nextPos = reader.getPointerIndex();
|
||||||
|
long value = LEB128.decode(reader, signed);
|
||||||
|
long pos = reader.getPointerIndex();
|
||||||
|
int size = (int) (pos - nextPos);
|
||||||
|
return new LEB128Info((int) nextPos, value, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DataTypeComponent[] getAllComponents(MemBuffer buf) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] bytes = new byte[4];
|
||||||
|
if (buf.getBytes(bytes, 0) != 4 || !"APS2".equals(new String(bytes))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteProvider provider = new MemBufferByteProvider(buf);
|
||||||
|
BinaryReader reader = new BinaryReader(provider, false);
|
||||||
|
|
||||||
|
ArrayList<DataTypeComponent> list = new ArrayList<>();
|
||||||
|
|
||||||
|
// assume APS2 format
|
||||||
|
list.add(new ReadOnlyDataTypeComponent(StringDataType.dataType, this, 4, 0, 0, "format",
|
||||||
|
null));
|
||||||
|
reader.setPointerIndex(4);
|
||||||
|
|
||||||
|
LEB128Info sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
long remainingRelocations = sleb128.value;
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "reloc_count", null));
|
||||||
|
|
||||||
|
sleb128 = LEB128Info.parse(reader, true);
|
||||||
|
long baseRelocOffset = sleb128.value;
|
||||||
|
list.add(sleb128.getComponent(this, list.size(), "reloc_baseOffset", null));
|
||||||
|
|
||||||
|
int groupIndex = 0;
|
||||||
|
long groupRelocOffset = baseRelocOffset;
|
||||||
|
while (remainingRelocations > 0) {
|
||||||
|
// NOTE: assumes 2-GByte MemBuffer limit
|
||||||
|
int offset = (int) reader.getPointerIndex();
|
||||||
|
|
||||||
|
long groupSize = LEB128.decode(reader, true);
|
||||||
|
if (groupSize > remainingRelocations) {
|
||||||
|
Msg.debug(this, "Group relocation count " + groupSize +
|
||||||
|
" exceeded total count " + remainingRelocations);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidElfRelocationGroup group =
|
||||||
|
new AndroidElfRelocationGroup(dataMgr, groupRelocOffset);
|
||||||
|
WrappedMemBuffer groupBuffer = new WrappedMemBuffer(buf, offset);
|
||||||
|
int groupLength = group.getLength(groupBuffer, -1);
|
||||||
|
DataTypeComponent dtc = new ReadOnlyDataTypeComponent(group, this, groupLength,
|
||||||
|
list.size(), offset, "reloc_group_" + groupIndex++, null);
|
||||||
|
list.add(dtc);
|
||||||
|
|
||||||
|
groupRelocOffset = group.getLastRelocationOffset(groupBuffer);
|
||||||
|
if (groupRelocOffset < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += groupLength;
|
||||||
|
reader.setPointerIndex(offset);
|
||||||
|
|
||||||
|
remainingRelocations -= groupSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent[] comps = new DataTypeComponent[list.size()];
|
||||||
|
return list.toArray(comps);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,96 +0,0 @@
|
||||||
package ghidra.app.util.bin.format.elf;
|
|
||||||
|
|
||||||
import java.util.Formatter;
|
|
||||||
|
|
||||||
import ghidra.docking.settings.Settings;
|
|
||||||
import ghidra.program.model.data.BuiltIn;
|
|
||||||
import ghidra.program.model.data.ByteDataType;
|
|
||||||
import ghidra.program.model.data.DataType;
|
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.program.model.data.Dynamic;
|
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
|
||||||
|
|
||||||
public class AndroidPackedRelocationTableDataType extends BuiltIn implements Dynamic {
|
|
||||||
|
|
||||||
private ElfRelocation[] relocs;
|
|
||||||
private int size;
|
|
||||||
|
|
||||||
public AndroidPackedRelocationTableDataType() {
|
|
||||||
this(new ElfRelocation[0], -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidPackedRelocationTableDataType(ElfRelocation[] relocs, int length) {
|
|
||||||
this(relocs, length, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidPackedRelocationTableDataType(ElfRelocation[] relocs, int length, DataTypeManager dtm) {
|
|
||||||
super(null, "AndroidPackedRelocationTable", dtm);
|
|
||||||
this.relocs = relocs;
|
|
||||||
this.size = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataType clone(DataTypeManager dtm) {
|
|
||||||
if (dtm == getDataTypeManager()) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
return new AndroidPackedRelocationTableDataType(relocs, size, dtm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return "Android Packed Relocation Table";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDefaultLabelPrefix() {
|
|
||||||
return "AndroidPackedRelocationTable";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
|
|
||||||
//TODO I think we need to reparse the data here, as the relocs data is not populated
|
|
||||||
|
|
||||||
if (relocs.length > 0) {
|
|
||||||
Formatter formatter = new Formatter();
|
|
||||||
|
|
||||||
for(ElfRelocation reloc : relocs) {
|
|
||||||
formatter.format("%d,%d,%d \n", reloc.getOffset(), reloc.getRelocationInfo(), reloc.getAddend());
|
|
||||||
}
|
|
||||||
|
|
||||||
return formatter.toString();
|
|
||||||
} else {
|
|
||||||
return "No relocs present";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLength() {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValue(MemBuffer buf, Settings settings, int length) {
|
|
||||||
return"Value";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLength(MemBuffer buf, int maxLength) {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canSpecifyLength() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDynamicallySized() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataType getReplacementBaseType() {
|
|
||||||
return ByteDataType.dataType;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -90,26 +90,35 @@ public class ElfDynamicType {
|
||||||
"Size in bytes of DT_FINI_ARRAY", ElfDynamicValueType.VALUE);
|
"Size in bytes of DT_FINI_ARRAY", ElfDynamicValueType.VALUE);
|
||||||
public static ElfDynamicType DT_RUNPATH = addDefaultDynamicType(29, "DT_RUNPATH",
|
public static ElfDynamicType DT_RUNPATH = addDefaultDynamicType(29, "DT_RUNPATH",
|
||||||
"Library search path (string ref)", ElfDynamicValueType.STRING);
|
"Library search path (string ref)", ElfDynamicValueType.STRING);
|
||||||
|
// see DF_ constants for flag definitions
|
||||||
public static ElfDynamicType DT_FLAGS = addDefaultDynamicType(30, "DT_FLAGS",
|
public static ElfDynamicType DT_FLAGS = addDefaultDynamicType(30, "DT_FLAGS",
|
||||||
"Flags for the object being loaded", ElfDynamicValueType.VALUE);
|
"Flags for the object being loaded", ElfDynamicValueType.VALUE);
|
||||||
|
|
||||||
public static ElfDynamicType DT_ENCODING = addDefaultDynamicType(32, "DT_ENCODING",
|
public static final int DF_ORIGIN = 0x1; // $ORIGIN processing required
|
||||||
"Start of encoded range", ElfDynamicValueType.VALUE);
|
public static final int DF_SYMBOLIC = 0x2; // Symbolic symbol resolution required
|
||||||
// public static ElfDynamicType DT_PREINIT_ARRAY = addDefaultDynamicType(32, "DT_PREINIT_ARRAY", "Array with addresses of preinit fct", ElfDynamicValueType.VALUE);
|
public static final int DF_TEXTREL = 0x4; // Text relocations exist
|
||||||
|
public static final int DF_BIND_NOW = 0x8; // Non-lazy binding required
|
||||||
|
public static final int DF_STATIC_TLS = 0x10; // Object uses static TLS scheme
|
||||||
|
|
||||||
|
// glibc and BSD disagree for DT_ENCODING
|
||||||
|
// public static ElfDynamicType DT_ENCODING = addDefaultDynamicType(32, "DT_ENCODING",
|
||||||
|
// "Start of encoded range", ElfDynamicValueType.VALUE);
|
||||||
|
public static ElfDynamicType DT_PREINIT_ARRAY = addDefaultDynamicType(32, "DT_PREINIT_ARRAY",
|
||||||
|
"Array with addresses of preinit fct", ElfDynamicValueType.VALUE);
|
||||||
public static ElfDynamicType DT_PREINIT_ARRAYSZ = addDefaultDynamicType(33,
|
public static ElfDynamicType DT_PREINIT_ARRAYSZ = addDefaultDynamicType(33,
|
||||||
"DT_PREINIT_ARRAYSZ", "Size in bytes of DT_PREINIT_ARRAY", ElfDynamicValueType.VALUE);
|
"DT_PREINIT_ARRAYSZ", "Size in bytes of DT_PREINIT_ARRAY", ElfDynamicValueType.VALUE);
|
||||||
|
|
||||||
// OS-specific range: 0x6000000d - 0x6ffff000
|
// OS-specific range: 0x6000000d - 0x6ffff000
|
||||||
|
|
||||||
public static ElfDynamicType DT_ANDROID_REL =
|
public static ElfDynamicType DT_ANDROID_REL = addDefaultDynamicType(0x6000000F,
|
||||||
addDefaultDynamicType(0x6000000F, "DT_ANDROID_REL", "Address of Rel relocs", ElfDynamicValueType.ADDRESS);
|
"DT_ANDROID_REL", "Address of Rel relocs", ElfDynamicValueType.ADDRESS);
|
||||||
public static ElfDynamicType DT_ANDROID_RELSZ = addDefaultDynamicType(0x60000010, "DT_ANDROID_RELSZ",
|
public static ElfDynamicType DT_ANDROID_RELSZ = addDefaultDynamicType(0x60000010,
|
||||||
"Total size of Rel relocs", ElfDynamicValueType.VALUE);
|
"DT_ANDROID_RELSZ", "Total size of Rel relocs", ElfDynamicValueType.VALUE);
|
||||||
|
|
||||||
public static ElfDynamicType DT_ANDROID_RELA =
|
public static ElfDynamicType DT_ANDROID_RELA = addDefaultDynamicType(0x60000011,
|
||||||
addDefaultDynamicType(0x60000011, "DT_ANDROID_RELA", "Address of Rela relocs", ElfDynamicValueType.ADDRESS);
|
"DT_ANDROID_RELA", "Address of Rela relocs", ElfDynamicValueType.ADDRESS);
|
||||||
public static ElfDynamicType DT_ANDROID_RELASZ = addDefaultDynamicType(0x60000012, "DT_ANDROID_RELASZ",
|
public static ElfDynamicType DT_ANDROID_RELASZ = addDefaultDynamicType(0x60000012,
|
||||||
"Total size of Rela relocs", ElfDynamicValueType.VALUE);
|
"DT_ANDROID_RELASZ", "Total size of Rela relocs", ElfDynamicValueType.VALUE);
|
||||||
|
|
||||||
// Value Range (??): 0x6ffffd00 - 0x6ffffdff
|
// Value Range (??): 0x6ffffd00 - 0x6ffffdff
|
||||||
|
|
||||||
|
@ -168,8 +177,23 @@ public class ElfDynamicType {
|
||||||
addDefaultDynamicType(0x6ffffff9, "DT_RELACOUNT", "", ElfDynamicValueType.VALUE);
|
addDefaultDynamicType(0x6ffffff9, "DT_RELACOUNT", "", ElfDynamicValueType.VALUE);
|
||||||
public static ElfDynamicType DT_RELCOUNT =
|
public static ElfDynamicType DT_RELCOUNT =
|
||||||
addDefaultDynamicType(0x6ffffffa, "DT_RELCOUNT", "", ElfDynamicValueType.VALUE);
|
addDefaultDynamicType(0x6ffffffa, "DT_RELCOUNT", "", ElfDynamicValueType.VALUE);
|
||||||
|
|
||||||
|
// see DF_1_ constants for flag definitions
|
||||||
public static ElfDynamicType DT_FLAGS_1 =
|
public static ElfDynamicType DT_FLAGS_1 =
|
||||||
addDefaultDynamicType(0x6ffffffb, "DT_FLAGS_1", "State flags", ElfDynamicValueType.VALUE);
|
addDefaultDynamicType(0x6ffffffb, "DT_FLAGS_1", "State flags", ElfDynamicValueType.VALUE);
|
||||||
|
|
||||||
|
public static final int DF_1_NOW = 0x1;
|
||||||
|
public static final int DF_1_GLOBAL = 0x2;
|
||||||
|
public static final int DF_1_GROUP = 0x4;
|
||||||
|
public static final int DF_1_NODELETE = 0x8;
|
||||||
|
public static final int DF_1_LOADFLTR = 0x10;
|
||||||
|
public static final int DF_1_INITFIRST = 0x20;
|
||||||
|
public static final int DF_1_NOOPEN = 0x40;
|
||||||
|
public static final int DF_1_ORIGIN = 0x80;
|
||||||
|
public static final int DF_1_DIRECT = 0x100;
|
||||||
|
public static final int DF_1_INTERPOSE = 0x400;
|
||||||
|
public static final int DF_1_NODEFLIB = 0x800;
|
||||||
|
|
||||||
public static ElfDynamicType DT_VERDEF = addDefaultDynamicType(0x6ffffffc, "DT_VERDEF",
|
public static ElfDynamicType DT_VERDEF = addDefaultDynamicType(0x6ffffffc, "DT_VERDEF",
|
||||||
"Address of version definition table", ElfDynamicValueType.ADDRESS);
|
"Address of version definition table", ElfDynamicValueType.ADDRESS);
|
||||||
public static ElfDynamicType DT_VERDEFNUM = addDefaultDynamicType(0x6ffffffd, "DT_VERDEFNUM",
|
public static ElfDynamicType DT_VERDEFNUM = addDefaultDynamicType(0x6ffffffd, "DT_VERDEFNUM",
|
||||||
|
|
|
@ -23,6 +23,7 @@ import generic.continues.GenericFactory;
|
||||||
import ghidra.app.util.bin.*;
|
import ghidra.app.util.bin.*;
|
||||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||||
import ghidra.app.util.bin.format.Writeable;
|
import ghidra.app.util.bin.format.Writeable;
|
||||||
|
import ghidra.app.util.bin.format.elf.ElfRelocationTable.TableFormat;
|
||||||
import ghidra.app.util.bin.format.elf.extend.ElfExtensionFactory;
|
import ghidra.app.util.bin.format.elf.extend.ElfExtensionFactory;
|
||||||
import ghidra.app.util.bin.format.elf.extend.ElfLoadAdapter;
|
import ghidra.app.util.bin.format.elf.extend.ElfLoadAdapter;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
@ -341,11 +342,11 @@ public class ElfHeader implements StructConverter, Writeable {
|
||||||
ElfDynamicType.DT_RELAENT, ElfDynamicType.DT_RELASZ, true);
|
ElfDynamicType.DT_RELAENT, ElfDynamicType.DT_RELASZ, true);
|
||||||
|
|
||||||
// Android versions
|
// Android versions
|
||||||
parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_ANDROID_REL, ElfDynamicType.DT_RELENT,
|
parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_ANDROID_REL, null,
|
||||||
ElfDynamicType.DT_ANDROID_RELSZ, false);
|
ElfDynamicType.DT_ANDROID_RELSZ, false);
|
||||||
|
|
||||||
parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_ANDROID_RELA,
|
parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_ANDROID_RELA, null,
|
||||||
ElfDynamicType.DT_RELAENT, ElfDynamicType.DT_ANDROID_RELASZ, true);
|
ElfDynamicType.DT_ANDROID_RELASZ, true);
|
||||||
|
|
||||||
parseJMPRelocTable(relocationTableList);
|
parseJMPRelocTable(relocationTableList);
|
||||||
|
|
||||||
|
@ -364,42 +365,8 @@ public class ElfHeader implements StructConverter, Writeable {
|
||||||
try {
|
try {
|
||||||
int sectionHeaderType = section.getType();
|
int sectionHeaderType = section.getType();
|
||||||
if (sectionHeaderType == ElfSectionHeaderConstants.SHT_REL ||
|
if (sectionHeaderType == ElfSectionHeaderConstants.SHT_REL ||
|
||||||
sectionHeaderType == ElfSectionHeaderConstants.SHT_RELA) {
|
sectionHeaderType == ElfSectionHeaderConstants.SHT_RELA ||
|
||||||
|
sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_REL ||
|
||||||
for (ElfRelocationTable relocTable : relocationTableList) {
|
|
||||||
if (relocTable.getFileOffset() == section.getOffset()) {
|
|
||||||
return; // skip reloc table previously parsed as dynamic entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int link = section.getLink(); // section index of associated symbol table
|
|
||||||
int info = section.getInfo(); // section index of section to which relocations apply (relocation offset base)
|
|
||||||
|
|
||||||
ElfSectionHeader sectionToBeRelocated = info != 0 ? getLinkedSection(info) : null;
|
|
||||||
String relocaBaseName =
|
|
||||||
sectionToBeRelocated != null ? sectionToBeRelocated.getNameAsString()
|
|
||||||
: "PT_LOAD";
|
|
||||||
|
|
||||||
ElfSectionHeader symbolTableSection = getLinkedSection(link,
|
|
||||||
ElfSectionHeaderConstants.SHT_DYNSYM, ElfSectionHeaderConstants.SHT_SYMTAB);
|
|
||||||
ElfSymbolTable symbolTable = getSymbolTable(symbolTableSection);
|
|
||||||
|
|
||||||
boolean addendTypeReloc = (sectionHeaderType == ElfSectionHeaderConstants.SHT_RELA);
|
|
||||||
|
|
||||||
Msg.debug(this,
|
|
||||||
"Elf relocation table section " + section.getNameAsString() +
|
|
||||||
" linked to symbol table section " + symbolTableSection.getNameAsString() +
|
|
||||||
" affecting " + relocaBaseName);
|
|
||||||
|
|
||||||
if (section.getOffset() < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
relocationTableList.add(ElfRelocationTable.createElfRelocationTable(reader, this,
|
|
||||||
section, section.getOffset(), section.getAddress(), section.getSize(),
|
|
||||||
section.getEntrySize(), addendTypeReloc, symbolTable, sectionToBeRelocated));
|
|
||||||
}
|
|
||||||
else if (sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_REL ||
|
|
||||||
sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_RELA) {
|
sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_RELA) {
|
||||||
|
|
||||||
for (ElfRelocationTable relocTable : relocationTableList) {
|
for (ElfRelocationTable relocTable : relocationTableList) {
|
||||||
|
@ -416,19 +383,24 @@ public class ElfHeader implements StructConverter, Writeable {
|
||||||
sectionToBeRelocated != null ? sectionToBeRelocated.getNameAsString()
|
sectionToBeRelocated != null ? sectionToBeRelocated.getNameAsString()
|
||||||
: "PT_LOAD";
|
: "PT_LOAD";
|
||||||
|
|
||||||
// For some Android binaries, the symbols table section is not referenced by the link value. Instead
|
ElfSectionHeader symbolTableSection;
|
||||||
// we can just reference the main dynamic symbol table.
|
if (link == 0) {
|
||||||
ElfSectionHeader symbolTableSection = (link != 0)
|
// dynamic symbol table assumed when link section value is 0
|
||||||
? getLinkedSection(link, ElfSectionHeaderConstants.SHT_DYNSYM, ElfSectionHeaderConstants.SHT_SYMTAB)
|
symbolTableSection = getSection(ElfSectionHeaderConstants.dot_dynsym);
|
||||||
: getSection(ElfSectionHeaderConstants.dot_dynsym);
|
}
|
||||||
|
else {
|
||||||
ElfSymbolTable symbolTable = getSymbolTable(symbolTableSection);
|
symbolTableSection = getLinkedSection(link,
|
||||||
|
ElfSectionHeaderConstants.SHT_DYNSYM, ElfSectionHeaderConstants.SHT_SYMTAB);
|
||||||
if (symbolTable == null) {
|
|
||||||
throw new NotFoundException("Referenced symbol section not found.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean addendTypeReloc = (sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_RELA);
|
ElfSymbolTable symbolTable = getSymbolTable(symbolTableSection);
|
||||||
|
if (symbolTable == null) {
|
||||||
|
throw new NotFoundException("Referenced relocation symbol section not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean addendTypeReloc =
|
||||||
|
(sectionHeaderType == ElfSectionHeaderConstants.SHT_RELA ||
|
||||||
|
sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_RELA);
|
||||||
|
|
||||||
Msg.debug(this,
|
Msg.debug(this,
|
||||||
"Elf relocation table section " + section.getNameAsString() +
|
"Elf relocation table section " + section.getNameAsString() +
|
||||||
|
@ -439,9 +411,18 @@ public class ElfHeader implements StructConverter, Writeable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
relocationTableList.add(ElfRelocationTable.createAndroidElfRelocationTable(reader, this,
|
ElfRelocationTable.TableFormat format = TableFormat.DEFAULT;
|
||||||
section, section.getOffset(), section.getAddress(), section.getSize(),
|
if (sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_REL ||
|
||||||
section.getEntrySize(), addendTypeReloc, symbolTable, sectionToBeRelocated));
|
sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_RELA) {
|
||||||
|
format = TableFormat.ANDROID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ElfRelocationTable relocTable = ElfRelocationTable.createElfRelocationTable(reader,
|
||||||
|
this, section, section.getOffset(), section.getAddress(), section.getSize(),
|
||||||
|
section.getEntrySize(), addendTypeReloc, symbolTable, sectionToBeRelocated,
|
||||||
|
format);
|
||||||
|
|
||||||
|
relocationTableList.add(relocTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (NotFoundException e) {
|
catch (NotFoundException e) {
|
||||||
|
@ -513,13 +494,20 @@ public class ElfHeader implements StructConverter, Writeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
long relocTableOffset = relocTableLoadHeader.getOffset(relocTableAddr);
|
long relocTableOffset = relocTableLoadHeader.getOffset(relocTableAddr);
|
||||||
long tableEntrySize = dynamicTable.getDynamicValue(relocEntrySizeType);
|
long tableEntrySize =
|
||||||
|
relocEntrySizeType != null ? dynamicTable.getDynamicValue(relocEntrySizeType) : -1;
|
||||||
long tableSize = dynamicTable.getDynamicValue(relocTableSizeType);
|
long tableSize = dynamicTable.getDynamicValue(relocTableSizeType);
|
||||||
|
|
||||||
relocationTableList.add(ElfRelocationTable.createElfRelocationTable(reader, this, null,
|
ElfRelocationTable.TableFormat format = TableFormat.DEFAULT;
|
||||||
relocTableOffset, relocTableAddr, tableSize, tableEntrySize, addendTypeReloc,
|
if (relocTableAddrType == ElfDynamicType.DT_ANDROID_REL ||
|
||||||
dynamicSymbolTable, null));
|
relocTableAddrType == ElfDynamicType.DT_ANDROID_RELA) {
|
||||||
|
format = TableFormat.ANDROID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ElfRelocationTable relocTable = ElfRelocationTable.createElfRelocationTable(reader,
|
||||||
|
this, null, relocTableOffset, relocTableAddr, tableSize, tableEntrySize,
|
||||||
|
addendTypeReloc, dynamicSymbolTable, null, format);
|
||||||
|
relocationTableList.add(relocTable);
|
||||||
}
|
}
|
||||||
catch (NotFoundException e) {
|
catch (NotFoundException e) {
|
||||||
// ignore - skip (required dynamic table value is missing)
|
// ignore - skip (required dynamic table value is missing)
|
||||||
|
@ -1581,6 +1569,9 @@ public class ElfHeader implements StructConverter, Writeable {
|
||||||
* @return the symbol table associated to the specified section header
|
* @return the symbol table associated to the specified section header
|
||||||
*/
|
*/
|
||||||
public ElfSymbolTable getSymbolTable(ElfSectionHeader symbolTableSection) {
|
public ElfSymbolTable getSymbolTable(ElfSectionHeader symbolTableSection) {
|
||||||
|
if (symbolTableSection == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
for (int i = 0; i < symbolTables.length; i++) {
|
for (int i = 0; i < symbolTables.length; i++) {
|
||||||
if (symbolTables[i].getFileOffset() == symbolTableSection.getOffset()) {
|
if (symbolTables[i].getFileOffset() == symbolTableSection.getOffset()) {
|
||||||
return symbolTables[i];
|
return symbolTables[i];
|
||||||
|
|
|
@ -101,30 +101,7 @@ public class ElfRelocation implements ByteArrayConverter, StructConverter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GenericFactory construction and initialization method for a ELF representative
|
* GenericFactory construction and initialization method for a ELF representative
|
||||||
* relocation entry (entry data will be 0)
|
* relocation entry
|
||||||
* @param factory instantiation factory.
|
|
||||||
* @param elfHeader ELF header
|
|
||||||
* @param relocationIndex index of entry in relocation table
|
|
||||||
* @param withAddend true if if RELA entry with addend, else false
|
|
||||||
* @return ELF relocation object
|
|
||||||
*/
|
|
||||||
static ElfRelocation createElfRelocation(GenericFactory factory, ElfHeader elfHeader,
|
|
||||||
int relocationIndex, boolean withAddend) {
|
|
||||||
|
|
||||||
Class<? extends ElfRelocation> elfRelocationClass = getElfRelocationClass(elfHeader);
|
|
||||||
ElfRelocation elfRelocation = (ElfRelocation) factory.create(elfRelocationClass);
|
|
||||||
try {
|
|
||||||
elfRelocation.initElfRelocation(null, elfHeader, relocationIndex, withAddend);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
// absence of reader should prevent any IOException from occurring
|
|
||||||
throw new AssertException("unexpected IO error", e);
|
|
||||||
}
|
|
||||||
return elfRelocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GenericFactory construction and initialization method for a ELF relocation entry
|
|
||||||
* @param reader binary reader positioned at start of relocation entry.
|
* @param reader binary reader positioned at start of relocation entry.
|
||||||
* @param elfHeader ELF header
|
* @param elfHeader ELF header
|
||||||
* @param relocationIndex index of entry in relocation table
|
* @param relocationIndex index of entry in relocation table
|
||||||
|
@ -133,15 +110,20 @@ public class ElfRelocation implements ByteArrayConverter, StructConverter {
|
||||||
* @param r_info The info value for the entry
|
* @param r_info The info value for the entry
|
||||||
* @param r_addend The addend for the entry
|
* @param r_addend The addend for the entry
|
||||||
* @return ELF relocation object
|
* @return ELF relocation object
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
static ElfRelocation createElfRelocation(FactoryBundledWithBinaryReader reader,
|
static ElfRelocation createElfRelocation(GenericFactory factory, ElfHeader elfHeader,
|
||||||
ElfHeader elfHeader, int relocationIndex, boolean withAddend, long r_offset, long r_info, long r_addend) throws IOException {
|
int relocationIndex, boolean withAddend, long r_offset, long r_info, long r_addend) {
|
||||||
|
|
||||||
Class<? extends ElfRelocation> elfRelocationClass = getElfRelocationClass(elfHeader);
|
Class<? extends ElfRelocation> elfRelocationClass = getElfRelocationClass(elfHeader);
|
||||||
ElfRelocation elfRelocation =
|
ElfRelocation elfRelocation = (ElfRelocation) factory.create(elfRelocationClass);
|
||||||
(ElfRelocation) reader.getFactory().create(elfRelocationClass);
|
try {
|
||||||
elfRelocation.initElfRelocation(elfHeader, relocationIndex, withAddend, r_offset, r_info, r_addend);
|
elfRelocation.initElfRelocation(elfHeader, relocationIndex, withAddend, r_offset,
|
||||||
|
r_info, r_addend);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// absence of reader should prevent any IOException from occurring
|
||||||
|
throw new AssertException("unexpected IO error", e);
|
||||||
|
}
|
||||||
return elfRelocation;
|
return elfRelocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,12 @@ import ghidra.util.Msg;
|
||||||
*/
|
*/
|
||||||
public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
|
|
||||||
|
public enum TableFormat {
|
||||||
|
DEFAULT, ANDROID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TableFormat format;
|
||||||
|
|
||||||
private ElfSectionHeader sectionToBeRelocated;
|
private ElfSectionHeader sectionToBeRelocated;
|
||||||
|
|
||||||
private ElfSymbolTable symbolTable;
|
private ElfSymbolTable symbolTable;
|
||||||
|
@ -61,28 +67,19 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
* @param addendTypeReloc true if addend type relocation table
|
* @param addendTypeReloc true if addend type relocation table
|
||||||
* @param symbolTable associated symbol table
|
* @param symbolTable associated symbol table
|
||||||
* @param sectionToBeRelocated or null for dynamic relocation table
|
* @param sectionToBeRelocated or null for dynamic relocation table
|
||||||
|
* @param format table format
|
||||||
* @return Elf relocation table object
|
* @return Elf relocation table object
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
static ElfRelocationTable createElfRelocationTable(FactoryBundledWithBinaryReader reader,
|
static ElfRelocationTable createElfRelocationTable(FactoryBundledWithBinaryReader reader,
|
||||||
ElfHeader header, ElfSectionHeader relocTableSection, long fileOffset, long addrOffset,
|
ElfHeader header, ElfSectionHeader relocTableSection, long fileOffset, long addrOffset,
|
||||||
long length, long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
|
long length, long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
|
||||||
ElfSectionHeader sectionToBeRelocated) throws IOException {
|
ElfSectionHeader sectionToBeRelocated, TableFormat format) throws IOException {
|
||||||
ElfRelocationTable elfRelocationTable =
|
ElfRelocationTable elfRelocationTable =
|
||||||
(ElfRelocationTable) reader.getFactory().create(ElfRelocationTable.class);
|
(ElfRelocationTable) reader.getFactory().create(ElfRelocationTable.class);
|
||||||
elfRelocationTable.initElfRelocationTable(reader, header, relocTableSection, fileOffset,
|
elfRelocationTable.initElfRelocationTable(reader, header, relocTableSection, fileOffset,
|
||||||
addrOffset, length, entrySize, addendTypeReloc, symbolTable, sectionToBeRelocated);
|
addrOffset, length, entrySize, addendTypeReloc, symbolTable, sectionToBeRelocated,
|
||||||
return elfRelocationTable;
|
format);
|
||||||
}
|
|
||||||
|
|
||||||
static ElfRelocationTable createAndroidElfRelocationTable(FactoryBundledWithBinaryReader reader,
|
|
||||||
ElfHeader header, ElfSectionHeader relocTableSection, long fileOffset, long addrOffset,
|
|
||||||
long length, long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
|
|
||||||
ElfSectionHeader sectionToBeRelocated) throws IOException {
|
|
||||||
ElfRelocationTable elfRelocationTable =
|
|
||||||
(ElfRelocationTable) reader.getFactory().create(ElfRelocationTable.class);
|
|
||||||
elfRelocationTable.initAndroidElfRelocationTable(reader, header, relocTableSection, fileOffset,
|
|
||||||
addrOffset, length, entrySize, addendTypeReloc, symbolTable, sectionToBeRelocated);
|
|
||||||
return elfRelocationTable;
|
return elfRelocationTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +92,7 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
private void initElfRelocationTable(FactoryBundledWithBinaryReader reader, ElfHeader header,
|
private void initElfRelocationTable(FactoryBundledWithBinaryReader reader, ElfHeader header,
|
||||||
ElfSectionHeader relocTableSection, long fileOffset, long addrOffset, long length,
|
ElfSectionHeader relocTableSection, long fileOffset, long addrOffset, long length,
|
||||||
long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
|
long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
|
||||||
ElfSectionHeader sectionToBeRelocated) throws IOException {
|
ElfSectionHeader sectionToBeRelocated, TableFormat format) throws IOException {
|
||||||
|
|
||||||
this.relocTableSection = relocTableSection;
|
this.relocTableSection = relocTableSection;
|
||||||
this.fileOffset = fileOffset;
|
this.fileOffset = fileOffset;
|
||||||
|
@ -105,6 +102,7 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
this.addendTypeReloc = addendTypeReloc;
|
this.addendTypeReloc = addendTypeReloc;
|
||||||
this.elfHeader = header;
|
this.elfHeader = header;
|
||||||
this.factory = reader.getFactory();
|
this.factory = reader.getFactory();
|
||||||
|
this.format = format;
|
||||||
|
|
||||||
this.sectionToBeRelocated = sectionToBeRelocated;
|
this.sectionToBeRelocated = sectionToBeRelocated;
|
||||||
this.symbolTable = symbolTable;
|
this.symbolTable = symbolTable;
|
||||||
|
@ -112,12 +110,12 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
long ptr = reader.getPointerIndex();
|
long ptr = reader.getPointerIndex();
|
||||||
reader.setPointerIndex(fileOffset);
|
reader.setPointerIndex(fileOffset);
|
||||||
|
|
||||||
List<ElfRelocation> relocList = new ArrayList<ElfRelocation>();
|
List<ElfRelocation> relocList;
|
||||||
|
if (format == TableFormat.ANDROID) {
|
||||||
int nRelocs = (int) (length / entrySize);
|
relocList = parseAndroidRelocations(reader);
|
||||||
for (int relocationIndex = 0; relocationIndex < nRelocs; ++relocationIndex) {
|
}
|
||||||
relocList.add(ElfRelocation.createElfRelocation(reader, header, relocationIndex,
|
else {
|
||||||
addendTypeReloc));
|
relocList = parseStandardRelocations(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.setPointerIndex(ptr);
|
reader.setPointerIndex(ptr);
|
||||||
|
@ -126,67 +124,52 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
relocList.toArray(relocs);
|
relocList.toArray(relocs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initAndroidElfRelocationTable(FactoryBundledWithBinaryReader reader, ElfHeader header,
|
private List<ElfRelocation> parseStandardRelocations(FactoryBundledWithBinaryReader reader)
|
||||||
ElfSectionHeader relocTableSection, long fileOffset, long addrOffset, long length,
|
throws IOException {
|
||||||
long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
|
|
||||||
ElfSectionHeader sectionToBeRelocated) throws IOException {
|
|
||||||
|
|
||||||
this.relocTableSection = relocTableSection;
|
List<ElfRelocation> relocations = new ArrayList<>();
|
||||||
this.fileOffset = fileOffset;
|
int nRelocs = (int) (length / entrySize);
|
||||||
this.addrOffset = addrOffset;
|
for (int relocationIndex = 0; relocationIndex < nRelocs; ++relocationIndex) {
|
||||||
this.length = length;
|
relocations.add(ElfRelocation.createElfRelocation(reader, elfHeader, relocationIndex,
|
||||||
this.entrySize = entrySize;
|
addendTypeReloc));
|
||||||
this.addendTypeReloc = addendTypeReloc;
|
}
|
||||||
this.elfHeader = header;
|
return relocations;
|
||||||
this.factory = reader.getFactory();
|
}
|
||||||
|
|
||||||
this.sectionToBeRelocated = sectionToBeRelocated;
|
private List<ElfRelocation> parseAndroidRelocations(FactoryBundledWithBinaryReader reader)
|
||||||
this.symbolTable = symbolTable;
|
throws IOException {
|
||||||
|
|
||||||
long ptr = reader.getPointerIndex();
|
|
||||||
reader.setPointerIndex(fileOffset);
|
|
||||||
|
|
||||||
String identifier = reader.readNextAsciiString(4);
|
String identifier = reader.readNextAsciiString(4);
|
||||||
if (!"APS2".equals(identifier)) {
|
if (!"APS2".equals(identifier)) {
|
||||||
Msg.error(this, "Invalid indentifier value for Android packed relocation table: " + identifier);
|
throw new IOException("Unsupported Android relocation table format");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ElfRelocation> relocList = parseAndroidRelocations(reader, header);
|
|
||||||
|
|
||||||
reader.setPointerIndex(ptr);
|
|
||||||
|
|
||||||
relocs = new ElfRelocation[relocList.size()];
|
|
||||||
relocList.toArray(relocs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ElfRelocation> parseAndroidRelocations(FactoryBundledWithBinaryReader reader, ElfHeader header) {
|
|
||||||
List<ElfRelocation> relocations = new ArrayList<>();
|
List<ElfRelocation> relocations = new ArrayList<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long RELOCATION_GROUPED_BY_INFO_FLAG = 1;
|
|
||||||
long RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
|
|
||||||
long RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
|
|
||||||
long RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
|
|
||||||
|
|
||||||
int relocationIndex = 0;
|
int relocationIndex = 0;
|
||||||
long remainingRelocations = LEB128.decode(reader, true);
|
long remainingRelocations = LEB128.decode(reader, true);
|
||||||
long offset = LEB128.decode(reader, true);
|
long offset = LEB128.decode(reader, true);
|
||||||
long addend = 0;
|
long addend = 0;
|
||||||
|
|
||||||
while (remainingRelocations > 0) {
|
while (remainingRelocations > 0) {
|
||||||
long groupSize = LEB128.decode(reader, true);
|
|
||||||
|
|
||||||
|
long groupSize = LEB128.decode(reader, true);
|
||||||
if (groupSize > remainingRelocations) {
|
if (groupSize > remainingRelocations) {
|
||||||
Msg.warn(this, "Group relocation count " + groupSize + " exceeded total count " + remainingRelocations);
|
Msg.warn(this, "Group relocation count " + groupSize +
|
||||||
|
" exceeded total count " + remainingRelocations);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
long groupFlags = LEB128.decode(reader, true);
|
long groupFlags = LEB128.decode(reader, true);
|
||||||
boolean groupedByInfo = (groupFlags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
|
boolean groupedByInfo =
|
||||||
boolean groupedByDelta = (groupFlags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
|
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
|
||||||
boolean groupedByAddend = (groupFlags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
|
boolean groupedByDelta = (groupFlags &
|
||||||
boolean groupHasAddend = (groupFlags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
|
AndroidElfRelocationGroup.RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
|
||||||
|
boolean groupedByAddend =
|
||||||
|
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
|
||||||
|
boolean groupHasAddend =
|
||||||
|
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
|
||||||
|
|
||||||
long groupOffsetDelta = groupedByDelta ? LEB128.decode(reader, true) : 0;
|
long groupOffsetDelta = groupedByDelta ? LEB128.decode(reader, true) : 0;
|
||||||
long groupRInfo = groupedByInfo ? LEB128.decode(reader, true) : 0;
|
long groupRInfo = groupedByInfo ? LEB128.decode(reader, true) : 0;
|
||||||
|
@ -197,6 +180,7 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
|
|
||||||
for (int i = 0; i < groupSize; i++) {
|
for (int i = 0; i < groupSize; i++) {
|
||||||
offset += groupedByDelta ? groupOffsetDelta : LEB128.decode(reader, true);
|
offset += groupedByDelta ? groupOffsetDelta : LEB128.decode(reader, true);
|
||||||
|
|
||||||
long info = groupedByInfo ? groupRInfo : LEB128.decode(reader, true);
|
long info = groupedByInfo ? groupRInfo : LEB128.decode(reader, true);
|
||||||
|
|
||||||
long rAddend = 0;
|
long rAddend = 0;
|
||||||
|
@ -207,14 +191,8 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
rAddend = addend;
|
rAddend = addend;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
relocations.add(ElfRelocation.createElfRelocation(reader.getFactory(),
|
||||||
relocations.add(ElfRelocation.createElfRelocation(reader, header, relocationIndex, addendTypeReloc,
|
elfHeader, relocationIndex++, addendTypeReloc, offset, info, rAddend));
|
||||||
offset, info, rAddend));
|
|
||||||
} catch (IOException e) {
|
|
||||||
Msg.error(this, "Error creating relocation entry");
|
|
||||||
}
|
|
||||||
|
|
||||||
relocationIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!groupHasAddend) {
|
if (!groupHasAddend) {
|
||||||
|
@ -223,7 +201,8 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
|
|
||||||
remainingRelocations -= groupSize;
|
remainingRelocations -= groupSize;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
}
|
||||||
|
catch (IOException e) {
|
||||||
Msg.error(this, "Error reading relocations.", e);
|
Msg.error(this, "Error reading relocations.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,9 +246,6 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
return symbolTable;
|
return symbolTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.app.util.bin.ByteArrayConverter#toBytes(ghidra.util.DataConverter)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] toBytes(DataConverter dc) {
|
public byte[] toBytes(DataConverter dc) {
|
||||||
byte[] bytes = new byte[relocs.length * relocs[0].sizeof()];
|
byte[] bytes = new byte[relocs.length * relocs[0].sizeof()];
|
||||||
|
@ -311,18 +287,14 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
|
||||||
return (int) entrySize;
|
return (int) entrySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.app.util.bin.StructConverter#toDataType()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public DataType toDataType() {
|
public DataType toDataType() {
|
||||||
if (relocTableSection.getType() == ElfSectionHeaderConstants.SHT_ANDROID_REL ||
|
if (format == TableFormat.ANDROID) {
|
||||||
relocTableSection.getType() == ElfSectionHeaderConstants.SHT_ANDROID_RELA ) {
|
return new AndroidElfRelocationTableDataType();
|
||||||
return new AndroidPackedRelocationTableDataType(relocs, (int) length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ElfRelocation relocationRepresentative =
|
ElfRelocation relocationRepresentative =
|
||||||
ElfRelocation.createElfRelocation(factory, elfHeader, -1, addendTypeReloc);
|
ElfRelocation.createElfRelocation(factory, elfHeader, -1, addendTypeReloc, 0, 0, 0);
|
||||||
DataType relocEntryDataType = relocationRepresentative.toDataType();
|
DataType relocEntryDataType = relocationRepresentative.toDataType();
|
||||||
return new ArrayDataType(relocEntryDataType, (int) (length / entrySize), (int) entrySize);
|
return new ArrayDataType(relocEntryDataType, (int) (length / entrySize), (int) entrySize);
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,8 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
|
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||||
|
Program program) {
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
String validationErrorStr = ElfLoaderOptionsFactory.validateOptions(loadSpec, options);
|
String validationErrorStr = ElfLoaderOptionsFactory.validateOptions(loadSpec, options);
|
||||||
if (validationErrorStr != null) {
|
if (validationErrorStr != null) {
|
||||||
|
@ -143,7 +144,8 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
Program program, TaskMonitor monitor, MessageLog log)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
GenericFactory factory = MessageLogContinuesFactory.create(log);
|
GenericFactory factory = MessageLogContinuesFactory.create(log);
|
||||||
|
@ -153,10 +155,6 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
||||||
catch (ElfException e) {
|
catch (ElfException e) {
|
||||||
throw new IOException(e.getMessage());
|
throw new IOException(e.getMessage());
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
|
||||||
// TODO: Caller should properly handle CancelledException instead
|
|
||||||
throw new IOException(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -158,9 +158,9 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||||
|
|
||||||
elf.getLoadAdapter().processElf(this, monitor);
|
elf.getLoadAdapter().processElf(this, monitor);
|
||||||
|
|
||||||
|
processRelocations(monitor);
|
||||||
processEntryPoints(monitor);
|
processEntryPoints(monitor);
|
||||||
processImports(monitor);
|
processImports(monitor);
|
||||||
processRelocations(monitor);
|
|
||||||
|
|
||||||
monitor.setMessage("Processing PLT/GOT ...");
|
monitor.setMessage("Processing PLT/GOT ...");
|
||||||
elf.getLoadAdapter().processGotPlt(this, monitor);
|
elf.getLoadAdapter().processGotPlt(this, monitor);
|
||||||
|
@ -469,17 +469,19 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// process dynamic entry points
|
// process dynamic entry points
|
||||||
createDynamicEntryPoints(ElfDynamicType.DT_INIT, null, monitor);
|
createDynamicEntryPoints(ElfDynamicType.DT_INIT, null, "_INIT_", monitor);
|
||||||
createDynamicEntryPoints(ElfDynamicType.DT_INIT_ARRAY, ElfDynamicType.DT_INIT_ARRAYSZ,
|
createDynamicEntryPoints(ElfDynamicType.DT_INIT_ARRAY, ElfDynamicType.DT_INIT_ARRAYSZ,
|
||||||
monitor);
|
"_INIT_", monitor);
|
||||||
createDynamicEntryPoints(ElfDynamicType.DT_FINI, null, monitor);
|
createDynamicEntryPoints(ElfDynamicType.DT_PREINIT_ARRAY, ElfDynamicType.DT_PREINIT_ARRAYSZ,
|
||||||
|
"_PREINIT_", monitor);
|
||||||
|
createDynamicEntryPoints(ElfDynamicType.DT_FINI, null, "_FINI_", monitor);
|
||||||
createDynamicEntryPoints(ElfDynamicType.DT_FINI_ARRAY, ElfDynamicType.DT_FINI_ARRAYSZ,
|
createDynamicEntryPoints(ElfDynamicType.DT_FINI_ARRAY, ElfDynamicType.DT_FINI_ARRAYSZ,
|
||||||
monitor);
|
"_FINI_", monitor);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDynamicEntryPoints(ElfDynamicType dynamicEntryType,
|
private void createDynamicEntryPoints(ElfDynamicType dynamicEntryType,
|
||||||
ElfDynamicType entryArraySizeType, TaskMonitor monitor) {
|
ElfDynamicType entryArraySizeType, String baseName, TaskMonitor monitor) {
|
||||||
|
|
||||||
ElfDynamicTable dynamicTable = elf.getDynamicTable();
|
ElfDynamicTable dynamicTable = elf.getDynamicTable();
|
||||||
if (dynamicTable == null) {
|
if (dynamicTable == null) {
|
||||||
|
@ -502,8 +504,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||||
long arraySize = dynamicTable.getDynamicValue(entryArraySizeType);
|
long arraySize = dynamicTable.getDynamicValue(entryArraySizeType);
|
||||||
long elementCount = arraySize / dt.getLength();
|
long elementCount = arraySize / dt.getLength();
|
||||||
|
|
||||||
String baseName = dynamicEntryType.name.startsWith("DT_INIT") ? "_INIT_" : "_FINI_";
|
|
||||||
|
|
||||||
for (int i = 0; i < elementCount; i++) {
|
for (int i = 0; i < elementCount; i++) {
|
||||||
Address addr = entryArrayAddr.add(i * dt.getLength());
|
Address addr = entryArrayAddr.add(i * dt.getLength());
|
||||||
Data data = createData(addr, dt);
|
Data data = createData(addr, dt);
|
||||||
|
@ -512,6 +512,9 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||||
}
|
}
|
||||||
Scalar value = (Scalar) data.getValue();
|
Scalar value = (Scalar) data.getValue();
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
if (i != 0 && value.getValue() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
long funcAddrOffset = elf.adjustAddressForPrelink(value.getValue());
|
long funcAddrOffset = elf.adjustAddressForPrelink(value.getValue());
|
||||||
Address funcAddr = createEntryFunction(baseName + i, funcAddrOffset, monitor);
|
Address funcAddr = createEntryFunction(baseName + i, funcAddrOffset, monitor);
|
||||||
if (funcAddr != null) {
|
if (funcAddr != null) {
|
||||||
|
|
|
@ -64,7 +64,6 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
|
||||||
* their data context. A null value is acceptable to indicate that a memory
|
* their data context. A null value is acceptable to indicate that a memory
|
||||||
* context is not available. DataTypes that need a context will return -1
|
* context is not available. DataTypes that need a context will return -1
|
||||||
* if the context is null.
|
* if the context is null.
|
||||||
|
|
||||||
* @return the number of components that make up this data prototype
|
* @return the number of components that make up this data prototype
|
||||||
* - if this is an Array, return the number of elements in the array.
|
* - if this is an Array, return the number of elements in the array.
|
||||||
* - if this datatype is a subcomponent of another datatype and it
|
* - if this datatype is a subcomponent of another datatype and it
|
||||||
|
@ -78,7 +77,7 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataTypeComponent[] getComps(MemBuffer buf) {
|
protected DataTypeComponent[] getComps(MemBuffer buf) {
|
||||||
Address addr = buf.getAddress();
|
Address addr = buf.getAddress();
|
||||||
DataTypeComponent[] comps = map.get(addr);
|
DataTypeComponent[] comps = map.get(addr);
|
||||||
if (comps == null) {
|
if (comps == null) {
|
||||||
|
@ -150,6 +149,12 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all dynamic components associated with the specified MemBuffer
|
||||||
|
* @param buf memory buffer positioned at start of data type instance
|
||||||
|
* @return all components or null if memory data is not valid for this
|
||||||
|
* data type.
|
||||||
|
*/
|
||||||
protected abstract DataTypeComponent[] getAllComponents(MemBuffer buf);
|
protected abstract DataTypeComponent[] getAllComponents(MemBuffer buf);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue