VirtualBox

source: vbox/trunk/src/bldprogs/gccplugin.cpp@ 56962

Last change on this file since 56962 was 56933, checked in by vboxsync, 10 years ago

gccplugin: Getting a bit further, can identify relevant function calls and detect NULL format string pointers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.0 KB
Line 
1/* $Id: gccplugin.cpp 56933 2015-07-14 14:46:43Z vboxsync $ */
2/** @file
3 * gccplugin - GCC plugin for checking IPRT format strings.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*********************************************************************************************************************************
19* Header Files *
20*********************************************************************************************************************************/
21#include <stdio.h>
22#include <iprt/cdefs.h>
23
24#include "plugin.h"
25#include "basic-block.h"
26#include "gimple.h"
27#include "tree.h"
28#include "tree-pass.h"
29#include "cp/cp-tree.h"
30
31
32/*********************************************************************************************************************************
33* Global Variables *
34*********************************************************************************************************************************/
35/** License indicator. */
36int plugin_is_GPL_compatible;
37
38
39/*********************************************************************************************************************************
40* Defined Constants And Macros *
41*********************************************************************************************************************************/
42#define GCCPLUGIN_DEBUG
43/** Debug printf. */
44#ifdef GCCPLUGIN_DEBUG
45# define dprintf(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
46#else
47# define dprintf(...) do { } while (0)
48#endif
49
50
51/** For use with messages. */
52#define MY_LOC(a_hPreferred, a_pState) \
53 (DECL_P(a_hPreferred) ? DECL_SOURCE_LOCATION(a_hPreferred) : gimple_location((a_pState)->hStmt))
54
55
56/*******************************************************************************
57* Structures and Typedefs *
58*******************************************************************************/
59/** Checker state. */
60typedef struct MYCHECKSTATE
61{
62 gimple hStmt;
63 long iFmt;
64 long iArgs;
65} MYCHECKSTATE;
66/** Pointer to my checker state. */
67typedef MYCHECKSTATE *PMYCHECKSTATE;
68
69
70/*********************************************************************************************************************************
71* Internal Functions *
72*********************************************************************************************************************************/
73static bool MyPassGateCallback(void);
74static unsigned int MyPassExecuteCallback(void);
75static tree AttributeHandler(tree *, tree, tree, int, bool *);
76
77
78/*********************************************************************************************************************************
79* Global Variables *
80*********************************************************************************************************************************/
81/** Plug-in info. */
82static const struct plugin_info g_PlugInInfo =
83{
84 .version = "0.0.0-ALPHA",
85 .help = "Implements the __iprt_format__ attribute for checking format strings and arguments."
86};
87
88/** My pass. */
89static struct gimple_opt_pass g_MyPass =
90{
91 .pass =
92 {
93 .type = GIMPLE_PASS,
94 .name = "*iprt-format-checks", /* asterisk = no dump */
95 .optinfo_flags = 0,
96 .gate = MyPassGateCallback,
97 .execute = MyPassExecuteCallback,
98 .sub = NULL,
99 .next = NULL,
100 .static_pass_number = 0,
101 .tv_id = TV_NONE,
102 .properties_required = 0,
103 .properties_provided = 0,
104 .properties_destroyed = 0,
105 .todo_flags_start = 0,
106 .todo_flags_finish = 0,
107 }
108};
109
110/** The registration info for my pass. */
111static const struct register_pass_info g_MyPassInfo =
112{
113 .pass = &g_MyPass.pass,
114 .reference_pass_name = "ssa",
115 .ref_pass_instance_number = 1,
116 .pos_op = PASS_POS_INSERT_BEFORE,
117};
118
119
120/** Attribute specification. */
121static const struct attribute_spec g_AttribSpecs[] =
122{
123 {
124 .name = "iprt_format",
125 .min_length = 2,
126 .max_length = 2,
127 .decl_required = false,
128 .type_required = true,
129 .function_type_required = true,
130 .handler = AttributeHandler,
131 .affects_type_identity = false
132 },
133 { NULL, 0, 0, false, false, false, NULL, false }
134};
135
136
137#ifdef GCCPLUGIN_DEBUG
138
139/**
140 * Debug function for printing the scope of a decl.
141 * @param hDecl Declaration to print scope for.
142 */
143static void dprintScope(tree hDecl)
144{
145 tree hScope = CP_DECL_CONTEXT(hDecl);
146 if (hScope == global_namespace)
147 return;
148 if (TREE_CODE(hScope) == RECORD_TYPE)
149 hScope = TYPE_NAME(hScope);
150
151 /* recurse */
152 dprintScope(hScope);
153
154 /* name the scope. */
155 dprintf("::%s", DECL_NAME(hScope) ? IDENTIFIER_POINTER(DECL_NAME(hScope)) : "<noname>");
156}
157
158
159/**
160 * Debug function for printing a declaration.
161 * @param hDecl The declaration to print.
162 */
163static void dprintDecl(tree hDecl)
164{
165 enum tree_code const enmDeclCode = TREE_CODE(hDecl);
166 tree const hType = TREE_TYPE(hDecl);
167 enum tree_code const enmTypeCode = hType ? TREE_CODE(hType) : (enum tree_code)-1;
168#if 0
169 if ( enmTypeCode == RECORD_TYPE
170 && enmDeclCode == TYPE_DECL
171 && DECL_ARTIFICIAL(hDecl))
172 dprint_class(hType);
173#endif
174
175 dprintf("%s ", tree_code_name[enmDeclCode]);
176 dprintScope(hDecl);
177 dprintf("::%s", DECL_NAME(hDecl) ? IDENTIFIER_POINTER(DECL_NAME(hDecl)) : "<noname>");
178 if (hType)
179 dprintf(" type %s", tree_code_name[enmTypeCode]);
180 dprintf(" @%s:%d", DECL_SOURCE_FILE(hDecl), DECL_SOURCE_LINE(hDecl));
181}
182
183#endif /* GCCPLUGIN_DEBUG */
184
185
186/**
187 * Gate callback for my pass that indicates whether it should execute or not.
188 * @returns true to execute.
189 */
190static bool MyPassGateCallback(void)
191{
192 dprintf("MyPassGateCallback:\n");
193 return true;
194}
195
196
197static void MyCheckFormatWorker(PMYCHECKSTATE pState, tree hFmtArg)
198{
199
200}
201
202
203/**
204 * Deal recursively with special format string constructs.
205 *
206 * This will call MyCheckFormatWorker to validate each format string.
207 *
208 * @param pState The format string check state.
209 * @param hFmtArg The format string node.
210 */
211static void MyCheckFormatRecursive(PMYCHECKSTATE pState, tree hFmtArg)
212{
213 if (integer_zerop(hFmtArg))
214 warning_at(MY_LOC(hFmtArg, pState), 0, "Format string should not be NULL");
215 else
216 {
217 /** @todo more to do here. ternary expression, ++. */
218
219 MyCheckFormatWorker(pState, hFmtArg);
220 }
221}
222
223
224/**
225 * Execute my pass.
226 * @returns Flags indicates stuff todo, we return 0.
227 */
228static unsigned int MyPassExecuteCallback(void)
229{
230 dprintf("MyPassExecuteCallback:\n");
231
232 /*
233 * Enumerate the basic blocks.
234 */
235 basic_block hBasicBlock;
236 FOR_EACH_BB(hBasicBlock)
237 {
238 dprintf(" hBasicBlock=%p\n", hBasicBlock);
239
240 /*
241 * Enumerate the statements in the current basic block.
242 * We're interested in calls to functions with the __iprt_format__ attribute.
243 */
244 for (gimple_stmt_iterator hStmtItr = gsi_start_bb(hBasicBlock); !gsi_end_p(hStmtItr); gsi_next(&hStmtItr))
245 {
246 gimple const hStmt = gsi_stmt(hStmtItr);
247 enum gimple_code const enmCode = gimple_code(hStmt);
248#ifdef GCCPLUGIN_DEBUG
249 unsigned const cOps = gimple_num_ops(hStmt);
250 dprintf(" hStmt=%p %s (%d) ops=%d\n", hStmt, gimple_code_name[enmCode], enmCode, cOps);
251 for (unsigned iOp = 0; iOp < cOps; iOp++)
252 {
253 tree const hOp = gimple_op(hStmt, iOp);
254 if (hOp)
255 dprintf(" %02d: %p, code %s(%d)\n", iOp, hOp, tree_code_name[TREE_CODE(hOp)], TREE_CODE(hOp));
256 else
257 dprintf(" %02d: NULL_TREE\n", iOp);
258 }
259#endif
260 if (enmCode == GIMPLE_CALL)
261 {
262 /*
263 * Check if the function type has the __iprt_format__ attribute.
264 */
265 tree const hFn = gimple_call_fn(hStmt);
266 tree const hFnType = gimple_call_fntype(hStmt);
267 tree const hAttr = lookup_attribute("iprt_format", TYPE_ATTRIBUTES(hFnType));
268#ifdef GCCPLUGIN_DEBUG
269 tree const hFnDecl = gimple_call_fndecl(hStmt);
270 dprintf(" hFn =%p %s(%d); args=%d\n",
271 hFn, tree_code_name[TREE_CODE(hFn)], TREE_CODE(hFn), gimple_call_num_args(hStmt));
272 if (hFnDecl)
273 dprintf(" hFnDecl=%p %s(%d) type=%p %s:%d\n", hFnDecl, tree_code_name[TREE_CODE(hFnDecl)],
274 TREE_CODE(hFnDecl), TREE_TYPE(hFnDecl), DECL_SOURCE_FILE(hFnDecl), DECL_SOURCE_LINE(hFnDecl));
275 if (hFnType)
276 dprintf(" hFnType=%p %s(%d)\n", hFnType, tree_code_name[TREE_CODE(hFnType)], TREE_CODE(hFnType));
277#endif
278 if (hAttr)
279 {
280 /*
281 * Yeah, it has the attribute!
282 */
283 tree const hAttrArgs = TREE_VALUE(hAttr);
284 MYCHECKSTATE State;
285 State.iFmt = TREE_INT_CST(TREE_VALUE(hAttrArgs)).to_shwi();
286 State.iArgs = TREE_INT_CST(TREE_VALUE(TREE_CHAIN(hAttrArgs))).to_shwi();
287 State.hStmt = hStmt;
288 dprintf(" %s() __iprt_format__(iFmt=%ld, iArgs=%ld)\n",
289 DECL_NAME(hFnDecl) ? IDENTIFIER_POINTER(DECL_NAME(hFnDecl)) : "<unamed>", State.iFmt, State.iArgs);
290
291 MyCheckFormatRecursive(&State, gimple_call_arg(hStmt, State.iFmt - 1));
292 }
293 }
294 }
295 }
296 return 0;
297}
298
299
300
301/**
302 * Validate the use of an attribute.
303 *
304 * @returns ??
305 * @param phOnNode The node the attribute is being used on.
306 * @param hAttrName The attribute name.
307 * @param hAttrArgs The attribute arguments.
308 * @param fFlags Some kind of flags...
309 * @param pfDontAddAttrib Whether to add the attribute to this node or not.
310 */
311static tree AttributeHandler(tree *phOnNode, tree hAttrName, tree hAttrArgs, int fFlags, bool *pfDontAddAttrib)
312{
313 dprintf("AttributeHandler: name=%s fFlags=%#x", IDENTIFIER_POINTER(hAttrName), fFlags);
314 long iFmt = TREE_INT_CST(TREE_VALUE(hAttrArgs)).to_shwi();
315 long iArgs = TREE_INT_CST(TREE_VALUE(TREE_CHAIN(hAttrArgs))).to_shwi();
316 dprintf(" iFmt=%ld iArgs=%ld", iFmt, iArgs);
317
318 tree hType = *phOnNode;
319 dprintf(" hType=%p %s(%d)\n", hType, tree_code_name[TREE_CODE(hType)], TREE_CODE(hType));
320
321 if (pfDontAddAttrib)
322 *pfDontAddAttrib = false;
323 return NULL_TREE;
324}
325
326
327/**
328 * Called when we can register attributes.
329 *
330 * @param pvEventData Ignored.
331 * @param pvUser Ignored.
332 */
333static void RegisterAttributesEvent(void *pvEventData, void *pvUser)
334{
335 NOREF(pvEventData); NOREF(pvUser);
336 dprintf("RegisterAttributesEvent: pvEventData=%p\n", pvEventData);
337
338 register_attribute(&g_AttribSpecs[0]);
339}
340
341
342/**
343 * The plug-in entry point.
344 *
345 * @returns 0 to indicate success?
346 * @param pPlugInInfo Plugin info structure.
347 * @param pGccVer GCC Version.
348 */
349int plugin_init(plugin_name_args *pPlugInInfo, plugin_gcc_version *pGccVer)
350{
351 dprintf("plugin_init: %s\n", pPlugInInfo->full_name);
352 dprintf("gcc version: basever=%s datestamp=%s devphase=%s revision=%s\n",
353 pGccVer->basever, pGccVer->datestamp, pGccVer->devphase, pGccVer->revision);
354
355 /* Ask for callback in which we may register the attribute. */
356 register_callback(pPlugInInfo->base_name, PLUGIN_ATTRIBUTES, RegisterAttributesEvent, NULL /*pvUser*/);
357
358 /* Register our pass. */
359 register_callback(pPlugInInfo->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, (void *)&g_MyPassInfo);
360
361 /* Register plug-in info. */
362 register_callback(pPlugInInfo->base_name, PLUGIN_INFO, NULL, (void *)&g_PlugInInfo);
363
364 return 0;
365}
366
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette