1 # Insert GAS CFI directives ("control frame information") into x86-64 asm input
4 # don't put CFI data in the .eh_frame ELF section (which we don't keep)
5 print ".cfi_sections .debug_frame"
7 # only emit CFI directives inside a function
10 # emit .loc directives with line numbers from original source
11 printf ".file 1 \"%s\"\n", ARGV[1]
14 # used to detect "call label; label:" trick
18 function get_const1
() {
19 # for instructions with 2 operands, get 1st operand (assuming it is constant)
20 match($
0, /-?
(0x
[0-9a
-fA
-F
]+|[0-9]+),/)
21 return parse_const
(substr($
0, RSTART, RLENGTH-1))
24 function canonicalize_reg
(register
) {
25 if (match(register
, /^r
/))
27 else if (match(register
, /^e
/))
28 return "r" substr(register
, 2, length(register
)-1)
29 else if (match(register
, /[hl
]$
/)) # AH, AL, BH, BL, etc
30 return "r" substr(register
, 1, 1) "x"
31 else # AX, BX, CX, etc
35 # only use if you already know there is 1 and only 1 register
36 match($
0, /%
[er
]?
([abcd
][xlh
]|si
|di
|bp
|8|9|10|11|12|13|14|15)/)
37 return canonicalize_reg
(substr($
0, RSTART+1, RLENGTH-1))
40 # for instructions with 2 operands, get 1st operand (assuming it is register)
41 match($
0, /%
[er
]?
([abcd
][xlh
]|si
|di
|bp
|8|9|10|11|12|13|14|15),/)
42 return canonicalize_reg
(substr($
0, RSTART+1, RLENGTH-2))
45 # for instructions with 2 operands, get 2nd operand (assuming it is register)
46 match($
0, /,%
[er
]?
([abcd
][xlh
]|si
|di
|bp
|8|9|10|11|12|13|14|15)/)
47 return canonicalize_reg
(substr($
0, RSTART+2, RLENGTH-2))
50 function adjust_sp_offset
(delta
) {
52 printf ".cfi_adjust_cfa_offset %d\n", delta
56 line_number = line_number
+ 1
58 # clean the input up before doing anything else
60 gsub(/(#|\/\/).*/, "")
62 # canonicalize whitespace
63 gsub(/[ \t]+/, " ") # mawk doesn't understand \s
70 # check for assembler directives which we care about
71 /^\.
(section
|data
|text
)/ {
72 # a .cfi_startproc/.cfi_endproc pair should be within the same section
73 # otherwise, clang will choke when generating ELF output
79 /^\.type
[a
-zA
-Z0
-9_
]+,\@
function/ {
80 functions
[substr($
2, 1, length($
2)-10)] =
1
82 # not interested in assembler directives beyond this, just pass them through
89 label =
substr($
1, 1, length($
1)-1) # drop trailing :
91 if (called == label
) {
92 # note adjustment of stack pointer from "call label; label:"
96 if (functions
[label
]) {
101 print ".cfi_startproc"
103 for (register in saved
)
104 delete saved
[register
]
105 for (register in dirty
)
106 delete dirty
[register
]
109 # an instruction may follow on the same line, so continue processing
116 printf ".loc 1 %d\n", line_number
120 # KEEPING UP WITH THE STACK POINTER
121 # %rsp should only be adjusted by pushing/popping or adding/subtracting constants
129 /addl? \$
-?
(0x
[0-9a
-fA
-F
]+|[0-9]+),%rsp
/ { adjust_sp_offset
(-get_const1
()) }
130 /subl? \$
-?
(0x
[0-9a
-fA
-F
]+|[0-9]+),%rsp
/ { adjust_sp_offset
(get_const1
()) }
133 if (match($
0, /call
[0-9]+f
/)) # "forward" label
134 called =
substr($
0, RSTART+5, RLENGTH-6)
135 else if (match($
0, /call
[0-9a
-zA
-Z_
]+/))
136 called =
substr($
0, RSTART+5, RLENGTH-5)
139 # TRACKING REGISTER VALUES FROM THE PREVIOUS STACK FRAME
141 /pushl?
%r(ax
|bx
|cx
|dx
|si
|di
|bp
|8|9|10|11|12|13|14|15)/ { # don't match "push (%reg)"
142 # if a register is being pushed, and its value has not changed since the
143 # beginning of this function, the pushed value can be used when printing
144 # local variables at the next level up the stack
145 # emit '.cfi_rel_offset' for that
149 if (!saved
[register
] && !dirty
[register
]) {
150 printf ".cfi_rel_offset %s,0\n", register
156 /movl?
%r(ax
|bx
|cx
|dx
|si
|di
|bp
|8|9|10|11|12|13|14|15),-?
(0x
[0-9a
-fA
-F
]+|[0-9]+)?\
(%rsp\
)/ {
159 if (match($
0, /-?
(0x
[0-9a
-fA
-F
]+|[0-9]+)\
(%rsp\
)/)) {
160 offset = parse_const
(substr($
0, RSTART, RLENGTH-6))
164 if (!saved
[register
] && !dirty
[register
]) {
165 printf ".cfi_rel_offset %s,%d\n", register
, offset
171 # IF REGISTER VALUES ARE UNCEREMONIOUSLY TRASHED
172 # ...then we want to know about it.
174 function trashed
(register
) {
175 if (in_function
&& !saved
[register
] && !dirty
[register
]) {
176 printf ".cfi_undefined %s\n", register
180 # this does NOT exhaustively check for all possible instructions which could
181 # overwrite a register value inherited from the caller (just the common ones)
182 /mov.
*,%
[er
]?
([abcd
][xlh
]|si
|di
|bp
|8|9|10|11|12|13|14|15)$
/ { trashed
(get_reg2
()) }
183 /(add
|addl
|sub|subl
|and
|or
|xor
|lea
|sal
|sar
|shl
|shr
).
*,%
[er
]?
([abcd
][xlh
]|si
|di
|bp
|8|9|10|11|12|13|14|15)$
/ {
186 /^i?mul
[^
,]*$
/ { trashed
("rax"); trashed
("rdx") }
187 /^i?mul.
*,%
[er
]?
([abcd
][xlh
]|si
|di
|bp
|8|9|10|11|12|13|14|15)$
/ { trashed
(get_reg2
()) }
188 /^i?div
/ { trashed
("rax"); trashed
("rdx") }
190 /(dec
|inc
|not
|neg
|pop
) %
[er
]?
([abcd
][xlh
]|si
|di
|bp
|8|9|10|11|12|13|14|15)/ { trashed
(get_reg
()) }
191 /cpuid
/ { trashed
("rax"); trashed
("rbx"); trashed
("rcx"); trashed
("rdx") }