From 08b20d5d21981b1080882514410c076701473d1b Mon Sep 17 00:00:00 2001 From: emteere <47253321+emteere@users.noreply.github.com> Date: Tue, 11 Apr 2023 19:48:36 +0000 Subject: [PATCH] GP-2308 Refactored parsing dialog adding includepaths and Achitecture, handle noreturn tags --- Ghidra/Features/Base/certification.manifest | 5 +- .../Base/data/parserprofiles/MacOSX_10.5.prf | 1 - .../Base/data/parserprofiles/MacOSX_10.9.prf | 1 - .../Base/data/parserprofiles/MacOSX_Cocoa.prf | 1 - .../data/parserprofiles/VisualStudio12_32.prf | 16 +- .../data/parserprofiles/VisualStudio12_64.prf | 13 +- .../data/parserprofiles/VisualStudio22_64.prf | 341 ++++++++++ .../data/parserprofiles/VisualStudio9.prf | 13 +- .../Base/data/parserprofiles/clib.prf | 12 +- .../data/parserprofiles/generic_clib_32.prf | 16 +- .../data/parserprofiles/generic_clib_64.prf | 16 +- .../Base/data/parserprofiles/linux_32.prf | 16 +- .../Base/data/parserprofiles/linux_64.prf | 16 +- .../Base/data/parserprofiles/vs12Local.prf | 13 +- .../CreateDefaultGDTArchivesScript.java | 71 ++- .../CreateExampleGDTArchiveScript.java | 56 +- .../plugin/core/cparser/CParserPlugin.java | 212 ++----- .../app/plugin/core/cparser/CParserTask.java | 83 ++- .../app/plugin/core/cparser/ParseDialog.java | 485 +++++++++++--- .../app/util/cparser/C/CParserUtils.java | 299 ++++++--- .../javacc/ghidra/app/util/cparser/C/C.jj | 79 ++- .../javacc/ghidra/app/util/cparser/CPP/CPP.jj | 15 +- .../ParseDialogParsingAndPromptsTest.java | 596 ++++++++++++++++++ .../plugin/core/cparser/ParseDialogTest.java | 22 +- .../ghidra/app/util/cparser/CParserTest.java | 16 +- .../ghidra/app/util/cparser/CParserTest.h | 10 +- .../CreateAVR8GDTArchiveScript.java | 10 +- .../CreateJNIGDTArchivesScript.java | 7 +- 28 files changed, 1948 insertions(+), 493 deletions(-) create mode 100644 Ghidra/Features/Base/data/parserprofiles/VisualStudio22_64.prf create mode 100644 Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogParsingAndPromptsTest.java diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index ce192cb3ef..1c4ad4dfee 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -29,8 +29,9 @@ data/parserprofiles/MacOSX_10.9.prf||GHIDRA||||END| data/parserprofiles/MacOSX_Cocoa.prf||GHIDRA||||END| data/parserprofiles/VisualStudio12_32.prf||GHIDRA||||END| data/parserprofiles/VisualStudio12_64.prf||GHIDRA||||END| -data/parserprofiles/VisualStudio9.prf||GHIDRA||reviewed||END| -data/parserprofiles/clib.prf||GHIDRA||reviewed||END| +data/parserprofiles/VisualStudio22_64.prf||GHIDRA||||END| +data/parserprofiles/VisualStudio9.prf||GHIDRA||||END| +data/parserprofiles/clib.prf||GHIDRA||||END| data/parserprofiles/generic_clib_32.prf||GHIDRA||||END| data/parserprofiles/generic_clib_64.prf||GHIDRA||||END| data/parserprofiles/linux_32.prf||GHIDRA||||END| diff --git a/Ghidra/Features/Base/data/parserprofiles/MacOSX_10.5.prf b/Ghidra/Features/Base/data/parserprofiles/MacOSX_10.5.prf index d6d7265a87..929ce4271e 100644 --- a/Ghidra/Features/Base/data/parserprofiles/MacOSX_10.5.prf +++ b/Ghidra/Features/Base/data/parserprofiles/MacOSX_10.5.prf @@ -207,7 +207,6 @@ zlib.h -DTARGET_API_MAC_OSX -DTARGET_COCOA -DHANDLE="unsigned long" --D_WCHAR_T -D_Complex -Drestrict -D__restrict diff --git a/Ghidra/Features/Base/data/parserprofiles/MacOSX_10.9.prf b/Ghidra/Features/Base/data/parserprofiles/MacOSX_10.9.prf index d084caa7ba..ef2649046e 100644 --- a/Ghidra/Features/Base/data/parserprofiles/MacOSX_10.9.prf +++ b/Ghidra/Features/Base/data/parserprofiles/MacOSX_10.9.prf @@ -323,7 +323,6 @@ zlib.h -D_MAC_ -DTARGET_API_MAC_OSX -DHANDLE="unsigned long" --D_WCHAR_T -D_Complex -Drestrict -D__restrict diff --git a/Ghidra/Features/Base/data/parserprofiles/MacOSX_Cocoa.prf b/Ghidra/Features/Base/data/parserprofiles/MacOSX_Cocoa.prf index ad295be77e..8daa8f9d32 100644 --- a/Ghidra/Features/Base/data/parserprofiles/MacOSX_Cocoa.prf +++ b/Ghidra/Features/Base/data/parserprofiles/MacOSX_Cocoa.prf @@ -28,7 +28,6 @@ Cocoa\Cocoa.h -D_MAC_ -DTARGET_API_MAC_OSX=1 -DHANDLE="unsigned long" --D_WCHAR_T -D_Complex -Drestrict -D__restrict diff --git a/Ghidra/Features/Base/data/parserprofiles/VisualStudio12_32.prf b/Ghidra/Features/Base/data/parserprofiles/VisualStudio12_32.prf index b65e318471..2dd5df02d2 100644 --- a/Ghidra/Features/Base/data/parserprofiles/VisualStudio12_32.prf +++ b/Ghidra/Features/Base/data/parserprofiles/VisualStudio12_32.prf @@ -172,12 +172,8 @@ wpcapi.h wscapi.h wsdapi.h wspiapi.h - rpcproxy.h --I/VC/VS12/src --I/VC/VS12/include --I/VC/SDK/v7.1A/Include -D_M_IX86=300 -D_MSC_VER=1200 -D_INTEGRAL_MAX_BITS=32 @@ -191,7 +187,6 @@ rpcproxy.h -DSTRSAFE_LIB -DSTRSAFE_LIB_IMPL -DLPSKBINFO=LPARAM --D_WCHAR_T_DEFINED -DCONST=const -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE @@ -205,3 +200,14 @@ rpcproxy.h -D_ThrowInfo=ThrowInfo -v0 -D__inner_checkReturn="" + +/data/HeaderFiles/VC/VS12/src +/data/HeaderFiles/VC/VS12/include +/data/HeaderFiles/VC/SDK/v7.1A/Include + +x86:LE:32:default + +windows + + + diff --git a/Ghidra/Features/Base/data/parserprofiles/VisualStudio12_64.prf b/Ghidra/Features/Base/data/parserprofiles/VisualStudio12_64.prf index aa7101f648..b30064ecc3 100644 --- a/Ghidra/Features/Base/data/parserprofiles/VisualStudio12_64.prf +++ b/Ghidra/Features/Base/data/parserprofiles/VisualStudio12_64.prf @@ -172,12 +172,8 @@ wpcapi.h wscapi.h wsdapi.h wspiapi.h - rpcproxy.h --I/VC/VS12/src --I/VC/VS12/include --I/VC/SDK/v7.1A/Include -D_M_IX86=300 -D_MSC_VER=1200 -D_INTEGRAL_MAX_BITS=64 @@ -193,7 +189,6 @@ rpcproxy.h -DSTRSAFE_LIB -DSTRSAFE_LIB_IMPL -DLPSKBINFO=LPARAM --D_WCHAR_T_DEFINED -DCONST=const -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE @@ -208,3 +203,11 @@ rpcproxy.h -D__unaligned="" -v0 -D__inner_checkReturn="" + +/data/HeaderFiles/VC/VS12/include +/data/HeaderFiles/VC/VS12/src +/data/HeaderFiles/VC/SDK/v7.1A/Include + + + + diff --git a/Ghidra/Features/Base/data/parserprofiles/VisualStudio22_64.prf b/Ghidra/Features/Base/data/parserprofiles/VisualStudio22_64.prf new file mode 100644 index 0000000000..88d953a047 --- /dev/null +++ b/Ghidra/Features/Base/data/parserprofiles/VisualStudio22_64.prf @@ -0,0 +1,341 @@ +# Core necessary files +winapifamily.h +winpackagefamily.h +sdkddkver.h +sal.h +no_sal2.h +corecrt.h +wtypes.h +winnt.h +winternl.h +#ntdef.h +# Common headers +dos.h +errno.h +malloc.h +signal.h +stdalign.h +stddef.h +stdio.h +stdlib.h +assert.h +crtdbg.h +ctype.h +conio.h +direct.h +fcntl.h +float.h +fpieee.h +inttypes.h +io.h +locale.h +complex.h +math.h +mbctype.h +mbstring.h +memory.h +minmax.h +new.h +process.h +search.h +share.h +winbase.h +winuser.h +Windows.h +# Security and identity (https://docs.microsoft.com/en-us/windows/win32/api/_security/) +accctrl.h +aclapi.h +aclui.h +advapi32.h +adtgen.h +authz.h +azroles.h +bcrypt.h +casetup.h +ccgplugins.h +celib.h +ntlsa.h +sspi.h +ntsecapi.h +ntsecpkg.h +schannel.h +certadm.h +certbcli.h +certcli.h +certenroll.h +certexit.h +certif.h +certmod.h +certpol.h +certpoleng.h +certsrv.h +certview.h +credssp.h +cryptdlg.h +cryptuiapi.h +cryptxml.h +diagnosticdataquery.h +diagnosticdataquerytypes.h +dpapi.h +dssec.h +iads.h +identitycommon.h +identityprovider.h +identitystore.h +keycredmgr.h +lmaccess.h +lsalookup.h +mmcobj.h +mscat.h +mssip.h +namedpipeapi.h +ncrypt.h +ncryptprotect.h +npapi.h +processthreadsapi.h +sas.h +scesvc.h +sddl.h +securityappcontainer.h +securitybaseapi.h +slpublic.h +subauth.h +tokenbinding.h +tpmvscmgr.h +wincred.h +wincrypt.h +winnetwk.h +winreg.h +winsafer.h +winscard.h +winsvc.h +wintrust.h +winwlx.h +xenroll.h +# Windows sockets +af_irda.h +in6addr.h +mstcpip.h +ws2def.h +winsock.h +winsock2.h +nsemail.h +nspapi.h +socketapi.h +# Nothing includes this; is it necessary? +#sporder.h +transportsettingcommon.h +ws2atm.h +ws2spi.h +mswsock.h +ws2tcpip.h +wsipv6ok.h +wsnwlink.h +wsrm.h +mswsockdef.h +# Remote Procedure Call (RPC) +midles.h +midlbase.h +rpc.h +rpcndr.h +rpcasync.h +rpcdcep.h +rpcnsi.h +rpcproxy.h +rpcssl.h +# COM +accctrl.h +callobj.h +combaseapi.h +comcat.h +ctxtcall.h +dmerror.h +docobj.h +eventsys.h +guiddef.h +iaccess.h +hstring.h +imessagedispatcher.h +messagedispatcherapi.h +objbase.h +objidlbase.h +objidl.h +ocidl.h +ole.h +ole2.h +oledlg.h +oleidl.h +roapi.h +rpcdce.h +servprov.h +shobjidl.h +txlogpub.h +unknwnbase.h +unknwn.h +urlmon.h +vbinterf.h +winddi.h +winerror.h +wtypesbase.h +# COM+ +comadmin.h +mtxdm.h +# More +inspectable.h +# Windows Internet +proofofpossessioncookieinfo.h +wininet.h +winineti.h +# Windows HTTP Services +winhttp.h +# Compression +compressapi.h +# TraceLogging +#traceloggingactivity.h +#traceloggingprovider.h +# Windows Error Reporting +errorrep.h +werapi.h +# Windows and MEssages +olectl.h +windef.h +windowsx.h +# Shell +appmgmt.h +appnotify.h +cpl.h +credentialprovider.h +dimm.h +imagetranscode.h +inputpanelconfiguration.h +intsafe.h +intshcut.h +mobsync.h +objectarray.h +pathcch.h +profinfo.h +propkeydef.h +scrnsave.h +shappmgr.h +shdeprecated.h +shidfact.h +shimgdata.h +shlwapi.h +shtypes.h +storageprovider.h +syncmgr.h +thumbcache.h +thumbnailstreamcache.h +tlogstg.h +userenv.h +# Windows Controls +commctrl.h +commoncontrols.h +dpa_dsa.h +prsht.h +richedit.h +richole.h +shlobj_core.h +shlobj.h +#textserv.h // C++ +tom.h +uxtheme.h +# Menus and other resources +resourceindexer.h +strsafe.h +verrsrc.h +winver.h +# Windows Accessibility Features +oleacc.h +uiautomationcore.h +uiautomationclient.h +uiautomationcoreapi.h +# Internationalization +datetimeapi.h +elscore.h +gb18030.h +imepad.h +imm.h +immdev.h +msime.h +msimeapi.h +muiload.h +spellcheck.h +spellcheckprovider.h +stringapiset.h +usp10.h +winnls.h +# HTTP Server API +#http.h // included by something else +# IP Helper +ifdef.h +inaddr.h +ip2string.h +ipexport.h +iphlpapi.h +icmpapi.h +iprtrmib.h +iptypes.h +netioapi.h +nldef.h +tcpestats.h +ws2ipdef.h +# Network Management +atacct.h +lmalert.h +lmapibuf.h +lmat.h +lmaudit.h +lmconfig.h +lmerrlog.h +lmjoin.h +lmmsg.h +lmremutl.h +lmserver.h +lmsvc.h +lmuse.h +lmwksta.h + +-D_MSC_VER=1924 +-D_INTEGRAL_MAX_BITS=64 +-DWINVER=0x0a00 +-D_WIN32_WINNT=0x0a00 +-D_AMD64_ +-D_M_AMD64 +-D_M_X64 +-D_WIN64 +-D_WIN32 +-D_USE_ATTRIBUTES_FOR_SAL +-D_CRTBLD +-D_OPENMP_NOFORCE_MANIFEST +-DSTRSAFE_LIB +-DSTRSAFE_LIB_IMPL +-DLPSKBINFO=LPARAM +-DCONST=const +-D_CRT_SECURE_NO_WARNINGS +-D_CRT_NONSTDC_NO_DEPRECATE +-D_CRT_NONSTDC_NO_WARNINGS +-D_CRT_OBSOLETE_NO_DEPRECATE +-D_ALLOW_KEYWORD_MACROS +-D_ASSERT_OK +-DSTRSAFE_NO_DEPRECATE +-D__possibly_notnullterminated +"-Dtype_info=\"void *\"", +-D_ThrowInfo=ThrowInfo +-D__unaligned= +-v0 +-D__inner_checkReturn= +-DWINAPI_PARTITION_APP=1 +-DWINAPI_PARTITION_SYSTEM=1 +-DWINAPI_PARTITION_GAMES=1 +-DSECURITY_WIN32 + +/data/HeaderFiles/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include +/data/HeaderFiles/VC/VS22/10.0.19041.0/shared +/data/HeaderFiles/VC/VS22/10.0.19041.0/ucrt +/data/HeaderFiles/VC/VS22/10.0.19041.0/um +/data/HeaderFiles/VC/VS22/10.0.19041.0/winrt + +x86:LE:64:default + +windows diff --git a/Ghidra/Features/Base/data/parserprofiles/VisualStudio9.prf b/Ghidra/Features/Base/data/parserprofiles/VisualStudio9.prf index 3a4b3376b8..d96adeb393 100644 --- a/Ghidra/Features/Base/data/parserprofiles/VisualStudio9.prf +++ b/Ghidra/Features/Base/data/parserprofiles/VisualStudio9.prf @@ -58,13 +58,10 @@ yvals.h CommDlg.h WinUser.h WinNls.h -C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src\internal.h +internal.h strsafe.h penwin.h --IC:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\Include --IC:\Program Files (x86)\Microsoft SDKs\Windows\v5.0\Include --IC:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src -D_M_IX86=500 -D_MSC_VER=9090 -D_MSC_EXTENSIONS @@ -85,5 +82,11 @@ penwin.h -DSTRSAFE_LIB_IMPL -DLPSKBINFO=LPARAM -D_PHNDLR=void * --D_WCHAR_T_DEFINED -DCONST=const + +C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\Include +C:\Program Files (x86)\Microsoft SDKs\Windows\v5.0\Include +C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src + +x86:LE:64:default + diff --git a/Ghidra/Features/Base/data/parserprofiles/clib.prf b/Ghidra/Features/Base/data/parserprofiles/clib.prf index 6cbd35baac..04fa31722d 100644 --- a/Ghidra/Features/Base/data/parserprofiles/clib.prf +++ b/Ghidra/Features/Base/data/parserprofiles/clib.prf @@ -78,13 +78,17 @@ wchar.h wctype.h wordexp.h --IU:\linux_include\include --IU:\linux_include\include\sys --IU:\linux_include\i386-redhat-linux\4.1.2\include -D_X86_ -D__STDC__ -D__WORDSIZE=32 -D__builtin_va_list=void * -D__DO_NOT_DEFINE_COMPILE -D_Complex --D_WCHAR_T + +U:\linux_include\include +U:\linux_include\include\sys +U:\linux_include\i386-redhat-linux\4.1.2\include + +x86:LE:32:default + +gcc diff --git a/Ghidra/Features/Base/data/parserprofiles/generic_clib_32.prf b/Ghidra/Features/Base/data/parserprofiles/generic_clib_32.prf index 3d331f0ffd..188be92604 100644 --- a/Ghidra/Features/Base/data/parserprofiles/generic_clib_32.prf +++ b/Ghidra/Features/Base/data/parserprofiles/generic_clib_32.prf @@ -219,11 +219,6 @@ arpa/nameser_compat.h arpa/telnet.h arpa/tftp.h --I/linux/include --I/linux/include/sys --I/linux/gcc/include --I/linux/x86_64-redhat-linux5E/include --I/linux/x86_64-redhat-linux5E/include/sys -D_X86_ -D__STDC__ -D_GNU_SOURCE @@ -231,10 +226,19 @@ arpa/tftp.h -D__builtin_va_list=void * -D__DO_NOT_DEFINE_COMPILE -D_Complex --D_WCHAR_T -D__NO_STRING_INLINES -D__NO_LONG_DOUBLE_MATH -D__signed__ -D__extension__="" -D__GLIBC_HAVE_LONG_LONG=1 -Daligned_u64=uint64_t + +/data/HeaderFiles/linux/include +/data/HeaderFiles/linux/include/sys +/data/HeaderFiles/linux/gcc/include +/data/HeaderFiles/linux/x86_64-redhat-linux5E/include +/data/HeaderFiles/linux/x86_64-redhat-linux5E/include/sys + +x86:LE:32:default + +gcc diff --git a/Ghidra/Features/Base/data/parserprofiles/generic_clib_64.prf b/Ghidra/Features/Base/data/parserprofiles/generic_clib_64.prf index 0c76e8d1aa..af46b60039 100644 --- a/Ghidra/Features/Base/data/parserprofiles/generic_clib_64.prf +++ b/Ghidra/Features/Base/data/parserprofiles/generic_clib_64.prf @@ -219,11 +219,6 @@ arpa/nameser_compat.h arpa/telnet.h arpa/tftp.h --I/linux/include --I/linux/include/sys --I/linux/gcc/include --I/linux/x86_64-redhat-linux5E/include --I/linux/x86_64-redhat-linux5E/include/sys -D_X86_ -D__STDC__ -D_GNU_SOURCE @@ -231,10 +226,19 @@ arpa/tftp.h -D__builtin_va_list=void * -D__DO_NOT_DEFINE_COMPILE -D_Complex --D_WCHAR_T -D__NO_STRING_INLINES -D__signed__ -D__extension__="" -D__GLIBC_HAVE_LONG_LONG=1 -D__need_sigset_t -Daligned_u64=uint64_t + +/data/HeaderFiles/linux/include +/data/HeaderFiles/linux/include/sys +/data/HeaderFiles/linux/gcc/include +/data/HeaderFiles/linux/x86_64-redhat-linux5E/include +/data/HeaderFiles/linux/x86_64-redhat-linux5E/include/sys + +x86:LE:64:default + +gcc diff --git a/Ghidra/Features/Base/data/parserprofiles/linux_32.prf b/Ghidra/Features/Base/data/parserprofiles/linux_32.prf index 5235499a8a..2c8fe49cf8 100644 --- a/Ghidra/Features/Base/data/parserprofiles/linux_32.prf +++ b/Ghidra/Features/Base/data/parserprofiles/linux_32.prf @@ -515,11 +515,6 @@ mechglue.h auth_gss.h auth_gssapi.h --I/linux/include --I/linux/include/sys --I/linux/gcc/include --I/linux/x86_64-redhat-linux5E/include --I/linux/x86_64-redhat-linux5E/include/sys -D_X86_ -D__STDC__ -D_POSIX_C_SOURCE @@ -528,10 +523,19 @@ auth_gssapi.h -D__builtin_va_list=void * -D__DO_NOT_DEFINE_COMPILE -D_Complex --D_WCHAR_T -D__NO_STRING_INLINES -D__NO_LONG_DOUBLE_MATH -D__signed__ -D__extension__="" -D__GLIBC_HAVE_LONG_LONG=1 -Daligned_u64=uint64_t + +/data/HeaderFiles/linux/include +/data/HeaderFiles/linux/include/sys +/data/HeaderFiles/linux/gcc/include +/data/HeaderFiles/linux/x86_64-redhat-linux5E/include +/data/HeaderFiles/linux/x86_64-redhat-linux5E/include/sys + +x86:LE:32:default + +gcc diff --git a/Ghidra/Features/Base/data/parserprofiles/linux_64.prf b/Ghidra/Features/Base/data/parserprofiles/linux_64.prf index f3c783c33b..2d714aef72 100644 --- a/Ghidra/Features/Base/data/parserprofiles/linux_64.prf +++ b/Ghidra/Features/Base/data/parserprofiles/linux_64.prf @@ -515,11 +515,6 @@ mechglue.h auth_gss.h auth_gssapi.h --I/linux/include --I/linux/include/sys --I/linux/gcc/include --I/linux/x86_64-redhat-linux5E/include --I/linux/x86_64-redhat-linux5E/include/sys -D_X86_ -D__STDC__ -D_POSIX_C_SOURCE @@ -528,9 +523,18 @@ auth_gssapi.h -D__builtin_va_list=void * -D__DO_NOT_DEFINE_COMPILE -D_Complex --D_WCHAR_T -D__NO_STRING_INLINES -D__signed__ -D__extension__="" -D__GLIBC_HAVE_LONG_LONG=1 -Daligned_u64=uint64_t + +/data/HeaderFiles/linux/include +/data/HeaderFiles/linux/include/sys +/data/HeaderFiles/linux/gcc/include +/data/HeaderFiles/linux/x86_64-redhat-linux5E/include +/data/HeaderFiles/linux/x86_64-redhat-linux5E/include/sys + +x86:LE:64:default + +gcc diff --git a/Ghidra/Features/Base/data/parserprofiles/vs12Local.prf b/Ghidra/Features/Base/data/parserprofiles/vs12Local.prf index 7427750503..cdf7b4b8bd 100644 --- a/Ghidra/Features/Base/data/parserprofiles/vs12Local.prf +++ b/Ghidra/Features/Base/data/parserprofiles/vs12Local.prf @@ -222,9 +222,6 @@ wscapi.h wsdapi.h wspiapi.h --I/VisualStudio/VS12/include --I/VisualStudio/Windows/v7.0a/Include --I/VisualStudio/VS12/src -D_M_IX86 -D_MSC_VER=9090 -D_INTEGRAL_MAX_BITS=32 @@ -239,7 +236,6 @@ wspiapi.h -DSTRSAFE_LIB -DSTRSAFE_LIB_IMPL -DLPSKBINFO=LPARAM --D_WCHAR_T_DEFINED -DCONST=const -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE @@ -250,3 +246,12 @@ wspiapi.h -D__possibly_notnullterminated -Dtype_info="void *" -v0 + +/data/HeaderFiles/VC/VS12/include +/data/HeaderFiles/VC/Windows/v7.0a/Include +/data/HeaderFiles/VC/VS12/src + +x86:LE:32:default + +windows + diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java index d84e1341c5..894068140e 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java @@ -31,6 +31,7 @@ import java.io.IOException; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; +import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.FileDataTypeManager; @@ -55,14 +56,16 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { } - private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, String compiler, String[] filenames, String[] args) + private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, String compiler, + String[] filenames, String includePaths[], String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { DataTypeManager openTypes[] = null; - parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, args); + parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, includePaths, args); } - private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler, String[] filenames, String[] args) + private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler, + String[] filenames, String[] includePaths, String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; @@ -71,9 +74,9 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f); - String messages = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, languageID, compiler, null, monitor); + CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, includePaths, args, dtMgr, languageID, compiler, monitor); - Msg.info(this, messages); + Msg.info(this, results.getFormattedParseMessage(null)); dtMgr.save(); dtMgr.close(); @@ -276,14 +279,16 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "wscapi.h", "wsdapi.h", "wspiapi.h", - "rpcproxy.h", }; + String includePaths[] = { + headerFilePath+"/VC/VS12/src", + headerFilePath+"/VC/VS12/include", + headerFilePath+"/VC/SDK/v7.1A/Include", + }; + String args[] = { - "-I"+headerFilePath+"/VC/VS12/src", - "-I"+headerFilePath+"/VC/VS12/include", - "-I"+headerFilePath+"/VC/SDK/v7.1A/Include", "-D_M_IX86=300", "-D_MSC_VER=1200", "-D_INTEGRAL_MAX_BITS=32", @@ -294,8 +299,6 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "-D_USE_ATTRIBUTES_FOR_SAL", "-D_CRTBLD", "-D_OPENMP_NOFORCE_MANIFEST", - "-DSTRSAFE_LIB", - "-DSTRSAFE_LIB_IMPL", "-DLPSKBINFO=LPARAM", "-DCONST=const", "-D_CRT_SECURE_NO_WARNINGS", @@ -312,7 +315,7 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "-D__inner_checkReturn=", }; - parseHeaderFilesToGDT(outputDirectory, "windows_vs12_32_new", "x86:LE:32:default", "windows", filenames, args); + parseHeaderFilesToGDT(outputDirectory, "windows_vs12_32_new", "x86:LE:32:default", "windows", filenames, includePaths, args); } @@ -494,14 +497,16 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "wscapi.h", "wsdapi.h", "wspiapi.h", - "rpcproxy.h", }; + String includePaths[] = { + headerFilePath+"/VC/VS12/src", + headerFilePath+"/VC/VS12/include", + headerFilePath+"/VC/SDK/v7.1A/Include", + }; + String args[] = { - "-I"+headerFilePath+"/VC/VS12/src", - "-I"+headerFilePath+"/VC/VS12/include", - "-I"+headerFilePath+"/VC/SDK/v7.1A/Include", "-D_MSC_VER=1200", "-D_INTEGRAL_MAX_BITS=64", "-DWINVER=0x0900", @@ -513,8 +518,6 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "-D_USE_ATTRIBUTES_FOR_SAL", "-D_CRTBLD", "-D_OPENMP_NOFORCE_MANIFEST", - "-DSTRSAFE_LIB", - "-DSTRSAFE_LIB_IMPL", "-DLPSKBINFO=LPARAM", "-DCONST=const", "-D_CRT_SECURE_NO_WARNINGS", @@ -532,7 +535,7 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "-D__inner_checkReturn=", }; - parseHeaderFilesToGDT(outputDirectory, "windows_vs12_64_new", "x86:LE:64:default", "windows", filenames, args); + parseHeaderFilesToGDT(outputDirectory, "windows_vs12_64_new", "x86:LE:64:default", "windows", filenames, includePaths, args); } @@ -761,12 +764,15 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "arpa/tftp.h", }; + String includePaths[] = { + headerFilePath+"/linux/include", + headerFilePath+"/linux/include/sys", + headerFilePath+"/linux/gcc/include", + headerFilePath+"/linux/x86_64-redhat-linux5E/include", + headerFilePath+"/linux/x86_64-redhat-linux5E/include/sys", + }; + String args[] = { - "-I"+headerFilePath+"/linux/include", - "-I"+headerFilePath+"/linux/include/sys", - "-I"+headerFilePath+"/linux/gcc/include", - "-I"+headerFilePath+"/linux/x86_64-redhat-linux5E/include", - "-I"+headerFilePath+"/linux/x86_64-redhat-linux5E/include/sys", "-D_X86_", "-D__STDC__", "-D_GNU_SOURCE", @@ -782,7 +788,7 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "-Daligned_u64=uint64_t", }; - parseHeaderFilesToGDT(outputDirectory, "generic_clib_64_new", "x86:LE:64:default", "gcc", filenames, args); + parseHeaderFilesToGDT(outputDirectory, "generic_clib_64_new", "x86:LE:64:default", "gcc", filenames, includePaths, args); } @@ -1011,12 +1017,15 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "arpa/tftp.h", }; + String includePaths[] = { + headerFilePath+"/linux/include", + headerFilePath+"/linux/include/sys", + headerFilePath+"/linux/gcc/include", + headerFilePath+"/linux/x86_64-redhat-linux5E/include", + headerFilePath+"/linux/x86_64-redhat-linux5E/include/sys", + }; + String args[] = { - "-I"+headerFilePath+"/linux/include", - "-I"+headerFilePath+"/linux/include/sys", - "-I"+headerFilePath+"/linux/gcc/include", - "-I"+headerFilePath+"/linux/x86_64-redhat-linux5E/include", - "-I"+headerFilePath+"/linux/x86_64-redhat-linux5E/include/sys", "-D_X86_", "-D__STDC__", "-D_GNU_SOURCE", @@ -1032,6 +1041,6 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "-Daligned_u64=uint64_t", }; - parseHeaderFilesToGDT(outputDirectory, "generic_clib_new", "x86:LE:32:default", "gcc", filenames, args); + parseHeaderFilesToGDT(outputDirectory, "generic_clib_new", "x86:LE:32:default", "gcc", filenames, includePaths, args); } } diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java index 3b9397a386..bab606bb29 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java @@ -31,6 +31,7 @@ import java.io.IOException; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; +import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.FileDataTypeManager; @@ -52,14 +53,16 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { parseGDT_WinVS22(); } - private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, String compiler, String[] filenames, String[] args) + private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, String compiler, + String[] filenames, String includePaths[], String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { DataTypeManager openTypes[] = null; - parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, args); + parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, includePaths, args); } - private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler, String[] filenames, String[] args) + private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler, + String[] filenames, String[] includePaths, String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; @@ -68,9 +71,9 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f); - String messages = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, languageID, compiler, null, monitor); + CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, includePaths, args, dtMgr, languageID, compiler, monitor); - Msg.info(this, messages); + Msg.info(this, results.getFormattedParseMessage(null)); dtMgr.save(); dtMgr.close(); @@ -79,7 +82,7 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { /** * Turn string into a file, delete old archive if it exists * - * @param dataTypeFile + * @param dataTypeFile name of archive file * * @return file */ @@ -112,12 +115,15 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "dsound.h", }; + String includeFiles[] = { + headerFilePath+"/VC/VS22/10.0.190141.0", + headerFilePath+"/VC/VS22/10.0.19041.0/um", + headerFilePath+"/VC/VS22/10.0.19041.0/shared", + headerFilePath+"/VC/VS22/10.0.19041.0/ucrt", + headerFilePath+"/VC/VS22/Community/VC/Tools/MSVC/14.30.30705/include", + }; + String args[] = { - "-I"+headerFilePath+"/VC/VS22/10.0.190141.0", - "-I"+headerFilePath+"/VC/VS22/10.0.19041.0/um", - "-I"+headerFilePath+"/VC/VS22/10.0.19041.0/shared", - "-I"+headerFilePath+"/VC/VS22/10.0.19041.0/ucrt", - "-I"+headerFilePath+"/VC/VS22/Community/VC/Tools/MSVC/14.30.30705/include", "-D_AMD64_", "-D_M_AMD64", "-D_M_X64", @@ -126,7 +132,7 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "-v0", }; - parseHeaderFilesToGDT(outputDirectory, "directX64", "x86:LE:64:default", "windows", filenames, args); + parseHeaderFilesToGDT(outputDirectory, "directX64", "x86:LE:64:default", "windows", filenames, includeFiles, args); } public void parseGDT_WinVS22() throws Exception { @@ -167,7 +173,7 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "complex.h", "math.h", "mbctype.h", - "mbstring.hs", + "mbstring.h", "memory.h", "minmax.h", "new.h", @@ -216,7 +222,7 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "dssec.h", "iads.h", "identitycommon.h", - "identityproviders.h", + "identityprovider.h", "identitystore.h", "keycredmgr.h", "lmaccess.h", @@ -237,7 +243,7 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "slpublic.h", "subauth.h", "tokenbinding.h", - "tpmsvcmgr.h", + "tpmvscmgr.h", "wincred.h", "wincrypt.h", "winnetwk.h", @@ -267,7 +273,7 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "mswsock.h", "ws2tcpip.h", "wsipv6ok.h", - "wslwlink.h", + "wsnwlink.h", "wsrm.h", "mswsockdef.h", @@ -375,7 +381,7 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "thumbcache.h", "thumbnailstreamcache.h", "tlogstg.h", - "usereng.h", + "userenv.h", "# Windows Controls", "commctrl.h", @@ -452,12 +458,15 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "lmwksta.h" }; + String includeFiles[] = { + headerFilePath+"/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", + headerFilePath+"/VC/VS22/10.0.19041.0/shared", + headerFilePath+"/VC/VS22/10.0.19041.0/ucrt", + headerFilePath+"/VC/VS22/10.0.19041.0/um", + headerFilePath+"/VC/VS22/10.0.19041.0/winrt", + }; + String args[] = { - "-I"+headerFilePath+"/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", - "-I"+headerFilePath+"/VC/VS22/10.0.19041.0/shared", - "-I"+headerFilePath+"/VC/VS22/10.0.19041.0/ucrt", - "-I"+headerFilePath+"/VC/VS22/10.0.19041.0/um", - "-I"+headerFilePath+"/VC/VS22/10.0.19041.0/winrt", "-D_MSC_VER=1924", "-D_INTEGRAL_MAX_BITS=64", "-DWINVER=0x0a00", @@ -473,7 +482,6 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "-DSTRSAFE_LIB", "-DSTRSAFE_LIB_IMPL", "-DLPSKBINFO=LPARAM", - "-D_WCHAR_T_DEFINED", "-DCONST=const", "-D_CRT_SECURE_NO_WARNINGS", "-D_CRT_NONSTDC_NO_DEPRECATE", @@ -494,6 +502,6 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { "-DSECURITY_WIN32", }; - parseHeaderFilesToGDT(outputDirectory, "windows_vs22_64_new", "x86:LE:64:default", "windows", filenames, args); + parseHeaderFilesToGDT(outputDirectory, "windows_vs22_64_new", "x86:LE:64:default", "windows", filenames, includeFiles, args); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java index 22f96f2627..bf55b2c8a1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java @@ -15,7 +15,8 @@ */ package ghidra.app.plugin.core.cparser; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.StringTokenizer; @@ -31,9 +32,8 @@ import ghidra.app.CorePluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; import ghidra.app.services.DataTypeManagerService; -import ghidra.app.util.cparser.C.CParser; -import ghidra.app.util.cparser.CPP.ParseException; -import ghidra.app.util.cparser.CPP.PreProcessor; +import ghidra.app.util.cparser.C.CParserUtils; +import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.framework.Application; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.PluginInfo; @@ -56,22 +56,20 @@ import ghidra.util.task.TaskMonitor; ) //@formatter:on public class CParserPlugin extends ProgramPlugin { - final static String PARSE_ACTION_NAME = "Import C DataTypes"; + public final static String PARSE_ACTION_NAME = "Import C DataTypes"; final static String USER_PROFILES_DIR = Application.getUserSettingsDirectory().getAbsolutePath() + File.separatorChar + "parserprofiles"; + private ParseDialog parseDialog; private File userProfileDir; - private String parserMessages; - private String cppMessages; + private CParseResults results; final static String DESCRIPTION = "Parse C and C Header files, extracting data definitions and function signatures."; - private static final String PARSER_DEBUG_OUTFILE = "CParserPlugin.out"; - public CParserPlugin(PluginTool plugintool) { super(plugintool); createActions(); @@ -145,26 +143,37 @@ public class CParserPlugin extends ProgramPlugin { if (parseDialog == null) { parseDialog = new ParseDialog(this); } - else { - parseDialog.toFront(); - } + parseDialog.setupForDisplay(); tool.showDialog(parseDialog); } /* * Parse into a saved data type data base file */ - protected void parse(String[] filenames, String options, String dataFilename) { - CParserTask parseTask = new CParserTask(this, filenames, options, dataFilename); + protected void parse(String[] filenames, String includePaths[], String options, + String languageIDString, String compilerSpecID, String dataFilename) { + + CParserTask parseTask = new CParserTask(this, dataFilename) + .setFileNames(filenames) + .setIncludePaths(includePaths) + .setOptions(options) + .setLanguageID(languageIDString) + .setCompilerID(compilerSpecID); + this.getTool().execute(parseTask, 500); } + /* * Parse C-source into a data type manager */ - protected void parse(String[] filenames, String options, DataTypeManager dtMgr, + protected void parse(String[] filenames, String includePaths[], String options, + String languageIDString, String compilerSpecID, DataTypeManager dtMgr, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException { + + results = null; + String[] args = parseOptions(options); DataTypeManager openDTmanagers[] = null; @@ -174,113 +183,22 @@ public class CParserPlugin extends ProgramPlugin { return; // parse canceled } - cppMessages = ""; - PreProcessor cpp = new PreProcessor(); - - cpp.setArgs(args); - - PrintStream os = System.out; - String homeDir = System.getProperty("user.home"); - String fName = homeDir + File.separator + "CParserPlugin.out"; try { - os = new PrintStream(new FileOutputStream(fName)); - } - catch (FileNotFoundException e2) { - Msg.error(this, "Unexpected Exception: " + e2.getMessage(), e2); - } - - PrintStream old = System.out; - System.setOut(os); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - cpp.setOutputStream(bos); - cpp.setMonitor(monitor); - - int fileCount = 0; - try { - for (String filename : filenames) { - if (monitor.isCancelled()) { - break; - } - // any file beginning with a "#" is assumed to be a comment - if (filename.trim().startsWith("#")) { - continue; - } - File file = new File(filename); - - if (file.isDirectory()) { - // process each header file in the directory - String[] children = file.list(); - if (children == null) { - continue; - } - for (String element : children) { - File child = new File(file.getAbsolutePath() + "/" + element); - if (child.getName().endsWith(".h")) { - parseFile(child.getAbsolutePath(), monitor, cpp); - } - } - } - else if (file.exists()) { - ++fileCount; - parseFile(filename, monitor, cpp); - } - else { - Msg.error(this, "Skipping file not found:" + filename); - } - } - } - catch (RuntimeException re) { - os.close(); - throw new ParseException(re.getMessage()); - } - - if (fileCount == 0) { - throw new ParseException("Failed to find any header files to parse!"); - } - else if (fileCount != filenames.length) { - Msg.warn(this, - "Only found " + fileCount + " of " + filenames.length + " specified header files"); - } - - // process all the defines and add any that are integer values into - // the Equates table - cpp.getDefinitions().populateDefineEquates(openDTmanagers, dtMgr); - - System.out.println(bos.toString()); - - System.setOut(old); - os.close(); - - if (!monitor.isCancelled()) { - monitor.setMessage("Parsing C"); - - CParser cParser = new CParser(dtMgr, true, openDTmanagers); - cParser.setParseFileName(PARSER_DEBUG_OUTFILE); - ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); - try { - parserMessages = ""; - cParser.setParseFileName(fName); - cParser.setMonitor(monitor); - cParser.parse(bis); - } - finally { - parserMessages = cParser.getParseMessages(); - } - + results = CParserUtils.parseHeaderFiles(openDTmanagers, filenames, includePaths, + args, dtMgr, languageIDString, compilerSpecID, monitor); + final boolean isProgramDtMgr = (dtMgr instanceof ProgramDataTypeManager); - + SwingUtilities.invokeLater(() -> { // CParserTask will show any errors - if (!cParser.didParseSucceed()) { + if (!results.successful()) { return; } if (isProgramDtMgr) { MultiLineMessageDialog.showModalMessageDialog(parseDialog.getComponent(), "C-Parse of Header Files Complete", "Successfully parsed header file(s) to Program.", - getFormattedParseMessage( - "Check the Manage Data Types window for added data types."), + getFormattedParseMessage("Check the Manage Data Types window for added data types."), MultiLineMessageDialog.INFORMATION_MESSAGE); } else { @@ -289,20 +207,22 @@ public class CParserPlugin extends ProgramPlugin { archiveName = ((FileDataTypeManager) dtMgr).getFilename(); } MultiLineMessageDialog.showModalMessageDialog(parseDialog.getComponent(), - "C-Parse of Header Files Complete. ", + "C-Parse of Header Files Complete", "Successfully parsed header file(s) to Archive File: " + archiveName, - getFormattedParseMessage(null), MultiLineMessageDialog.INFORMATION_MESSAGE); + getFormattedParseMessage(null), + MultiLineMessageDialog.INFORMATION_MESSAGE); } }); } - + catch (IOException e) { + // ignore + } } /** * Get open data type managers. * User can Use Open managers, Select not to use, or Cancel * - * @param openDTmanagers open mgrs, null if don't use * @return array of open data type managers * * @throws CancelledException if user cancels @@ -338,7 +258,7 @@ public class CParserPlugin extends ProgramPlugin { "" + "

