I have the source code for a huge (exaggerated) c
program with multiple files. I was hoping if there was any way to find all the functions (both standard and builtin) used (both declared and called) in the program. I know I can compile it and track the function
and system
calls
using tools like ltrace
and strace
, by redirecting the output to a file first and then using grep
to select the function calls. Or I can use regex on shell on all the files, but I don't know regex (yet). So does any one know a tool that could help me to quickly find all the functions called and declared in a c program?

- 877
- 1
- 7
- 28
-
Just `nm` would be enough. – KamilCuk Nov 26 '20 at 14:28
-
ok thank you so much, I forgat about that one! – EHM Nov 26 '20 at 14:30
-
@KamilCuk, should I delete this question? – EHM Nov 26 '20 at 14:31
-
But how would I know where exactly they have been used? by exactly I mean which line and which file? – EHM Nov 26 '20 at 14:37
-
@ehm: you didn't ask for that in your question. :-) You could try ctags. – rici Nov 26 '20 at 14:55
-
Och, right. Then you can't. You can use profiling methods (I am thinking about -fstack-usage and custom callback functions) to log all called functions. But you can't possible know exactly what functions will be called, because some decisions (function pointers) are made at runtime. – KamilCuk Nov 26 '20 at 14:56
-
@rici, I guess I didnt know what I really wanted until you guys pointed it out – EHM Nov 26 '20 at 14:59
-
And, there are _many_ tools that generate callgraph. A short google search and you may get https://www.gnu.org/software/cflow/manual/cflow.html or https://github.com/chaudron/cally . There are tools that go through generated assembly code, or that work on C source files, or that work on RTL output files from gcc. Recently, I used https://github.com/kuopinghsu/callgraph-gen . From there pick the root of the graph - ie. `main` - and pick all nodes. And anyway, if you compile your program with proper options, unused functions will be removed from the binary anyway. – KamilCuk Nov 26 '20 at 15:07
-
how come I didint find them when I searched, may be my phrasing was wrong, Thank you I will check them out. – EHM Nov 26 '20 at 15:15
3 Answers
Check if this helps, comments in code:
#!/bin/bash
# For each .c in the current path (recursive)
for f in $(find . -name '*.c'); do
# Extract the functions and his location with aux-info
# Send the results to output.info
# Send errors to /dev/null
gcc -aux-info output.info $f 2>/dev/null
# Extract function names from output.info excluding std headers and comments
grep -Ev "/usr|compiled from" output.info
done
# Remove output.info
rm output.info
Since it seems that you are trying to extract your own code following a pattern: type function(params)
, you can avoid using gcc -aux-info
, try the following:
#!/bin/bash
# For each .c in the current path (recursive)
for f in $(find . -name '*.c'); do
# Extract the lines starting with a letter or underscore
# and ending with ')' or '{' or '}' ignoring trailing spaces
grep --with-filename --line-number --color '^[a-z|A-Z|_].*[)|{|} ]$' $f
done
Another way combining readelf
and awk
, notice that in this case you need to provide the name of the program/binary instead of the source files:
# Column 3 != 0 and Column 4 = "FUNC" and Column 8 not starting with _
readelf -sW your_program | awk '$3 != 0 && $4 == "FUNC" && $8 !~ /^ ?_/ {print $8}'

- 39,972
- 7
- 52
- 94
-
1
-
-
Oh, I see, well, it is very hard to extract the function names without using `-gcc aux-info`, what if the function is written in this way: https://ideone.com/BzTCpx ? – David Ranieri Nov 26 '20 at 21:04
-
ok, I see, trying to write a script for this is like writing my own lexer, but assuming such a style (where function names are written like `name(params)`) is used in the code (because it is in my case), would there be a better way? Noobies are annoying! Sorry for that – EHM Nov 26 '20 at 21:30
-
Because of function pointers, no, there's not. Consider this program:
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle = dlopen(argv[1], RTLD_LAZY);
void (*f)(void) = dlsym(handle, argv[2]);
f();
dlclose(handle);
}
It should be obvious that you can't possibly hope to make a list of all the functions that it can call.

- 45,431
- 5
- 48
- 98
-
But if the program I am using is statically reading the code wouldn't it be able to find all the function calls. – EHM Nov 26 '20 at 16:04
@David, gave me a good answer, but after learning for loops
in bash
I came up with a method that is a little cleaner as far as messages are concerned. And the best thing about this method is that it scans the file statically
which means even if some functions are assigned to a pointer as @joseph pointed you will be able to see a list of all the function calls
made. The out put of this program isn't as clean as it could be as I don't know much regex, so if any one knows a better way don't forget to clean it up,
#!/bin/bash
#go through each .c file in the directory
for f in $(find . -maxdepth 1 -name "*.c");do
#print the name of the file
echo "$f"
#print the content of the file
#and then choose the lines that contain function calls
cat -n $f|grep -v "if\|else\|for\|while\|return"| grep --color=always "_*(\|[[:alpha:]]*("
done

- 877
- 1
- 7
- 28
-
You have an edit in my answer, check if it helps, I get nice results with it – David Ranieri Nov 27 '20 at 12:01
-
Ok, that looks very nice thank you so much, but it isnt getting the functions that are being called like exit() for example, but I will change the regex, thank you for showing me the direction – EHM Nov 27 '20 at 12:25
-
Oh, I totally misunderstood the question then, I thought you just wanted the function names, I'm afraid you won't be able to avoid using a parser like ctags – David Ranieri Nov 27 '20 at 12:37