4a4a3b6319b4a685f3be9db45f13bfd242388779
[musl] / tools / add-cfi.i386.awk
1 # Insert GAS CFI directives ("control frame information") into x86-32 asm input
2 #
3 # CFI directives tell the assembler how to generate "stack frame" debug info
4 # This information can tell a debugger (like gdb) how to find the current stack
5 #   frame at any point in the program code, and how to find the values which
6 #   various registers had at higher points in the call stack
7 # With this information, the debugger can show a backtrace, and you can move up
8 #   and down the call stack and examine the values of local variables
9
10 BEGIN {
11   # don't put CFI data in the .eh_frame ELF section (which we don't keep)
12   print ".cfi_sections .debug_frame"
13
14   # only emit CFI directives inside a function
15   in_function = 0
16
17   # emit .loc directives with line numbers from original source
18   printf ".file 1 \"%s\"\n", ARGV[1]
19   line_number = 0
20
21   # used to detect "call label; label:" trick
22   called = ""
23 }
24
25 function hex2int(str,   i) {
26   str = tolower(str)
27
28   for (i = 1; i <= 16; i++) {
29     char = substr("0123456789abcdef", i, 1)
30     lookup[char] = i-1
31   }
32
33   result = 0
34   for (i = 1; i <= length(str); i++) {
35     result = result * 16
36     char   = substr(str, i, 1)
37     result = result + lookup[char]
38   }
39   return result
40 }
41
42 function parse_const(str) {
43   sign = sub(/^-/, "", str)
44   hex  = sub(/^0x/, "", str)
45   if (hex)
46     n = hex2int(str)
47   else
48     n = str+0
49   return sign ? -n : n
50 }
51
52 function get_const1() {
53   # for instructions with 2 operands, get 1st operand (assuming it is constant)
54   match($0, /-?(0x[0-9a-fA-F]+|[0-9]+),/)
55   return parse_const(substr($0, RSTART, RLENGTH-1))
56 }
57 function get_reg() {
58   # only use if you already know there is 1 and only 1 register
59   match($0, /%e(ax|bx|cx|dx|si|di|bp)/)
60   return substr($0, RSTART+1, 3)
61 }
62 function get_reg1() {
63   # for instructions with 2 operands, get 1st operand (assuming it is register)
64   match($0, /%e(ax|bx|cx|dx|si|di|bp),/)
65   return substr($0, RSTART+1, 3)
66 }
67 function get_reg2() {
68   # for instructions with 2 operands, get 2nd operand (assuming it is register)
69   match($0, /,%e(ax|bx|cx|dx|si|di|bp)/)
70   return substr($0, RSTART+RLENGTH-3, 3)
71 }
72
73 function adjust_sp_offset(delta) {
74   if (in_function)
75     printf ".cfi_adjust_cfa_offset %d\n", delta
76 }
77
78 {
79   line_number = line_number + 1
80
81   # clean the input up before doing anything else
82   # delete comments
83   gsub(/(#|\/\/).*/, "")
84
85   # canonicalize whitespace
86   gsub(/[ \t]+/, " ") # mawk doesn't understand \s
87   gsub(/ *, */, ",")
88   gsub(/ *: */, ": ")
89   gsub(/ $/, "")
90   gsub(/^ /, "")
91 }
92
93 # check for assembler directives which we care about
94 /^\.(section|data|text)/ {
95   # a .cfi_startproc/.cfi_endproc pair should be within the same section
96   # otherwise, clang will choke when generating ELF output
97   if (in_function) {
98     print ".cfi_endproc"
99     in_function = 0
100   }
101 }
102 /^\.type [a-zA-Z0-9_]+,\@function/ {
103   functions[substr($2, 1, length($2)-10)] = 1
104 }
105 # not interested in assembler directives beyond this, just pass them through
106 /^\./ {
107   print
108   next
109 }
110
111 /^[a-zA-Z0-9_]+:/ {
112   label = substr($1, 1, length($1)-1) # drop trailing :
113
114   if (called == label) {
115     # note adjustment of stack pointer from "call label; label:"
116     adjust_sp_offset(4)
117   }
118
119   if (functions[label]) {
120     if (in_function)
121       print ".cfi_endproc"
122
123     in_function = 1
124     print ".cfi_startproc"
125
126     for (register in saved)
127       delete saved[register]
128     for (register in dirty)
129       delete dirty[register]
130   }
131
132   # an instruction may follow on the same line, so continue processing
133 }
134
135 /^$/ { next }
136
137 {
138   called = ""
139   printf ".loc 1 %d\n", line_number
140   print
141 }
142
143 # KEEPING UP WITH THE STACK POINTER
144 # We do NOT attempt to understand foolish and ridiculous tricks like stashing
145 #   the stack pointer and then using %esp as a scratch register, or bitshifting
146 #   it or taking its square root or anything stupid like that.
147 # %esp should only be adjusted by pushing/popping or adding/subtracting constants
148 #
149 /pushl?/ {
150   if (match($0, / %(ax|bx|cx|dx|di|si|bp|sp)/))
151     adjust_sp_offset(2)
152   else
153     adjust_sp_offset(4)
154 }
155 /popl?/ {
156   if (match($0, / %(ax|bx|cx|dx|di|si|bp|sp)/))
157     adjust_sp_offset(-2)
158   else
159     adjust_sp_offset(-4)
160 }
161 /addl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%esp/ { adjust_sp_offset(-get_const1()) }
162 /subl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%esp/ { adjust_sp_offset(get_const1()) }
163
164 /call/ {
165   if (match($0, /call [0-9]+f/)) # "forward" label
166     called = substr($0, RSTART+5, RLENGTH-6)
167   else if (match($0, /call [0-9a-zA-Z_]+/))
168     called = substr($0, RSTART+5, RLENGTH-5)
169 }
170
171 # TRACKING REGISTER VALUES FROM THE PREVIOUS STACK FRAME
172 #
173 /pushl? %e(ax|bx|cx|dx|si|di|bp)/ { # don't match "push (%reg)"
174   # if a register is being pushed, and its value has not changed since the
175   #   beginning of this function, the pushed value can be used when printing
176   #   local variables at the next level up the stack
177   # emit '.cfi_rel_offset' for that
178
179   if (in_function) {
180     register = get_reg()
181     if (!saved[register] && !dirty[register]) {
182       printf ".cfi_rel_offset %s,0\n", register
183       saved[register] = 1
184     }
185   }
186 }
187
188 /movl? %e(ax|bx|cx|dx|si|di|bp),-?(0x[0-9a-fA-F]+|[0-9]+)?\(%esp\)/ {
189   if (in_function) {
190     register = get_reg()
191     if (match($0, /-?(0x[0-9a-fA-F]+|[0-9]+)\(%esp\)/)) {
192       offset = parse_const(substr($0, RSTART, RLENGTH-6))
193     } else {
194       offset = 0
195     }
196     if (!saved[register] && !dirty[register]) {
197       printf ".cfi_rel_offset %s,%d\n", register, offset
198       saved[register] = 1
199     }
200   }
201 }
202
203 # IF REGISTER VALUES ARE UNCEREMONIOUSLY TRASHED
204 # ...then we want to know about it.
205 #
206 function trashed(register) {
207   if (in_function && !saved[register] && !dirty[register]) {
208     printf ".cfi_undefined %s\n", register
209   }
210   dirty[register] = 1
211 }
212 # this does NOT exhaustively check for all possible instructions which could
213 # overwrite a register value inherited from the caller (just the common ones)
214 /mov.*,%e(ax|bx|cx|dx|si|di|bp)/  { trashed(get_reg2()) }
215 /(add|addl|sub|subl|and|or|xor|lea|sal|sar|shl|shr) %e(ax|bx|cx|dx|si|di|bp),/ {
216   trashed(get_reg1())
217 }
218 /i?mul [^,]*$/                    { trashed("eax"); trashed("edx") }
219 /i?mul %e(ax|bx|cx|dx|si|di|bp),/ { trashed(get_reg1()) }
220 /i?div/                           { trashed("eax"); trashed("edx") }
221 /(dec|inc|not|neg|pop) %e(ax|bx|cx|dx|si|di|bp)/  { trashed(get_reg()) }
222 /cpuid/ { trashed("eax"); trashed("ebx"); trashed("ecx"); trashed("edx") }
223
224 END {
225   if (in_function)
226     print ".cfi_endproc"
227 }