Skip to content

Commit 2da50a6

Browse files
author
Craig Ringer
committed
WIP stack self-analysis
1 parent a22ccc4 commit 2da50a6

File tree

5 files changed

+577
-0
lines changed

5 files changed

+577
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
main
2+
unwind
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
run-unwind: unwind
2+
rm -f ~/core/*.core
3+
./unwind foobarbaz 420 1
4+
5+
run-main: main
6+
rm -f ~/core/*.core
7+
#./main foobarbaz 420 0 ||true
8+
./main foobarbaz 420 1 ||true
9+
10+
main: main.c
11+
gcc -fPIE -Wall -Wextra -O0 -ggdb3 -fomit-frame-pointer -fno-inline main.c -o main
12+
13+
unwind: unwind.c
14+
gcc -fPIE -Wall -Wextra -O0 -ggdb3 -fomit-frame-pointer -fno-inline unwind.c -lunwind -o unwind
15+
16+
clean:
17+
rm -f main unwind
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* This demo program seeks to demonstrate how saving the stack
3+
* pointer before a longjmp() makes it possible to debug a program
4+
* after it returns from the longjmp(), assuming there
5+
* was no overwrite of the stack.
6+
*/
7+
8+
#include <stdio.h>
9+
#include <string.h>
10+
#include <stdlib.h>
11+
#include <setjmp.h>
12+
13+
static sigjmp_buf b;
14+
static void * last_jumped_from;
15+
16+
static void
17+
crash(void)
18+
{
19+
char padding[20] = "foo bar baz";
20+
fprintf(stdout, "I'm going to crash: %s\n", padding);
21+
abort();
22+
}
23+
24+
/*
25+
* longjmp() to caller() from getframe()
26+
*/
27+
static void
28+
callee(int dojump)
29+
{
30+
if (dojump)
31+
longjmp(b, 1);
32+
else
33+
abort();
34+
}
35+
36+
/*
37+
static long long int __attribute__((noinline))
38+
getcaller(void)
39+
{
40+
unsigned long long int current;
41+
asm volatile ( "movq %%rsp, %0\n" : "=rm" (current) );
42+
return current;
43+
}
44+
*/
45+
46+
/*
47+
* Save current stack pointer %rsp to a global, and also print it.
48+
* Doesn't jump, it's tryign to get the frame for the caller.
49+
*/
50+
static void
51+
getframe(int arg, const char * const msg, int dojump)
52+
{
53+
int some_local = rand();
54+
/*
55+
unsigned long long int current;
56+
asm volatile ( "movq %%rsp, %0\n" : "=rm" (current) );
57+
*/
58+
59+
/*
60+
* See https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
61+
*
62+
* You can get the frame in gdb with
63+
*
64+
* frame view last_jumped_from+16 getframe
65+
*
66+
* frame view last_jumped_from-16 getframe
67+
*
68+
* once this returns! Woah.
69+
*
70+
* However, it doesn't get a backtrace from there.
71+
*/
72+
void * current;
73+
current = __builtin_frame_address(0);
74+
75+
last_jumped_from = current;
76+
fprintf(stderr, "frame = 0x%p\n", current);
77+
fprintf(stdout, "arg = %d\n", arg);
78+
fprintf(stdout, "rand = %d\n", some_local);
79+
fprintf(stdout, "msg = %s\n", msg);
80+
callee(dojump);
81+
}
82+
83+
/*
84+
* Stack padding to simulate a callstack before the getframe().
85+
*
86+
* In postgres errfinish() will be called with a much deeper stack
87+
* than the handler function that consumes the result so the upper
88+
* parts of the popped stack are likely to be intact. Simulate this.
89+
* Don't allow the compiler to elide the stack padding var.
90+
*/
91+
static void
92+
call_getframe(int arg, const char * const msg, int dojump)
93+
{
94+
volatile char stackpadding[500];
95+
memset((char *)&stackpadding[0], '\x7f', 500);
96+
getframe(arg, msg, dojump);
97+
}
98+
99+
static void
100+
caller(int arg, const char * const msg, int dojump)
101+
{
102+
if (sigsetjmp(b, 0) != 0) {
103+
/* jumped */
104+
crash();
105+
} else {
106+
call_getframe(arg, msg, dojump);
107+
}
108+
}
109+
110+
int main(int argc, const char * const argv[])
111+
{
112+
if (argc != 4) {
113+
fprintf(stderr, "usage: %s <<int>> <<msg>> <<dojump>>\n", argv[0]);
114+
exit(1);
115+
}
116+
caller(atoi(argv[2]), argv[1], atoi(argv[3]));
117+
}

0 commit comments

Comments
 (0)