diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java index 7081714925..70647aa43c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java @@ -34,7 +34,6 @@ import ghidra.app.util.bin.format.elf.extend.ElfLoadAdapter; import ghidra.app.util.bin.format.elf.relocation.*; import ghidra.app.util.importer.MessageLog; import ghidra.framework.options.Options; -import ghidra.framework.store.LockException; import ghidra.program.database.mem.FileBytes; import ghidra.program.database.register.AddressRangeObjectMap; import ghidra.program.model.address.*; @@ -136,7 +135,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { resolve(monitor); if (elf.e_shnum() == 0) { - // create/expand segments to their fullsize if not sections are defined + // create/expand segments to their fullsize if no sections are defined expandProgramHeaderBlocks(monitor); } @@ -146,8 +145,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { return; } - pruneDiscardableBlocks(); - markupElfHeader(monitor); markupProgramHeaders(monitor); markupSectionHeaders(monitor); @@ -183,10 +180,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { } } - private void adjustSegmentAndSectionFileAllocations(ByteProvider byteProvider) + private void adjustSegmentAndSectionFileAllocations( + ByteProvider byteProvider) throws IOException { - // Identify file ranges not consumed by segments and sections + // Identify file ranges not allocated to segments or sections RangeMap fileMap = new RangeMap(); fileMap.paintRange(0, byteProvider.length() - 1, -1); // -1: unallocated @@ -220,17 +218,17 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { // Ignore header regions which will always be allocated to blocks int elfHeaderSize = elf.toDataType().getLength(); - fileMap.paintRange(0, elfHeaderSize - 1, -4); + fileMap.paintRange(0, elfHeaderSize - 1, -4); // -4: header block int programHeaderSize = elf.e_phentsize() * elf.e_phnum(); if (programHeaderSize != 0) { - fileMap.paintRange(elf.e_phoff(), elf.e_phoff() + programHeaderSize - 1, -4); + fileMap.paintRange(elf.e_phoff(), elf.e_phoff() + programHeaderSize - 1, -4); // -4: header block } int sectionHeaderSize = elf.e_shentsize() * elf.e_shnum(); if (sectionHeaderSize != 0) { - fileMap.paintRange(elf.e_shoff(), elf.e_shoff() + sectionHeaderSize - 1, -4); + fileMap.paintRange(elf.e_shoff(), elf.e_shoff() + sectionHeaderSize - 1, -4); // -4: header block } - // Unused file ranges - add as OTHER blocks + // Add unallocated non-zero file regions as OTHER blocks IndexRangeIterator rangeIterator = fileMap.getIndexRangeIterator(0); int unallocatedIndex = 0; while (rangeIterator.hasNext()) { @@ -239,10 +237,18 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { if (value != -1) { continue; } + + long start = range.getStart(); + long length = range.getEnd() - start + 1; + + if (isZeroFilledFileRegion(byteProvider, start, length)) { + continue; + } + String name = UNALLOCATED_NAME_PREFIX + unallocatedIndex++; try { - addInitializedMemorySection(null, range.getStart(), - range.getEnd() - range.getStart() + 1, AddressSpace.OTHER_SPACE.getMinAddress(), + addInitializedMemorySection(null, start, length, + AddressSpace.OTHER_SPACE.getMinAddress(), name, false, false, false, null, false, false); } catch (AddressOverflowException e) { @@ -251,45 +257,19 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { } } - private void pruneDiscardableBlocks() { - try { - for (MemoryBlock block : memory.getBlocks()) { - long size = block.getSize(); - // prune any zero-filled unallocated block or segment blocks smaller than DISCARDABLE_SEGMENT_SIZE - if (!block.getName().startsWith(UNALLOCATED_NAME_PREFIX)) { - // Don't prune segments when sections are absent - if (elf.e_shnum() == 0 || size > DISCARDABLE_SEGMENT_SIZE || - !block.getName().startsWith(SEGMENT_NAME_PREFIX)) { - continue; - } - } - if (isZeroFilledBlock(block)) { - Msg.debug(this, - "Removing discardable alignment/filler segment at " + block.getStart()); - memory.removeBlock(block, TaskMonitor.DUMMY); - } - } + private boolean isZeroFilledFileRegion(ByteProvider byteProvider, long start, long length) + throws IOException { + int bufSize = 16 * 1024; + if (length < bufSize) { + bufSize = (int) length; } - catch (LockException | MemoryAccessException e) { - throw new AssertException(e); // should never happen - } - } - - private boolean isZeroFilledBlock(MemoryBlock block) throws MemoryAccessException { - int bufSize = 8 * 1024; - long blockSize = block.getSize(); - if (blockSize < bufSize) { - bufSize = (int) blockSize; - } - byte[] bytes = new byte[bufSize]; - Address addr = block.getStart(); - long cnt = 0; - while (cnt < blockSize) { - int readLen = block.getBytes(addr.add(cnt), bytes, 0, bufSize); - if (readLen <= 0 || !isZeroedArray(bytes, readLen)) { + long remaining = length; + while (remaining > 0) { + byte[] bytes = byteProvider.readBytes(start, Math.min(remaining, bufSize)); + if (!isZeroedArray(bytes, bytes.length)) { return false; } - cnt += readLen; + remaining -= bytes.length; } return true; } @@ -303,6 +283,29 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { return true; } + private boolean isDiscardableFillerSegment(MemoryLoadable loadable, String blockName, + Address start, long fileOffset, long length) throws IOException { + if (elf.e_shnum() == 0 || elf.e_phnum() == 0) { + return false; // only prune if both sections and program headers are present + } + if (length > DISCARDABLE_SEGMENT_SIZE || !blockName.startsWith(SEGMENT_NAME_PREFIX)) { + return false; + } + + if (elf.getLoadAdapter().hasFilteredLoadInputStream(this, loadable, start)) { + // block is unable to map directly to file bytes - read from filtered input stream + try (InputStream dataInput = + getInitializedBlockInputStream(loadable, start, fileOffset, length)) { + byte[] bytes = new byte[(int) length]; + return dataInput.read(bytes) == bytes.length && isZeroedArray(bytes, bytes.length); + } + } + + byte[] bytes = new byte[(int) length]; + return fileBytes.getModifiedBytes(fileOffset, bytes) == bytes.length && + isZeroedArray(bytes, bytes.length); + } + @Override public MessageLog getLog() { return log; @@ -3220,11 +3223,17 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { boolean w, boolean x, TaskMonitor monitor) throws IOException, AddressOverflowException, CancelledException { + long revisedLength = checkBlockLimit(name, dataLength, true); + + if (isDiscardableFillerSegment(loadable, name, start, fileOffset, dataLength)) { + Msg.debug(this, + "Discarding " + dataLength + "-byte alignment/filler " + name + " at " + start); + return null; + } + // TODO: MemoryBlockUtil poorly and inconsistently handles duplicate name errors (can throw RuntimeException). // Are we immune from such errors? If not, how should they be handled? - long revisedLength = checkBlockLimit(name, dataLength, true); - if (start.isNonLoadedMemoryAddress()) { r = false; w = false; @@ -3245,19 +3254,32 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { blockComment += " (section truncated)"; } - if (elf.getLoadAdapter().hasFilteredLoadInputStream(this, loadable, start)) { - // block is unable to map directly to file bytes - load from input stream - try (InputStream dataInput = - getInitializedBlockInputStream(loadable, start, fileOffset, revisedLength)) { - return MemoryBlockUtils.createInitializedBlock(program, isOverlay, name, start, - dataInput, revisedLength, blockComment, BLOCK_SOURCE_NAME, r, w, x, log, - monitor); + MemoryBlock block = null; + try { + if (elf.getLoadAdapter().hasFilteredLoadInputStream(this, loadable, start)) { + // block is unable to map directly to file bytes - load from input stream + try (InputStream dataInput = + getInitializedBlockInputStream(loadable, start, fileOffset, revisedLength)) { + block = MemoryBlockUtils.createInitializedBlock(program, isOverlay, name, start, + dataInput, revisedLength, blockComment, BLOCK_SOURCE_NAME, r, w, x, log, + monitor); + } + } + else { + // create block using direct mapping to file bytes + block = MemoryBlockUtils.createInitializedBlock(program, isOverlay, name, start, + fileBytes, fileOffset, revisedLength, blockComment, BLOCK_SOURCE_NAME, r, w, x, + log); } } - - // create block using direct mapping to file bytes - return MemoryBlockUtils.createInitializedBlock(program, isOverlay, name, start, fileBytes, - fileOffset, revisedLength, blockComment, BLOCK_SOURCE_NAME, r, w, x, log); + finally { + if (block == null) { + Address end = start.addNoWrap(revisedLength - 1); + Msg.error(this, "Unexpected ELF memory bock load conflict when creating '" + name + + "' at " + start.toString(true) + "-" + end.toString(true)); + } + } + return block; } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MemorySectionResolver.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MemorySectionResolver.java index b030036e87..c64c04105b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MemorySectionResolver.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MemorySectionResolver.java @@ -368,18 +368,12 @@ public abstract class MemorySectionResolver { maxAddr = block.getEnd(); } else { - // block may be null due to unexpected conflict + // block may be null due to unexpected conflict - allow to continue block = createInitializedBlock(section.key, false, blockName, address, fileOffset, rangeSize, section.getComment(), section.isReadable(), section.isWritable(), section.isExecute(), monitor); minAddr = address; maxAddr = address.addNoWrap(rangeSize - 1); - if (block == null) { - // This is a bug but allow load to continue by not referring to block below - Msg.error(this, - "Unexpected ELF memory bock load conflict when creating '" + blockName + - "' at " + minAddr.toString(true) + "-" + maxAddr.toString(true)); - } } if (fileLoadRangeMap != null) { long chunkFileOffset = section.getFileOffset() + sectionByteOffset; diff --git a/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/ElfSH4RelocationFixupHandler.java b/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/ElfSH4RelocationFixupHandler.java new file mode 100644 index 0000000000..d7357ecd72 --- /dev/null +++ b/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/ElfSH4RelocationFixupHandler.java @@ -0,0 +1,61 @@ +/* ### + * 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.relocation; + +import ghidra.app.plugin.core.reloc.RelocationFixupHandler; +import ghidra.app.util.opinion.ElfLoader; +import ghidra.program.model.address.Address; +import ghidra.program.model.lang.Language; +import ghidra.program.model.lang.Processor; +import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.reloc.Relocation; +import ghidra.program.model.util.CodeUnitInsertionException; + +public class ElfSH4RelocationFixupHandler extends RelocationFixupHandler { + + @Override + public boolean processRelocation(Program program, Relocation relocation, Address oldImageBase, + Address newImageBase) throws MemoryAccessException, CodeUnitInsertionException { + + switch (relocation.getType()) { + case SH_ElfRelocationConstants.R_SH_DIR32: + case SH_ElfRelocationConstants.R_SH_REL32: + return process32BitRelocation(program, relocation, oldImageBase, newImageBase); + +// case SH_ElfRelocationConstants.R_SH_DIR8WPN: +// case SH_ElfRelocationConstants.R_SH_DIR8WPZ: +// case SH_ElfRelocationConstants.R_SH_IND12W: +// case SH_ElfRelocationConstants.R_SH_DIR8WPL: + + } + return false; + } + + @Override + public boolean handlesProgram(Program program) { + if (!ElfLoader.ELF_NAME.equals(program.getExecutableFormat())) { + return false; + } + Language language = program.getLanguage(); + if (language.getLanguageDescription().getSize() != 32) { + return false; + } + Processor processor = language.getProcessor(); + return ("SuperH4".equals(processor.toString()) || "SuperH".equals(processor.toString())); + } + +} diff --git a/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/SH_ElfRelocationConstants.java b/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/SH_ElfRelocationConstants.java new file mode 100644 index 0000000000..27e7f89ecd --- /dev/null +++ b/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/SH_ElfRelocationConstants.java @@ -0,0 +1,134 @@ +/* ### + * 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.relocation; + +public class SH_ElfRelocationConstants { + + public static final int R_SH_NONE = 0; // No operation needed + public static final int R_SH_DIR32 = 1; // (S + A) */ + public static final int R_SH_REL32 = 2; // (S + A) - P + public static final int R_SH_DIR8WPN = 3; // 8-bit PC relative branch divided by 2 : (((S + A) - P) >> 1) & 0xff + public static final int R_SH_IND12W = 4; // 12-bit PC relative branch divided by 2 : (((S + A) - P) >> 1) & 0xfff + public static final int R_SH_DIR8WPL = 5; // 8-bit PC unsigned-relative branch divided by 4 : (((S + A) - P) >> 2) & 0xff + public static final int R_SH_DIR8WPZ = 6; // 8-bit PC unsigned-relative branch divided by 2 : (((S + A) - P) >> 1) & 0xff + public static final int R_SH_DIR8BP = 7; + public static final int R_SH_DIR8W = 8; + public static final int R_SH_DIR8L = 9; + + // Relocation numbering in this file corresponds to GNU binutils and + // values below this point may differ significantly from those specified + // for other uses (e.g., see https://android.googlesource.com/platform/external/elfutils/+/android-4.1.2_r1/libelf/elf.h ) + + public static final int R_SH_LOOP_START = 10; + public static final int R_SH_LOOP_END = 11; + + public static final int R_SH_GNU_VTINHERIT = 22; + public static final int R_SH_GNU_VTENTRY = 23; + public static final int R_SH_SWITCH8 = 24; + + public static final int R_SH_SWITCH16 = 25; + public static final int R_SH_SWITCH32 = 26; + public static final int R_SH_USES = 27; + public static final int R_SH_COUNT = 28; + public static final int R_SH_ALIGN = 29; + public static final int R_SH_CODE = 30; + public static final int R_SH_DATA = 31; + public static final int R_SH_LABEL = 32; + + public static final int R_SH_DIR16 = 33; + public static final int R_SH_DIR8 = 34; + public static final int R_SH_DIR8UL = 35; + public static final int R_SH_DIR8UW = 36; + public static final int R_SH_DIR8U = 37; + public static final int R_SH_DIR8SW = 38; + public static final int R_SH_DIR8S = 39; + public static final int R_SH_DIR4UL = 40; + public static final int R_SH_DIR4UW = 41; + public static final int R_SH_DIR4U = 42; + public static final int R_SH_PSHA = 43; + public static final int R_SH_PSHL = 44; + public static final int R_SH_DIR5U = 45; + public static final int R_SH_DIR6U = 46; + public static final int R_SH_DIR6S = 47; + public static final int R_SH_DIR10S = 48; + public static final int R_SH_DIR10SW = 49; + public static final int R_SH_DIR10SL = 50; + public static final int R_SH_DIR10SQ = 51; + + public static final int R_SH_DIR16S = 53; + + public static final int R_SH_TLS_GD_32 = 144; + public static final int R_SH_TLS_LD_32 = 145; + public static final int R_SH_TLS_LDO_32 = 146; + public static final int R_SH_TLS_IE_32 = 147; + public static final int R_SH_TLS_LE_32 = 148; + public static final int R_SH_TLS_DTPMOD32 = 149; + public static final int R_SH_TLS_DTPOFF32 = 150; + public static final int R_SH_TLS_TPOFF32 = 151; + + public static final int R_SH_GOT32 = 160; + public static final int R_SH_PLT32 = 161; + public static final int R_SH_COPY = 162; + public static final int R_SH_GLOB_DAT = 163; + public static final int R_SH_JMP_SLOT = 164; + public static final int R_SH_RELATIVE = 165; + public static final int R_SH_GOTOFF = 166; + public static final int R_SH_GOTPC = 167; + public static final int R_SH_GOTPLT32 = 168; + public static final int R_SH_GOT_LOW16 = 169; + public static final int R_SH_GOT_MEDLOW16 = 170; + public static final int R_SH_GOT_MEDHI16 = 171; + public static final int R_SH_GOT_HI16 = 172; + public static final int R_SH_GOTPLT_LOW16 = 173; + public static final int R_SH_GOTPLT_MEDLOW16 = 174; + public static final int R_SH_GOTPLT_MEDHI16 = 175; + public static final int R_SH_GOTPLT_HI16 = 176; + public static final int R_SH_PLT_LOW16 = 177; + public static final int R_SH_PLT_MEDLOW16 = 178; + public static final int R_SH_PLT_MEDHI16 = 179; + public static final int R_SH_PLT_HI16 = 180; + public static final int R_SH_GOTOFF_LOW16 = 181; + public static final int R_SH_GOTOFF_MEDLOW16 = 182; + public static final int R_SH_GOTOFF_MEDHI16 = 183; + public static final int R_SH_GOTOFF_HI16 = 184; + public static final int R_SH_GOTPC_LOW16 = 185; + public static final int R_SH_GOTPC_MEDLOW16 = 186; + public static final int R_SH_GOTPC_MEDHI16 = 187; + public static final int R_SH_GOTPC_HI16 = 188; + public static final int R_SH_GOT10BY4 = 189; + public static final int R_SH_GOTPLT10BY4 = 190; + public static final int R_SH_GOT10BY8 = 191; + public static final int R_SH_GOTPLT10BY8 = 192; + public static final int R_SH_COPY64 = 193; + public static final int R_SH_GLOB_DAT64 = 194; + public static final int R_SH_JMP_SLOT64 = 195; + public static final int R_SH_RELATIVE64 = 196; + + public static final int R_SH_SHMEDIA_CODE = 242; + public static final int R_SH_PT_16 = 243; + public static final int R_SH_IMMS16 = 244; + public static final int R_SH_IMMU16 = 245; + public static final int R_SH_IMM_LOW16 = 246; + public static final int R_SH_IMM_LOW16_PCREL = 247; + public static final int R_SH_IMM_MEDLOW16 = 248; + public static final int R_SH_IMM_MEDLOW16_PCREL = 249; + public static final int R_SH_IMM_MEDHI16 = 250; + public static final int R_SH_IMM_MEDHI16_PCREL = 251; + public static final int R_SH_IMM_HI16 = 252; + public static final int R_SH_IMM_HI16_PCREL = 253; + public static final int R_SH_64 = 254; + public static final int R_SH_64_PCREL = 255; +} diff --git a/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/SH_ElfRelocationHandler.java b/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/SH_ElfRelocationHandler.java new file mode 100644 index 0000000000..314335d2c0 --- /dev/null +++ b/Ghidra/Processors/SuperH4/src/main/java/ghidra/app/util/bin/format/elf/relocation/SH_ElfRelocationHandler.java @@ -0,0 +1,136 @@ +/* ### + * 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.relocation; + +import ghidra.app.util.bin.format.elf.*; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.Memory; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.util.exception.NotFoundException; + +public class SH_ElfRelocationHandler extends ElfRelocationHandler { + + @Override + public boolean canRelocate(ElfHeader elf) { + return elf.e_machine() == ElfConstants.EM_SH && elf.is32Bit(); + } + + @Override + public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation, + Address relocationAddress) throws MemoryAccessException, NotFoundException { + + ElfHeader elf = elfRelocationContext.getElfHeader(); + if (elf.e_machine() != ElfConstants.EM_SH || !elf.is32Bit()) { + return; + } + + Program program = elfRelocationContext.getProgram(); + + Memory memory = program.getMemory(); + + int type = relocation.getType(); + if (type == SH_ElfRelocationConstants.R_SH_NONE) { + return; + } + int symbolIndex = relocation.getSymbolIndex(); + + int addend = (int) relocation.getAddend(); + + ElfSymbol sym = elfRelocationContext.getSymbol(symbolIndex); + String symbolName = sym.getNameAsString(); + + int offset = (int) relocationAddress.getOffset(); + + Address symbolAddr = elfRelocationContext.getSymbolAddress(sym); + int symbolValue = (int) elfRelocationContext.getSymbolValue(sym); + + int newValue = 0; + + switch (type) { + case SH_ElfRelocationConstants.R_SH_DIR32: { //32 bit absolute relocation + if (elfRelocationContext.extractAddend()) { + addend = memory.getInt(relocationAddress); + } + if (addend != 0 && isUnsupportedExternalRelocation(program, relocationAddress, + symbolAddr, symbolName, addend, elfRelocationContext.getLog())) { + addend = 0; // prefer bad fixup for EXTERNAL over really-bad fixup + } + newValue = symbolValue + addend; + memory.setInt(relocationAddress, newValue); + break; + } + case SH_ElfRelocationConstants.R_SH_REL32: { // 32 bit PC relative relocation + if (elfRelocationContext.extractAddend()) { + addend = memory.getInt(relocationAddress); + } + newValue = (symbolValue + addend) - offset; + memory.setInt(relocationAddress, newValue); + break; + } + case SH_ElfRelocationConstants.R_SH_DIR8WPN: // 8-bit PC relative branch divided by 2 + case SH_ElfRelocationConstants.R_SH_DIR8WPZ: { // 8-bit PC unsigned-relative branch divided by 2 + short oldShortValue = memory.getShort(relocationAddress); + if (elfRelocationContext.extractAddend()) { + addend = oldShortValue & 0xff; + if (type == SH_ElfRelocationConstants.R_SH_DIR8WPN && (addend & 0x80) != 0) { + addend -= 0x100; // sign-extend addend for R_SH_DIR8WPN + } + } + newValue = ((symbolValue + addend) - offset) >> 1; + newValue = (oldShortValue & 0xff00) | (newValue & 0xff); + memory.setShort(relocationAddress, (short) newValue); + break; + } + case SH_ElfRelocationConstants.R_SH_IND12W: { // 12-bit PC relative branch divided by 2 + short oldShortValue = memory.getShort(relocationAddress); + if (elfRelocationContext.extractAddend()) { + addend = oldShortValue & 0xfff; + if ((addend & 0x800) != 0) { + addend -= 0x1000; // sign-extend addend + } + } + newValue = ((symbolValue + addend) - offset) >> 1; + newValue = (oldShortValue & 0xf000) | (newValue & 0xfff); + memory.setShort(relocationAddress, (short) newValue); + break; + } + case SH_ElfRelocationConstants.R_SH_DIR8WPL: { // 8-bit PC unsigned-relative branch divided by 4 + short oldShortValue = memory.getShort(relocationAddress); + if (elfRelocationContext.extractAddend()) { + addend = oldShortValue & 0xff; + } + newValue = ((symbolValue + addend) - offset) >> 2; + newValue = (oldShortValue & 0xff00) | (newValue & 0xff); + memory.setShort(relocationAddress, (short) newValue); + break; + } + + case SH_ElfRelocationConstants.R_SH_COPY: { + markAsWarning(program, relocationAddress, "R_SH_COPY", symbolName, symbolIndex, + "Runtime copy not supported", elfRelocationContext.getLog()); + break; + } + + default: { + markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName, + elfRelocationContext.getLog()); + break; + } + } + } + +}