diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0924f10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +build +dist +bin +.gradle +.project +.classpath +.settings +.antProperties.xml diff --git a/Module.manifest b/Module.manifest new file mode 100644 index 0000000..e69de29 diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..e827d99 --- /dev/null +++ b/build.gradle @@ -0,0 +1,33 @@ +// Builds a Ghidra Extension for a given Ghidra installation. +// +// An absolute path to the Ghidra installation directory must be supplied either by setting the +// GHIDRA_INSTALL_DIR environment variable or Gradle project property: +// +// > export GHIDRA_INSTALL_DIR= +// > gradle +// +// or +// +// > gradle -PGHIDRA_INSTALL_DIR= +// +// Gradle should be invoked from the directory of the project to build. Please see the +// application.gradle.version property in /Ghidra/application.properties +// for the correction version of Gradle to use for the Ghidra installation you specify. + +//----------------------START "DO NOT MODIFY" SECTION------------------------------ +def ghidraInstallDir + +if (System.env.GHIDRA_INSTALL_DIR) { + ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR +} +else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { + ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") +} + +if (ghidraInstallDir) { + apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" +} +else { + throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") +} +//----------------------END "DO NOT MODIFY" SECTION------------------------------- diff --git a/extension.properties b/extension.properties new file mode 100644 index 0000000..ce34d9f --- /dev/null +++ b/extension.properties @@ -0,0 +1,5 @@ +name=@extname@ +description=Analyser for DWARF1 debug information +author=Rafal Harabien +createdOn= +version=@extversion@ diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Analyzer.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Analyzer.java new file mode 100644 index 0000000..3e6ac2f --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Analyzer.java @@ -0,0 +1,60 @@ +/* ### + * 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 com.github.rafalh.ghidra.dwarfone; + +import ghidra.app.services.AbstractAnalyzer; +import ghidra.app.services.AnalyzerType; +import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.ElfSectionProvider; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.options.Options; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.listing.Program; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * Analyzer of DWARF1 debug information. + */ +public class DWARF1Analyzer extends AbstractAnalyzer { + + public DWARF1Analyzer() { + super("DWARF1 Analyzer", "Analyzer of DWARF debug data in version 1", AnalyzerType.BYTE_ANALYZER); + setDefaultEnablement(false); + setSupportsOneTimeAnalysis(); + } + + @Override + public boolean canAnalyze(Program program) { + var sectionProvider = ElfSectionProvider.createSectionProviderFor(program); + return sectionProvider.hasSection(SectionNames.DEBUG); + } + + @Override + public void registerOptions(Options options, Program program) { + + options.registerOption("Option name goes here", false, null, + "Option description goes here"); + } + + @Override + public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) + throws CancelledException { + + var programAnalyzer = new DWARF1ProgramAnalyzer(program, monitor, log); + programAnalyzer.process(); + return true; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1ProgramAnalyzer.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1ProgramAnalyzer.java new file mode 100644 index 0000000..a0ca63e --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1ProgramAnalyzer.java @@ -0,0 +1,132 @@ +package com.github.rafalh.ghidra.dwarfone; + +import java.io.IOException; +import java.util.Optional; + +import com.github.rafalh.ghidra.dwarfone.model.AddrAttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.AttributeName; +import com.github.rafalh.ghidra.dwarfone.model.BlockAttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.DebugInfoEntry; +import com.github.rafalh.ghidra.dwarfone.model.LocationAtomOp; +import com.github.rafalh.ghidra.dwarfone.model.LocationDescription; +import com.github.rafalh.ghidra.dwarfone.model.StringAttributeValue; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteArrayProvider; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.ElfSectionProvider; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; +import ghidra.program.model.symbol.SourceType; +import ghidra.util.exception.InvalidInputException; +import ghidra.util.task.TaskMonitor; + +public class DWARF1ProgramAnalyzer { + private final Program program; + private final TaskMonitor monitor; + private final MessageLog log; + + public DWARF1ProgramAnalyzer(Program program, TaskMonitor monitor, MessageLog log) { + this.program = program; + this.monitor = monitor; + this.log = log; + } + + public boolean process() { + var sectionProvider = ElfSectionProvider.createSectionProviderFor(program); + try { + var debug = sectionProvider.getSectionAsByteProvider(SectionNames.DEBUG); + processDebugSection(debug); + //log.appendMsg("Finished parsing DWARF1"); + return true; + } catch (IOException e) { + log.appendException(e); + return false; + } + } + + private boolean isLittleEndian() { + return !program.getLanguage().isBigEndian(); + } + + private void processDebugSection(ByteProvider bp) throws IOException { + BinaryReader br = new BinaryReader(bp, isLittleEndian()); + while (br.getPointerIndex() < bp.length() && !monitor.isCancelled()) { + var die = new DebugInfoEntry(br); + processDebugInfoEntry(die); + } + } + + private void processDebugInfoEntry(DebugInfoEntry die) throws IOException { + //log.appendMsg(die.toString()); + switch (die.getTag()) { + case GLOBAL_VARIABLE: + processGlobalVariable(die); + break; + case GLOBAL_SUBROUTINE: + processGlobalSubrountine(die); + break; + default: + // skip other tags + } + } + + private LocationDescription decodeLocation(byte[] encodedLocation) throws IOException { + var bp = new ByteArrayProvider(encodedLocation); + return LocationDescription.read(bp, isLittleEndian()); + } + + private Long offsetFromLocation(LocationDescription location) { + var locationAtoms = location.getAtoms(); + if (locationAtoms.size() == 1 && locationAtoms.get(0).getOp() == LocationAtomOp.ADDR) { + return locationAtoms.get(0).getArg(); + } + log.appendMsg("Complex location not supported: " + locationAtoms); + return null; + } + + private void processGlobalVariable(DebugInfoEntry die) throws IOException { + Optional nameAttributeOptional = die.getAttribute(AttributeName.NAME); + Optional locationAttributeOptional = die.getAttribute(AttributeName.LOCATION); + if (nameAttributeOptional.isEmpty() || locationAttributeOptional.isEmpty()) { + return; + } + String name = nameAttributeOptional.get().get(); + byte[] encodedLocation = locationAttributeOptional.get().get(); + LocationDescription location = decodeLocation(encodedLocation); + Long offset = offsetFromLocation(location); + //log.appendMsg(name + " " + Long.toHexString(offset)); + Address addr = toAddr(offset); + try { + program.getSymbolTable().createLabel(addr, name, SourceType.IMPORTED); + } catch (InvalidInputException e) { + log.appendException(e); + } + } + + private void processGlobalSubrountine(DebugInfoEntry die) { + Optional nameAttributeOptional = die.getAttribute(AttributeName.NAME); + Optional lowPcAttributeOptional = die.getAttribute(AttributeName.LOW_PC); + Optional highPcAttributeOptional = die.getAttribute(AttributeName.HIGH_PC); + if (nameAttributeOptional.isEmpty() || lowPcAttributeOptional.isEmpty() || highPcAttributeOptional.isEmpty()) { + return; + } + String name = nameAttributeOptional.get().get(); + long lowPc = lowPcAttributeOptional.get().get(); + //long highPc = highPcAttributeOptional.get().get(); + //log.appendMsg(name + " " + Long.toHexString(lowPc.longValue())); + + Address addr = toAddr(lowPc); + try { + program.getSymbolTable().createLabel(addr, name, SourceType.IMPORTED); + } catch (InvalidInputException e) { + log.appendException(e); + } + } + + private final Address toAddr(Number offset) { + return program.getAddressFactory().getDefaultAddressSpace().getAddress( + offset.longValue(), true); + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/SectionNames.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/SectionNames.java new file mode 100644 index 0000000..9ec8d2e --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/SectionNames.java @@ -0,0 +1,5 @@ +package com.github.rafalh.ghidra.dwarfone; + +public class SectionNames { + public static final String DEBUG = "debug"; +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AddrAttributeValue.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AddrAttributeValue.java new file mode 100644 index 0000000..3f04888 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AddrAttributeValue.java @@ -0,0 +1,18 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +public class AddrAttributeValue implements AttributeValue { + private final long addr; + + public AddrAttributeValue(long addr) { + this.addr = addr; + } + + public long get() { + return addr; + } + + @Override + public String toString() { + return "addr(" + Long.toString(addr) + ")"; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AttributeName.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AttributeName.java new file mode 100644 index 0000000..2df3864 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AttributeName.java @@ -0,0 +1,81 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum AttributeName { + SIBLING(0x0010), + LOCATION(0x0020), + NAME(0x0030), + FUND_TYPE(0x0050), + MOD_FUND_TYPE(0x0060), + USER_DEF_TYPE(0x0070), + MOD_U_D_TYPE(0x0080), + ORDERING(0x0090), + SUBSCR_DATA(0x00A0), + BYTE_SIZE(0x00B0), + BIT_OFFSET(0x00C0), + BIT_SIZE(0x00D0), + ELEMENT_LIST(0x00F0), + STMT_LIST(0x0100), + LOW_PC(0x0110), + HIGH_PC(0x0120), + LANGUAGE(0x0130), + MEMBER(0x0140), + DISCR(0x0150), + DISCR_VALUE(0x0160), + STRING_LENGTH(0x0190), + COMMON_REFERENCE(0x01A0), + COMP_DIR(0x01B0), + CONST_VALUE(0x01C0), + CONTAINING_TYPE(0x01D0), + DEFAULT_VALUE(0x01E0), + FRIENDS(0x01F0), + INLINE(0x0200), + IS_OPTIONAL(0x0210), + LOWER_BOUND(0x0220), + PROGRAM(0x0230), + PRIVATE(0x0240), + PRODUCER(0x0250), + PROTECTED(0x0260), + PROTOTYPED(0x0270), + PUBLIC(0x0280), + PURE_VIRTUAL(0x0290), + RETURN_ADDRBLOCK2(0x02A0), + SPECIFICATIONREFERENCE(0x02B0), + START_SCOPE(0x02C0), + STRIDE_SIZE(0x02E0), + UPPER_BOUND(0x02F0), + VIRTUAL(0x0300), + USER(null); + + public static final int MASK = 0xFFF0; + private static final int LO_USER = 0x2000; + private static final int HI_USER = 0x3ff0; + private static final Map VALUE_MAP; + + private Integer value; + + static { + VALUE_MAP = Stream.of(AttributeName.values()) + .filter(at -> at.value != null) + .collect(Collectors.toUnmodifiableMap(at -> at.value, Function.identity())); + } + + AttributeName(Integer value) { + this.value = value; + } + + public static AttributeName decode(int value) { + if (value >= LO_USER && value <= HI_USER) { + return USER; + } + AttributeName at = VALUE_MAP.get(value); + if (at == null) { + throw new IllegalArgumentException("invalid attribute value " + value); + } + return at; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AttributeValue.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AttributeValue.java new file mode 100644 index 0000000..c1d9de0 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/AttributeValue.java @@ -0,0 +1,4 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +public interface AttributeValue { +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/BlockAttributeValue.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/BlockAttributeValue.java new file mode 100644 index 0000000..01e58b2 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/BlockAttributeValue.java @@ -0,0 +1,13 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +public class BlockAttributeValue implements AttributeValue { + private final byte[] block; + + public BlockAttributeValue(byte[] block) { + this.block = block; + } + + public byte[] get() { + return block; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/ConstAttributeValue.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/ConstAttributeValue.java new file mode 100644 index 0000000..fa12744 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/ConstAttributeValue.java @@ -0,0 +1,20 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.util.Objects; + +public class ConstAttributeValue implements AttributeValue { + private final Number value; + + public ConstAttributeValue(Number value) { + this.value = value; + } + + public Number get() { + return value; + } + + @Override + public String toString() { + return Objects.toString(value); + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/DebugInfoEntry.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/DebugInfoEntry.java new file mode 100644 index 0000000..f2b76e0 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/DebugInfoEntry.java @@ -0,0 +1,89 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import ghidra.app.util.bin.BinaryReader; + +public class DebugInfoEntry { + + private final int rawTag; + private final Tag tag; + private final Map attributes = new HashMap<>(); + private final Map userAttributes = new HashMap<>(); + + public DebugInfoEntry(BinaryReader reader) throws IOException { + long length = reader.readNextUnsignedInt(); + long endIndex = reader.getPointerIndex() + length - 4; + if (length < 8) { + rawTag = -1; + tag = Tag.NULL; + reader.setPointerIndex(endIndex); + } else { + rawTag = reader.readNextUnsignedShort(); + tag = Tag.decode(rawTag); + } + while (reader.getPointerIndex() < endIndex) { + Map.Entry at = readAttribute(reader); + AttributeName attributeName = AttributeName.decode(at.getKey()); + if (attributeName != AttributeName.USER) { + attributes.put(attributeName, at.getValue()); + } else { + userAttributes.put(at.getKey(), at.getValue()); + } + } + } + + private Map.Entry readAttribute(BinaryReader reader) throws IOException { + int rawNameForm = reader.readNextUnsignedShort(); + int rawName = rawNameForm & AttributeName.MASK; + int rawForm = rawNameForm & Form.MASK; + Form form = Form.decode(rawForm); + AttributeValue value = readAttributeValue(reader, form); + return Map.entry(rawName, value); + } + + private AttributeValue readAttributeValue(BinaryReader reader, Form form) throws IOException { + int blockLength; + switch (form) { + case ADDR: + // FIXME: is it always 32-bit? + return new AddrAttributeValue(reader.readNextUnsignedInt()); + case REF: + return new RefAttributeValue(reader.readNextUnsignedInt()); + case DATA2: + return new ConstAttributeValue(reader.readNextShort()); + case DATA4: + return new ConstAttributeValue(reader.readNextInt()); + case DATA8: + return new ConstAttributeValue(reader.readNextLong()); + case BLOCK2: + blockLength = reader.readNextUnsignedShort(); + return new BlockAttributeValue(reader.readNextByteArray(blockLength)); + case BLOCK4: + blockLength = reader.readNextInt(); + return new BlockAttributeValue(reader.readNextByteArray(blockLength)); + case STRING: + return new StringAttributeValue(reader.readNextAsciiString()); + default: + throw new IllegalArgumentException("unknown form"); + } + } + + @Override + public String toString() { + return Objects.toString(tag) + attributes.toString(); + } + + public Tag getTag() { + return tag; + } + + @SuppressWarnings("unchecked") + public Optional getAttribute(AttributeName name) { + return Optional.ofNullable((T) attributes.get(name)); + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/Form.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/Form.java new file mode 100644 index 0000000..e7d2150 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/Form.java @@ -0,0 +1,39 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum Form { + ADDR(0x1), + REF(0x2), + BLOCK2(0x3), + BLOCK4(0x4), + DATA2(0x5), + DATA4(0x6), + DATA8(0x7), + STRING(0x8); + + public static final int MASK = 0x000F; + private static final Map VALUE_MAP; + + private int value; + + static { + VALUE_MAP = Stream.of(Form.values()) + .collect(Collectors.toUnmodifiableMap(form -> form.value, Function.identity())); + } + + Form(int value) { + this.value = value; + } + + public static Form decode(int value) { + Form form = VALUE_MAP.get(value); + if (form == null) { + throw new IllegalArgumentException("invalid form value " + value); + } + return form; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/FundamentalType.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/FundamentalType.java new file mode 100644 index 0000000..8d7a761 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/FundamentalType.java @@ -0,0 +1,59 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum FundamentalType { + CHAR(0x0001), + SIGNED_CHAR(0x0002), + UNSIGNED_CHAR(0x0003), + SHORT(0x0004), + SIGNED_SHORT(0x0005), + UNSIGNED_SHORT(0x0006), + INTEGER(0x0007), + SIGNED_INTEGER(0x0008), + UNSIGNED_INTEGER(0x0009), + LONG(0x000A), + SIGNED_LONG(0x000B), + UNSIGNED_LONG(0x000C), + POINTER(0x000D), + FLOAT(0x000E), + DBL_PREC_FLOAT(0x000F), + EXT_PREC_FLOAT(0x0010), + COMPLEX(0x0011), + DBL_PREC_COMPLEX(0x0012), + VOID(0x0014), + BOOLEAN(0x0015), + EXT_PREC_COMPLEX(0x0016), + LABEL(0x0017), + USER(null); + + private static final int LO_USER = 0x8000; + private static final int HI_USER = 0xFFFF; + private static final Map VALUE_MAP; + + private Integer value; + + static { + VALUE_MAP = Stream.of(FundamentalType.values()) + .filter(at -> at.value != null) + .collect(Collectors.toUnmodifiableMap(ft -> ft.value, Function.identity())); + } + + FundamentalType(Integer value) { + this.value = value; + } + + public static FundamentalType fromValue(int value) { + if (value >= LO_USER && value <= HI_USER) { + return USER; + } + FundamentalType ft = VALUE_MAP.get(value); + if (ft == null) { + throw new IllegalArgumentException("invalid fundamental type " + value); + } + return ft; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationAtom.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationAtom.java new file mode 100644 index 0000000..fac0b53 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationAtom.java @@ -0,0 +1,24 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +public class LocationAtom { + private final LocationAtomOp op; + private final Long arg; + + public LocationAtom(LocationAtomOp op, Long arg) { + this.op = op; + this.arg = arg; + } + + public LocationAtomOp getOp() { + return op; + } + + public Long getArg() { + return arg; + } + + @Override + public String toString() { + return op.toString() + (arg != null ? "(" + arg.toString() + ")" : ""); + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationAtomOp.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationAtomOp.java new file mode 100644 index 0000000..1663e8f --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationAtomOp.java @@ -0,0 +1,44 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum LocationAtomOp { + REG(0x01), + BASEREG(0x02), + ADDR(0x03), + CONST(0x04), + DEREF2(0x05), + DEREF4(0x06), + ADD(0x07), + USER(null); + + private static final int LO_USER = 0xE0; + private static final int HI_USER = 0xFF; + private static final Map VALUE_MAP; + + private Integer value; + + static { + VALUE_MAP = Stream.of(LocationAtomOp.values()) + .filter(op -> op.value != null) + .collect(Collectors.toUnmodifiableMap(op -> op.value, Function.identity())); + } + + LocationAtomOp(Integer value) { + this.value = value; + } + + public static LocationAtomOp decode(int value) { + if (value >= LO_USER && value <= HI_USER) { + return USER; + } + LocationAtomOp op = VALUE_MAP.get(value); + if (op == null) { + throw new IllegalArgumentException("invalid location atom " + value); + } + return op; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationDescription.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationDescription.java new file mode 100644 index 0000000..70862a1 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/LocationDescription.java @@ -0,0 +1,45 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteProvider; + +public class LocationDescription { + private final List atoms; + + public LocationDescription(List atoms) { + this.atoms = List.copyOf(atoms); + } + + public List getAtoms() { + return atoms; + } + + public static LocationDescription read(ByteProvider bp, boolean isLittleEndian) throws IOException { + BinaryReader br = new BinaryReader(bp, isLittleEndian); + List atoms = new ArrayList<>(); + while (br.getPointerIndex() < bp.length()) { + int id = br.readNextUnsignedByte(); + var op = LocationAtomOp.decode(id); + Long arg; + switch (op) { + case REG: + case BASEREG: + case ADDR: + case CONST: + arg = (long) br.readNextInt(); + break; + default: + arg = null; + break; + } + atoms.add(new LocationAtom(op, arg)); + } + return new LocationDescription(atoms); + } + + +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/RefAttributeValue.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/RefAttributeValue.java new file mode 100644 index 0000000..4fa7565 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/RefAttributeValue.java @@ -0,0 +1,18 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +public class RefAttributeValue implements AttributeValue { + private final long offset; + + public RefAttributeValue(long offset) { + this.offset = offset; + } + + public long get() { + return offset; + } + + @Override + public String toString() { + return "ref(" + offset + ")"; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/StringAttributeValue.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/StringAttributeValue.java new file mode 100644 index 0000000..33d02d5 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/StringAttributeValue.java @@ -0,0 +1,20 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.util.Objects; + +public class StringAttributeValue implements AttributeValue { + private final String string; + + public StringAttributeValue(String string) { + this.string = string; + } + + public String get() { + return string; + } + + @Override + public String toString() { + return "\"" + Objects.toString(string) + "\""; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/Tag.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/Tag.java new file mode 100644 index 0000000..cb0c50c --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/Tag.java @@ -0,0 +1,71 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum Tag { + NULL(null), + PADDING(0x0000), + ARRAY_TYPE(0x0001), + CLASS_TYPE(0x0002), + ENTRY_POINT(0x0003), + ENUMERATION_TYPE(0x0004), + FORMAL_PARAMETER(0x0005), + GLOBAL_SUBROUTINE(0x0006), + GLOBAL_VARIABLE(0x0007), + LABEL(0x000A), + LEXICAL_BLOCK(0x000B), + LOCAL_VARIABLE(0x000C), + MEMBER(0x000D), + POINTER_TYPE(0x000F), + REFERENCE_TYPE(0x0010), + COMPILE_UNIT(0x0011), + // SOURCE_FILE(0x0011), // reserved - synonym for COMPILE_UNIT + STRING_TYPE(0x0012), + STRUCTURE_TYPE(0x0013), + SUBROUTINE(0x0014), + SUBROUTINE_TYPE(0x0015), + TYPEDEF(0x0016), + UNION_TYPE(0x0017), + UNSPECIFIED_PARAMETERS(0x0018), + VARIANT(0x0019), + COMMON_BLOCK(0x001A), + COMMON_INCLUSION(0x001B), + INHERITANCE(0x001C), + INLINED_SUBROUTINE(0x001D), + MODULE(0x001E), + PTR_TO_MEMBER_TYPE(0x001F), + SET_TYPE(0x0020), + SUBRANGE_TYPE(0x0021), + WITH_STMT(0x0022), + USER(null); + + private Integer value; + + private static final int LO_USER = 0x4080; + private static final int HI_USER = 0xFFFF; + private static final Map VALUE_MAP; + + static { + VALUE_MAP = Stream.of(Tag.values()) + .filter(tag -> tag.value != null) + .collect(Collectors.toUnmodifiableMap(tag -> tag.value, Function.identity())); + } + + Tag(Integer value) { + this.value = value; + } + + public static Tag decode(int value) { + if (value >= LO_USER && value <= HI_USER) { + return USER; + } + Tag tag = VALUE_MAP.get(value); + if (tag == null) { + throw new IllegalArgumentException("invalid tag value " + value); + } + return tag; + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/model/TypeModifier.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/TypeModifier.java new file mode 100644 index 0000000..bade432 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/model/TypeModifier.java @@ -0,0 +1,41 @@ +package com.github.rafalh.ghidra.dwarfone.model; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum TypeModifier { + POINTER_TO(0x01), + REFERENCE_TO(0x02), + CONST(0x03), + VOLATILE(0x04), + USER(null); + + private static final int LO_USER = 0x80; + private static final int HI_USER = 0xFF; + private static final Map VALUE_MAP; + + private Integer value; + + static { + VALUE_MAP = Stream.of(TypeModifier.values()) + .filter(mod -> mod.value != null) + .collect(Collectors.toUnmodifiableMap(mod -> mod.value, Function.identity())); + } + + TypeModifier(Integer value) { + this.value = value; + } + + public static TypeModifier fromValue(int value) { + if (value >= LO_USER && value <= HI_USER) { + return USER; + } + TypeModifier mod = VALUE_MAP.get(value); + if (mod == null) { + throw new IllegalArgumentException("invalid mod value " + value); + } + return mod; + } +}