I know that if I am inside some function foo() which is called somewhere from bar() function, then this return address is pushed on stack.
You can probe around the stack like so
// assuming a 32 bit machine here
void digInStack(void) {
int i;
long sneak[1];
// feel free to adjust the search limits
for( i = -32; i <= 32; ++i) {
printf("offset %3d: data 0x%08X\n", i, sneak[i]);
}
}
You can get away with this because C is famous for not be very particular in how you index an array. Here, you declare a dummy array on the stack, and then peek around +/- relative to that.
As Rob Walker pointed out, you definitely need to know your compilers calling convention, to understand the data you are looking at. You might print out the address of a few functions, and look for values that are in a similar range, and intuit where the return address is, relative to the dummy array.
Word of caution - read all you want, but don't modify anything using that array, unless either (a) you are absolutely sure about what part of the stack it is you are modifying, or (b) just want to see an interesting/unpredictable crash mode.