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);  

// 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);
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


Reply via email to