On Tue, Jul 14, 2020 at 11:23 PM Masoud Gholami <[email protected]> wrote:
>
> Hi,
>
> I am writing a plugin that uses the PLUGIN_PRAGMAS event to register a
> custom pragma that is expected to be before a function call as follows:
>
> int main() {
>
> char *filename = “path/to/file”;
> #pragma inject_before_call
> File *f = fopen(filename, …); // marked fopen (by the
> pragma)
> …
> fclose(f);
> char *filename2 = “path/to/file2”;
> File *f2 = fopen(filename2, …); // non-marked fopen
> …
> fclose(f2);
> return 0;
>
> }
>
> In fact, I am using the inject_before_call pragma to mark some fopen calls in
> the code (in this example, the first fopen call is marked). Then, for each
> marked fopen call, some extra expressions/statements/declarations are
> injected into the code before calling the marked function. For example, the
> above main function would be transformed as follows:
>
> int main() {
>
> char *filename = “/path/to/file”;
> File *tmp_f = fopen(“/path/to/another/file”, “w+");
> fclose(tmp_f);
> File *f = fopen(filename, …);
> …
> fclose(f);
> char *filename2 = “path/to/file2”; // codes not injected for the
> non-marked fopen
> File *f2 = fopen(filename2, …);
> …
> fclose(f2);
> return 0;
>
> }
>
> Here, because of the inject_before_call pragma, the grey code is injected
> into the main function before calling the marked fopen. It simply opens a new
> file (“/path/to/another/file”) and closes it.
> The thing about the injected code is that it should be inserted only if a
> fopen call is marked by a inject_before_call pragma. And if after the
> inject_before_call pragma no fopen calls are made, the user gets an error
> (the pragma should be only inserted before a fopen call).
>
> I implemented this in 3 steps as follows:
>
> 1. detection of the marked fopen calls: I created a pragma_handler which
> remembers the location_t of all inject_before_call pragmas. Then using a pass
> (before ssa), I look for the statements/expressions that are in the next line
> of each remembered location. If it’s a fopen call, it is considered as a
> marked call and the code should be inserted before the fopen call. If it’s
> something other than a fopen call, an error will be generated. However, I’m
> not aware if there are any better ways to detect the marked calls.
>
> Here is the simplified pass to find the marked fopen calls (generating errors
> not covered):
>
> unsigned int execute(function *func) {
> basic_block bb;
> FOR_EACH_BB_FN (bb, func) {
> for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
> gsi_next (&gsi)) {
> gimple *stmt = gsi_stmt (gsi);
> if (gimple_is_fopen(stmt)) {
> if (marked_fopen(stmt)) {
> handle_marked_fopen(stmt);
> }
> }
> }
> }
> }
>
> 2. create the GIMPLE representation of the code to be injected: after finding
> the marked fopen calls, I construct some declaration and expressions as
> follows:
>
> // create the strings “/path/to/another/file" and “w+"
> tree another_path = build_string (20, “/path/to/another/file");
> fix_string_type (another_path);
> tree mode = build_string (3, “w+\0");
> fix_string_type (mode);
>
> // create a call to the fopen function with the created strings
> tree fopen_decl = lookup_qualified_name (global_namespace,
> get_identifier("fopen"), 0, true, false);
> gimple *new_open_call = gimple_build_call(fopen_decl, 2, another_path, mode);
>
> // create the tmp_f declaration
> f_decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(“tmp_f"),
> fileptr_type_node);
> pushdecl (f_decl);
> rest_of_decl_compilation (f_decl, 0, 0);
That's the wrong interface for GIMPLE code. Is f_decl supposed to be
a global variable
or a function local one? For the latter simply use
f_decl = create_tmp_var (fileptr_type_node, "tmp_f");
> // set the lhs of the fopen call to be f_decl
> gimple_call_set_lhs(new_open_call, f_decl)
>
> // create a call to the fclose function with the tmp_f variable
> tree fclose_decl = lookup_qualified_name (global_namespace,
> get_identifier("fclose"), 0, true, false);
Likewise lookup_qualified_name is a frontend specific function, since
there's no builtin declaration
for fclose you'll have to build one yourself.
> gimple *new_close_call = gimple_build_call(fclose_decl, 1, f_decl);
>
>
> 3. add the created GIMPLE trees to the code (basic-blocks):
>
> basic_block bb = gimple_bb(stmt);
> for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next
> (&gsi)) { gimple *st = gsi_stmt (gsi);
> if (st == stmt) { // the marked fopen call
> gsi_insert_before(&gsi, new_open_call, GSI_NEW_STMT);
> gsi_insert_after(&gsi, new_open_call, GSI_NEW_STMT);
> break;
> }
> }
>
> This is how I implemented the plugin. However, after compiling a sample code
> (like the main function above), I get segmentation fault. By defining another
> pass to print the statements of the code and by executing this pass after
> the previous pass (that injects the code), I see correct results (i.e., the
> injected code is correctly generated and inserted into the right location).
> But when I debug the sample code, I see that only the last injected statement
> (fclose) is executed with NULL in the f_decl variable which causes the
> segmentation fault. I searched everywhere, read all the documentations I
> could find, and digged into the gcc code for other pragmas (i.e. omp
> parallel, etc.). But still I have no success in doing this correctly. Could
> you please point me where the problem is?
>
> Thanks,
> M. Gholami
>
>