# Insert GAS CFI directives ("control frame information") into x86-32 asm input # # CFI directives tell the assembler how to generate "stack frame" debug info # This information can tell a debugger (like gdb) how to find the current stack # frame at any point in the program code, and how to find the values which # various registers had at higher points in the call stack # With this information, the debugger can show a backtrace, and you can move up # and down the call stack and examine the values of local variables BEGIN { # don't put CFI data in the .eh_frame ELF section (which we don't keep) print ".cfi_sections .debug_frame" # only emit CFI directives inside a function in_function = 0 } function hex2int(str, i) { str = tolower(str) for (i = 1; i <= 16; i++) { char = substr("0123456789abcdef", i, 1) lookup[char] = i-1 } result = 0 for (i = 1; i <= length(str); i++) { result = result * 16 char = substr(str, i, 1) result = result + lookup[char] } return result } function get_const1() { # for instructions with 2 operands, get 1st operand (assuming it is constant) match($0, /-?(0x[0-9a-fA-F]+|[0-9]+),/) return parse_const(substr($0, RSTART, RLENGTH-1)) } function parse_const(const) { if (substr(const, 1, 1) == "-") { if (substr(const, 2, 2) == "0x") { return -hex2int(substr(const, 4, length(const)-3)) } else { return const } } else { if (substr(const, 1, 2) == "0x") { return hex2int(substr(const, 3, length(const)-2)) } else { return const } } } function get_reg() { # only use if you already know there is 1 and only 1 register match($0, /%e(ax|bx|cx|dx|si|di|bp)/) return substr($0, RSTART+1, 3) } function get_reg1() { # for instructions with 2 operands, get 1st operand (assuming it is register) match($0, /%e(ax|bx|cx|dx|si|di|bp)\s*,/) return substr($0, RSTART+1, 3) } function get_reg2() { # for instructions with 2 operands, get 2nd operand (assuming it is register) match($0, /,\s*%e(ax|bx|cx|dx|si|di|bp)/) return substr($0, RSTART+RLENGTH-3, 3) } function adjust_sp_offset(delta) { if (in_function) { printf ".cfi_adjust_cfa_offset %d\n", delta } } { print } /^.global\s+\w+/ { globals[$2] = 1 } /^\w+:/ { label = substr($1, 1, length($1)-1) # drop trailing : if (globals[label]) { if (in_function) print ".cfi_endproc" in_function = 1 print ".cfi_startproc" for (register in saved) delete saved[register] for (register in dirty) delete dirty[register] } } # KEEPING UP WITH THE STACK POINTER # We do NOT attempt to understand foolish and ridiculous tricks like stashing # the stack pointer and then using %esp as a scratch register, or bitshifting # it or taking its square root or anything stupid like that. # %esp should only be adjusted by pushing/popping or adding/subtracting constants # /pushl?/ { if (match($0, /\s+%(ax|bx|cx|dx|di|si|bp|sp)/)) adjust_sp_offset(2) else adjust_sp_offset(4) } /popl?/ { if (match($0, /\s+%(ax|bx|cx|dx|di|si|bp|sp)/)) adjust_sp_offset(-2) else adjust_sp_offset(-4) } /addl?\s+\$-?(0x[0-9a-fA-F]+|[0-9]+),\s*%esp/ { adjust_sp_offset(-get_const1()) } /subl?\s+\$-?(0x[0-9a-fA-F]+|[0-9]+),\s*%esp/ { adjust_sp_offset(get_const1()) } # TRACKING REGISTER VALUES FROM THE PREVIOUS STACK FRAME # /pushl?\s+%e(ax|bx|cx|dx|si|di|bp)/ { # don't match "push (%reg)" # if a register is being pushed, and its value has not changed since the # beginning of this function, the pushed value can be used when printing # local variables at the next level up the stack # emit '.cfi_rel_offset' for that if (in_function) { register = get_reg() if (!saved[register] && !dirty[register]) { printf ".cfi_rel_offset %s,0\n", register saved[register] = 1 } } } /movl?\s+%e(ax|bx|cx|dx|si|di|bp),\s*-?(0x[0-9a-fA-F]+|[0-9]+)?\(%esp\)/ { if (in_function) { register = get_reg() if (match($0, /-?(0x[0-9a-fA-F]+|[0-9]+)\(%esp\)/)) { offset = parse_const(substr($0, RSTART, RLENGTH-6)) } else { offset = 0 } if (!saved[register] && !dirty[register]) { printf ".cfi_rel_offset %s,%d\n", register, offset saved[register] = 1 } } } # IF REGISTER VALUES ARE UNCEREMONIOUSLY TRASHED # ...then we want to know about it. # function trashed(register) { if (in_function && !saved[register] && !dirty[register]) { printf ".cfi_undefined %s\n", register } dirty[register] = 1 } # this does NOT exhaustively check for all possible instructions which could # overwrite a register value inherited from the caller (just the common ones) /mov.*,%e(ax|bx|cx|dx|si|di|bp)/ { trashed(get_reg2()) } /(add|addl|sub|subl|and|or|xor|lea|sal|sar|shl|shr)\s+%e(ax|bx|cx|dx|si|di|bp),/ { trashed(get_reg1()) } /i?mul\s+[^,]*$/ { trashed("eax"); trashed("edx") } /i?mul\s+%e(ax|bx|cx|dx|si|di|bp),/ { trashed(get_reg1()) } /^(\w+:)?\s*i?div/ { trashed("eax"); trashed("edx") } /(dec|inc|not|neg|pop)\s+%e(ax|bx|cx|dx|si|di|bp)/ { trashed(get_reg()) } /^(\w+:)\s*cpuid/ { trashed("eax"); trashed("ebx"); trashed("ecx"); trashed("edx") } END { if (in_function) print ".cfi_endproc" }