From d68f3697e388732faced52063cda7fca3881ec9a Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Fri, 22 Nov 2019 16:38:14 -0500 Subject: [PATCH] GT-3320 changed manner in which Android ELF APS2 relocation table is represented within listing. Corrected Android relocation handling when sections are not present. --- .../app/util/bin/MemBufferByteProvider.java | 157 ++++++++++++++ .../format/elf/AndroidElfRelocationData.java | 89 ++++++++ .../format/elf/AndroidElfRelocationGroup.java | 195 ++++++++++++++++++ .../elf/AndroidElfRelocationOffset.java | 133 ++++++++++++ .../AndroidElfRelocationTableDataType.java | 169 +++++++++++++++ .../AndroidPackedRelocationTableDataType.java | 96 --------- .../util/bin/format/elf/ElfDynamicType.java | 48 +++-- .../app/util/bin/format/elf/ElfHeader.java | 101 +++++---- .../util/bin/format/elf/ElfRelocation.java | 46 ++--- .../bin/format/elf/ElfRelocationTable.java | 154 ++++++-------- .../ghidra/app/util/opinion/ElfLoader.java | 10 +- .../app/util/opinion/ElfProgramBuilder.java | 19 +- .../program/model/data/DynamicDataType.java | 9 +- 13 files changed, 924 insertions(+), 302 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemBufferByteProvider.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationData.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationGroup.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationOffset.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidElfRelocationTableDataType.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/AndroidPackedRelocationTableDataType.java 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 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 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