diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemBufferByteProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemBufferByteProvider.java
new file mode 100644
index 0000000000..c8f50af18e
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemBufferByteProvider.java
@@ -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;
+
+/**
+ * MemBufferByteProvider
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
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationData.java
new file mode 100644
index 0000000000..3100ec816f
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationData.java
@@ -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;
+
+/**
+ * AndroidElfRelocationData
provides a dynamic LEB128 data
+ * component for packed Android ELF Relocation Table.
+ * See {@link AndroidElfRelocationTableDataType}.
+ *
+ * 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;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationGroup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationGroup.java
new file mode 100644
index 0000000000..fc5f3e7b51
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationGroup.java
@@ -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;
+
+/**
+ * AndroidElfRelocationGroup
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 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
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationOffset.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationOffset.java
new file mode 100644
index 0000000000..48949851b8
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationOffset.java
@@ -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;
+
+/**
+ * AndroidElfRelocationOffset
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.
+ *
+ * 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;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationTableDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationTableDataType.java
new file mode 100644
index 0000000000..1b4b37bc19
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationTableDataType.java
@@ -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;
+
+/**
+ * AndroidElfRelocationTableDataType
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 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;
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidPackedRelocationTableDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidPackedRelocationTableDataType.java
deleted file mode 100644
index 67409fb8db..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidPackedRelocationTableDataType.java
+++ /dev/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;
- }
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDynamicType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDynamicType.java
index 4d846ec0f4..849d391b70 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDynamicType.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDynamicType.java
@@ -90,26 +90,35 @@ public class ElfDynamicType {
"Size in bytes of DT_FINI_ARRAY", ElfDynamicValueType.VALUE);
public static ElfDynamicType DT_RUNPATH = addDefaultDynamicType(29, "DT_RUNPATH",
"Library search path (string ref)", ElfDynamicValueType.STRING);
+ // see DF_ constants for flag definitions
public static ElfDynamicType DT_FLAGS = addDefaultDynamicType(30, "DT_FLAGS",
"Flags for the object being loaded", ElfDynamicValueType.VALUE);
- 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 final int DF_ORIGIN = 0x1; // $ORIGIN processing required
+ public static final int DF_SYMBOLIC = 0x2; // Symbolic symbol resolution required
+ 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,
"DT_PREINIT_ARRAYSZ", "Size in bytes of DT_PREINIT_ARRAY", ElfDynamicValueType.VALUE);
// OS-specific range: 0x6000000d - 0x6ffff000
-
- public static ElfDynamicType DT_ANDROID_REL =
- addDefaultDynamicType(0x6000000F, "DT_ANDROID_REL", "Address of Rel relocs", ElfDynamicValueType.ADDRESS);
- public static ElfDynamicType DT_ANDROID_RELSZ = addDefaultDynamicType(0x60000010, "DT_ANDROID_RELSZ",
- "Total size of Rel relocs", ElfDynamicValueType.VALUE);
- public static ElfDynamicType DT_ANDROID_RELA =
- addDefaultDynamicType(0x60000011, "DT_ANDROID_RELA", "Address of Rela relocs", ElfDynamicValueType.ADDRESS);
- public static ElfDynamicType DT_ANDROID_RELASZ = addDefaultDynamicType(0x60000012, "DT_ANDROID_RELASZ",
- "Total size of Rela relocs", ElfDynamicValueType.VALUE);
+ public static ElfDynamicType DT_ANDROID_REL = addDefaultDynamicType(0x6000000F,
+ "DT_ANDROID_REL", "Address of Rel relocs", ElfDynamicValueType.ADDRESS);
+ public static ElfDynamicType DT_ANDROID_RELSZ = addDefaultDynamicType(0x60000010,
+ "DT_ANDROID_RELSZ", "Total size of Rel relocs", ElfDynamicValueType.VALUE);
+
+ public static ElfDynamicType DT_ANDROID_RELA = addDefaultDynamicType(0x60000011,
+ "DT_ANDROID_RELA", "Address of Rela relocs", ElfDynamicValueType.ADDRESS);
+ public static ElfDynamicType DT_ANDROID_RELASZ = addDefaultDynamicType(0x60000012,
+ "DT_ANDROID_RELASZ", "Total size of Rela relocs", ElfDynamicValueType.VALUE);
// Value Range (??): 0x6ffffd00 - 0x6ffffdff
@@ -168,8 +177,23 @@ public class ElfDynamicType {
addDefaultDynamicType(0x6ffffff9, "DT_RELACOUNT", "", ElfDynamicValueType.VALUE);
public static ElfDynamicType DT_RELCOUNT =
addDefaultDynamicType(0x6ffffffa, "DT_RELCOUNT", "", ElfDynamicValueType.VALUE);
+
+ // see DF_1_ constants for flag definitions
public static ElfDynamicType DT_FLAGS_1 =
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",
"Address of version definition table", ElfDynamicValueType.ADDRESS);
public static ElfDynamicType DT_VERDEFNUM = addDefaultDynamicType(0x6ffffffd, "DT_VERDEFNUM",
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java
index 8dc703d047..f839b36a07 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java
@@ -23,6 +23,7 @@ import generic.continues.GenericFactory;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
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.ElfLoadAdapter;
import ghidra.program.model.data.*;
@@ -339,13 +340,13 @@ public class ElfHeader implements StructConverter, Writeable {
parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_RELA,
ElfDynamicType.DT_RELAENT, ElfDynamicType.DT_RELASZ, true);
-
+
// Android versions
- parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_ANDROID_REL, ElfDynamicType.DT_RELENT,
+ parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_ANDROID_REL, null,
ElfDynamicType.DT_ANDROID_RELSZ, false);
- parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_ANDROID_RELA,
- ElfDynamicType.DT_RELAENT, ElfDynamicType.DT_ANDROID_RELASZ, true);
+ parseDynamicRelocTable(relocationTableList, ElfDynamicType.DT_ANDROID_RELA, null,
+ ElfDynamicType.DT_ANDROID_RELASZ, true);
parseJMPRelocTable(relocationTableList);
@@ -364,7 +365,9 @@ public class ElfHeader implements StructConverter, Writeable {
try {
int sectionHeaderType = section.getType();
if (sectionHeaderType == ElfSectionHeaderConstants.SHT_REL ||
- sectionHeaderType == ElfSectionHeaderConstants.SHT_RELA) {
+ sectionHeaderType == ElfSectionHeaderConstants.SHT_RELA ||
+ sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_REL ||
+ sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_RELA) {
for (ElfRelocationTable relocTable : relocationTableList) {
if (relocTable.getFileOffset() == section.getOffset()) {
@@ -380,55 +383,24 @@ public class ElfHeader implements StructConverter, Writeable {
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;
+ ElfSectionHeader symbolTableSection;
+ if (link == 0) {
+ // dynamic symbol table assumed when link section value is 0
+ symbolTableSection = getSection(ElfSectionHeaderConstants.dot_dynsym);
+ }
+ else {
+ symbolTableSection = getLinkedSection(link,
+ ElfSectionHeaderConstants.SHT_DYNSYM, ElfSectionHeaderConstants.SHT_SYMTAB);
}
- 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) {
-
- 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";
-
- // For some Android binaries, the symbols table section is not referenced by the link value. Instead
- // we can just reference the main dynamic symbol table.
- ElfSectionHeader symbolTableSection = (link != 0)
- ? getLinkedSection(link, ElfSectionHeaderConstants.SHT_DYNSYM, ElfSectionHeaderConstants.SHT_SYMTAB)
- : getSection(ElfSectionHeaderConstants.dot_dynsym);
-
ElfSymbolTable symbolTable = getSymbolTable(symbolTableSection);
-
if (symbolTable == null) {
- throw new NotFoundException("Referenced symbol section not found.");
+ throw new NotFoundException("Referenced relocation symbol section not found.");
}
- boolean addendTypeReloc = (sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_RELA);
+ boolean addendTypeReloc =
+ (sectionHeaderType == ElfSectionHeaderConstants.SHT_RELA ||
+ sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_RELA);
Msg.debug(this,
"Elf relocation table section " + section.getNameAsString() +
@@ -439,9 +411,18 @@ public class ElfHeader implements StructConverter, Writeable {
return;
}
- relocationTableList.add(ElfRelocationTable.createAndroidElfRelocationTable(reader, this,
- section, section.getOffset(), section.getAddress(), section.getSize(),
- section.getEntrySize(), addendTypeReloc, symbolTable, sectionToBeRelocated));
+ ElfRelocationTable.TableFormat format = TableFormat.DEFAULT;
+ if (sectionHeaderType == ElfSectionHeaderConstants.SHT_ANDROID_REL ||
+ 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) {
@@ -513,13 +494,20 @@ public class ElfHeader implements StructConverter, Writeable {
}
long relocTableOffset = relocTableLoadHeader.getOffset(relocTableAddr);
- long tableEntrySize = dynamicTable.getDynamicValue(relocEntrySizeType);
+ long tableEntrySize =
+ relocEntrySizeType != null ? dynamicTable.getDynamicValue(relocEntrySizeType) : -1;
long tableSize = dynamicTable.getDynamicValue(relocTableSizeType);
- relocationTableList.add(ElfRelocationTable.createElfRelocationTable(reader, this, null,
- relocTableOffset, relocTableAddr, tableSize, tableEntrySize, addendTypeReloc,
- dynamicSymbolTable, null));
+ ElfRelocationTable.TableFormat format = TableFormat.DEFAULT;
+ if (relocTableAddrType == ElfDynamicType.DT_ANDROID_REL ||
+ 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) {
// 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
*/
public ElfSymbolTable getSymbolTable(ElfSectionHeader symbolTableSection) {
+ if (symbolTableSection == null) {
+ return null;
+ }
for (int i = 0; i < symbolTables.length; i++) {
if (symbolTables[i].getFileOffset() == symbolTableSection.getOffset()) {
return symbolTables[i];
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfRelocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfRelocation.java
index 6b53dba372..3ad6e4a960 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfRelocation.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfRelocation.java
@@ -101,30 +101,7 @@ public class ElfRelocation implements ByteArrayConverter, StructConverter {
/**
* GenericFactory construction and initialization method for a ELF representative
- * relocation entry (entry data will be 0)
- * @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
+ * relocation entry
* @param reader binary reader positioned at start of relocation entry.
* @param elfHeader ELF header
* @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_addend The addend for the entry
* @return ELF relocation object
- * @throws IOException
*/
- static ElfRelocation createElfRelocation(FactoryBundledWithBinaryReader reader,
- ElfHeader elfHeader, int relocationIndex, boolean withAddend, long r_offset, long r_info, long r_addend) throws IOException {
+ static ElfRelocation createElfRelocation(GenericFactory factory, ElfHeader elfHeader,
+ int relocationIndex, boolean withAddend, long r_offset, long r_info, long r_addend) {
Class extends ElfRelocation> elfRelocationClass = getElfRelocationClass(elfHeader);
- ElfRelocation elfRelocation =
- (ElfRelocation) reader.getFactory().create(elfRelocationClass);
- elfRelocation.initElfRelocation(elfHeader, relocationIndex, withAddend, r_offset, r_info, r_addend);
+ ElfRelocation elfRelocation = (ElfRelocation) factory.create(elfRelocationClass);
+ try {
+ 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;
}
@@ -193,12 +175,12 @@ public class ElfRelocation implements ByteArrayConverter, StructConverter {
* @param r_addend The addend for the entry
* @throws IOException
*/
- protected void initElfRelocation(ElfHeader elfHeader, int relocationTableIndex,
+ protected void initElfRelocation(ElfHeader elfHeader, int relocationTableIndex,
boolean withAddend, long r_offset, long r_info, long r_addend) throws IOException {
this.is32bit = elfHeader.is32Bit();
this.relocationIndex = relocationTableIndex;
this.hasAddend = withAddend;
-
+
if (is32bit) {
this.r_offset = r_offset & Conv.INT_MASK;
this.r_info = r_info & Conv.INT_MASK;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfRelocationTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfRelocationTable.java
index c02e36a56b..759811d56c 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfRelocationTable.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfRelocationTable.java
@@ -33,6 +33,12 @@ import ghidra.util.Msg;
*/
public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
+ public enum TableFormat {
+ DEFAULT, ANDROID;
+ }
+
+ private TableFormat format;
+
private ElfSectionHeader sectionToBeRelocated;
private ElfSymbolTable symbolTable;
@@ -61,28 +67,19 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
* @param addendTypeReloc true if addend type relocation table
* @param symbolTable associated symbol table
* @param sectionToBeRelocated or null for dynamic relocation table
+ * @param format table format
* @return Elf relocation table object
* @throws IOException
*/
static ElfRelocationTable createElfRelocationTable(FactoryBundledWithBinaryReader reader,
ElfHeader header, ElfSectionHeader relocTableSection, long fileOffset, long addrOffset,
long length, long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
- ElfSectionHeader sectionToBeRelocated) throws IOException {
+ ElfSectionHeader sectionToBeRelocated, TableFormat format) throws IOException {
ElfRelocationTable elfRelocationTable =
(ElfRelocationTable) reader.getFactory().create(ElfRelocationTable.class);
elfRelocationTable.initElfRelocationTable(reader, header, relocTableSection, fileOffset,
- addrOffset, length, entrySize, addendTypeReloc, symbolTable, sectionToBeRelocated);
- return elfRelocationTable;
- }
-
- 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);
+ addrOffset, length, entrySize, addendTypeReloc, symbolTable, sectionToBeRelocated,
+ format);
return elfRelocationTable;
}
@@ -95,7 +92,7 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
private void initElfRelocationTable(FactoryBundledWithBinaryReader reader, ElfHeader header,
ElfSectionHeader relocTableSection, long fileOffset, long addrOffset, long length,
long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
- ElfSectionHeader sectionToBeRelocated) throws IOException {
+ ElfSectionHeader sectionToBeRelocated, TableFormat format) throws IOException {
this.relocTableSection = relocTableSection;
this.fileOffset = fileOffset;
@@ -105,6 +102,7 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
this.addendTypeReloc = addendTypeReloc;
this.elfHeader = header;
this.factory = reader.getFactory();
+ this.format = format;
this.sectionToBeRelocated = sectionToBeRelocated;
this.symbolTable = symbolTable;
@@ -112,12 +110,12 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
long ptr = reader.getPointerIndex();
reader.setPointerIndex(fileOffset);
- List relocList = new ArrayList();
-
- int nRelocs = (int) (length / entrySize);
- for (int relocationIndex = 0; relocationIndex < nRelocs; ++relocationIndex) {
- relocList.add(ElfRelocation.createElfRelocation(reader, header, relocationIndex,
- addendTypeReloc));
+ List relocList;
+ if (format == TableFormat.ANDROID) {
+ relocList = parseAndroidRelocations(reader);
+ }
+ else {
+ relocList = parseStandardRelocations(reader);
}
reader.setPointerIndex(ptr);
@@ -126,79 +124,65 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
relocList.toArray(relocs);
}
- private void initAndroidElfRelocationTable(FactoryBundledWithBinaryReader reader, ElfHeader header,
- ElfSectionHeader relocTableSection, long fileOffset, long addrOffset, long length,
- long entrySize, boolean addendTypeReloc, ElfSymbolTable symbolTable,
- ElfSectionHeader sectionToBeRelocated) throws IOException {
+ private List parseStandardRelocations(FactoryBundledWithBinaryReader reader)
+ throws IOException {
- this.relocTableSection = relocTableSection;
- this.fileOffset = fileOffset;
- this.addrOffset = addrOffset;
- this.length = length;
- this.entrySize = entrySize;
- this.addendTypeReloc = addendTypeReloc;
- this.elfHeader = header;
- this.factory = reader.getFactory();
+ List relocations = new ArrayList<>();
+ int nRelocs = (int) (length / entrySize);
+ for (int relocationIndex = 0; relocationIndex < nRelocs; ++relocationIndex) {
+ relocations.add(ElfRelocation.createElfRelocation(reader, elfHeader, relocationIndex,
+ addendTypeReloc));
+ }
+ return relocations;
+ }
- this.sectionToBeRelocated = sectionToBeRelocated;
- this.symbolTable = symbolTable;
-
- long ptr = reader.getPointerIndex();
- reader.setPointerIndex(fileOffset);
+ private List parseAndroidRelocations(FactoryBundledWithBinaryReader reader)
+ throws IOException {
String identifier = reader.readNextAsciiString(4);
if (!"APS2".equals(identifier)) {
- Msg.error(this, "Invalid indentifier value for Android packed relocation table: " + identifier);
- return;
+ throw new IOException("Unsupported Android relocation table format");
}
- List relocList = parseAndroidRelocations(reader, header);
-
- reader.setPointerIndex(ptr);
-
- relocs = new ElfRelocation[relocList.size()];
- relocList.toArray(relocs);
- }
-
- private List parseAndroidRelocations(FactoryBundledWithBinaryReader reader, ElfHeader header) {
List relocations = new ArrayList<>();
-
- 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;
-
+
+ try {
int relocationIndex = 0;
long remainingRelocations = LEB128.decode(reader, true);
long offset = LEB128.decode(reader, true);
long addend = 0;
-
+
while (remainingRelocations > 0) {
+
long groupSize = LEB128.decode(reader, true);
-
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;
}
-
+
long groupFlags = LEB128.decode(reader, true);
- 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;
-
+ boolean groupedByInfo =
+ (groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
+ boolean groupedByDelta = (groupFlags &
+ 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 groupRInfo = groupedByInfo ? LEB128.decode(reader, true) : 0;
-
+
if (groupedByAddend && groupHasAddend) {
addend += LEB128.decode(reader, true);
}
-
+
for (int i = 0; i < groupSize; i++) {
offset += groupedByDelta ? groupOffsetDelta : LEB128.decode(reader, true);
+
long info = groupedByInfo ? groupRInfo : LEB128.decode(reader, true);
-
+
long rAddend = 0;
if (groupHasAddend) {
if (!groupedByAddend) {
@@ -206,27 +190,22 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
}
rAddend = addend;
}
-
- try {
- relocations.add(ElfRelocation.createElfRelocation(reader, header, relocationIndex, addendTypeReloc,
- offset, info, rAddend));
- } catch (IOException e) {
- Msg.error(this, "Error creating relocation entry");
- }
-
- relocationIndex++;
+
+ relocations.add(ElfRelocation.createElfRelocation(reader.getFactory(),
+ elfHeader, relocationIndex++, addendTypeReloc, offset, info, rAddend));
}
-
+
if (!groupHasAddend) {
addend = 0;
}
-
+
remainingRelocations -= groupSize;
}
- } catch (IOException e) {
+ }
+ catch (IOException e) {
Msg.error(this, "Error reading relocations.", e);
}
-
+
return relocations;
}
@@ -267,9 +246,6 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
return symbolTable;
}
- /**
- * @see ghidra.app.util.bin.ByteArrayConverter#toBytes(ghidra.util.DataConverter)
- */
@Override
public byte[] toBytes(DataConverter dc) {
byte[] bytes = new byte[relocs.length * relocs[0].sizeof()];
@@ -311,18 +287,14 @@ public class ElfRelocationTable implements ElfFileSection, ByteArrayConverter {
return (int) entrySize;
}
- /**
- * @see ghidra.app.util.bin.StructConverter#toDataType()
- */
@Override
public DataType toDataType() {
- if (relocTableSection.getType() == ElfSectionHeaderConstants.SHT_ANDROID_REL ||
- relocTableSection.getType() == ElfSectionHeaderConstants.SHT_ANDROID_RELA ) {
- return new AndroidPackedRelocationTableDataType(relocs, (int) length);
+ if (format == TableFormat.ANDROID) {
+ return new AndroidElfRelocationTableDataType();
}
-
+
ElfRelocation relocationRepresentative =
- ElfRelocation.createElfRelocation(factory, elfHeader, -1, addendTypeReloc);
+ ElfRelocation.createElfRelocation(factory, elfHeader, -1, addendTypeReloc, 0, 0, 0);
DataType relocEntryDataType = relocationRepresentative.toDataType();
return new ArrayDataType(relocEntryDataType, (int) (length / entrySize), (int) entrySize);
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoader.java
index 65bc926587..989dbe1edb 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoader.java
@@ -90,7 +90,8 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
}
@Override
- public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List