Add C23 enum types support for C parser

This commit is contained in:
axd1x8a
2026-03-11 20:02:07 +00:00
committed by emteere
parent d52f314270
commit f91d1df064
3 changed files with 202 additions and 27 deletions
@@ -517,35 +517,54 @@ public class CParser {
}
private DataType allocateEnumDT(Token t, ArrayList<EnumMember> 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<EnumMember> 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);
}
} else {
// length doesn't really matter, forward declaration with no values
enumDT.setLength(normalEnumLen);
}
int minLen = enumDT.getMinimumPossibleLength();
int finalEnumLength;
if (underlyingTypeDecl == null && isPacked) {
finalEnumLength = minLen;
} else {
finalEnumLength = Math.max(minLen, initialEnumSize);
}
enumDT.setLength(finalEnumLength);
return addDef(enums, enumDT.getName(), enumDT);
}
@@ -1211,7 +1230,7 @@ TOKEN :
|
<UNALIGNED : "__unaligned" >
|
<PACKED : "__packed" >
<PACKED : ("__packed" | "__packed__" | "packed") >
|
<ATTRIBUTE : "__attribute" (["_"])* >
|
@@ -1835,7 +1854,7 @@ Declaration TypeQualifier(Declaration dec) : {}
<RESTRICT> |
<EXTENSION> |
<STATIC> |
<PACKED> |
<PACKED> { dec.addQualifier(PACKED); } |
<UNALIGNED> |
( DeclSpec(dec) )
)
@@ -2255,23 +2274,33 @@ DataType EnumSpecifier() : {
Token t= null;
DataType dt;
ArrayList<EnumMember> list;
Declaration dec = new Declaration();
Declaration attributeDecl = new Declaration();
Declaration typeDecl = null;
boolean isPacked = false;
}
{
<ENUM>
[ LOOKAHEAD(2) AttributeSpecList(attributeDecl) ]
(
LOOKAHEAD(3)
[AttributeSpecList(dec)] [ t= <IDENTIFIER> ] "{" list= EnumeratorList() "}"
LOOKAHEAD(4)
[ t= <IDENTIFIER> ]
[ 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= <IDENTIFIER>
[ 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);
}
}
)
@@ -479,6 +479,69 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest {
((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);
@@ -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
**/