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:
ghidra1 2019-11-22 16:38:14 -05:00
parent 3f6ca95996
commit d68f3697e3
13 changed files with 924 additions and 302 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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