diff --git a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj index 49d6bd44d4e..9de5cdb7fca 100644 --- a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj +++ b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj @@ -517,35 +517,54 @@ public class CParser { } - private DataType allocateEnumDT(Token t, ArrayList list) { - String enumName = (t != null ? t.image : ("enum_" + cnt++)); - - // get the normal enum size, which is an int - // TODO: allow for packing of enum to smallest value with either dataOrganization, or packing flag - int normalEnumLen = (dtMgr != null ? dtMgr.getDataOrganization().getIntegerSize() : 4); + private DataType allocateEnumDT(Token t, Declaration underlyingTypeDecl, ArrayList list, boolean isPacked) { + String enumName = (t != null ? t.image : ("enum_" + cnt++)); + int defaultSize = (dtMgr != null && dtMgr.getDataOrganization() != null ? dtMgr.getDataOrganization().getIntegerSize() : 4); + int initialEnumSize = defaultSize; - // create an initial enum and add all new members - EnumDataType enumDT= new EnumDataType(getCurrentCategoryPath(), enumName, 8, dtMgr); + if (underlyingTypeDecl != null) { + DataType resolvedUnderlyingType = underlyingTypeDecl.getDataType(); + if (resolvedUnderlyingType == null) { + addNearParseMessage("Error: Enum '" + enumName + "' has undefined underlying type '" + + underlyingTypeDecl.getName() + "'. Using default integer size (" + defaultSize + ")."); + } else { + int typeLength = resolvedUnderlyingType.getLength(); + if (typeLength == 1 || typeLength == 2 || typeLength == 4 || typeLength == 8) { + initialEnumSize = typeLength; + } else { + addNearParseMessage("Warning: Enum '" + enumName + "' specified underlying type '" + + resolvedUnderlyingType.getDisplayName() + + "' has a non-standard length (" + typeLength + + "). Enums should have a length of 1, 2, 4, or 8. Using default integer size (" + defaultSize + ")."); + } + + if (!(resolvedUnderlyingType instanceof AbstractIntegerDataType)) { + addNearParseMessage("Warning: Enum '" + enumName + "' has underlying type '" + + resolvedUnderlyingType.getDisplayName() + + "' which is not a standard C integer or char type. Behavior may be unexpected."); + } + } + } + + EnumDataType enumDT = new EnumDataType(getCurrentCategoryPath(), enumName, initialEnumSize, dtMgr); if (list != null) { for (EnumMember member : list) { try { - enumDT.add(member.name, member.value); + enumDT.add(member.name, member.value); } catch (IllegalArgumentException exc) { addNearParseMessage("duplicate enum value: " + enumName + " : " + member.name + " : " + member.value); } } - // get the minimum length to represent the values and resize if too big - int minLen = enumDT.getMinimumPossibleLength(); - if (minLen > normalEnumLen) { - enumDT.setLength(minLen); - } else { - enumDT.setLength(normalEnumLen); - } + } + + int minLen = enumDT.getMinimumPossibleLength(); + int finalEnumLength; + if (underlyingTypeDecl == null && isPacked) { + finalEnumLength = minLen; } else { - // length doesn't really matter, forward declaration with no values - enumDT.setLength(normalEnumLen); + finalEnumLength = Math.max(minLen, initialEnumSize); } - + enumDT.setLength(finalEnumLength); return addDef(enums, enumDT.getName(), enumDT); } @@ -1211,7 +1230,7 @@ TOKEN : | | - + | | @@ -1835,7 +1854,7 @@ Declaration TypeQualifier(Declaration dec) : {} | | | - | + { dec.addQualifier(PACKED); } | | ( DeclSpec(dec) ) ) @@ -2255,23 +2274,33 @@ DataType EnumSpecifier() : { Token t= null; DataType dt; ArrayList list; - Declaration dec = new Declaration(); + Declaration attributeDecl = new Declaration(); + Declaration typeDecl = null; + boolean isPacked = false; } { + [ LOOKAHEAD(2) AttributeSpecList(attributeDecl) ] ( - LOOKAHEAD(3) - [AttributeSpecList(dec)] [ t= ] "{" list= EnumeratorList() "}" + LOOKAHEAD(4) + [ t= ] + [ LOOKAHEAD(2) ":" typeDecl= TypeName() ] + "{" list= EnumeratorList() "}" + [ LOOKAHEAD(2) AttributeSpecList(attributeDecl) ] { - dt = allocateEnumDT(t, list); + isPacked = attributeDecl.getQualifiers().contains(PACKED); + dt = allocateEnumDT(t, typeDecl, list, isPacked); } | t= + [ LOOKAHEAD(2) ":" typeDecl= TypeName() ] + [ LOOKAHEAD(2) AttributeSpecList(attributeDecl) ] { dt= getEnumDef(t.image); // not defined yet, define an empty one if (dt == null) { - dt = allocateEnumDT(t, null); + isPacked = attributeDecl.getQualifiers().contains(PACKED); + dt = allocateEnumDT(t, typeDecl, null, isPacked); } } ) diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java index 4ab5fdbeeac..d0096b59a4f 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java @@ -479,6 +479,69 @@ public void testHeaderParsing() throws Exception { ((Enum) dt).getValue("SHIFTED3")); assertEquals("enum options_enum not correct", 15 >> 3 << 3, ((Enum) dt).getValue("SHIFTED4")); + + dt = dtMgr.getDataType(new CategoryPath("/"), "_C23_enum_char"); + assertTrue(dt instanceof Enum); + assertEquals("enum _C23_enum_char size not correct", 1, dt.getLength()); + dt = dtMgr.getDataType(new CategoryPath("/"), "_C23_enum_short"); + assertTrue(dt instanceof Enum); + assertEquals("enum _C23_enum_short size not correct", 2, dt.getLength()); + dt = dtMgr.getDataType(new CategoryPath("/"), "_C23_enum_int"); + assertTrue(dt instanceof Enum); + assertEquals("enum _C23_enum_int size not correct", 4, dt.getLength()); + dt = dtMgr.getDataType(new CategoryPath("/"), "_C23_enum_long"); + assertTrue(dt instanceof Enum); + assertEquals("enum _C23_enum_long size not correct", 4, dt.getLength()); + dt = dtMgr.getDataType(new CategoryPath("/"), "_C23_enum_longlong"); + assertTrue(dt instanceof Enum); + assertEquals("enum _C23_enum_longlong size not correct", 8, dt.getLength()); + dt = dtMgr.getDataType(new CategoryPath("/"), "_C23_enum_DWORD"); + assertTrue(dt instanceof Enum); + assertEquals("enum _C23_enum_DWORD size not correct", 4, dt.getLength()); + + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_style_1"); + assertTrue(dt instanceof Enum); + assertEquals("enum packed_enum_style_1 size not correct", 1, dt.getLength()); + + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_style_2"); + assertTrue(dt instanceof Enum); + assertEquals("enum packed_enum_style_2 size not correct", 1, dt.getLength()); + + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_cpp_style_1"); + assertTrue(dt instanceof Enum); + assertEquals("enum packed_enum_cpp_style_1 size not correct", 1, dt.getLength()); + + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_cpp_style_2"); + assertTrue(dt instanceof Enum); + assertEquals("enum packed_enum_cpp_style_2 size not correct", 1, dt.getLength()); + + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_cpp_style_gnu"); + assertTrue(dt instanceof Enum); + assertEquals("enum packed_enum_cpp_style_gnu size not correct", 1, dt.getLength()); + + dt = dtMgr.getDataType(new CategoryPath("/"), "non_packed_enum"); + assertTrue(dt instanceof Enum); + assertEquals("enum non_packed_enum size not correct", 4, dt.getLength()); + + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_negative_enum"); + assertTrue(dt instanceof Enum); + assertEquals("enum packed_negative_enum size not correct", 1, dt.getLength()); + assertEquals("enum packed_negative_enum value not correct", -1, + ((Enum) dt).getValue("A")); + assertEquals("enum packed_negative_enum value not correct", -2, + ((Enum) dt).getValue("B")); + assertEquals("enum packed_negative_enum value not correct", -3, + ((Enum) dt).getValue("C")); + + dt = dtMgr.getDataType(new CategoryPath("/"), "normal_negative_enum"); + assertTrue(dt instanceof Enum); + assertEquals("enum normal_negative_enum size not correct", 4, dt.getLength()); + assertEquals("enum normal_negative_enum value not correct", -1, + ((Enum) dt).getValue("A")); + assertEquals("enum normal_negative_enum value not correct", -2, + ((Enum) dt).getValue("B")); + assertEquals("enum normal_negative_enum value not correct", -3, + ((Enum) dt).getValue("C")); dt = dtMgr.getDataType(new CategoryPath("/functions"), "__checkint"); assertTrue("not a function", dt instanceof FunctionDefinition); diff --git a/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/CParserTest.h b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/CParserTest.h index 0b536ee0e72..a6cd59e188d 100644 --- a/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/CParserTest.h +++ b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/CParserTest.h @@ -958,6 +958,89 @@ typedef int FuncUseEnum(PARAM_TYPE ptype); typedef enum _PARAM_TYPE { A, B, C } PARAM_TYPE; +/** + ** C23 enum types + **/ +enum _C23_enum_char: char { + A = 1, + B = 2, + C = 3 +}; +enum _C23_enum_short : short +{ + A = 1, + B = 2, + C = 3 +}; +enum _C23_enum_int : int +{ + A = 1, + B = 2, + C = 3 +}; +enum _C23_enum_long : long +{ + A = 1, + B = 2, + C = 3 +}; +enum _C23_enum_longlong : long long +{ + A = 1, + B = 2, + C = 3 +}; +enum _C23_enum_DWORD : DWORD { + A = 1, + B = 2, + C = 3 +}; + + +/** + ** Packed enums + **/ +enum packed_enum_style_1 { + A = 1, + B = 2, + C = 3 +} __attribute__((__packed__)); +enum __attribute__((__packed__)) packed_enum_style_2 { + A = 1, + B = 2, + C = 3 +}; +enum [[packed]] packed_enum_cpp_style_1 { + A = 1, + B = 2, + C = 3 +}; +enum packed_enum_cpp_style_2 { + A = 1, + B = 2, + C = 3 +} [[packed]]; +enum [[gnu::packed]] packed_enum_cpp_style_gnu { + A = 1, + B = 2, + C = 3 +}; +enum non_packed_enum { + D = 4, + E = 5, + F = 6 +}; +enum packed_negative_enum { + A = -1, + B = -2, + C = -3 +} __attribute__((__packed__)); +enum normal_negative_enum { + A = -1, + B = -2, + C = -3 +}; + /** ** Casting **/