The new archive will become dependent on these archives
" + "for any datatypes already defined in them
(only unique
" + "data types will be added to the new archive).", - "Use Open Archives?", "Don't Use Open Archives", OptionDialog.QUESTION_MESSAGE); + "Use Open Archives", "Don't Use Open Archives", OptionDialog.QUESTION_MESSAGE); if (result == OptionDialog.CANCEL_OPTION) { throw new CancelledException("User Cancelled"); } @@ -349,62 +269,40 @@ public class CParserPlugin extends ProgramPlugin { return openDTmanagers; } + + public CParseResults getParseResults() { + return results; + } + + public String getParseMessage() { + return (results != null ? results.cParseMessages() : ""); + } - public String getFormattedParseMessage(String errMsg) { + protected String getFormattedParseMessage(String errMsg) { String message = ""; if (errMsg != null) { message += errMsg + "\n\n"; } - String msg = getParseMessage(); + String msg = (results == null ? null : results.cParseMessages()); if (msg != null && msg.length() != 0) { message += "CParser Messages:\n" + msg + "\n\n"; } - msg = getPreProcessorMessage(); + msg = (results == null ? null : results.cppParseMessages()); if (msg != null && msg.length() != 0) { - message += "PreProcessor Messages:\n" + getPreProcessorMessage(); + message += "PreProcessor Messages:\n" + msg; } return message; } - /** - * Get any parse messages produced by parsing good, or informational - * - * @return messages from parser - */ - public String getParseMessage() { - return parserMessages; - } - - public String getPreProcessorMessage() { - return cppMessages; - } - - private void parseFile(String filename, TaskMonitor monitor, PreProcessor cpp) - throws ghidra.app.util.cparser.CPP.ParseException { - monitor.setMessage("PreProcessing " + filename); - try { - Msg.info(this, "parse " + filename); - cpp.parse(filename); - } - catch (Throwable e) { - Msg.error(this, "Parsing file :" + filename); - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - - throw new ghidra.app.util.cparser.CPP.ParseException(e.getMessage()); - } - finally { - cppMessages += cpp.getParseMessages(); - } - } - /* * Parse into the current programs data type manager */ - protected void parse(String[] filenames, String options) { + protected void parse(String[] filenames, String[] includePaths, String options, + String languageIDString, String compilerIDString) { if (currentProgram == null) { Msg.showInfo(getClass(), parseDialog.getComponent(), "No Open Program", "A program must be open to \"Parse to Program\""); @@ -416,8 +314,14 @@ public class CParserPlugin extends ProgramPlugin { if (result == OptionDialog.CANCEL_OPTION) { return; } + CParserTask parseTask = - new CParserTask(this, filenames, options, currentProgram.getDataTypeManager()); + new CParserTask(this, currentProgram.getDataTypeManager()) + .setFileNames(filenames) + .setIncludePaths(includePaths) + .setOptions(options) + .setLanguageID(languageIDString) + .setCompilerID(compilerIDString); tool.execute(parseTask); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java index b02ba60e0a..68ae94864a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java @@ -20,44 +20,97 @@ import java.io.File; import javax.swing.SwingUtilities; import docking.widgets.dialogs.MultiLineMessageDialog; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.data.*; import ghidra.util.Msg; import ghidra.util.exception.DuplicateFileException; import ghidra.util.task.Task; import ghidra.util.task.TaskMonitor; /** - * This is called by the dialog box. + * Background task to parse files for cparser plugin * * */ class CParserTask extends Task { - private String[] filenames; - private String options; + private CParserPlugin plugin; private String dataFileName; + + private String[] filenames; + private String[] includePaths; + + private String options; + + private String languageString; + private String compilerString; + private DataTypeManager dtMgr; - CParserTask(CParserPlugin plugin, String[] filenames, String options, String dataFileName) { + + /** + * Create task to parse to a dataFile + * + * @param plugin CParserPlugin that will do the work + * @param dataFileName name of the file to parse to + */ + CParserTask(CParserPlugin plugin, String dataFileName) { super("Parsing C Files", true, false, false); this.plugin = plugin; - this.filenames = filenames; - this.options = options; this.dataFileName = dataFileName; } - public CParserTask(CParserPlugin plugin, String[] filenames, String options, - DataTypeManager dataTypeManager) { + /** + * Create task to parse to a dataTypeManager + * + * @param plugin + * @param dataTypeManager + */ + public CParserTask(CParserPlugin plugin, DataTypeManager dataTypeManager) { super("Parsing C Files", true, false, false); this.plugin = plugin; - this.filenames = filenames; - this.options = options; this.dtMgr = dataTypeManager; } + /** + * Create task to parse to a ProgramDataTypeManager + * + * @param plugin + * @param dataTypeManager + */ + public CParserTask(CParserPlugin plugin, ProgramBasedDataTypeManager dataTypeManager) { + super("Parsing C Files", true, false, false); + + this.plugin = plugin; + this.dtMgr = dataTypeManager; + } + + public CParserTask setLanguageID(String languageID) { + this.languageString = languageID; + return this; + } + + public CParserTask setCompilerID(String compilerID) { + this.compilerString = compilerID; + return this; + } + + public CParserTask setIncludePaths(String includePaths[]) { + this.includePaths = includePaths.clone(); + return this; + } + + public CParserTask setFileNames(String names[]) { + this.filenames = names.clone(); + return this; + } + + public CParserTask setOptions(String options) { + this.options = options; + return this; + } + private String getFirstMessageLine(final String errMsg) { int indexOf = errMsg.indexOf('\n'); String msg = errMsg; @@ -77,7 +130,7 @@ class CParserTask extends Task { fileDtMgr = dtMgr; } - plugin.parse(filenames, options, dtMgr, monitor); + plugin.parse(filenames, includePaths, options, languageString, compilerString, dtMgr, monitor); if (dataFileName != null) { // TODO: does not consider existing datatypes if (dtMgr.getDataTypeCount(true) != 0) { @@ -103,6 +156,10 @@ class CParserTask extends Task { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + // no results, was canceled + if (plugin.getParseResults() == null) { + return; + } MultiLineMessageDialog.showModalMessageDialog( plugin.getDialog().getComponent(), "Parse Errors", "File was not created due to parse errors: " + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/ParseDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/ParseDialog.java index 70d64ef2fc..3b13c69e75 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/ParseDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/ParseDialog.java @@ -15,31 +15,44 @@ */ package ghidra.app.plugin.core.cparser; -import java.awt.BorderLayout; -import java.awt.Dimension; +import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.*; import java.util.*; +import java.util.List; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.TableModel; +import org.apache.commons.lang3.ObjectUtils; + import docking.*; import docking.action.*; import docking.widgets.OptionDialog; +import docking.widgets.button.BrowseButton; import docking.widgets.combobox.GhidraComboBox; import docking.widgets.dialogs.InputDialog; import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.filechooser.GhidraFileChooserMode; +import docking.widgets.label.GLabel; import docking.widgets.pathmanager.PathnameTablePanel; +import docking.widgets.table.GTableCellRenderer; +import docking.widgets.table.GTableCellRenderingData; import generic.jar.ResourceFile; +import generic.theme.GThemeDefaults.Colors.Tables; +import ghidra.app.plugin.core.processors.SetLanguageDialog; +import ghidra.app.util.Option; +import ghidra.app.util.cparser.C.CParserUtils; +import ghidra.app.util.exporter.Exporter; import ghidra.framework.Application; import ghidra.framework.options.SaveState; import ghidra.framework.preferences.Preferences; import ghidra.framework.store.db.PackedDatabase; import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.lang.CompilerSpecID; +import ghidra.program.model.lang.LanguageID; import ghidra.util.HelpLocation; import ghidra.util.Msg; import ghidra.util.filechooser.ExtensionFileFilter; @@ -75,6 +88,13 @@ class ParseDialog extends ReusableDialogComponentProvider { private PathnameTablePanel pathPanel; private JTextArea parseOptionsField; + + protected JComponent languagePanel; + protected JTextField languageTextField; + protected JButton languageButton; + protected String languageIDString = null; + protected String compilerIDString = null; + private GhidraComboBox comboBox; private DefaultComboBoxModel comboModel; private DockingAction saveAction; @@ -86,44 +106,75 @@ class ParseDialog extends ReusableDialogComponentProvider { private TableModelListener tableListener; private ItemListener comboItemListener; private TableModel tableModel; + + private PathnameTablePanel includePathPanel; + private TableModel parsePathTableModel; + private TableModelListener parsePathTableListener; + private ArrayList itemList; private ComboBoxItemComparator comparator; private ResourceFile parentUserFile; private boolean saveAsInProgress; + private boolean initialBuild = true; + + private boolean userDefined = false; + private String currentProfileName = null; ParseDialog(CParserPlugin plugin) { super("Parse C Source", false, true, true, false); this.plugin = plugin; - itemList = new ArrayList<>(); - comparator = new ComboBoxItemComparator(); - addWorkPanel(buildMainPanel()); - addDismissButton(); - createActions(); - setActionsEnabled(); } + public void setupForDisplay() { + if (initialBuild) { + itemList = new ArrayList<>(); + comparator = new ComboBoxItemComparator(); + addWorkPanel(buildMainPanel()); + addDismissButton(); + createActions(); + setActionsEnabled(); + + // setup based on save state + if (currentProfileName != null) { + for (int i = 0; i < itemList.size(); i++) { + ComboBoxItem item = itemList.get(i); + if (userDefined == item.isUserDefined && currentProfileName.equals(item.file.getName())) { + comboBox.setSelectedIndex(i); + break; + } + } + } + } else { + toFront(); + } + } + void writeState(SaveState saveState) { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); - saveState.putString(CURRENT_PROFILE, item.file.getName()); - saveState.putBoolean(USER_DEFINED, item.isUserDefined); + // Get the current state if the dialog has been displayed + if (!initialBuild) { + ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + + currentProfileName = item.file.getName(); + userDefined = item.isUserDefined; + + } + saveState.putString(CURRENT_PROFILE, currentProfileName); + saveState.putBoolean(USER_DEFINED, userDefined); } void readState(SaveState saveState) { - String name = saveState.getString(CURRENT_PROFILE, null); - if (name != null) { - boolean userDefined = saveState.getBoolean(USER_DEFINED, true); - for (int i = 0; i < itemList.size(); i++) { - ComboBoxItem item = itemList.get(i); - if (userDefined == item.isUserDefined && name.equals(item.file.getName())) { - comboBox.setSelectedIndex(i); - break; - } - } + currentProfileName = saveState.getString(CURRENT_PROFILE, null); + if (currentProfileName != null) { + userDefined = saveState.getBoolean(USER_DEFINED, true); } } void closeProfile() { + // dialog not built yet + if (initialBuild) { + return; + } ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); if (item.isChanged) { processItemChanged(item); @@ -136,6 +187,8 @@ class ParseDialog extends ReusableDialogComponentProvider { } protected JPanel buildMainPanel() { + initialBuild = true; + mainPanel = new JPanel(new BorderLayout(10, 5)); comboModel = new DefaultComboBoxModel<>(); @@ -164,14 +217,64 @@ class ParseDialog extends ReusableDialogComponentProvider { pathPanel.setFileChooserProperties("Choose Source Files", LAST_IMPORT_C_DIRECTORY, GhidraFileChooserMode.FILES_AND_DIRECTORIES, true, new ExtensionFileFilter(new String[] { "h" }, "C Header Files")); + + // Set default render to display red if file would not we found + // Using include paths + pathPanel.getTable().setDefaultRenderer(String.class, new GTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(GTableCellRenderingData data) { + JLabel label = (JLabel) super.getTableCellRendererComponent(data); + Object value = data.getValue(); + + String pathName = (String) value; + pathName = (pathName == null ? "" : pathName.trim()); + + if (pathName.length() == 0 || pathName.startsWith("#")) { + return label; + } + + boolean fileExists = true; + File file = new File(pathName); + fileExists = file.exists(); + + // file not found directly, see if one of the include paths will find the file + if (!fileExists) { + fileExists = doesFileExist(pathName, fileExists); + } + + label.setText(pathName.toString()); + if (!fileExists) { + label.setForeground(data.isSelected() ? Tables.FG_ERROR_SELECTED + : Tables.FG_ERROR_UNSELECTED); + } + + return label; + } + }); + tableListener = e -> { ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); - item.isChanged = true; + item.isChanged = !initialBuild; setActionsEnabled(); }; tableModel = pathPanel.getTable().getModel(); tableModel.addTableModelListener(tableListener); + + includePathPanel = new PathnameTablePanel(null, true, false); + includePathPanel.setBorder(BorderFactory.createTitledBorder("Include paths")); + includePathPanel.setFileChooserProperties("Choose Source Files", LAST_IMPORT_C_DIRECTORY, + GhidraFileChooserMode.FILES_AND_DIRECTORIES, true, + new ExtensionFileFilter(new String[] { "h" }, "C Header Files")); + + parsePathTableListener = e -> { + ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + item.isChanged = !initialBuild; + setActionsEnabled(); + pathPanel.getTable().repaint(); + }; + parsePathTableModel = includePathPanel.getTable().getModel(); + parsePathTableModel.addTableModelListener(parsePathTableListener); JPanel optionsPanel = new JPanel(new BorderLayout()); optionsPanel.setBorder(BorderFactory.createTitledBorder("Parse Options")); @@ -182,7 +285,13 @@ class ParseDialog extends ReusableDialogComponentProvider { JScrollPane pane = new JScrollPane(parseOptionsField); pane.getViewport().setPreferredSize(new Dimension(300, 200)); optionsPanel.add(pane, BorderLayout.CENTER); - + + JPanel archPanel = new JPanel(new BorderLayout()); + archPanel.setBorder(BorderFactory.createTitledBorder("Program Architecture:")); + archPanel.add(new GLabel(" ", SwingConstants.RIGHT)); + languagePanel = buildLanguagePanel(); + archPanel.add(languagePanel); + // create Parse Button parseButton = new JButton("Parse to Program"); @@ -195,23 +304,103 @@ class ParseDialog extends ReusableDialogComponentProvider { parseToFileButton.setToolTipText("Parse files and output to archive file"); addButton(parseToFileButton); - pathPanel.setPreferredSize(new Dimension(pathPanel.getPreferredSize().width, 200)); - JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pathPanel, optionsPanel); - splitPane.setResizeWeight(0.50); + mainPanel.add(comboPanel, BorderLayout.NORTH); - mainPanel.add(splitPane, BorderLayout.CENTER); + + includePathPanel.setPreferredSize(new Dimension(pathPanel.getPreferredSize().width, 200)); + JSplitPane optionsPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, includePathPanel, optionsPanel); + optionsPane.setResizeWeight(0.50); + + pathPanel.setPreferredSize(new Dimension(pathPanel.getPreferredSize().width, 200)); + JSplitPane outerPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pathPanel, optionsPane); + outerPane.setResizeWeight(0.50); + + mainPanel.add(outerPane, BorderLayout.CENTER); + + mainPanel.add(archPanel, BorderLayout.SOUTH); setHelpLocation(new HelpLocation(plugin.getName(), "Parse_C_Source")); loadProfile(); + initialBuild = false; return mainPanel; } + private boolean doesFileExist(String pathName, boolean fileExists) { + String[] includePaths = includePathPanel.getPaths(); + for (String path : includePaths) { + File file = CParserUtils.getFile(path, pathName); + if (file == null) { + continue; + } + fileExists = file.exists(); + if (fileExists) { + break; + } + } + return fileExists; + } + + private JComponent buildLanguagePanel() { + languageTextField = new JTextField(); + languageTextField.setEditable(false); + languageTextField.setFocusable(false); + + languageButton = new BrowseButton(); + languageButton.addActionListener(e -> { + SetLanguageDialog dialog = new SetLanguageDialog(plugin.getTool(), languageIDString, compilerIDString, + "Select Program Architecture for File DataType Archive"); + LanguageID languageId = dialog.getLanguageDescriptionID(); + CompilerSpecID compilerSpecId = dialog.getCompilerSpecDescriptionID(); + if ((languageId == null) || (compilerSpecId == null)) { + return; + } + + String newLanguageIDString = languageId.getIdAsString(); + String newCompilerIDString = compilerSpecId.getIdAsString(); + + if (!Objects.equals(newLanguageIDString, languageIDString) || + !Objects.equals(newCompilerIDString, compilerIDString)) { + itemChanged(); + } + + languageIDString = newLanguageIDString; + compilerIDString = newCompilerIDString; + + updateArchitectureDescription(); + }); + + updateArchitectureDescription(); + + languageButton.setName("Set Processor Architecture"); + Font font = languageButton.getFont(); + languageButton.setFont(font.deriveFont(Font.BOLD)); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(languageTextField, BorderLayout.CENTER); + panel.add(languageButton, BorderLayout.EAST); + return panel; + } + + private void updateArchitectureDescription() { + String newProgramArchitectureSummary = "64/32 (primarily for backward compatibility)"; + + if (languageIDString != null) { + StringBuilder buf = new StringBuilder(); + buf.append(languageIDString); + buf.append(" / "); + buf.append(compilerIDString != null ? compilerIDString : "none"); + newProgramArchitectureSummary = buf.toString(); + } + + languageTextField.setText(newProgramArchitectureSummary); + } + private void selectionChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.DESELECTED) { ComboBoxItem item = (ComboBoxItem) e.getItem(); - if (item.isChanged && !saveAsInProgress) { + if (item.isChanged && !saveAsInProgress && !initialBuild) { if (item.isUserDefined) { if (OptionDialog.showOptionDialog(rootPanel, "Save Changes to Profile?", "Profile " + item.file.getName() + @@ -256,53 +445,6 @@ class ParseDialog extends ReusableDialogComponentProvider { } } - private void loadProfile() { - if (docListener != null) { - parseOptionsField.getDocument().removeDocumentListener(docListener); - } - tableModel.removeTableModelListener(tableListener); - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); - - StringBuffer sb = new StringBuffer(); - ArrayList pathList = new ArrayList<>(); - try { - BufferedReader br = - new BufferedReader(new InputStreamReader(item.file.getInputStream())); - String line = null; - while ((line = br.readLine()) != null) { - line = line.trim(); - if (line.startsWith("-") || (line.length() == 0 && sb.length() > 0)) { - // this is a compiler directive - sb.append(line + "\n"); - } - else if (line.length() > 0) { - File f = new File(line); - pathList.add(f.getPath()); - } - } - String[] paths = new String[pathList.size()]; - paths = pathList.toArray(paths); - pathPanel.setPaths(paths); - parseOptionsField.setText(sb.toString()); - - br.close(); - } - catch (FileNotFoundException e) { - Msg.showInfo(getClass(), getComponent(), "File Not Found", - "Could not find file\n" + item.file.getAbsolutePath()); - } - catch (IOException e) { - Msg.showError(this, getComponent(), "Error Loading Profile", - "Exception occurred while reading file\n" + item.file.getAbsolutePath() + ": " + e); - } - finally { - // add a document listener to the options field - addDocumentListener(); - tableModel.addTableModelListener(tableListener); - setActionsEnabled(); - } - } - private void addDocumentListener() { if (docListener == null) { docListener = new DocumentListener() { @@ -327,6 +469,9 @@ class ParseDialog extends ReusableDialogComponentProvider { private void itemChanged() { ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + if (item == null) { + return; + } item.isChanged = true; setActionsEnabled(); } @@ -466,6 +611,7 @@ class ParseDialog extends ReusableDialogComponentProvider { } file.delete(); } + saveAsInProgress = true; ComboBoxItem newItem = new ComboBoxItem(file, true); if (itemList.contains(newItem)) { itemList.remove(newItem); @@ -476,7 +622,6 @@ class ParseDialog extends ReusableDialogComponentProvider { index = -index - 1; } itemList.add(index, newItem); - saveAsInProgress = true; writeProfile(newItem.file); newItem.isChanged = false; item.isChanged = false; @@ -491,6 +636,97 @@ class ParseDialog extends ReusableDialogComponentProvider { } } + private void loadProfile() { + if (docListener != null) { + parseOptionsField.getDocument().removeDocumentListener(docListener); + } + tableModel.removeTableModelListener(tableListener); + parsePathTableModel.removeTableModelListener(parsePathTableListener); + ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + item.isChanged = false; + + StringBuffer sb = new StringBuffer(); + ArrayList pathList = new ArrayList<>(); + ArrayList includeList = new ArrayList<>(); + String langString = null; + String compileString = null; + try { + BufferedReader br = + new BufferedReader(new InputStreamReader(item.file.getInputStream())); + String line = null; + while ((line = br.readLine()) != null && line.trim().length() > 0) { + line = line.trim(); + + pathList.add(line); + } + + while ((line = br.readLine()) != null && line.trim().length() > 0) { + line = line.trim(); + + sb.append(line + "\n"); + } + + // get paths + while ((line = br.readLine()) != null && line.trim().length() > 0) { + line = line.trim(); + + includeList.add(line); + } + + // get language + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.length() > 0) { + langString = (line.length() == 0 ? null : line); + break; + } + } + + // get compiler spec + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.length() > 0) { + compileString = (line.length() == 0 ? null : line); + break; + } + } + + + String[] paths = new String[pathList.size()]; + paths = pathList.toArray(paths); + pathPanel.setPaths(paths); + + String[] incpaths = new String[includeList.size()]; + incpaths = includeList.toArray(incpaths); + includePathPanel.setPaths(incpaths); + + parseOptionsField.setText(sb.toString()); + + languageIDString = langString; + + compilerIDString = compileString; + + updateArchitectureDescription(); + + br.close(); + } + catch (FileNotFoundException e) { + Msg.showInfo(getClass(), getComponent(), "File Not Found", + "Could not find file\n" + item.file.getAbsolutePath()); + } + catch (IOException e) { + Msg.showError(this, getComponent(), "Error Loading Profile", + "Exception occurred while reading file\n" + item.file.getAbsolutePath() + ": " + e); + } + finally { + // add a document listener to the options field + addDocumentListener(); + tableModel.addTableModelListener(tableListener); + parsePathTableModel.addTableModelListener(parsePathTableListener); + setActionsEnabled(); + } + } + private void writeProfile(ResourceFile outputFile) { // write the pathnames try { @@ -498,7 +734,7 @@ class ParseDialog extends ReusableDialogComponentProvider { new BufferedWriter(new OutputStreamWriter(outputFile.getOutputStream())); String[] paths = pathPanel.getPaths(); for (String path : paths) { - writer.write(path); + writer.write(path.trim()); writer.newLine(); } writer.newLine(); @@ -510,6 +746,30 @@ class ParseDialog extends ReusableDialogComponentProvider { writer.write(tok); writer.newLine(); } + writer.newLine(); + + // Write paths + String [] includePaths = includePathPanel.getPaths(); + for (String path : includePaths) { + writer.write(path.trim()); + writer.newLine(); + } + writer.newLine(); + + // Write Language ID Spec + if (languageIDString != null) { + writer.write(languageIDString); + } + writer.newLine(); + writer.newLine(); + + // Write Compiler ID Spec + if (compilerIDString != null) { + writer.write(compilerIDString); + } + writer.newLine(); + writer.newLine(); + writer.close(); } catch (IOException e) { @@ -537,6 +797,7 @@ class ParseDialog extends ReusableDialogComponentProvider { private void doParse(boolean parseToFile) { clearStatusText(); String options = getParseOptions(); + String[] includePaths = includePathPanel.getPaths(); String[] paths = pathPanel.getPaths(); if (paths.length == 0) { @@ -547,15 +808,21 @@ class ParseDialog extends ReusableDialogComponentProvider { paths = expandPaths(paths); pathPanel.setPaths(paths); + + if (languageIDString == null || compilerIDString == null ) { + Msg.showWarn(getClass(), rootPanel, "Program Architecture not Specified", + "A Program Architecture must be specified in order to parse to a file."); + return; + } if (parseToFile) { File file = getSaveFile(); if (file != null) { - plugin.parse(paths, options, file.getAbsolutePath()); + plugin.parse(paths, includePaths, options, languageIDString, compilerIDString, file.getAbsolutePath()); } } else { - plugin.parse(paths, options); + plugin.parse(paths, includePaths, options, languageIDString, compilerIDString); } } @@ -605,7 +872,12 @@ class ParseDialog extends ReusableDialogComponentProvider { private void addToComboModel(ResourceFile parent, boolean isUserDefined) { ResourceFile[] children = parent.listFiles(); - for (ResourceFile resourceFile : children) { + List sorted = Arrays.asList(children); + // sort each set of files, system will go first + // User local files second + // new files at the end + Collections.sort(sorted); + for (ResourceFile resourceFile : sorted) { if (resourceFile.getName().startsWith(".")) { continue; } @@ -687,7 +959,7 @@ class ParseDialog extends ReusableDialogComponentProvider { return parseOptionsField.getText(); } - private class ComboBoxItem { + class ComboBoxItem { private ResourceFile file; private boolean isUserDefined; private boolean isChanged; @@ -745,4 +1017,55 @@ class ParseDialog extends ReusableDialogComponentProvider { return 1; } } + + //================================================================================================== + // Methods for Testing + //================================================================================================== + + + GhidraComboBox getParseComboBox() { + return comboBox; + } + + PathnameTablePanel getSourceFiles() { + return this.pathPanel; + } + + PathnameTablePanel getIncludePaths() { + return this.includePathPanel; + } + + JTextArea getParseOptionsTextField() { + return this.parseOptionsField; + } + + JButton getLanguageButton() { + return this.languageButton; + } + + JTextField getLanguageText() { + return this.languageTextField; + } + + JButton getParseButton() { + return this.parseButton; + } + + JButton getParseToFileButton() { + return this.parseToFileButton; + } + + ArrayList getProfiles() { + return this.itemList; + } + + ComboBoxItem getCurrentItem() { + ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + + return item; + } + + ResourceFile getUserProfileParent() { + return parentUserFile; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java index ff8dffb1f1..076df67d9d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java @@ -17,27 +17,52 @@ package ghidra.app.util.cparser.C; import java.io.*; import java.util.Arrays; -import java.util.Iterator; + +import javax.help.UnsupportedOperationException; import generic.theme.GThemeDefaults.Colors; import generic.theme.GThemeDefaults.Colors.Messages; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.cparser.CPP.PreProcessor; import ghidra.framework.plugintool.ServiceProvider; -import ghidra.program.database.ProgramDB; +import ghidra.framework.store.LockException; import ghidra.program.model.data.*; import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; import ghidra.program.model.listing.Program; import ghidra.program.util.DefaultLanguageService; import ghidra.util.*; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; public class CParserUtils { - + private CParserUtils() { // utils class } + + public record CParseResults(PreProcessor preProcessor, String cppParseMessages, String cParseMessages, boolean successful) { + public String getFormattedParseMessage(String errMsg) { + String message = ""; + + if (errMsg != null) { + message += errMsg + "\n\n"; + } + + String msg = cppParseMessages; + if (msg != null && msg.length() != 0) { + message += "CParser Messages:\n" + msg + "\n\n"; + } + + msg = cppParseMessages; + if (msg != null && msg.length() != 0) { + message += "PreProcessor Messages:\n" + msg; + } + + return message; + } + } /** * Parse the given function signature text. Any exceptions will be handled herein @@ -88,7 +113,7 @@ public class CParserUtils { * [0]= part before function name * [1]= function name * [2]= parameter body after function name - * @param signature + * @param signature function signature string to split * @return parts array or null if split failed */ private static String[] splitFunctionSignature(String signature) { @@ -136,7 +161,7 @@ public class CParserUtils { /** * Get a temporary name of a specified length (tttt....) - * @param length + * @param length of temporary string * @return temporary name string */ private static String getTempName(int length) { @@ -158,6 +183,7 @@ public class CParserUtils { * parsing exceptions. This allows clients to perform exception handling that * better matches their workflow. * @return the data type that is created as a result of parsing; null if there was a problem + * @throws ParseException for catastrophic errors in C parsing */ public static FunctionDefinitionDataType parseSignature(DataTypeManagerService service, Program program, String signatureText, boolean handleExceptions) throws ParseException { @@ -237,8 +263,6 @@ public class CParserUtils { * * @param dataFileName name of data type archive file (include the .gdt extension) * - * @param cpp provided PreProcessor, useful if parsed Define's need to be examined after parsing - * * @param monitor used to cancel or provide results * * @return the data types in the ghidra .gdt archive file @@ -250,15 +274,48 @@ public class CParserUtils { */ public static FileDataTypeManager parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String args[], String dataFileName, - PreProcessor cpp, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - File file = new File(dataFileName); - FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(file); - parseHeaderFiles(openDTMgrs, filenames, args, dtMgr, cpp, monitor); + return parseHeaderFiles(openDTMgrs, filenames, null, args, dataFileName, monitor); + } + + /** + * Parse a set of C Header files and associated parsing arguments, returning a new File Data TypeManager + * with in the provided dataFileName. + * + * Note: Using another open archive while parsing will cause: + * - a dependence on the other archive + * - any missing data types while parsing are supplied if present from an openDTMgr + * - after parsing all data types parsed with an equivalent data type in any openDTMgr + * replaced by the data type from the openDTMgr + * + * NOTE: This will only occur if the data type from the openDTMgr's is equivalent. + * + * @param openDTMgrs array of datatypes managers to use for undefined data types + * + * @param filenames names of files in order to parse, could include strings with + * "#" at start, which are ignored as comments + * @param includePaths paths to include files, instead of using "-I" in args + * @param args arguments for parsing, "-D=", ( "-I" use includePaths parm instead) + * + * @param dataFileName name of data type archive file (include the .gdt extension) + * + * @param monitor used to cancel or provide results + * + * @return the data types in the ghidra .gdt archive file + * + * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing + * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing + * @throws IOException if there io are errors saving the archive + * + */ + + public static FileDataTypeManager parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String includePaths[], String args[], String dataFileName, + TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + ghidra.app.util.cparser.CPP.ParseException, IOException { - dtMgr.save(); - return dtMgr; + return parseHeaderFiles(openDTMgrs, filenames, includePaths, args, dataFileName, null, null, monitor); } @@ -280,6 +337,7 @@ public class CParserUtils { * * @param filenames names of files in order to parse, could include strings with * "#" at start, which are ignored as comments + * @param includePaths path to include files, could also be in args with "-I" * @param args arguments for parsing, "-D=", "-I" * * @param dataFileName name of data type archive file (include the .gdt extension) @@ -287,8 +345,6 @@ public class CParserUtils { * @param languageId language identication to use for data type organization definitions (int, long, ptr size) * @param compileSpecId compiler specification to use for parsing * - * @param cpp provided PreProcessor, useful if parsed Define's need to be examined after parsing - * * @param monitor used to cancel or provide results * * @return the data types in the ghidra .gdt archive file @@ -298,14 +354,17 @@ public class CParserUtils { * @throws IOException if there io are errors saving the archive * */ - public static DataTypeManager parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String args[], String dataFileName, - String languageId, String compileSpecId, PreProcessor cpp, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + public static FileDataTypeManager parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String includePaths[], String args[], String dataFileName, + String languageId, String compileSpecId, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { File file = new File(dataFileName); FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(file); - String messages = parseHeaderFiles(openDTMgrs, filenames, args, dtMgr, languageId, compileSpecId, cpp, monitor); + CParseResults results; + results = parseHeaderFiles(openDTMgrs, filenames, includePaths, args, dtMgr, languageId, compileSpecId, monitor); + + String messages = results.getFormattedParseMessage(null); Msg.info(CParserUtils.class, messages); dtMgr.save(); @@ -331,15 +390,13 @@ public class CParserUtils { * * @param filenames names of files in order to parse, could include strings with * "#" at start, which are ignored as comments - * @param args arguments for parsing, "-D=", "-I" + * @param args arguments for parsing, "-D=", ( "-I" use includePaths parm instead) * * @param existingDTMgr datatypes will be populated into this provided DTMgr, can pass Program or File DTMgr * * @param languageId language identication to use for data type organization definitions (int, long, ptr size) * @param compileSpecId compiler specification to use for parsing * - * @param cpp provided PreProcessor, useful if parsed Define's need to be examined after parsing - * * @param monitor used to cancel or provide results * * @return a formatted string of any output from pre processor parsing or C parsing @@ -349,39 +406,84 @@ public class CParserUtils { * @throws IOException if there io are errors saving the archive * */ - public static String parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String args[], DataTypeManager existingDTMgr, - String languageId, String compileSpecId, PreProcessor cpp, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + public static CParseResults parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String args[], DataTypeManager existingDTMgr, + String languageId, String compileSpecId, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + ghidra.app.util.cparser.CPP.ParseException, IOException { + + return parseHeaderFiles(openDTMgrs, filenames, null, args, existingDTMgr, languageId, compileSpecId, monitor); + } + + /** + * Parse a set of C Header files and associated parsing arguments, data types are added to the provided + * DTMgr. + * + * Note: Using another open archive while parsing will cause: + * - a dependence on the other archive + * - any missing data types while parsing are supplied if present from an openDTMgr + * - after parsing all data types parsed with an equivalent data type in any openDTMgr + * replaced by the data type from the openDTMgr + * + * NOTE: This will only occur if the data type from the openDTMgr's is equivalent. + * + * NOTE: Providing the correct languageID and compilerSpec is very important for header files that might use sizeof() + * @param openDTMgrs array of datatypes managers to use for undefined data types + * + * @param filenames names of files in order to parse, could include strings with + * "#" at start, which are ignored as comments + * @param includePaths paths to include files, instead of using "-I" in args + * @param args arguments for parsing, "-D=", ( "-I" use includePaths parm instead) + * + * @param existingDTMgr datatypes will be populated into this provided DTMgr, can pass Program or File DTMgr + * + * @param languageId language identication to use for data type organization definitions (int, long, ptr size) + * @param compileSpecId compiler specification to use for parsing + * + * @param monitor used to cancel or provide results + * + * @return a formatted string of any output from pre processor parsing or C parsing + * + * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing + * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing + * @throws IOException if there io are errors saving the archive + * + */ + public static CParseResults parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String includePaths[], String args[], DataTypeManager existingDTMgr, + String languageId, String compileSpecId, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { Language language = DefaultLanguageService.getLanguageService().getLanguage(new LanguageID(languageId)); CompilerSpec compilerSpec = language.getCompilerSpecByID(new CompilerSpecID(compileSpecId)); - - String dtmgrName = existingDTMgr.getName(); - if (existingDTMgr instanceof FileDataTypeManager) { - dtmgrName = ((FileDataTypeManager) existingDTMgr).getPath(); - } - - ProgramDB program = new ProgramDB(dtmgrName, language, compilerSpec, CParserUtils.class); - try { - DataTypeManager programDtm = program.getDataTypeManager(); - String messages = parseHeaderFiles(openDTMgrs, filenames, args, programDtm, cpp, monitor); - - int txId = existingDTMgr.startTransaction("Add Types"); - try { - Iterator allDataTypes = programDtm.getAllDataTypes(); - while (allDataTypes.hasNext()) { - existingDTMgr.resolve(allDataTypes.next(), null); - } - } - finally { - existingDTMgr.endTransaction(txId, true); - } - return messages; - } - finally { - program.release(CParserUtils.class); + if (existingDTMgr instanceof StandAloneDataTypeManager) { + try { + ((StandAloneDataTypeManager) existingDTMgr).setProgramArchitecture(language, compilerSpec.getCompilerSpecID(), + StandAloneDataTypeManager.LanguageUpdateOption.UNCHANGED, monitor); + } + catch (CompilerSpecNotFoundException e) { + e.printStackTrace(); + } + catch (LanguageNotFoundException e) { + e.printStackTrace(); + } + catch (CancelledException e) { + // ignore + } + catch (LockException e) { + e.printStackTrace(); + } + catch (UnsupportedOperationException e) { + e.printStackTrace(); + } + catch (IOException e) { + e.printStackTrace(); + } + catch (IncompatibleLanguageException e) { + // Shouldn't happen, unless already had a language + e.printStackTrace(); + } } + + return parseHeaderFiles(openDTMgrs, filenames, includePaths, args, existingDTMgr, monitor); } /** @@ -405,12 +507,11 @@ public class CParserUtils { * * @param filenames names of files in order to parse, could include strings with * "#" at start, which are ignored as comments - * @param args arguments for parsing, "-D=", "-I" + * @param includePaths paths to include files, instead of using "-I" in args + * @param args arguments for parsing, "-D=", ( "-I" use includePaths parm instead) * * @param dtMgr datatypes will be populated into this provided DTMgr, can pass Program or File DTMgr * - * @param cpp provided PreProcessor, useful if parsed Define's need to be examined after parsing - * * @param monitor used to cancel or provide results * * @return a formatted string of any output from pre processor parsing or C parsing @@ -418,17 +519,16 @@ public class CParserUtils { * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing */ - public static String parseHeaderFiles(DataTypeManager[] openDTmanagers, String[] filenames, - String args[], DataTypeManager dtMgr, PreProcessor cpp, TaskMonitor monitor) + public static CParseResults parseHeaderFiles(DataTypeManager[] openDTmanagers, String[] filenames, String[] includePaths, + String args[], DataTypeManager dtMgr, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException { String cppMessages = ""; - if (cpp == null) { - cpp = new PreProcessor(); - } - ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PreProcessor cpp = new PreProcessor(); + cpp.setArgs(args); + cpp.addIncludePaths(includePaths); PrintStream os = System.out; String fName = dtMgr.getName().replace(".gdt","")+"_CParser.out"; @@ -442,8 +542,10 @@ public class CParserUtils { System.setOut(os); cpp.setMonitor(monitor); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); cpp.setOutputStream(bos); + boolean parseSucceeded = false; try { for (String filename : filenames) { if (monitor.isCancelled()) { @@ -469,9 +571,9 @@ public class CParserUtils { parseFile(filename, monitor, cpp); } } - } catch (RuntimeException re) { + parseSucceeded = true; + } catch (Throwable e) { Msg.info(cpp, cpp.getParseMessages()); - throw new ghidra.app.util.cparser.CPP.ParseException(re.getMessage()); } finally { System.out.println(bos); os.flush(); @@ -481,12 +583,16 @@ public class CParserUtils { } cppMessages = cpp.getParseMessages(); + if (!parseSucceeded) { + return new CParseResults(cpp, "", cppMessages, false); + } // process all the defines and add any that are integer values into // the Equates table cpp.getDefinitions().populateDefineEquates(openDTmanagers, dtMgr); String parserMessages = ""; + boolean cparseSucceeded = false; if (!monitor.isCancelled()) { monitor.setMessage("Parsing C"); @@ -497,12 +603,15 @@ public class CParserUtils { cParser.setParseFileName(fName); cParser.setMonitor(monitor); cParser.parse(bis); + cparseSucceeded = cParser.didParseSucceed(); + } catch (RuntimeException re) { + Msg.info(cpp, cpp.getParseMessages()); } finally { parserMessages = cParser.getParseMessages(); } } - return getFormattedParseMessage(parserMessages, cppMessages, null); + return new CParseResults(cpp, parserMessages, cppMessages, cparseSucceeded); } private static String parseFile(String filename, TaskMonitor monitor, PreProcessor cpp) @@ -522,26 +631,6 @@ public class CParserUtils { return cpp.getParseMessages(); } - private static String getFormattedParseMessage(String parseMessage, String cppMessage, String errMsg) { - String message = ""; - - if (errMsg != null) { - message += errMsg + "\n\n"; - } - - String msg = parseMessage; - if (msg != null && msg.length() != 0) { - message += "CParser Messages:\n" + msg + "\n\n"; - } - - msg = cppMessage; - if (msg != null && msg.length() != 0) { - message += "PreProcessor Messages:\n" + msg; - } - - return message; - } - private static DataTypeManager[] getDataTypeManagers(DataTypeManagerService service) { if (service == null) { @@ -680,4 +769,54 @@ public class CParserUtils { errorIndex + "
" + successFailureBuffer; } + + public static File getFile(String parent, String filename) { + File file = findFile(parent, filename); + if (file != null) { + return file; + } + // filename lower + file = findFile(parent, filename.toLowerCase()); + if (file != null) { + return file; + } + // parent and filename lower + file = findFile(parent.toLowerCase(), filename.toLowerCase()); + if (file != null) { + return file; + } + // parent and filename upper + file = findFile(parent.toUpperCase(), filename.toUpperCase()); + return file; + } + + private static File findFile(String parent, String filename) { + File iFile = null; + + iFile = new File(parent + File.separator + filename); + if (iFile.exists()) + return iFile; + + // try just in this directory + File sameiFile = new File(parent + File.separator + + (new File(filename)).getName()); + if (sameiFile.exists()) + return sameiFile; + + // try all files in this directory doing to-lower on both input file and output file + // if match return it + File folder = new File(parent); + if (folder.isDirectory()) { + File[] listOfFiles = folder.listFiles(); + + if (listOfFiles != null) { + for (File file : listOfFiles) { + if (file.isFile() && filename.compareToIgnoreCase(file.getName()) == 0) { + return file; + } + } + } + } + return null; + } } 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 84628aee6b..c6e6d59c74 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 @@ -622,7 +622,7 @@ public class CParser { * @param dec Declaration * @param funcDT function data type to qualify */ - private void applyFunctionQualifiers(Declaration dec, FunctionDefinitionDataType funcDT) { + private void applyFunctionQualifiers(Declaration dec, FunctionDefinition funcDT) { List qualifierList = dec.getQualifiers(); if (qualifierList.contains(NORETURN) ) { funcDT.setNoReturn(true); @@ -1773,18 +1773,18 @@ Declaration TypeQualifier(Declaration dec) : {} | | | - ( DeclSpec() ) + ( DeclSpec(dec) ) ) { return dec; } } -void AttributeSpec() : {} +void AttributeSpec(Declaration dec) : {} { - ( ("(") SubIdent() (")") ) | + ( ("(") SubIdent(dec) (")") ) | AsmStatement() | - ( "[" "[" AttributeList() "]" "]" ) | + ( "[" "[" AttributeList(dec) "]" "]" ) | AlignmentSpecifier() } @@ -1794,48 +1794,59 @@ void AlignmentSpecifier() : { Declaration dt = new Declaration(); } < ALIGNAS > "(" ( TypeQualifier(dt) | AssignmentExpression() ) ")" } -void AttributeList() : { } +void AttributeList(Declaration dec) : { } { - AttributeToken() [ ("," AttributeToken())+ ] + AttributeToken(dec) [ ("," AttributeToken(dec))+ ] } -void AttributeToken() : { Declaration dt = new Declaration(); } +void AttributeToken(Declaration dec) : { } { - ( < IDENTIFIER > | TypeQualifier(dt) ) [ ":" ":" ( < IDENTIFIER > | TypeQualifier(dt) ) ] [ "(" ( < IDENTIFIER > | Constant() ) [ ( "," (< IDENTIFIER > | Constant()) )+ ] ")" ] + ( < IDENTIFIER > | TypeQualifier(dec) ) [ ":" ":" ( < IDENTIFIER > | TypeQualifier(dec) ) ] [ "(" ( < IDENTIFIER > | Constant() ) [ ( "," (< IDENTIFIER > | Constant()) )+ ] ")" ] } -void AttributeSpecList() : {} +void AttributeSpecList(Declaration dec) : {} { - ( AttributeSpec() )+ + ( AttributeSpec(dec) )+ } -void SubIdent() : { Declaration dt = new Declaration(); } +void SubIdent(Declaration dec) : { + Token id = null; + Declaration dt = new Declaration(); +} { - ( ( "(" [SubIdent()] ")" ) | - ( | TypeQualifier(dt)) [ ( "(" [SubIdent()] ")") | ("=" SubIdent()) ] + ( ( "(" [SubIdent(dec)] ")" ) | + (id= | TypeQualifier(dec)) [ ( "(" [SubIdent(dt)] ")") | ("=" SubIdent(dt)) ] [ "," - SubIdent() + SubIdent(dt) ] | Constant() [ "," - SubIdent() + SubIdent(dt) ] ) + { + if (id != null && "noreturn".equals(id.image.replace("_", ""))) { + dec.addQualifier(NORETURN); + if (dec.getDataType() instanceof FunctionDefinition) { + applyFunctionQualifiers(dec, (FunctionDefinition) dec.getDataType()); + } + } + } } -void DeclSpec() : { +void DeclSpec(Declaration dec) : { } { - ( ( | ) "(" DeclSpecifier() ")" ) | - AttributeSpecList() + ( ( | ) "(" DeclSpecifier(dec) ")" ) | + AttributeSpecList(dec) } -void DeclSpecifier() : { +void DeclSpecifier(Declaration dec) : { + Token id = null; } { - "(" DeclSpecifier() ")" | - ()+ [ "(" - DeclConstant() ( DeclConstant() )* - ")" ] + "(" DeclSpecifier(dec) ")" | + (id= { if (id != null && "noreturn".equals(id.image)) dec.addQualifier(NORETURN); } )+ + [ "(" DeclConstant() ( DeclConstant() )* ")" ] } void DeclConstant() : {} @@ -1971,6 +1982,7 @@ DataType StructOrUnionSpecifier() : { Token parent; Token sname; Composite comp; + Declaration dec = new Declaration(); } { { @@ -1983,7 +1995,7 @@ DataType StructOrUnionSpecifier() : { LOOKAHEAD(3) [ t= [ ":" parent=] { comp=defineNamedComposite(t, null, comp); } // no parent yet, since there are no guts - ] "{" [StructDeclarationList(comp)] "}" [ AttributeSpecList() ] + ] "{" [StructDeclarationList(comp)] "}" [ AttributeSpecList(dec) ] | sname= ) @@ -1998,10 +2010,13 @@ DataType StructOrUnionSpecifier() : { } } -Composite StructOrUnion() : {Composite comp;} +Composite StructOrUnion() : { + Composite comp; + Declaration dec = new Declaration(); +} { ( - ( DeclSpec() | PragmaSpec() )* + ( DeclSpec(dec) | PragmaSpec() )* { comp = new StructureDataType(getCurrentCategoryPath(), ANONYMOUS_STRUCT_PREFIX + cnt++, 0, dtMgr); @@ -2015,7 +2030,7 @@ Composite StructOrUnion() : {Composite comp;} } | - ( DeclSpec() )* { + ( DeclSpec(dec) )* { comp = new UnionDataType(getCurrentCategoryPath(), ANONYMOUS_UNION_PREFIX + cnt++, dtMgr); // Always set the packing, because by default structures should be aligned @@ -2068,6 +2083,7 @@ void InitDeclarator(Declaration dt) : { Declaration dec; } void StructDeclaration(Composite comp, CompositeHandler compositeHandler) : { Declaration dt = null; + Declaration dec = new Declaration(); } { LineDef() | @@ -2078,7 +2094,7 @@ void StructDeclaration(Composite comp, CompositeHandler compositeHandler) : { [ StructDeclaratorList(dt, comp, compositeHandler) { dt= null; } ] - [ AttributeSpecList() ] + [ AttributeSpecList(dec) ] ";" ) { @@ -2174,12 +2190,13 @@ DataType EnumSpecifier() : { Token t= null; DataType dt; ArrayList list; + Declaration dec = new Declaration(); } { ( LOOKAHEAD(3) - [AttributeSpecList()] [ t= ] "{" list= EnumeratorList() "}" + [AttributeSpecList(dec)] [ t= ] "{" list= EnumeratorList() "}" { String enumName= (t != null ? t.image : ("enum_" + cnt++)); EnumDataType enuum= new EnumDataType(getCurrentCategoryPath(), enumName, 4, dtMgr); @@ -2248,7 +2265,7 @@ Declaration Declarator(Declaration dt, DataType container) : { } { ( - [ dt = TypeQualifierList(dt) ] [ dt = Pointer(dt) ] dec= DirectDeclarator(dt, container) [ AttributeSpecList() ] + [ dt = TypeQualifierList(dt) ] [ dt = Pointer(dt) ] dec= DirectDeclarator(dt, container) [ AttributeSpecList(dec) ] ) { return dec; diff --git a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/CPP/CPP.jj b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/CPP/CPP.jj index 4db24f649d..4725c46480 100644 --- a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/CPP/CPP.jj +++ b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/CPP/CPP.jj @@ -1164,6 +1164,16 @@ public class PreProcessor { public void addIncludePath(String path) { pathList.addElement(path); } + + public void addIncludePaths(String[] paths) { + if (paths == null || paths.length < 1) { + return; + } + + for (String path : paths) { + addIncludePath(path); + } + } public int getNumericType(String val) { try { @@ -1238,7 +1248,7 @@ public class PreProcessor { outputStream = new PrintStream(fos); } - public void parse(String filename) throws ParseException { + public boolean parse(String filename) throws ParseException { if (verboseLevel == 1) { addParseMessage(null, "PreProcessor: Reading from file " + filename + " . . ."); @@ -1261,7 +1271,7 @@ public class PreProcessor { } if (fis == null) { addParseMessage(null, "PreProcessor: File " + filename + " not found."); - return; + return false; } fileStack.push(filename); alreadyDone = new HashMap(); @@ -1282,6 +1292,7 @@ public class PreProcessor { addParseMessage(filename, "PreProcessor Token Error: " + e.getMessage()); throw e; } + return true; } PreProcessor(PreProcessor parent) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogParsingAndPromptsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogParsingAndPromptsTest.java new file mode 100644 index 0000000000..c4bddf24c1 --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogParsingAndPromptsTest.java @@ -0,0 +1,596 @@ +/* ### + * 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 ghidra.app.plugin.core.cparser; + +import static org.junit.Assert.*; + +import java.awt.Window; +import java.io.File; +import java.util.ArrayList; + +import javax.swing.JTextArea; + +import org.junit.*; + +import docking.action.DockingActionIf; +import docking.widgets.combobox.GhidraComboBox; +import docking.widgets.dialogs.InputDialog; +import docking.widgets.filechooser.GhidraFileChooser; +import docking.widgets.pathmanager.PathnameTablePanel; +import generic.jar.ResourceFile; +import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; +import ghidra.app.plugin.core.cparser.ParseDialog.ComboBoxItem; +import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; +import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin; +import ghidra.app.plugin.core.processors.SetLanguageDialog; +import ghidra.framework.plugintool.PluginTool; +import ghidra.plugin.importer.NewLanguagePanel; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.Program; +import ghidra.test.*; +import utilities.util.FileUtilities; + +public class ParseDialogParsingAndPromptsTest extends AbstractGhidraHeadedIntegrationTest { + + private static final String TITLE = "CParser Results Summary"; + + private TestEnv env; + private PluginTool tool; + + private Program program; + private CParserPlugin plugin; + + private DockingActionIf cparserAction; + + private DataTypeManagerPlugin dtmPlugin; + + + @Before + public void setUp() throws Exception { + program = getNotepad(); + + env = new TestEnv(); + } + + private Program getNotepad() throws Exception { + ClassicSampleX86ProgramBuilder builder = + new ClassicSampleX86ProgramBuilder("notepad", false, this); + return builder.getProgram(); + } + + @After + public void tearDown() throws Exception { + env.dispose(); + } + + private void initTool(Program prog) throws Exception { + if (prog != null) { + tool = env.showTool(prog); + } else { + tool = env.showTool(); + } + + tool.addPlugin(CodeBrowserPlugin.class.getName()); + tool.addPlugin(GoToAddressLabelPlugin.class.getName()); + tool.addPlugin(CParserPlugin.class.getName()); + tool.addPlugin(DataTypeManagerPlugin.class.getName()); + plugin = getPlugin(tool, CParserPlugin.class); + + cparserAction = getAction(plugin, CParserPlugin.PARSE_ACTION_NAME); + dtmPlugin = getPlugin(tool, DataTypeManagerPlugin.class); + } + + @Test + public void testImportToProgramNoneOpen() throws Exception { + initTool(null); + + ParseDialog parseDialog = showParseDialog(); + + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + + pressButtonByText(parseDialog, "Parse to Program", false); + + pressButtonByText(waitForDialogComponent("No Open Program"), "OK", false); + + } + + @Test + public void testImportToProgramNoArch() throws Exception { + program = getNotepad(); + + initTool(program); + + ParseDialog parseDialog = showParseDialog(); + + this.setSelectedParseProfile(parseDialog, "MacOSX_10.5.prf"); + + pressButtonByText(parseDialog, "Parse to Program", false); + + String langText = parseDialog.getLanguageText().getText(); + assertEquals("64/32 (primarily for backward compatibility)", langText); + + pressButtonByText(waitForDialogComponent("Program Architecture not Specified"), "OK", false); + } + + @Test + public void testImportToProgramConfirm() throws Exception { + program = getNotepad(); + + initTool(program); + + ParseDialog parseDialog = showParseDialog(); + + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + + pressButtonByText(parseDialog, "Parse to Program", false); + + pressButtonByText(waitForDialogComponent("Confirm"), "Cancel", false); + } + + @Test + public void testImportToProgram() throws Exception { + + initTool(program); + + ParseDialog parseDialog = showParseDialog(); + + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + + // write out a dummy header file to read + File dummyHeader = this.createTempFile("dummy.h"); + + FileUtilities.deleteDir(dummyHeader); + + String files[] = {dummyHeader.getPath()}; + + this.setFiles(parseDialog, files); + + pressButtonByText(parseDialog, "Parse to Program", false); + + pressButtonByText(waitForDialogComponent("Confirm"), "Continue", false); + + // dummy file empty, error + pressButtonByText(waitForDialogComponent("Parse Errors"), "OK", false); + + // dummy file full, OK + FileUtilities.writeStringToFile(dummyHeader, + "typedef int wchar_t;\n" + + "struct mystruct {\n" + + " wchar_t defined_wchar_t;\n" + + "};\n"); + + this.setFiles(parseDialog, files); + + pressButtonByText(parseDialog, "Parse to Program", false); + + pressButtonByText(waitForDialogComponent("Confirm"), "Continue", false); + + pressButtonByText(waitForDialogComponent("C-Parse of Header Files Complete"), "OK", false); + + DataType dataType = program.getDataTypeManager().getDataType("/"+dummyHeader.getName()+ "/" + "mystruct"); + + assertNotNull("mystruct parsed into program", dataType); + } + + @Test + public void testImportToProgramWithIncludePaths() throws Exception { + + initTool(program); + + ParseDialog parseDialog = showParseDialog(); + + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + + // write out a dummy header file to read + File dummyHeader = this.createTempFile("dummy.h"); + + FileUtilities.deleteDir(dummyHeader); + + String files[] = {dummyHeader.getName()}; + + this.setFiles(parseDialog, files); + + String includePath[] = {dummyHeader.getParent()}; + + this.setIncludePaths(parseDialog, includePath); + + pressButtonByText(parseDialog, "Parse to Program", false); + + pressButtonByText(waitForDialogComponent("Confirm"), "Continue", false); + + // dummy file empty, error + pressButtonByText(waitForDialogComponent("Parse Errors"), "OK", false); + + // dummy file full, OK + FileUtilities.writeStringToFile(dummyHeader, + "typedef int wchar_t;\n" + + "struct mystruct {\n" + + " wchar_t defined_wchar_t;\n" + + "};\n"); + + this.setFiles(parseDialog, files); + + pressButtonByText(parseDialog, "Parse to Program", false); + + pressButtonByText(waitForDialogComponent("Confirm"), "Continue", false); + + pressButtonByText(waitForDialogComponent("C-Parse of Header Files Complete"), "OK", false); + + DataType dataType = program.getDataTypeManager().getDataType("/"+dummyHeader.getName()+ "/" + "mystruct"); + + assertNotNull("mystruct parsed into program", dataType); + } + + @Test + public void testImportToProgramOpenArchives() throws Exception { + initTool(program); + + ParseDialog parseDialog = showParseDialog(); + + // open an archive + dtmPlugin.openDataTypeArchive("windows_vs12_64"); + + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + + pressButtonByText(parseDialog, "Parse to Program", false); + + pressButtonByText(waitForDialogComponent("Confirm"), "Continue", false); + + pressButtonByText(waitForDialogComponent("Use Open Archives?"), "Don't Use Open Archives", false); + } + + // switch between two + // change, test ask save if change + @Test + public void testSetLanguage() throws Exception { + initTool(program); + + ParseDialog parseDialog = showParseDialog(); + + // + // test switch to new profile after changes, NO to save changes + + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + + setLanguage(parseDialog, "8051:BE:16:default", "default"); + + runSwing(() -> { + this.setSelectedParseProfile(parseDialog, "VisualStudio12_64.prf"); + }, false); + + pressButtonByText(waitForDialogComponent("Save Changes to Another Profile?"), "No", false); + + + assertEquals("VisualStudio12_64.prf",parseDialog.getCurrentItem().getName()); + + // + // test forced save as new profile, doesn't exist + setLanguage(parseDialog, "8051:BE:16:default", "default"); + + runSwing(() -> { + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + }, false); + + // make sure profile is gone + ResourceFile userProfileParent = parseDialog.getUserProfileParent(); + File f = new File(userProfileParent.getAbsolutePath(),"MyTestProfile.prf"); + f.delete(); + + pressButtonByText(waitForDialogComponent("Save Changes to Another Profile?"), "Yes", false); + + InputDialog dialog = waitForDialogComponent(InputDialog.class); + + dialog.setValue("MyTestProfile"); + pressButtonByText(dialog, "OK"); + waitForSwing(); + + assertEquals("MyTestProfile.prf",parseDialog.getCurrentItem().getName()); + + // test save as forced to an existing profile + + runSwing(() -> { + this.setSelectedParseProfile(parseDialog, "VisualStudio12_64.prf"); + }, false); + + setLanguage(parseDialog, "x86:LE:64:default", "gcc"); + + runSwing(() -> { + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + }, false); + + pressButtonByText(waitForDialogComponent("Save Changes to Another Profile?"), "Yes", false); + + dialog = waitForDialogComponent(InputDialog.class); + + dialog.setValue("MyTestProfile"); + pressButtonByText(dialog, "OK"); + waitForSwing(); + + pressButtonByText(waitForDialogComponent("Overwrite Existing File?"), "Yes", false); + + waitForSwing(); + + assertEquals("MyTestProfile.prf",parseDialog.getCurrentItem().getName()); + + // test save the current USER profile when switching + + runSwing(() -> { + this.setSelectedParseProfile(parseDialog, "MyTestProfile.prf"); + }, false); + + setLanguage(parseDialog, "8051:BE:16:default", "default"); + + runSwing(() -> { + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + }, false); + + pressButtonByText(waitForDialogComponent("Save Changes to Profile?"), "Yes", false); + + assertEquals("VisualStudio12_32.prf",parseDialog.getCurrentItem().getName()); + + } + + private void setLanguage(ParseDialog parseDialog, String langID, String compID) { + runSwing(() -> { + pressButton(parseDialog.getLanguageButton()); + }, false); + + SetLanguageDialog dlg = waitForDialogComponent(SetLanguageDialog.class); + assertNotNull(dlg); + NewLanguagePanel languagePanel = + (NewLanguagePanel) getInstanceField("selectLangPanel", dlg); + assertNotNull(languagePanel); + + waitForSwing(); + + runSwing(() -> { + NewLanguagePanel selectLangPanel = + (NewLanguagePanel) getInstanceField("selectLangPanel", dlg); + selectLangPanel.setSelectedLcsPair( + new LanguageCompilerSpecPair(new LanguageID(langID), new CompilerSpecID(compID))); + }, true); + + waitForSwing(); + + pressButtonByText(dlg, "OK"); + } + + // test parse to file, choose file + @Test + public void testImportToFile() throws Exception { + + initTool(program); + + ParseDialog parseDialog = showParseDialog(); + + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + + setLanguage(parseDialog, "8051:BE:16:default", "default"); + + final File tmpDir = createTempDirectory("GDT"); + FileUtilities.checkedMkdir(tmpDir); + + // open an archive + // write out a dummy header file to read + File dummyHeader = this.createTempFile("dummy.h"); + + FileUtilities.deleteDir(dummyHeader); + + String files[] = {dummyHeader.getName()}; + + this.setFiles(parseDialog, files); + + String includePath[] = {dummyHeader.getParent()}; + + this.setIncludePaths(parseDialog, includePath); + + pressButtonByText(parseDialog, "Parse to File...", false); + + final GhidraFileChooser fileChooser = waitForDialogComponent(GhidraFileChooser.class); + + runSwing(() -> fileChooser.setSelectedFile(new File(tmpDir, "dummy"))); + + waitForUpdateOnChooser(fileChooser); + + pressButtonByName(fileChooser.getComponent(), "OK"); + + // dummy file empty, error + pressButtonByText(waitForDialogComponent("Parse Errors"), "OK", false); + + // dummy file full, OK + FileUtilities.writeStringToFile(dummyHeader, + "typedef int wchar_t;\n" + + "struct mystruct {\n" + + " wchar_t defined_wchar_t;\n" + + "};\n"); + + this.setFiles(parseDialog, files); + + pressButtonByText(parseDialog, "Parse to File...", false); + + final GhidraFileChooser existsChooser = waitForDialogComponent(GhidraFileChooser.class); + + final File GDTarchiveFile = new File(tmpDir, "dummy.gdt"); + + runSwing(() -> existsChooser.setSelectedFile(GDTarchiveFile), true); + + waitForUpdateOnChooser(existsChooser); + + pressButtonByName(existsChooser.getComponent(), "OK"); + + pressButtonByText(waitForDialogComponent("Overwrite Existing File?"), "Yes", false); + + pressButtonByText(waitForDialogComponent("C-Parse of Header Files Complete"), "OK", false); + + // open the file archive + FileDataTypeManager fileArchive = FileDataTypeManager.openFileArchive(GDTarchiveFile, false); + + DataType dataType = fileArchive.getDataType("/"+dummyHeader.getName()+ "/" + "mystruct"); + + assertNotNull("mystruct parsed into program", dataType); + } + + // test parse to file, choose file + @Test + public void testImportToFileUseArchive() throws Exception { + + initTool(program); + + ParseDialog parseDialog = showParseDialog(); + + // open an archive + dtmPlugin.openDataTypeArchive("windows_vs12_64"); + + this.setSelectedParseProfile(parseDialog, "VisualStudio12_32.prf"); + + setLanguage(parseDialog, "8051:BE:16:default", "default"); + + final File tmpDir = createTempDirectory("GDT"); + FileUtilities.checkedMkdir(tmpDir); + + // open an archive + // write out a dummy header file to read + File dummyHeader = this.createTempFile("dummy.h"); + + FileUtilities.deleteDir(dummyHeader); + + String files[] = {dummyHeader.getName()}; + + String includePath[] = {dummyHeader.getParent()}; + + this.setIncludePaths(parseDialog, includePath); + + // dummy file full, OK + FileUtilities.writeStringToFile(dummyHeader, + "typedef int wchar_t;\n" + + "struct mystruct {\n" + + " wchar_t defined_wchar_t;\n" + + " wint_t defined_from_windows;\n" + + "};\n"); + + this.setFiles(parseDialog, files); + + pressButtonByText(parseDialog, "Parse to File...", false); + + final GhidraFileChooser existsChooser = waitForDialogComponent(GhidraFileChooser.class); + + final File GDTarchiveFile = new File(tmpDir, "dummy.gdt"); + + runSwing(() -> existsChooser.setSelectedFile(GDTarchiveFile), true); + + waitForUpdateOnChooser(existsChooser); + + pressButtonByName(existsChooser.getComponent(), "OK"); + + pressButtonByText(waitForDialogComponent("Use Open Archives?"), "Use Open Archives", false); + + pressButtonByText(waitForDialogComponent("C-Parse of Header Files Complete"), "OK", false); + + // open the file archive + FileDataTypeManager fileArchive = FileDataTypeManager.openFileArchive(GDTarchiveFile, false); + + DataType dataType = fileArchive.getDataType("/"+dummyHeader.getName()+ "/" + "mystruct"); + + assertNotNull("mystruct parsed into program", dataType); + + Structure struct = (Structure) dataType; + + DataTypeComponent component = struct.getComponent(1); + assertEquals (component.getDataType().getName(), "wint_t"); + } + + + private void startSetLanguage(LanguageID languageID, CompilerSpecID compilerSpecID) throws Exception { + if (languageID == null) { + throw new RuntimeException("languageID == null not allowed"); + } + if (compilerSpecID == null) { + throw new RuntimeException("compilerSpecID == null not allowed"); + } + + SetLanguageDialog dlg = waitForDialogComponent(SetLanguageDialog.class); + assertNotNull(dlg); + NewLanguagePanel languagePanel = + (NewLanguagePanel) getInstanceField("selectLangPanel", dlg); + assertNotNull(languagePanel); + + waitForSwing(); + + runSwing(() -> { + NewLanguagePanel selectLangPanel = + (NewLanguagePanel) getInstanceField("selectLangPanel", dlg); + selectLangPanel.setSelectedLcsPair( + new LanguageCompilerSpecPair(languageID, compilerSpecID)); + }, true); + + waitForSwing(); + + pressButtonByText(dlg, "OK"); + } + + private void assertResultDialog() { + Window aboutDialog = waitForWindow(TITLE); + assertNotNull(aboutDialog); + pressButtonByText(aboutDialog, "OK"); + } + + private ParseDialog showParseDialog() { + + //ActionContext actionContext = cbPlugin.getProvider().getActionContext(null); + performAction(cparserAction, false); + ParseDialog parseDialog = waitForDialogComponent(ParseDialog.class); + assertNotNull(parseDialog); + return parseDialog; + } + + private void setOption(ParseDialog dialog, String options) { + runSwing(() -> { + JTextArea parseOptionsTextField = dialog.getParseOptionsTextField(); + parseOptionsTextField.setText(options); + }); + } + + private void setIncludePaths(ParseDialog dialog, String paths[]) { + runSwing(() -> { + PathnameTablePanel incPaths = dialog.getIncludePaths(); + incPaths.setPaths(paths); + }); + } + + private void setFiles(ParseDialog dialog, String files[]) { + runSwing(() -> { + PathnameTablePanel sourceFiles = dialog.getSourceFiles(); + sourceFiles.setPaths(files); + }); + } + + private void setSelectedParseProfile(ParseDialog dialog, String profileName) { + runSwing(() -> { + GhidraComboBox parseComboBox = dialog.getParseComboBox(); + ArrayList profiles = dialog.getProfiles(); + int index = 0; + for (ComboBoxItem comboBoxItem : profiles) { + if (profileName.equals(comboBoxItem.getName())) { + parseComboBox.setSelectedIndex(index); + break; + } + index++; + } + }); + } + +} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogTest.java index f2d0ff3844..e3d14c13e5 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogTest.java @@ -352,17 +352,21 @@ public class ParseDialogTest extends AbstractGhidraHeadedIntegrationTest { BufferedReader br = new BufferedReader(new InputStreamReader(profileFile.getInputStream())); String line = null; - while ((line = br.readLine()) != null) { + + // read paths + while ((line = br.readLine()) != null && line.trim().length() > 0) { line = line.trim(); - if (line.startsWith("-") || (line.length() == 0 && buffy.length() > 0)) { - // this is a compiler directive - buffy.append(line + "\n"); - } - else if (line.length() > 0) { - File f = new File(line); - pathList.add(f.getPath()); - } + + pathList.add(line); } + + // read options + while ((line = br.readLine()) != null && line.trim().length() > 0) { + line = line.trim(); + + buffy.append(line + "\n"); + } + paths = pathList; defaultPrfOptions = buffy.toString(); 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 a06e8235d6..52282a5823 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 @@ -222,11 +222,11 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { String parseMessages = parser.getParseMessages(); System.out.println(parseMessages); - assertTrue("Duplicate ENUM message", parseMessages.contains("duplicate enum value: options_enum : PLUS_SET : 16")); + assertTrue("Duplicate ENUM message missing", parseMessages.contains("duplicate enum value: options_enum : PLUS_SET : 16")); - assertTrue("Duplicate ENUM message", parseMessages.contains("Static_Asssert has failed \"\"math fail!\"\"")); + assertTrue("Duplicate ENUM message missing", parseMessages.contains("Static_Asssert has failed \"\"math fail!\"\"")); - assertTrue("Duplicate ENUM message", parseMessages.contains("Static_Asssert has failed \"\"1 + 1 == 3, fail!\"\"")); + assertTrue("Duplicate ENUM message missing", parseMessages.contains("Static_Asssert has failed \"\"1 + 1 == 3, fail!\"\"")); DataType dt; DataType pointedToDT; @@ -362,8 +362,16 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { dt = dtMgr.getDataType(new CategoryPath("/functions"), "_Noreturn_exit"); assertTrue("not a function", dt instanceof FunctionDefinition); - assertTrue("Caller should purge", ((FunctionDefinition) dt).hasNoReturn()); + assertTrue("Caller should noreturn", ((FunctionDefinition) dt).hasNoReturn()); + dt = dtMgr.getDataType(new CategoryPath("/functions"), "win_exit"); + assertTrue("not a function", dt instanceof FunctionDefinition); + assertTrue("Caller should noreturn", ((FunctionDefinition) dt).hasNoReturn()); + + dt = dtMgr.getDataType(new CategoryPath("/functions"), "gcc_exit"); + assertTrue("not a function", dt instanceof FunctionDefinition); + assertTrue("Caller should noreturn", ((FunctionDefinition) dt).hasNoReturn()); + dt = dtMgr.getDataType(new CategoryPath("/"), "UINT2"); assertTrue(dt instanceof TypeDef); assertEquals("ushort", ((TypeDef) dt).getBaseDataType().getName()); 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 c26101d2d1..6dd802956c 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 @@ -378,12 +378,18 @@ int fputs( char * , void * ) __asm("_" "fputs" "$FPOOTS"); void _exit(int) __attribute__((noreturn)); +// NoReturn + +extern void gcc_exit (int __status) __attribute__ ((__noreturn__)); + +__declspec(noreturn) void __cdecl win_exit(int _Code); + +void _exit(int) __attribute__((noreturn)); + // C11 noreturn void _Noreturn _Noreturn_exit(int); - - // C23 Attributes int [[deprecated]] imDeprecated(int); int [[gnu::deprecated]] imDeprecatedToo(int) ; diff --git a/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java b/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java index 121d95c7dd..4682daca4a 100644 --- a/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java +++ b/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java @@ -31,6 +31,7 @@ import generic.jar.ResourceFile; import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; +import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.app.util.cparser.CPP.DefineTable; import ghidra.app.util.cparser.CPP.ParseException; import ghidra.app.util.cparser.CPP.PreProcessor; @@ -395,17 +396,14 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript { */ private void parseProcessorDefs(String procName, FileDataTypeManager dtMgr, DataTypeManager[] openTypes) throws ParseException, ghidra.app.util.cparser.C.ParseException, IOException { - PreProcessor cpp; String args[] = Arrays.append(orig_args, "-D__AVR_"+procName+"__"); - - cpp = new PreProcessor(); - String messages = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, "avr8:LE:16:atmega256", "gcc", cpp, monitor); + CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, "avr8:LE:16:atmega256", "gcc", monitor); - Msg.info(this, messages); + Msg.info(this, results.getFormattedParseMessage(null)); - storeExtraDefinitions(procName, dtMgr, openTypes, cpp); + storeExtraDefinitions(procName, dtMgr, openTypes, results.preProcessor()); } /** diff --git a/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java b/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java index 9d55d457d1..4091798a8f 100644 --- a/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java +++ b/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java @@ -33,6 +33,7 @@ import generic.jar.ResourceFile; import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; +import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.FileDataTypeManager; @@ -64,9 +65,9 @@ public class CreateJNIGDTArchivesScript extends GhidraScript { FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f); - String messages = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, languageID, compiler, null, monitor); + CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, languageID, compiler, monitor); - Msg.info(this, messages); + Msg.info(this, results.getFormattedParseMessage(null)); dtMgr.save(); dtMgr.close(); @@ -113,7 +114,6 @@ public class CreateJNIGDTArchivesScript extends GhidraScript { "-D__builtin_va_list=void *", "-D__DO_NOT_DEFINE_COMPILE", "-D_Complex", - "-D_WCHAR_T", "-D__NO_STRING_INLINES", "-D__signed__", "-D__extension__=", @@ -159,7 +159,6 @@ public class CreateJNIGDTArchivesScript extends GhidraScript { "-D__builtin_va_list=void *", "-D__DO_NOT_DEFINE_COMPILE", "-D_Complex", - "-D_WCHAR_T", "-D__NO_STRING_INLINES", "-D__signed__", "-D__extension__=",