diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Analyzer.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Analyzer.java index 39962ad..4467fdf 100644 --- a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Analyzer.java +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Analyzer.java @@ -39,7 +39,7 @@ public DWARF1Analyzer() { @Override public boolean canAnalyze(Program program) { var sectionProvider = ElfSectionProvider.createSectionProviderFor(program); - return sectionProvider.hasSection(SectionNames.DEBUG); + return sectionProvider.hasSection(DWARF1SectionNames.DEBUG); } @Override diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1FunctionImporter.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1FunctionImporter.java new file mode 100644 index 0000000..328ccb1 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1FunctionImporter.java @@ -0,0 +1,117 @@ +package com.github.rafalh.ghidra.dwarfone; + +import java.util.ArrayList; +import java.util.List; +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.DebugInfoEntry; +import com.github.rafalh.ghidra.dwarfone.model.RefAttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.Tag; + +import ghidra.app.util.importer.MessageLog; +import ghidra.program.database.function.OverlappingFunctionException; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.Pointer; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Function.FunctionUpdateType; +import ghidra.program.model.listing.FunctionManager; +import ghidra.program.model.listing.ParameterImpl; +import ghidra.program.model.listing.ReturnParameterImpl; +import ghidra.program.model.listing.Variable; +import ghidra.program.model.symbol.SourceType; +import ghidra.util.exception.DuplicateNameException; +import ghidra.util.exception.InvalidInputException; + +public class DWARF1FunctionImporter { + private final DWARF1Program dwarfProgram; + private final MessageLog log; + private final DWARF1TypeManager dwarfTypeManager; + private final DWARF1TypeExtractor typeExtractor; + private final FunctionManager functionManager; + + DWARF1FunctionImporter(DWARF1Program dwarfProgram, MessageLog log, DWARF1TypeManager dwarfTypeManager, DWARF1TypeExtractor typeExtractor) { + this.dwarfProgram = dwarfProgram; + this.log = log; + this.dwarfTypeManager = dwarfTypeManager; + this.typeExtractor = typeExtractor; + functionManager = dwarfProgram.getProgram().getFunctionManager(); + } + + void processSubrountine(DebugInfoEntry die) { + Optional nameOptional = DWARF1ImportUtils.extractName(die); + Optional lowPcAttributeOptional = die.getAttribute(AttributeName.LOW_PC); + Optional highPcAttributeOptional = die.getAttribute(AttributeName.HIGH_PC); + + if (nameOptional.isEmpty() || lowPcAttributeOptional.isEmpty() || highPcAttributeOptional.isEmpty()) { + return; + } + + String name = nameOptional.get(); + long lowPc = lowPcAttributeOptional.get().get(); + long highPc = highPcAttributeOptional.get().get(); + //log.appendMsg(name + " " + Long.toHexString(lowPc.longValue())); + + Address lowAddr = dwarfProgram.toAddr(lowPc); + Address highAddr = dwarfProgram.toAddr(highPc); + + // Prefix name with the class name if this is a member function + Optional classDtOpt = determineMemberClassType(die); + if (classDtOpt.isPresent() && !name.contains(classDtOpt.get().getName())) { + name = classDtOpt.get().getName() + "::" + name; + } + + DataType returnDt = typeExtractor.extractDataType(die); + + Function fun = functionManager.getFunctionAt(lowAddr); + try { + if (fun == null) { + AddressSetView funSet = dwarfProgram.getSet().intersectRange(lowAddr, highAddr); + fun = functionManager.createFunction(name, lowAddr, funSet, SourceType.IMPORTED); + } else { + fun.setName(name, SourceType.IMPORTED); + } + + Variable returnParam = new ReturnParameterImpl(returnDt, dwarfProgram.getProgram()); + List params = new ArrayList<>(); + for (DebugInfoEntry childDie : die.getChildren()) { + if (childDie.getTag() == Tag.FORMAL_PARAMETER) { + String paramName = DWARF1ImportUtils.extractName(childDie).orElse(null); + DataType dt = typeExtractor.extractDataType(childDie); + params.add(new ParameterImpl(paramName, dt, dwarfProgram.getProgram())); + } + } + + fun.updateFunction(null, returnParam, params, FunctionUpdateType.DYNAMIC_STORAGE_FORMAL_PARAMS, true, SourceType.IMPORTED); + } catch (DuplicateNameException | InvalidInputException | OverlappingFunctionException e) { + log.appendException(e); + } + } + + private Optional determineMemberClassType(DebugInfoEntry die) { + // Function defined in class body + if (die.getParent().getTag() == Tag.CLASS_TYPE) { + return Optional.of(dwarfTypeManager.getUserDataType(die.getParent().getRef())); + } + // Function defined outside of the class body should have AT_member attribute + Optional memberAttributeOptional = die.getAttribute(AttributeName.MEMBER); + if (memberAttributeOptional.isPresent()) { + return Optional.of(dwarfTypeManager.getUserDataType(memberAttributeOptional.get().get())); + } + // Determine the class based on the "this" parameter because for some compilers (e.g. PS2) normal + // ways does not work... + for (DebugInfoEntry childDie : die.getChildren()) { + if (childDie.getTag() == Tag.FORMAL_PARAMETER && Optional.of("this").equals(DWARF1ImportUtils.extractName(childDie))) { + DataType dt = typeExtractor.extractDataType(childDie); + if (dt instanceof Pointer) { + dt = ((Pointer) dt).getDataType(); + } + return Optional.of(dt); + } + } + return Optional.empty(); + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1ImportUtils.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1ImportUtils.java new file mode 100644 index 0000000..224a28d --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1ImportUtils.java @@ -0,0 +1,38 @@ +package com.github.rafalh.ghidra.dwarfone; + +import java.io.IOException; +import java.util.Optional; + +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.LocationDescription; +import com.github.rafalh.ghidra.dwarfone.model.StringAttributeValue; + +import ghidra.app.util.bin.ByteArrayProvider; + +public class DWARF1ImportUtils { + private DWARF1ImportUtils() { + // empty + } + + static Optional extractName(DebugInfoEntry die) { + return die.getAttribute(AttributeName.NAME) + .map(StringAttributeValue::get); + } + + static Optional extractLocation(DebugInfoEntry die, DWARF1Program dwarfProgram) { + return die.getAttribute(AttributeName.LOCATION) + .map(av -> decodeLocation(av.get(), dwarfProgram.isLittleEndian())); + + } + + private static LocationDescription decodeLocation(byte[] encodedLocation, boolean isLittleEndian) { + var bp = new ByteArrayProvider(encodedLocation); + try { + return LocationDescription.read(bp, isLittleEndian); + } catch (IOException e) { + throw new IllegalArgumentException("Failed to parse location", e); + } + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Program.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Program.java new file mode 100644 index 0000000..c92f857 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1Program.java @@ -0,0 +1,52 @@ +package com.github.rafalh.ghidra.dwarfone; + +import java.util.HashMap; +import java.util.Map; + +import com.github.rafalh.ghidra.dwarfone.model.DebugInfoEntry; + +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.listing.Program; + +public class DWARF1Program { + + private final Program program; + private final AddressSetView set; + private final Map dieMap = new HashMap<>(); + + DWARF1Program(Program program, AddressSetView set) { + this.program = program; + this.set = set; + } + + public Program getProgram() { + return program; + } + + public boolean isLittleEndian() { + return !program.getLanguage().isBigEndian(); + } + + public DataTypeManager getDataTypeManager() { + return program.getDataTypeManager(); + } + + public DebugInfoEntry getDebugInfoEntry(long ref) { + return dieMap.get(ref); + } + + public void addEntry(DebugInfoEntry die) { + dieMap.put(die.getRef(), die); + } + + public AddressSetView getSet() { + return set; + } + + public final Address toAddr(Number offset) { + return program.getAddressFactory().getDefaultAddressSpace().getAddress( + offset.longValue(), 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 index 376cce6..2ee4d27 100644 --- a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1ProgramAnalyzer.java +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1ProgramAnalyzer.java @@ -2,86 +2,52 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.ListIterator; import java.util.Map; 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.AttributeUtils; -import com.github.rafalh.ghidra.dwarfone.model.AttributeValue; -import com.github.rafalh.ghidra.dwarfone.model.BlockAttributeValue; -import com.github.rafalh.ghidra.dwarfone.model.ConstAttributeValue; import com.github.rafalh.ghidra.dwarfone.model.DebugInfoEntry; -import com.github.rafalh.ghidra.dwarfone.model.Format; -import com.github.rafalh.ghidra.dwarfone.model.FundamentalType; -import com.github.rafalh.ghidra.dwarfone.model.LocationAtomOp; -import com.github.rafalh.ghidra.dwarfone.model.LocationDescription; import com.github.rafalh.ghidra.dwarfone.model.RefAttributeValue; -import com.github.rafalh.ghidra.dwarfone.model.StringAttributeValue; import com.github.rafalh.ghidra.dwarfone.model.Tag; -import com.github.rafalh.ghidra.dwarfone.model.TypeModifier; 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.database.function.OverlappingFunctionException; -import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.data.BuiltInDataTypeManager; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.DataTypeConflictException; -import ghidra.program.model.data.DataTypeConflictHandler; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.EnumDataType; -import ghidra.program.model.data.Pointer; -import ghidra.program.model.data.PointerDataType; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.Structure; -import ghidra.program.model.data.StructureDataType; -import ghidra.program.model.data.Union; -import ghidra.program.model.data.UnionDataType; -import ghidra.program.model.listing.Function; -import ghidra.program.model.listing.Function.FunctionUpdateType; -import ghidra.program.model.listing.FunctionManager; -import ghidra.program.model.listing.ParameterImpl; import ghidra.program.model.listing.Program; -import ghidra.program.model.listing.ReturnParameterImpl; -import ghidra.program.model.listing.Variable; -import ghidra.program.model.symbol.SourceType; -import ghidra.program.model.util.CodeUnitInsertionException; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.InvalidInputException; import ghidra.util.task.TaskMonitor; public class DWARF1ProgramAnalyzer { private final Program program; - private final AddressSetView set; private final TaskMonitor monitor; private final MessageLog log; - private final Map userDataTypeMap = new HashMap<>(); - private final CategoryPath categoryPath; - private final List dieList = new ArrayList<>(); - private final Map dieMap = new HashMap<>(); + private final DWARF1Program dwarfProgram; + private final DWARF1TypeManager dwarfTypeManager; + private final DWARF1TypeExtractor typeExtractor; + private final DWARF1TypeImporter dwarfTypeImporter; + private final DWARF1FunctionImporter dwarfFunctionImporter; + private final DWARF1VariableImporter dwarfVariableImporter; public DWARF1ProgramAnalyzer(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) { this.program = program; - this.set = set; this.monitor = monitor; this.log = log; - this.categoryPath = new CategoryPath("/DWARF"); + dwarfProgram = new DWARF1Program(program, set); + dwarfTypeManager = new DWARF1TypeManager(dwarfProgram, log); + typeExtractor = new DWARF1TypeExtractor(dwarfProgram, log, dwarfTypeManager); + dwarfTypeImporter = new DWARF1TypeImporter(dwarfProgram, log, dwarfTypeManager, typeExtractor); + dwarfFunctionImporter = new DWARF1FunctionImporter(dwarfProgram, log, dwarfTypeManager, typeExtractor); + dwarfVariableImporter = new DWARF1VariableImporter(dwarfProgram, log, typeExtractor); + dwarfTypeManager.setTypeImporter(dwarfTypeImporter); } public boolean process() { var sectionProvider = ElfSectionProvider.createSectionProviderFor(program); try { - var debug = sectionProvider.getSectionAsByteProvider(SectionNames.DEBUG); + var debug = sectionProvider.getSectionAsByteProvider(DWARF1SectionNames.DEBUG); processDebugSection(debug); //log.appendMsg("Finished parsing DWARF1"); return true; @@ -99,6 +65,7 @@ private void processDebugSection(ByteProvider bp) throws IOException { BinaryReader br = new BinaryReader(bp, isLittleEndian()); DebugInfoEntry parent = null; DebugInfoEntry prev = null; + List dieList = new ArrayList<>(); while (br.getPointerIndex() < bp.length() && !monitor.isCancelled()) { long offset = br.getPointerIndex(); @@ -114,11 +81,13 @@ private void processDebugSection(ByteProvider bp) throws IOException { } var die = new DebugInfoEntry(br, parent); if (die.getTag() != Tag.NULL) { + dwarfProgram.addEntry(die); dieList.add(die); - dieMap.put(offset, die); } prev = die; } + + for (DebugInfoEntry die : dieList) { processDebugInfoEntry(die); } @@ -129,20 +98,20 @@ private void processDebugInfoEntry(DebugInfoEntry die) { try { switch (die.getTag()) { case GLOBAL_VARIABLE: - processGlobalVariable(die); + dwarfVariableImporter.processGlobalVariable(die); break; case LOCAL_VARIABLE: - processLocalVariable(die); + dwarfVariableImporter.processLocalVariable(die); break; case GLOBAL_SUBROUTINE: - processSubrountine(die); + dwarfFunctionImporter.processSubrountine(die); break; case SUBROUTINE: - processSubrountine(die); + dwarfFunctionImporter.processSubrountine(die); break; case CLASS_TYPE: case ENUMERATION_TYPE: - processTypeDebugInfoEntry(die); + dwarfTypeImporter.processTypeDebugInfoEntry(die); break; case TYPEDEF: // TODO @@ -154,543 +123,4 @@ private void processDebugInfoEntry(DebugInfoEntry die) { throw new RuntimeException("Failed to process debug info entry " + die, e); } } - - private Optional processTypeDebugInfoEntry(DebugInfoEntry die) { - try { - switch (die.getTag()) { - case CLASS_TYPE: - case STRUCTURE_TYPE: - return processClassType(die); - case UNION_TYPE: - return processUnionType(die); - case ENUMERATION_TYPE: - return processEnumType(die); - case ARRAY_TYPE: - return processArrayType(die); - case SUBROUTINE_TYPE: - return processSubrountineType(die); - case TYPEDEF: - case STRING_TYPE: - case POINTER_TYPE: - case PTR_TO_MEMBER_TYPE: - case SET_TYPE: - case SUBRANGE_TYPE: - // TODO - log.appendMsg("Skipping type: " + die); - return Optional.empty(); - default: - // skip other tags - return Optional.empty(); - } - } catch (Exception e) { - throw new RuntimeException("Failed to process type debug info entry " + die, e); - } - } - - private Optional processSubrountineType(DebugInfoEntry die) { - // TODO - var dt = new PointerDataType(DataType.VOID); - userDataTypeMap.put(die.getRef(), dt); - return Optional.of(dt); - } - - private Optional processArrayType(DebugInfoEntry die) throws IOException { - byte[] subscrData = die.getAttribute(AttributeName.SUBSCR_DATA) - .map(av -> av.get()) - .orElseThrow(() -> new IllegalArgumentException("array type without subscr_data " + die)); - var bp = new ByteArrayProvider(subscrData); - List dims = new ArrayList<>(); - DataType baseDt = null; - BinaryReader br = new BinaryReader(bp, isLittleEndian()); - while (br.getPointerIndex() < bp.length()) { - Format fmt = Format.decode(br.readNextByte()); - if (fmt == Format.ET) { - Map.Entry attributeEntry = AttributeUtils.readAttribute(br); - var at = AttributeName.decode(attributeEntry.getKey()); - var av = attributeEntry.getValue(); - if (at == AttributeName.FUND_TYPE) { - FundamentalType ft = FundamentalType.fromValue(((ConstAttributeValue) av).get().intValue()); - baseDt = convertFundamentalTypeToDataType(ft); - } else if (at == AttributeName.USER_DEF_TYPE) { - baseDt = getUserDataType(((RefAttributeValue) av).get()); - } else if (at == AttributeName.MOD_FUND_TYPE) { - baseDt = decodeModFundType(((BlockAttributeValue) av).get()); - } else if (at == AttributeName.MOD_U_D_TYPE) { - baseDt = decodeModUserDefType(((BlockAttributeValue) av).get()); - } else { - log.appendMsg("Unsupported type " + at + " in " + die); - break; - } - } else if (fmt == Format.FT_C_C) { - // type of index - unused - FundamentalType.fromValue(br.readNextUnsignedShort()); - int begin = br.readNextInt(); - int end = br.readNextInt(); - dims.add(end - begin); - } else { - log.appendMsg("Unsupported format " + fmt + " in " + die); - break; - } - } - if (baseDt == null) { - return Optional.empty(); - } - DataType dt = baseDt; - Collections.reverse(dims); - for (int dim : dims) { - if (dim <= 0) { - log.appendMsg("Bad array dim " + dim + " in " + die); - return Optional.empty(); - } - dt = new ArrayDataType(dt, dim, -1); - } - userDataTypeMap.put(die.getRef(), dt); - return Optional.of(dt); - } - - private Optional processClassType(DebugInfoEntry die) { - Optional byteSizeOpt = die.getAttribute(AttributeName.BYTE_SIZE).map(av -> av.get()); - if (byteSizeOpt.isEmpty()) { - log.appendMsg("Skipping structure without size " + die); - return Optional.empty(); - } - String name = extractName(die) - // FIXME: anonymous class? - .filter(n -> !n.equals("@class")) - .orElseGet(() -> "anon_" + die.getRef()); - int size = byteSizeOpt.get().intValue(); - DataTypeManager dataTypeManager = program.getDataTypeManager(); - DataType existingDt = dataTypeManager.getDataType(categoryPath, name); - if (existingDt != null) { - // already imported - userDataTypeMap.put(die.getRef(), existingDt); - return Optional.of(existingDt); - } - StructureDataType sdt = new StructureDataType(categoryPath, name, size, dataTypeManager); - Structure newDt = (Structure) dataTypeManager.addDataType(sdt, DataTypeConflictHandler.DEFAULT_HANDLER); - userDataTypeMap.put(die.getRef(), newDt); - //log.appendMsg("Struct " + name); - for (DebugInfoEntry childDie : die.getChildren()) { - switch (childDie.getTag()) { - case MEMBER: - processClassTypeMember(newDt, childDie); - break; - case INHERITANCE: - processClassTypeInheritance(newDt, childDie); - break; - default: - log.appendMsg("Unexpected child of class type: " + childDie.getTag()); - } - } - return Optional.of(newDt); - } - - private void processClassTypeInheritance(Structure sdt, DebugInfoEntry die) { - DataType baseDt = extractDataType(die); - sdt.replaceAtOffset(0, baseDt, -1, "__base", null); - } - - private void processClassTypeMember(Structure sdt, DebugInfoEntry die) { - String memberName = extractName(die).orElse(null); - //log.appendMsg("Member " + childNameOpt); - DataType memberDt = extractDataType(die); - int memberOffset = extractMemberOffset(die); - assert memberDt != null; - if (memberOffset >= sdt.getLength()) { - // TODO: memberDt.isDynamicallySized() - return; - } - sdt.replaceAtOffset(memberOffset, memberDt, -1, memberName, null); - } - - private Optional processUnionType(DebugInfoEntry die) { - String name = extractName(die).orElseGet(() -> "anon_" + die.getRef()); - DataTypeManager dataTypeManager = program.getDataTypeManager(); - DataType existingDt = dataTypeManager.getDataType(categoryPath, name); - if (existingDt != null) { - // already imported - userDataTypeMap.put(die.getRef(), existingDt); - return Optional.of(existingDt); - } - UnionDataType udt = new UnionDataType(categoryPath, name, dataTypeManager); - Union newDt = (Union) dataTypeManager.addDataType(udt, DataTypeConflictHandler.DEFAULT_HANDLER); - userDataTypeMap.put(die.getRef(), newDt); - //log.appendMsg("Struct " + name); - for (DebugInfoEntry childDie : die.getChildren()) { - switch (childDie.getTag()) { - case MEMBER: - processUnionTypeMember(newDt, childDie); - break; - default: - log.appendMsg("Unexpected child of union type: " + childDie.getTag()); - } - } - return Optional.of(newDt); - } - - private void processUnionTypeMember(Union union, DebugInfoEntry die) { - String memberName = extractName(die).orElse(null); - //log.appendMsg("Member " + childNameOpt); - DataType memberDt = extractDataType(die); - union.add(memberDt, memberName, null); - } - - private Optional processEnumType(DebugInfoEntry die) throws IOException { - Optional byteSizeOpt = die.getAttribute(AttributeName.BYTE_SIZE).map(av -> av.get()); - Optional elementListOpt = die.getAttribute(AttributeName.ELEMENT_LIST).map(av -> av.get()); - - String name = extractName(die).orElseGet(() -> "anon_" + die.getRef()); - DataTypeManager dataTypeManager = program.getDataTypeManager(); - DataType existingDt = dataTypeManager.getDataType(categoryPath, name); - if (existingDt != null) { - // already imported? - userDataTypeMap.put(die.getRef(), existingDt); - return Optional.of(existingDt); - } - - int size = byteSizeOpt.orElse(4).intValue(); - var edt = new EnumDataType(categoryPath, name, size); - if (elementListOpt.isPresent()) { - processEnumElementList(edt, elementListOpt.get(), size); - } - - DataType newDt = dataTypeManager.addDataType(edt, DataTypeConflictHandler.DEFAULT_HANDLER); - userDataTypeMap.put(die.getRef(), newDt); - return Optional.of(newDt); - } - - private void processEnumElementList(EnumDataType edt, byte[] encodedElementList, int size) throws IOException { - var bp = new ByteArrayProvider(encodedElementList); - BinaryReader br = new BinaryReader(bp, isLittleEndian()); - while (br.getPointerIndex() < bp.length()) { - long value = br.readNextInt(); // FIXME: should use machine specific FT_long size - String name = br.readNextAsciiString(); - edt.add(name, value); - } - } - - private Optional extractName(DebugInfoEntry die) { - return die.getAttribute(AttributeName.NAME) - .map(StringAttributeValue::get); - } - - private int extractMemberOffset(DebugInfoEntry die) { - Optional locationAttributeOptional = die.getAttribute(AttributeName.LOCATION); - byte[] encodedLocation = locationAttributeOptional - .orElseThrow(() -> new IllegalArgumentException("expected location in " + die)) - .get(); - LocationDescription location = decodeLocation(encodedLocation); - var atoms = location.getAtoms(); - if (atoms.size() == 2 && atoms.get(0).getOp() == LocationAtomOp.CONST && atoms.get(1).getOp() == LocationAtomOp.ADD) { - return atoms.get(0).getArg().intValue(); - } - throw new IllegalArgumentException("unparsable member location " + atoms); - } - - private DataType extractDataType(DebugInfoEntry die) { - Optional fundTypeOpt = die.getAttribute(AttributeName.FUND_TYPE) - .map(ConstAttributeValue::get) - .map(Number::intValue) - .map(FundamentalType::fromValue); - Optional userDefTypeOpt = die.getAttribute(AttributeName.USER_DEF_TYPE); - Optional modFundTypeOpt = die.getAttribute(AttributeName.MOD_FUND_TYPE) - .map(av -> av.get()); - Optional modUserDefTypeOpt = die.getAttribute(AttributeName.MOD_U_D_TYPE) - .map(av -> av.get()); - if (fundTypeOpt.isPresent()) { - var ftDt = convertFundamentalTypeToDataType(fundTypeOpt.get()); - if (ftDt == null) { - log.appendMsg("failed to map ft to dt: " + fundTypeOpt.get()); - } - return Optional.ofNullable(ftDt).orElse(DataType.DEFAULT); - } - if (modFundTypeOpt.isPresent()) { - return decodeModFundType(modFundTypeOpt.get()); - } - if (userDefTypeOpt.isPresent()) { - return getUserDataType(userDefTypeOpt.get().get()); - } - if (modUserDefTypeOpt.isPresent()) { - return decodeModUserDefType(modUserDefTypeOpt.get()); - } - log.appendMsg("Unknown type " + die); - return DataType.DEFAULT; - } - - private DataType decodeModFundType(byte[] data) { - var bp = new ByteArrayProvider(data); - BinaryReader br = new BinaryReader(bp, isLittleEndian()); - FundamentalType ft; - List mods = new ArrayList<>(); - long maxOffset = bp.length() - 2; - try { - while (br.getPointerIndex() < maxOffset) { - mods.add(TypeModifier.fromValue(br.readNextUnsignedByte())); - } - ft = FundamentalType.fromValue(br.readNextUnsignedShort()); - } catch (IOException e) { - throw new IllegalStateException("Failed to decode mod fund type", e); - } - DataType baseDt = convertFundamentalTypeToDataType(ft); - return applyTypeModifiers(mods, baseDt); - } - - private DataType decodeModUserDefType(byte[] data) { - var bp = new ByteArrayProvider(data); - BinaryReader br = new BinaryReader(bp, isLittleEndian()); - Long udtRef; - List mods = new ArrayList<>(); - long maxOffset = bp.length() - 4; - try { - while (br.getPointerIndex() < maxOffset) { - mods.add(TypeModifier.fromValue(br.readNextUnsignedByte())); - } - udtRef = br.readNextUnsignedInt(); - } catch (IOException e) { - throw new IllegalStateException("Failed to decode mod ud type", e); - } - DataType baseDt = getUserDataType(udtRef); - return applyTypeModifiers(mods, baseDt); - } - - private DataType applyTypeModifiers(List mods, DataType dt) { - // apply modifiers in reverse order - ListIterator it = mods.listIterator(mods.size()); - while (it.hasPrevious()) { - TypeModifier mod = it.previous(); - if (mod == TypeModifier.POINTER_TO || mod == TypeModifier.REFERENCE_TO) { - dt = new PointerDataType(dt); - } - } - return dt; - } - - private DataType getUserDataType(long ref) { - var dtOpt = Optional.ofNullable(userDataTypeMap.get(ref)); - if (dtOpt.isEmpty()) { - // FIXME: dirty fix, may cause infinite recursion... - Optional.ofNullable(dieMap.get(ref)) - .ifPresent(die -> { - processTypeDebugInfoEntry(die); - }); - // try again... - dtOpt = Optional.ofNullable(userDataTypeMap.get(ref)); - } - if (dtOpt.isEmpty()) { - log.appendMsg("Cannot find user type " + Long.toHexString(ref)); - } - return dtOpt.orElse(DataType.DEFAULT); - } - - private DataType convertFundamentalTypeToDataType(FundamentalType ft) { - DataTypeManager dataTypeManager = BuiltInDataTypeManager.getDataTypeManager(); - switch (ft) { - case CHAR: - return dataTypeManager.getDataType(CategoryPath.ROOT, "char"); - case SIGNED_CHAR: - return dataTypeManager.getDataType(CategoryPath.ROOT, "schar"); - case UNSIGNED_CHAR: - return dataTypeManager.getDataType(CategoryPath.ROOT, "uchar"); - case SHORT: - case SIGNED_SHORT: - return dataTypeManager.getDataType(CategoryPath.ROOT, "short"); - case UNSIGNED_SHORT: - return dataTypeManager.getDataType(CategoryPath.ROOT, "ushort"); - case INTEGER: - case SIGNED_INTEGER: - return dataTypeManager.getDataType(CategoryPath.ROOT, "int"); - case UNSIGNED_INTEGER: - return dataTypeManager.getDataType(CategoryPath.ROOT, "uint"); - case LONG: - case SIGNED_LONG: - return dataTypeManager.getDataType(CategoryPath.ROOT, "long"); - case UNSIGNED_LONG: - return dataTypeManager.getDataType(CategoryPath.ROOT, "ulong"); - case POINTER: - return dataTypeManager.getDataType(CategoryPath.ROOT, "pointer"); - case FLOAT: - return dataTypeManager.getDataType(CategoryPath.ROOT, "float"); - case DBL_PREC_FLOAT: - return dataTypeManager.getDataType(CategoryPath.ROOT, "double"); - case EXT_PREC_FLOAT: - return dataTypeManager.getDataType(CategoryPath.ROOT, "longdouble"); - case VOID: - return DataType.VOID; - case BOOLEAN: - return dataTypeManager.getDataType(CategoryPath.ROOT, "bool"); - default: - return DataType.DEFAULT; - } - } - - private LocationDescription decodeLocation(byte[] encodedLocation) { - var bp = new ByteArrayProvider(encodedLocation); - try { - return LocationDescription.read(bp, isLittleEndian()); - } catch (IOException e) { - throw new IllegalArgumentException("Failed to parse location", e); - } - } - - 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) { - Optional nameOptional = extractName(die); - Optional locationAttributeOptional = die.getAttribute(AttributeName.LOCATION); - if (nameOptional.isEmpty() || locationAttributeOptional.isEmpty()) { - return; - } - String name = nameOptional.get(); - byte[] encodedLocation = locationAttributeOptional.get().get(); - LocationDescription location = decodeLocation(encodedLocation); - Long offset = offsetFromLocation(location); - //log.appendMsg(name + " " + Long.toHexString(offset)); - if (offset == 0) { - log.appendMsg("Skipping variable with null address: " + name); - return; - } - Address addr = toAddr(offset); - try { - program.getSymbolTable().createLabel(addr, name, SourceType.IMPORTED); - } catch (InvalidInputException e) { - log.appendException(e); - } - - DataType dt = extractDataType(die); -// try { -// DataUtilities.createData(program, addr, dt, -1, false, ClearDataMode.CLEAR_ALL_CONFLICT_DATA); -// } catch (CodeUnitInsertionException e) { -// log.appendException(e); -// } - - if (!dt.isDynamicallySized() && dt.getLength() > 0) { - try { - // a bit brutal... there should be an option for clearing - program.getListing().clearCodeUnits(addr, addr.add(dt.getLength()), false); - program.getListing().createData(addr, dt); - } catch (CodeUnitInsertionException | DataTypeConflictException e) { - log.appendException(e); - } - } - } - - - private void processLocalVariable(DebugInfoEntry die) { - if (die.getParent().getTag() != Tag.COMPILE_UNIT) { - // ignore parameters and local variables - // we are only interested in static variables - return; - } - processGlobalVariable(die); - } - - private void processSubrountine(DebugInfoEntry die) { - Optional nameOptional = extractName(die); - Optional lowPcAttributeOptional = die.getAttribute(AttributeName.LOW_PC); - Optional highPcAttributeOptional = die.getAttribute(AttributeName.HIGH_PC); - - if (nameOptional.isEmpty() || lowPcAttributeOptional.isEmpty() || highPcAttributeOptional.isEmpty()) { - return; - } - - String name = nameOptional.get(); - long lowPc = lowPcAttributeOptional.get().get(); - long highPc = highPcAttributeOptional.get().get(); - //log.appendMsg(name + " " + Long.toHexString(lowPc.longValue())); - - Address lowAddr = toAddr(lowPc); - Address highAddr = toAddr(highPc); -// try { -// program.getSymbolTable().createLabel(lowAddr, name, SourceType.IMPORTED); -// } catch (InvalidInputException e) { -// log.appendException(e); -// } - - // Prefix name with the class name if this is a member function - Optional classDtOpt = determineMemberClassType(die); - if (classDtOpt.isPresent() && !name.contains(classDtOpt.get().getName())) { - name = classDtOpt.get().getName() + "::" + name; - } - - - DataType returnDt = extractDataType(die); - FunctionManager functionManager = program.getFunctionManager(); - - // TODO: option - //program.getListing().clearCodeUnits(lowAddr, highAddr, false); - - Function fun = functionManager.getFunctionAt(lowAddr); - if (fun == null) { - try { - AddressSetView funSet = set.intersectRange(lowAddr, highAddr); - fun = functionManager.createFunction(name, lowAddr, funSet, SourceType.IMPORTED); - } catch (InvalidInputException | OverlappingFunctionException e) { - log.appendException(e); - return; - } - } else { - try { - fun.setName(name, SourceType.IMPORTED); - } catch (DuplicateNameException | InvalidInputException e) { - log.appendException(e); - } - } -// try { -// fun.setReturnType(returnDt, SourceType.IMPORTED); -// } catch (InvalidInputException e) { -// log.appendException(e); -// } - - try { - Variable returnParam = new ReturnParameterImpl(returnDt, program); - List params = new ArrayList<>(); - for (DebugInfoEntry childDie : die.getChildren()) { - if (childDie.getTag() == Tag.FORMAL_PARAMETER) { - String paramName = extractName(childDie).orElse(null); - DataType dt = extractDataType(childDie); - params.add(new ParameterImpl(paramName, dt, program)); - } - } - - fun.updateFunction(null, returnParam, params, FunctionUpdateType.DYNAMIC_STORAGE_FORMAL_PARAMS, true, SourceType.IMPORTED); - } catch (DuplicateNameException | InvalidInputException e) { - log.appendException(e); - } - } - - private Optional determineMemberClassType(DebugInfoEntry die) { - // Function defined in class body - if (die.getParent().getTag() == Tag.CLASS_TYPE) { - return Optional.of(getUserDataType(die.getParent().getRef())); - } - // Function defined outside of the class body should have AT_member attribute - Optional memberAttributeOptional = die.getAttribute(AttributeName.MEMBER); - if (memberAttributeOptional.isPresent()) { - return Optional.of(getUserDataType(memberAttributeOptional.get().get())); - } - // Determine the class based on the "this" parameter because for some compilers (e.g. PS2) normal - // ways does not work... - for (DebugInfoEntry childDie : die.getChildren()) { - if (childDie.getTag() == Tag.FORMAL_PARAMETER && Optional.of("this").equals(extractName(childDie))) { - DataType dt = extractDataType(childDie); - if (dt instanceof Pointer) { - dt = ((Pointer) dt).getDataType(); - } - return Optional.of(dt); - } - } - return Optional.empty(); - } - - 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/DWARF1SectionNames.java similarity index 52% rename from src/main/java/com/github/rafalh/ghidra/dwarfone/SectionNames.java rename to src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1SectionNames.java index 9ec8d2e..d277054 100644 --- a/src/main/java/com/github/rafalh/ghidra/dwarfone/SectionNames.java +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1SectionNames.java @@ -1,5 +1,9 @@ package com.github.rafalh.ghidra.dwarfone; -public class SectionNames { +public class DWARF1SectionNames { public static final String DEBUG = "debug"; + + private DWARF1SectionNames() { + // empty + } } diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeExtractor.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeExtractor.java new file mode 100644 index 0000000..baaf1ba --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeExtractor.java @@ -0,0 +1,131 @@ +package com.github.rafalh.ghidra.dwarfone; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; +import java.util.Optional; + +import com.github.rafalh.ghidra.dwarfone.model.AttributeName; +import com.github.rafalh.ghidra.dwarfone.model.AttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.BlockAttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.ConstAttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.DebugInfoEntry; +import com.github.rafalh.ghidra.dwarfone.model.FundamentalType; +import com.github.rafalh.ghidra.dwarfone.model.RefAttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.TypeModifier; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteArrayProvider; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.PointerDataType; + +public class DWARF1TypeExtractor { + + private final MessageLog log; + private final DWARF1Program program; + private final DWARF1TypeManager dwarfTypeManager; + + public DWARF1TypeExtractor(DWARF1Program program, MessageLog log, DWARF1TypeManager dwarfTypeManager) { + this.program = program; + this.log = log; + this.dwarfTypeManager = dwarfTypeManager; + } + + DataType extractDataType(DebugInfoEntry die) { + Optional fundTypeOpt = die.getAttribute(AttributeName.FUND_TYPE) + .map(ConstAttributeValue::get) + .map(Number::intValue) + .map(FundamentalType::fromValue); + Optional userDefTypeOpt = die.getAttribute(AttributeName.USER_DEF_TYPE); + Optional modFundTypeOpt = die.getAttribute(AttributeName.MOD_FUND_TYPE) + .map(av -> av.get()); + Optional modUserDefTypeOpt = die.getAttribute(AttributeName.MOD_U_D_TYPE) + .map(av -> av.get()); + if (fundTypeOpt.isPresent()) { + var ftDt = dwarfTypeManager.convertFundamentalTypeToDataType(fundTypeOpt.get()); + if (ftDt == null) { + log.appendMsg("failed to map ft to dt: " + fundTypeOpt.get()); + } + return Optional.ofNullable(ftDt).orElse(DataType.DEFAULT); + } + if (modFundTypeOpt.isPresent()) { + return decodeModFundType(modFundTypeOpt.get()); + } + if (userDefTypeOpt.isPresent()) { + return dwarfTypeManager.getUserDataType(userDefTypeOpt.get().get()); + } + if (modUserDefTypeOpt.isPresent()) { + return decodeModUserDefType(modUserDefTypeOpt.get()); + } + log.appendMsg("Unknown type " + die); + return DataType.DEFAULT; + } + + DataType extractDataType(AttributeName at, AttributeValue av) { + switch (at) { + case FUND_TYPE: + FundamentalType ft = FundamentalType.fromValue(((ConstAttributeValue) av).get().intValue()); + return dwarfTypeManager.convertFundamentalTypeToDataType(ft); + case USER_DEF_TYPE: + return dwarfTypeManager.getUserDataType(((RefAttributeValue) av).get()); + case MOD_FUND_TYPE: + return decodeModFundType(((BlockAttributeValue) av).get()); + case MOD_U_D_TYPE: + return decodeModUserDefType(((BlockAttributeValue) av).get()); + default: + throw new IllegalArgumentException("Unsupported type attribute " + at); + } + } + + private DataType decodeModFundType(byte[] data) { + var bp = new ByteArrayProvider(data); + BinaryReader br = new BinaryReader(bp, program.isLittleEndian()); + FundamentalType ft; + List mods = new ArrayList<>(); + long maxOffset = bp.length() - 2; + try { + while (br.getPointerIndex() < maxOffset) { + mods.add(TypeModifier.fromValue(br.readNextUnsignedByte())); + } + ft = FundamentalType.fromValue(br.readNextUnsignedShort()); + } catch (IOException e) { + throw new IllegalStateException("Failed to decode mod fund type", e); + } + DataType baseDt = dwarfTypeManager.convertFundamentalTypeToDataType(ft); + return applyTypeModifiers(mods, baseDt); + } + + private DataType decodeModUserDefType(byte[] data) { + var bp = new ByteArrayProvider(data); + BinaryReader br = new BinaryReader(bp, program.isLittleEndian()); + Long udtRef; + List mods = new ArrayList<>(); + long maxOffset = bp.length() - 4; + try { + while (br.getPointerIndex() < maxOffset) { + mods.add(TypeModifier.fromValue(br.readNextUnsignedByte())); + } + udtRef = br.readNextUnsignedInt(); + } catch (IOException e) { + throw new IllegalStateException("Failed to decode mod ud type", e); + } + DataType baseDt = dwarfTypeManager.getUserDataType(udtRef); + return applyTypeModifiers(mods, baseDt); + } + + private DataType applyTypeModifiers(List mods, DataType dt) { + // apply modifiers in reverse order + ListIterator it = mods.listIterator(mods.size()); + while (it.hasPrevious()) { + TypeModifier mod = it.previous(); + if (mod == TypeModifier.POINTER_TO || mod == TypeModifier.REFERENCE_TO) { + dt = new PointerDataType(dt); + } + } + return dt; + } + + +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeImporter.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeImporter.java new file mode 100644 index 0000000..aa02f80 --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeImporter.java @@ -0,0 +1,263 @@ +package com.github.rafalh.ghidra.dwarfone; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.github.rafalh.ghidra.dwarfone.model.AttributeName; +import com.github.rafalh.ghidra.dwarfone.model.AttributeUtils; +import com.github.rafalh.ghidra.dwarfone.model.AttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.BlockAttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.ConstAttributeValue; +import com.github.rafalh.ghidra.dwarfone.model.DebugInfoEntry; +import com.github.rafalh.ghidra.dwarfone.model.Format; +import com.github.rafalh.ghidra.dwarfone.model.FundamentalType; +import com.github.rafalh.ghidra.dwarfone.model.LocationAtomOp; +import com.github.rafalh.ghidra.dwarfone.model.LocationDescription; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteArrayProvider; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.data.ArrayDataType; +import ghidra.program.model.data.CategoryPath; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeConflictHandler; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.data.EnumDataType; +import ghidra.program.model.data.PointerDataType; +import ghidra.program.model.data.Structure; +import ghidra.program.model.data.StructureDataType; +import ghidra.program.model.data.Union; +import ghidra.program.model.data.UnionDataType; + +public class DWARF1TypeImporter { + + private final DWARF1Program program; + private final MessageLog log; + private final CategoryPath categoryPath; + private final DWARF1TypeManager dwarfTypeManager; + private final DWARF1TypeExtractor typeExtractor; + + public DWARF1TypeImporter(DWARF1Program program, MessageLog log, DWARF1TypeManager dwarfTypeManager, DWARF1TypeExtractor typeExtractor) { + this.program = program; + this.log = log; + this.dwarfTypeManager = dwarfTypeManager; + this.typeExtractor = typeExtractor; + this.categoryPath = new CategoryPath("/DWARF"); + } + + Optional processTypeDebugInfoEntry(DebugInfoEntry die) { + try { + switch (die.getTag()) { + case CLASS_TYPE: + case STRUCTURE_TYPE: + return processClassType(die); + case UNION_TYPE: + return processUnionType(die); + case ENUMERATION_TYPE: + return processEnumType(die); + case ARRAY_TYPE: + return processArrayType(die); + case SUBROUTINE_TYPE: + return processSubrountineType(die); + case TYPEDEF: + case STRING_TYPE: + case POINTER_TYPE: + case PTR_TO_MEMBER_TYPE: + case SET_TYPE: + case SUBRANGE_TYPE: + // TODO + log.appendMsg("Skipping type: " + die); + return Optional.empty(); + default: + // skip other tags + return Optional.empty(); + } + } catch (Exception e) { + throw new RuntimeException("Failed to process type debug info entry " + die, e); + } + } + + private Optional processSubrountineType(DebugInfoEntry die) { + // TODO + var dt = new PointerDataType(DataType.VOID); + dwarfTypeManager.registerType(die.getRef(), dt); + return Optional.of(dt); + } + + private Optional processArrayType(DebugInfoEntry die) throws IOException { + byte[] subscrData = die.getAttribute(AttributeName.SUBSCR_DATA) + .map(av -> av.get()) + .orElseThrow(() -> new IllegalArgumentException("array type without subscr_data " + die)); + var bp = new ByteArrayProvider(subscrData); + List dims = new ArrayList<>(); + DataType baseDt = null; + BinaryReader br = new BinaryReader(bp, program.isLittleEndian()); + while (br.getPointerIndex() < bp.length()) { + Format fmt = Format.decode(br.readNextByte()); + if (fmt == Format.ET) { + Map.Entry attributeEntry = AttributeUtils.readAttribute(br); + var at = AttributeName.decode(attributeEntry.getKey()); + var av = attributeEntry.getValue(); + baseDt = typeExtractor.extractDataType(at, av); + } else if (fmt == Format.FT_C_C) { + // type of index - unused + FundamentalType.fromValue(br.readNextUnsignedShort()); + int begin = br.readNextInt(); + int end = br.readNextInt(); + dims.add(end - begin); + } else { + log.appendMsg("Unsupported format " + fmt + " in " + die); + break; + } + } + if (baseDt == null) { + return Optional.empty(); + } + DataType dt = baseDt; + Collections.reverse(dims); + for (int dim : dims) { + if (dim <= 0) { + log.appendMsg("Bad array dim " + dim + " in " + die); + return Optional.empty(); + } + dt = new ArrayDataType(dt, dim, -1); + } + dwarfTypeManager.registerType(die.getRef(), dt); + return Optional.of(dt); + } + + private Optional processClassType(DebugInfoEntry die) { + Optional byteSizeOpt = die.getAttribute(AttributeName.BYTE_SIZE).map(av -> av.get()); + if (byteSizeOpt.isEmpty()) { + log.appendMsg("Skipping structure without size " + die); + return Optional.empty(); + } + String name = DWARF1ImportUtils.extractName(die) + // FIXME: anonymous class? + .filter(n -> !n.equals("@class")) + .orElseGet(() -> "anon_" + die.getRef()); + int size = byteSizeOpt.get().intValue(); + DataTypeManager dataTypeManager = program.getDataTypeManager(); + DataType existingDt = dataTypeManager.getDataType(categoryPath, name); + if (existingDt != null) { + // already imported + dwarfTypeManager.registerType(die.getRef(), existingDt); + return Optional.of(existingDt); + } + StructureDataType sdt = new StructureDataType(categoryPath, name, size, dataTypeManager); + Structure newDt = (Structure) dataTypeManager.addDataType(sdt, DataTypeConflictHandler.DEFAULT_HANDLER); + dwarfTypeManager.registerType(die.getRef(), newDt); + //log.appendMsg("Struct " + name); + for (DebugInfoEntry childDie : die.getChildren()) { + switch (childDie.getTag()) { + case MEMBER: + processClassTypeMember(newDt, childDie); + break; + case INHERITANCE: + processClassTypeInheritance(newDt, childDie); + break; + default: + log.appendMsg("Unexpected child of class type: " + childDie.getTag()); + } + } + return Optional.of(newDt); + } + + private void processClassTypeInheritance(Structure sdt, DebugInfoEntry die) { + DataType baseDt = typeExtractor.extractDataType(die); + sdt.replaceAtOffset(0, baseDt, -1, "__base", null); + } + + private void processClassTypeMember(Structure sdt, DebugInfoEntry die) { + String memberName = DWARF1ImportUtils.extractName(die).orElse(null); + //log.appendMsg("Member " + childNameOpt); + DataType memberDt = typeExtractor.extractDataType(die); + int memberOffset = extractMemberOffset(die); + assert memberDt != null; + if (memberOffset >= sdt.getLength()) { + // TODO: memberDt.isDynamicallySized() + return; + } + sdt.replaceAtOffset(memberOffset, memberDt, -1, memberName, null); + } + + private Optional processUnionType(DebugInfoEntry die) { + String name = DWARF1ImportUtils.extractName(die).orElseGet(() -> "anon_" + die.getRef()); + DataTypeManager dataTypeManager = program.getDataTypeManager(); + DataType existingDt = dataTypeManager.getDataType(categoryPath, name); + if (existingDt != null) { + // already imported + dwarfTypeManager.registerType(die.getRef(), existingDt); + return Optional.of(existingDt); + } + UnionDataType udt = new UnionDataType(categoryPath, name, dataTypeManager); + Union newDt = (Union) dataTypeManager.addDataType(udt, DataTypeConflictHandler.DEFAULT_HANDLER); + dwarfTypeManager.registerType(die.getRef(), newDt); + //log.appendMsg("Struct " + name); + for (DebugInfoEntry childDie : die.getChildren()) { + switch (childDie.getTag()) { + case MEMBER: + processUnionTypeMember(newDt, childDie); + break; + default: + log.appendMsg("Unexpected child of union type: " + childDie.getTag()); + } + } + return Optional.of(newDt); + } + + private void processUnionTypeMember(Union union, DebugInfoEntry die) { + String memberName = DWARF1ImportUtils.extractName(die).orElse(null); + //log.appendMsg("Member " + childNameOpt); + DataType memberDt = typeExtractor.extractDataType(die); + union.add(memberDt, memberName, null); + } + + private Optional processEnumType(DebugInfoEntry die) throws IOException { + Optional byteSizeOpt = die.getAttribute(AttributeName.BYTE_SIZE).map(av -> av.get()); + Optional elementListOpt = die.getAttribute(AttributeName.ELEMENT_LIST).map(av -> av.get()); + + String name = DWARF1ImportUtils.extractName(die).orElseGet(() -> "anon_" + die.getRef()); + DataTypeManager dataTypeManager = program.getDataTypeManager(); + DataType existingDt = dataTypeManager.getDataType(categoryPath, name); + if (existingDt != null) { + // already imported? + dwarfTypeManager.registerType(die.getRef(), existingDt); + return Optional.of(existingDt); + } + + int size = byteSizeOpt.orElse(4).intValue(); + var edt = new EnumDataType(categoryPath, name, size); + if (elementListOpt.isPresent()) { + processEnumElementList(edt, elementListOpt.get(), size); + } + + DataType newDt = dataTypeManager.addDataType(edt, DataTypeConflictHandler.DEFAULT_HANDLER); + dwarfTypeManager.registerType(die.getRef(), newDt); + return Optional.of(newDt); + } + + private void processEnumElementList(EnumDataType edt, byte[] encodedElementList, int size) throws IOException { + var bp = new ByteArrayProvider(encodedElementList); + BinaryReader br = new BinaryReader(bp, program.isLittleEndian()); + while (br.getPointerIndex() < bp.length()) { + long value = br.readNextInt(); // FIXME: should use machine specific FT_long size + String name = br.readNextAsciiString(); + edt.add(name, value); + } + } + + private int extractMemberOffset(DebugInfoEntry die) { + LocationDescription location = DWARF1ImportUtils.extractLocation(die, program) + .orElseThrow(() -> new IllegalArgumentException("expected location in " + die)); + var atoms = location.getAtoms(); + if (atoms.size() == 2 && atoms.get(0).getOp() == LocationAtomOp.CONST && atoms.get(1).getOp() == LocationAtomOp.ADD) { + return atoms.get(0).getArg().intValue(); + } + throw new IllegalArgumentException("unparsable member location " + atoms); + } +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeManager.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeManager.java new file mode 100644 index 0000000..01feb5f --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1TypeManager.java @@ -0,0 +1,94 @@ +package com.github.rafalh.ghidra.dwarfone; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.github.rafalh.ghidra.dwarfone.model.FundamentalType; + +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.data.BuiltInDataTypeManager; +import ghidra.program.model.data.CategoryPath; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeManager; + +public class DWARF1TypeManager { + + private final DWARF1Program program; + private final MessageLog log; + private DWARF1TypeImporter dwarfTypeImporter; + + private final Map userDataTypeMap = new HashMap<>(); + + public DWARF1TypeManager(DWARF1Program program, MessageLog log) { + this.program = program; + this.log = log; + } + + DataType getUserDataType(long ref) { + var dtOpt = Optional.ofNullable(userDataTypeMap.get(ref)); + if (dtOpt.isEmpty()) { + // FIXME: dirty fix, may cause infinite recursion... + Optional.ofNullable(program.getDebugInfoEntry(ref)) + .ifPresent(die -> { + dwarfTypeImporter.processTypeDebugInfoEntry(die); + }); + // try again... + dtOpt = Optional.ofNullable(userDataTypeMap.get(ref)); + } + if (dtOpt.isEmpty()) { + log.appendMsg("Cannot find user type " + Long.toHexString(ref)); + } + return dtOpt.orElse(DataType.DEFAULT); + } + + DataType convertFundamentalTypeToDataType(FundamentalType ft) { + DataTypeManager dataTypeManager = BuiltInDataTypeManager.getDataTypeManager(); + switch (ft) { + case CHAR: + return dataTypeManager.getDataType(CategoryPath.ROOT, "char"); + case SIGNED_CHAR: + return dataTypeManager.getDataType(CategoryPath.ROOT, "schar"); + case UNSIGNED_CHAR: + return dataTypeManager.getDataType(CategoryPath.ROOT, "uchar"); + case SHORT: + case SIGNED_SHORT: + return dataTypeManager.getDataType(CategoryPath.ROOT, "short"); + case UNSIGNED_SHORT: + return dataTypeManager.getDataType(CategoryPath.ROOT, "ushort"); + case INTEGER: + case SIGNED_INTEGER: + return dataTypeManager.getDataType(CategoryPath.ROOT, "int"); + case UNSIGNED_INTEGER: + return dataTypeManager.getDataType(CategoryPath.ROOT, "uint"); + case LONG: + case SIGNED_LONG: + return dataTypeManager.getDataType(CategoryPath.ROOT, "long"); + case UNSIGNED_LONG: + return dataTypeManager.getDataType(CategoryPath.ROOT, "ulong"); + case POINTER: + return dataTypeManager.getDataType(CategoryPath.ROOT, "pointer"); + case FLOAT: + return dataTypeManager.getDataType(CategoryPath.ROOT, "float"); + case DBL_PREC_FLOAT: + return dataTypeManager.getDataType(CategoryPath.ROOT, "double"); + case EXT_PREC_FLOAT: + return dataTypeManager.getDataType(CategoryPath.ROOT, "longdouble"); + case VOID: + return DataType.VOID; + case BOOLEAN: + return dataTypeManager.getDataType(CategoryPath.ROOT, "bool"); + default: + return DataType.DEFAULT; + } + } + + public void registerType(long ref, DataType dt) { + userDataTypeMap.put(ref, dt); + } + + public void setTypeImporter(DWARF1TypeImporter dwarfTypeImporter) { + this.dwarfTypeImporter = dwarfTypeImporter; + } + +} diff --git a/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1VariableImporter.java b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1VariableImporter.java new file mode 100644 index 0000000..0db4cbd --- /dev/null +++ b/src/main/java/com/github/rafalh/ghidra/dwarfone/DWARF1VariableImporter.java @@ -0,0 +1,80 @@ +package com.github.rafalh.ghidra.dwarfone; + +import java.util.Optional; + +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.Tag; + +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeConflictException; +import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.util.exception.InvalidInputException; + +public class DWARF1VariableImporter { + private final DWARF1Program dwarfProgram; + private final MessageLog log; + private final DWARF1TypeExtractor typeExtractor; + + DWARF1VariableImporter(DWARF1Program dwarfProgram, MessageLog log, DWARF1TypeExtractor typeExtractor) { + this.dwarfProgram = dwarfProgram; + this.log = log; + this.typeExtractor = typeExtractor; + } + + void processGlobalVariable(DebugInfoEntry die) { + Optional nameOptional = DWARF1ImportUtils.extractName(die); + Optional locationOptional = DWARF1ImportUtils.extractLocation(die, dwarfProgram); + if (nameOptional.isEmpty() || locationOptional.isEmpty()) { + return; + } + String name = nameOptional.get(); + LocationDescription location = locationOptional.get(); + Long offset = offsetFromLocation(location); + //log.appendMsg(name + " " + Long.toHexString(offset)); + if (offset == 0) { + log.appendMsg("Skipping variable with null address: " + name); + return; + } + Address addr = dwarfProgram.toAddr(offset); + try { + dwarfProgram.getProgram().getSymbolTable().createLabel(addr, name, SourceType.IMPORTED); + } catch (InvalidInputException e) { + log.appendException(e); + } + + DataType dt = typeExtractor.extractDataType(die); + + if (!dt.isDynamicallySized() && dt.getLength() > 0) { + try { + // a bit brutal... there should be an option for clearing + dwarfProgram.getProgram().getListing().clearCodeUnits(addr, addr.add(dt.getLength()), false); + dwarfProgram.getProgram().getListing().createData(addr, dt); + } catch (CodeUnitInsertionException | DataTypeConflictException e) { + log.appendException(e); + } + } + } + + void processLocalVariable(DebugInfoEntry die) { + if (die.getParent().getTag() != Tag.COMPILE_UNIT) { + // ignore parameters and local variables + // we are only interested in static variables + return; + } + processGlobalVariable(die); + } + + 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; + } +}