I forgot to send the daily report yesterday, so this one covers the work done on both days
AIM : - make the analyzer call the function with the updated call-string representation ( even the ones that doesn’t have a superedge ) - make the analyzer figure out the point of return from the function called without the superedge - make the analyser figure out the correct point to return back in the caller function - make enode and eedge representing the return call - test the changes on the example I created before - speculate what GCC generates for a vfunc call and discuss how can we use it to our advantage — PROGRESS ( changes can be seen on "refs/users/arsenic/heads/analyzer_extension “ branch of the repository ) : - Thanks to the new call-string representation, I was able to push calls to the call stack which doesn’t have a superedge and was successfully able to see the calls happening via the function pointer. - To detect the returning point of the function I used the fact that such supernodes would contain an EXIT bb, would not have any return superedge and would still have a pending call-stack. - Now the next part was to find out the destination node of the return, for this I again made use of the new call string and created a custom accessor to get the caller and callee supernodes of the return call, then I extracted the gcall* from the caller supernode to ulpdate the program state, - now that I have got next state and next point, it was time to put the final piece of puzzle together and create exploded node and edge representing the returning call. - I tested the changes on the the following program where the analyzer was earlier giving a false negative due to not detecting call via a function pointer ``` #include <stdio.h> #include <stdlib.h> void fun(int *int_ptr) { free(int_ptr); } int test() { int *int_ptr = (int*)malloc(sizeof(int)); void (*fun_ptr)(int *) = &fun; (*fun_ptr)(int_ptr); return 0; } void test_2() { test(); } ``` ( compiler explorer link : https://godbolt.org/z/9KfenGET9 <https://godbolt.org/z/9KfenGET9> ) and results were showing success where the analyzer was now able to successfully detect, call and return from the function that was called via the function pointer and no longer reported the memory leak it was reporting before. : ) - I think I should point this out, in the process I created a lot of custom function to access/alter some data which was not possible before. - now that calls via function pointer are taken care of, it was time to see what exactly happen what GCC generates when a function is dispatched dynamically, and as planned earlier, I went to ipa-devirt.c ( devirtualizer’s implementation of GCC ) to investigate. - althogh I didn’t understood everything that was happening there but here are some of the findings I though might be interesting for the project :- > the polymorphic call is called with a OBJ_TYPE_REF which contains otr_type( a type of class whose method is called) and otr_token (the index into virtual table where address is taken) > the devirtualizer builds a type inheritance graph to keep track of entire inheritance hierarchy > the most interesting function I found was “possible_polymorphic_call_targets()” which returns the vector of all possible targets of polymorphic call represented by a calledge or a gcall. > what I understood the devirtualizer do is to search in these polymorphic calls and filter out the the calls which are more likely to be called ( known as likely calls ) and then turn them into speculative calls which are later turned into direct calls. - another thing I was curious to know was, how would analyzer behave when encountered with a polymorphic call now that we are splitting the superedges at every call. the results were interesting, I was able to see analyzer splitting supernodes for the calls right away but this time they were not connected via a intraprocedural edge making the analyzer crashing at the callsite ( I would look more into it tomorrow ) the example I used was : - ``` struct A { virtual int foo (void) { return 42; } }; struct B: public A { int foo (void) { return 0; } }; int test() { struct B b, *bptr=&b; bptr->foo(); return bptr->foo(); } ``` ( compiler explorer link : https://godbolt.org/z/d986ab7MY <https://godbolt.org/z/d986ab7MY> ) — STATUS AT THE END OF THE DAY :- - make the analyzer call the function with the updated call-string representation ( even the ones that doesn’t have a superedge ) (done) - make the analyzer figure out the point of return from the function called without the superedge (done) - make the analyser figure out the correct point to return back in the caller function (done) - make enode and eedge representing the return call (done) - test the changes on the example I created before (done) - speculate what GCC generates for a vfunc call and discuss how can we use it to our advantage (done) Thank you - Ankur