ghidra icon indicating copy to clipboard operation
ghidra copied to clipboard

ExportToHeaderAction: pointer to array has incorrect type

Open mattiasgrenfeldt opened this issue 1 year ago • 2 comments

Describe the bug When I export my types to a C header file using the ExportToHeaderAction, fields in structs that are pointers to arrays will get an incorrect declaration in the header file.

To Reproduce Steps to reproduce the behavior:

  1. Make a struct having one field with the type int[12]*. Like so: image
  2. Export the type to a C header by right clicking the type in the Data Type Manager and clicking "Export C Header...".
  3. The result will look like this:
typedef unsigned char   undefined;

typedef unsigned char    byte;
typedef unsigned char    dwfenc;
typedef unsigned int    dword;
typedef unsigned long    qword;
typedef unsigned int    undefined4;
typedef unsigned long    undefined8;
typedef unsigned short    word;
typedef struct structA structA, *PstructA;

struct structA {
    int[12] * something;
};

When I try to compile it using gcc, it's not happy about the something field.

Expected behavior From what I've found online, structA should instead look like this:

struct structA {
    int (*something)[12];
};

Environment:

  • Ghidra Version: 10.2.3

mattiasgrenfeldt avatar Apr 21 '23 13:04 mattiasgrenfeldt

Similarly to my other issue #5240 , the decompiler view and export seems to handle this well: image

mattiasgrenfeldt avatar Apr 21 '23 13:04 mattiasgrenfeldt

My current workaround is to make a copy of Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java and change getTypeDeclaration:

private String getTypeDeclaration(/* ... */){
    // ...
    if (componentString == null) {
        if (dataType instanceof BitFieldDataType) {
            BitFieldDataType bfDt = (BitFieldDataType) dataType;
            name += ":" + bfDt.getDeclaredBitSize();
            dataType = bfDt.getBaseDataType();
        } else if (dataType instanceof Array) {
            Array array = (Array) dataType;
            name += getArrayDimensions(array);
            dataType = getArrayBaseType(array);
        } else if (dataType instanceof Pointer pointer) { // <-- Added this else if and its body
            DataType element = getPointerBaseDataType(pointer);
            if (element != null) {
                name = "*" + name;
                dataType = element;
                if (dataType instanceof Array array) {
                    name = "(" + name + ")" + getArrayDimensions(array);
                    dataType = getArrayBaseType(array);
                }
            }
        }
        // ...
    }
    // ...
}

EDIT: This "workaround" was incorrect, I rewrote the code to something else that worked later.

mattiasgrenfeldt avatar Apr 24 '23 09:04 mattiasgrenfeldt

EDIT: This "workaround" was incorrect, I rewrote the code to something else that worked later.

@mattiasgrenfeldt Apologies for taking so long to get to this, but I'm preparing to address it. I'm curious if you'd like to share your final solution.

ryanmkurtz avatar Sep 11 '23 16:09 ryanmkurtz

I assume you added some support for pointers to pointers to pointers, for example:

struct structA {
    int (*****something)[12];
};

ryanmkurtz avatar Sep 11 '23 19:09 ryanmkurtz

Hello Ryan! Sorry for not answering earlier, but here is what I changed in getTypeDeclaration:

        if (componentString == null) {
            if (dataType instanceof BitFieldDataType) {
                BitFieldDataType bfDt = (BitFieldDataType) dataType;
                name += ":" + bfDt.getDeclaredBitSize();
                dataType = bfDt.getBaseDataType();
            }
            // Remove the else ifs and add a while
            while (true) {
                if (dataType instanceof Array array) {
                    name += "[" + array.getNumElements() + "]";
                    dataType = array.getDataType();
                } else if (dataType instanceof Pointer pointer) {
                    DataType elem = pointer.getDataType();
                    if (elem == null) {
                        break;
                    }
                    name = "*" + name;
                    dataType = elem;
                    if (dataType instanceof Array) {
                        name = "(" + name + ")";
                    }
                } else {
                    break;
                }
            }
            // End of modification

            DataType baseDataType = getBaseDataType(dataType);
            if (baseDataType instanceof FunctionDefinition) {
                componentString = getFunctionPointerString((FunctionDefinition) baseDataType, name,
                        dataType, writeEnabled, monitor);
            } else {
                componentString = getDataTypePrefix(dataType) + cleanIdentifier(dataType.getDisplayName());
                if (name.length() != 0) {
                    componentString += " " + name;
                }
            }
            sb.append(componentString);
        }

Because it might not just be pointers in series, there might be any combination of pointer to array of pointers to arrays, etc. Example

struct structA {
    int (*(*****something)[12])[2][3];
};

mattiasgrenfeldt avatar Sep 18 '23 09:09 mattiasgrenfeldt

@mattiasgrenfeldt, thanks, i've tested this and will go with your final implementation.

ryanmkurtz avatar Sep 20 '23 13:09 ryanmkurtz