| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568 |
- From: =?UTF-8?q?Ignacy=20Gaw=C4=99dzki?=
- <[email protected]>
- Date: Thu, 20 Mar 2025 12:07:21 +0100
- Subject: [PATCH] tools: Rework adding of CFI annotations.
- MIME-Version: 1.0
- Content-Type: text/plain; charset=UTF-8
- Content-Transfer-Encoding: 8bit
- Rework awk scripts used to add CFI annotations to i386 and x86_64
- assembly, in order to properly maintain CFA offset across in-function
- jumps.
- Add arm and aarch64 versions of these scripts.
- Signed-off-by: Ignacy GawÄdzki <[email protected]>
- ---
- create mode 100644 tools/add-cfi.aarch64.awk
- create mode 100644 tools/add-cfi.arm.awk
- --- /dev/null
- +++ b/tools/add-cfi.aarch64.awk
- @@ -0,0 +1,287 @@
- +# Insert GAS CFI directives ("control frame information") into AArch64 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 = ""
- +
- + # Emit .loc directives with line numbers from original source.
- + printf ".file 1 \"%s\"\n", ARGV[1]
- + line_number = 0
- +
- + re_label = "([0-9+|[a-zA-Z_][a-zA-Z0-9_]*)"
- +
- + # Build an associative array of canonical register names.
- + for (i = 0; i < 30; ++i)
- + regname["x" i] = regname["w" i] = "x" i
- + regname["x30"] = regname["w30"] = regname["lr"] = "x30"
- + regname["xzr"] = regname["wzr"] = "xzr"
- + regname["sp"] = regname["wsp"] = "sp"
- +}
- +
- +{
- + ++line_number
- +
- + # Clean the input up before doing anything else.
- + # Delete comments.
- + gsub(/^#.*|\/\/.*|\/\*.*\*\//, "")
- +
- + # Canonicalize whitespace.
- + gsub(/[ \t]+/, " ") # Mawk doesn't understand \s.
- + gsub(/ *, */, ",")
- + gsub(/ *: */, ": ")
- + gsub(/ $/, "")
- + gsub(/^ /, "")
- +}
- +
- +# Check for assembler directives which we care about.
- +/^\.(section|data|text)/ {
- + # A .cfi_startproc/.cfi_endproc pair should be within the same section
- + # otherwise, clang will choke when generating ELF output.
- + if (in_function) {
- + print ".cfi_endproc"
- + in_function = ""
- + }
- +}
- +
- +# Record each function name.
- +/^\.type [a-zA-Z0-9_]+( STT_FUNCTION|,[#@%"]function)/ {
- + functions[substr($2, 1, length($2) - 10)] = 1
- +}
- +
- +# Not interested in assembler directives beyond this, just pass them through.
- +/^\./ {
- + print
- + next
- +}
- +
- +# Helper to adjust CFA offset.
- +function adjust_sp_offset(delta) {
- + if (in_function) {
- + printf ".cfi_adjust_cfa_offset %d\n", delta
- + cfa_offset[in_function] += delta
- + }
- +}
- +
- +# Helper to invalidate unsaved register.
- +function trashed(reg) {
- + if (in_function && !(reg in saved) && !(reg in dirty))
- + printf ".cfi_undefined %s\n", reg
- + dirty[reg] = 1
- +}
- +
- +# Helper to process jumps to labels by saving the current CFA offset.
- +function jump_to_label(label) {
- + if (in_function) {
- + if (match(label, /^[0-9]+f$/)) # "forward" label
- + cfa_offset[substr(label, 1, RLENGTH - 1)] = cfa_offset[in_function]
- + else if (match(label, /^[a-zA-Z_][a-zA-Z0-9_]*$/))
- + cfa_offset[label] = cfa_offset[in_function]
- + }
- +}
- +
- +# Helper to set relative offset of registers pushed on the stack.
- +function push_regs(regs, numregs, i) {
- + adjust_sp_offset(numregs * 4)
- + for (i = 1; i <= numregs; ++i) {
- + reg = regname[regs[i]]
- + if (!(reg in saved) && !(reg in dirty)) {
- + printf ".cfi_rel_offset %s,%i\n", reg, ((i - 1) * 4)
- + saved[reg] = 1
- + }
- + }
- +}
- +
- +# Helper to invalidate unsaved registers popped from the stack.
- +function pop_regs(regs, numregs, i) {
- + adjust_sp_offset(numregs * -4)
- + for (i = 1; i <= numregs; ++i) {
- + reg = regname[regs[i]]
- + trashed(reg)
- + }
- +}
- +
- +# Helper to save a single register saved in SP-relative locations.
- +function save_reg(reg, offset) {
- + reg = regname[reg]
- + if (!(reg in saved) && !(reg in dirty)) {
- + printf ".cfi_rel_offset %s,%d\n", reg, offset
- + saved[reg] = 1
- + }
- +}
- +
- +# Process labels.
- +$0 ~ "^" re_label ":" {
- + # Parse each leading label.
- + while (match($0, "^" re_label ":")) {
- +
- + # Extract label name.
- + label = substr($1, 1, RLENGTH - 1)
- +
- + # Remove label from current line.
- + sub("^" re_label ": ?", "")
- +
- + if (label in functions) {
- + if (in_function) {
- + print ".cfi_endproc"
- + for (l in called)
- + delete called[l]
- + }
- +
- + in_function = label
- + print ".cfi_startproc"
- +
- + for (reg in saved)
- + delete saved[reg]
- + for (reg in dirty)
- + delete dirty[reg]
- + }
- +
- + printf "%s:\n", label
- +
- + # If this label has been jumped to, define the CFA offset to its
- + # value at the location of the jump.
- + if (!(label in functions) && in_function && label in cfa_offset) {
- + if (cfa_offset[in_function] != cfa_offset[label]) {
- + printf ".cfi_def_cfa_offset %d\n", cfa_offset[label]
- + cfa_offset[in_function] = cfa_offset[label]
- + }
- + delete cfa_offset[label]
- + }
- +
- + # If this label has been called, possibly invalidate LR.
- + if (label in called && !(label in functions)) {
- + trashed("lr")
- + delete called[label]
- + }
- + }
- + # An instruction may follow on the same line, so continue processing.
- +}
- +
- +# Skip empty line.
- +/^$/ { next }
- +
- +# Issue source line number.
- +{
- + printf ".loc 1 %d\n", line_number
- + print
- +}
- +
- +# Process jumps to label (using B*).
- +/^b[^xrl]/ {
- + jump_to_label($2)
- +}
- +
- +# Process jumps to label (using [CT]BN?Z).
- +/^[ct]bn?z / {
- + if (match($2, /,.+$/))
- + jump_to_label(substr($2, RSTART + 1, RLENGTH - 1))
- +}
- +
- +# Issue relative offsets of registers stored in SP-relative locations.
- +/^st(n?p|r[bh]?|l[lu]?r|tr|ur) .+,\[(sp|x30)[,\]]/ {
- + if (in_function) {
- + if (match($2, /(,#?[+-]?(0x[0-9a-fA-F]+|[0-9]+))?\]$/)) {
- + # Offset with no write-back.
- + if (RLENGTH == 1)
- + offset = 0
- + else
- + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 3))
- + split($2, operands, ",")
- + if (match($1, /^stn?p$/)) {
- + if (match(operands[1], /^x/)) {
- + save_reg(operands[1], offset)
- + save_reg(operands[2], offset + 8)
- + }
- + } else if (match(operands[1], /x^/))
- + save_reg(operands[1], offset)
- + } else if (match($2, /,#?[+-]?(0x[0-9a-fA-F]+|[0-9]+)\]!$/)) {
- + # Pre-index with write-back.
- + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 4))
- + adjust_sp_offset(-offset)
- + split($2, operands, ",")
- + if ($1 == "stp") {
- + if (match(operands[1], /^x/)) {
- + save_reg(operands[1], 0)
- + save_reg(operands[2], 8)
- + }
- + } else if (match(operands[1], /^x/))
- + save_reg(operands[1], 0)
- + } else if (match($2, /,#?[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/)) {
- + # Post-index
- + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 2))
- + split($2, operands, ",")
- + if ($1 == "stp") {
- + if (match(operands[1], /^x/)) {
- + save_reg(operands[1], 0)
- + save_reg(operands[2], 8)
- + }
- + } else if (match(operands[1], /^x/))
- + save_reg(operands[1], 0)
- + adjust_sp_offset(-offset)
- + }
- + }
- +}
- +
- +# Adjust CFA offset when decreasing SP.
- +/subs?(\.[nw])? sp,sp,/ {
- + if (in_function && match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/))
- + adjust_sp_offset(parse_const(substr($2, RSTART + 2, RLENGTH - 2)))
- +}
- +
- +# Adjust CFA offset when increasing SP.
- +/adds?(\.[nw])? sp,sp,/ {
- + if (in_function && match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/))
- + adjust_sp_offset(-parse_const(substr($2, RSTART + 2, RLENGTH - 2)))
- +}
- +
- +# Process calls to labels.
- +/bl[a-z]* / {
- + if (match($2, /^[0-9]+f$/)) # "forward" label
- + called[substr($2, 1, RLENGTH - 1)] = 1
- + else if (match($2, /^[a-zA-Z_][0-9a-zA-Z_]*$/))
- + called[$2] = 1
- +}
- +
- +# Invalidate unsaved registers being written to.
- +/^(adcs?|adds?|adrp?|ands?|asrv?|bfc|bfi|bfm|bfxil|bics?|cin[cv]|cl[sz]|cneg|crc32[a-z]+|csel|csetm?|csin[cv]|csneg|eo[nr]|extr|ldap(r[bh]?|ur(s?[bhw]?))|ldar[bh]?|ldax[pr][bh]?|ldlar[bh]?|ldr((aa)?|s?[bhw])|ldtrs?[bhw]?|ldurs?[bhw]?|ldxr[bh]?|ls[lr]v?|madd|mneg|mov[knz]?|mrs|msub|mul|mvn|negs?|ngcs?|orn|orr|pac[a-z0-9]+|rbit|rev(16|32)?|rorv?|sbcs?|sbfiz|sbfm|sbfx|sdiv|smaddl|smnegl|smsubl|smul[hl]|subs?|sxt[bhw]|sysl|ubfiz|ubfm|ubfx|udiv|umaddl|umnegl|umsubl|umul[hl]|uxt[bhw]) ([xw]([0-9]|[12][0-9]|30)|sp),/ {
- + split($2, args, ",")
- + reg = args[1]
- + if (reg != "sp")
- + trashed(regname[reg])
- +}
- +
- +# Invalidate unsaved registers being written to by atomic operations in memory.
- +/^ld(add|clr|eor|set|[su](max|min))/ {
- + split($2, args, ",")
- + trashed(regname[args[2]])
- +}
- +
- +# Invalidate unsaved registers being written to by pair loading.
- +/^ld[nx]p(sw)? / {
- + split($2, args, ",")
- + trashed(regname[args[1]])
- + trashed(regname[args[2]])
- +}
- +
- +# Invalidate unsaved registers being written to by long instructions.
- +/^(smlals?|smlal(bb|bt|tb|tt)|smlaldx?|smlsldx?|smull|umaal|umlal|umulls?) / {
- + split($2, args, ",")
- + trashed(regname[args[1]])
- + trashed(regname[args[2]])
- +}
- +
- +END {
- + # Issue end of function if still inside one.
- + if (in_function)
- + print ".cfi_endproc"
- +}
- --- /dev/null
- +++ b/tools/add-cfi.arm.awk
- @@ -0,0 +1,367 @@
- +# Insert GAS CFI directives ("control frame information") into ARM 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 = ""
- +
- + # Emit .loc directives with line numbers from original source.
- + printf ".file 1 \"%s\"\n", ARGV[1]
- + line_number = 0
- +
- + re_label = "([0-9+|[a-zA-Z_][a-zA-Z0-9_]*)"
- +
- + # Build an associative array of canonical register names.
- + for (i = 0; i < 10; ++i) {
- + regname["r" i] = "r" i
- + regnum["r" i] = i
- + }
- + regname["r10"] = regname["sl"] = "r10"
- + regnum["r10"] = regnum["sl"] = 10
- + regname["r11"] = regname["fp"] = "r11"
- + regnum["r11"] = regnum["fp"] = 11
- + regname["r12"] = regname["ip"] = "r12"
- + regnum["r12"] = regnum["ip"] = 12
- + regname["r13"] = regname["sp"] = "r13"
- + regnum["r13"] = regnum["sp"] = 13
- + regname["r14"] = regname["lr"] = "r14"
- + regnum["r14"] = regnum["lr"] = 14
- + regname["r15"] = regname["pc"] = "r15"
- + regnum["r15"] = regnum["pc"] = 15
- +}
- +
- +{
- + ++line_number
- +
- + # Clean the input up before doing anything else.
- + # Delete comments.
- + gsub(/(^#|@|\/\/).*|\/\*.*\*\//, "")
- +
- + # Canonicalize whitespace.
- + gsub(/[ \t]+/, " ") # Mawk doesn't understand \s.
- + gsub(/ *, */, ",")
- + gsub(/ *: */, ": ")
- + gsub(/ $/, "")
- + gsub(/^ /, "")
- +}
- +
- +# Check for assembler directives which we care about.
- +/^\.(section|data|text)/ {
- + # A .cfi_startproc/.cfi_endproc pair should be within the same section
- + # otherwise, clang will choke when generating ELF output.
- + if (in_function) {
- + print ".cfi_endproc"
- + in_function = ""
- + }
- +}
- +
- +# Record each function name.
- +/^\.type [a-zA-Z0-9_]+( STT_FUNCTION|,[#@%"]function)/ {
- + functions[substr($2, 1, length($2) - 10)] = 1
- +}
- +
- +# Not interested in assembler directives beyond this, just pass them through.
- +/^\./ {
- + print
- + next
- +}
- +
- +# Helper to adjust CFA offset.
- +function adjust_sp_offset(delta) {
- + if (in_function) {
- + printf ".cfi_adjust_cfa_offset %d\n", delta
- + cfa_offset[in_function] += delta
- + }
- +}
- +
- +# Helper to invalidate unsaved register.
- +function trashed(reg) {
- + if (in_function && !(reg in saved) && !(reg in dirty))
- + printf ".cfi_undefined %s\n", reg
- + dirty[reg] = 1
- +}
- +
- +# Helper to process jumps to labels by saving the current CFA offset.
- +function jump_to_label(label) {
- + if (in_function) {
- + if (match(label, /^[0-9]+f$/)) # "forward" label
- + cfa_offset[substr(label, 1, RLENGTH - 1)] = cfa_offset[in_function]
- + else if (match(label, /^[a-zA-Z_][a-zA-Z0-9_]*$/))
- + cfa_offset[label] = cfa_offset[in_function]
- + }
- +}
- +
- +# Helper to save a single register saved in SP-relative locations.
- +function save_reg(reg, offset) {
- + reg = regname[reg]
- + if (!(reg in saved) && !(reg in dirty)) {
- + printf ".cfi_rel_offset %s,%d\n", reg, offset
- + saved[reg] = 1
- + }
- +}
- +
- +# Helper to save registers relative to SP.
- +function save_regs(regs, numregs, i) {
- + for (i = 1; i <= numregs; ++i)
- + save_reg(regname[regs[i]], (i - 1) * -4)
- +}
- +
- +# Helper to set relative offset of registers pushed on the stack.
- +function push_regs(regs, numregs, i) {
- + adjust_sp_offset(numregs * 4)
- + for (i = 1; i <= numregs; ++i)
- + save_reg(regname[regs[i]], (i - 1) * 4)
- +}
- +
- +# Helper to invalidate unsaved registers popped from the stack.
- +function pop_regs(regs, numregs, i) {
- + adjust_sp_offset(numregs * -4)
- + for (i = 1; i <= numregs; ++i) {
- + reg = regname[regs[i]]
- + trashed(reg)
- + }
- +}
- +
- +# Helper to parse register lists.
- +function split_reglist(arg, regs, num, toks, tmp, dash, i, j) {
- + while (match(arg, /^{[^}]+}/)) {
- + num = split(substr(arg, RSTART + 1, RLENGTH - 2), toks, ",")
- + for (i = 1; i <= num; ++i)
- + if (match(toks[i], /^r([0-9]|1[0-5])-r([0-9]|1[0-5])$/)) {
- + dash = index(toks[i], "-")
- + first = 0 + substr(toks[i], 2, dash - 2)
- + last = 0 + substr(toks[i], dash + 2)
- + for (j = first; j <= last; ++j)
- + tmp[j]
- + } else
- + tmp[regnum[toks[i]]]
- + arg = substr(arg, RSTART + RLENGTH)
- + if (!match(arg, /^[\t ]*[+|][\t ]*/))
- + break
- + arg = substr(arg, RLENGTH + 1)
- + }
- + num = 0
- + for (i = 0; i < 16; ++i) {
- + if (!(i in tmp))
- + continue
- + regs[++num] = regname["r" i]
- + }
- + return num
- +}
- +
- +# Process labels.
- +$0 ~ "^" re_label ":" {
- + # Parse each leading label.
- + while (match($0, "^" re_label ":")) {
- +
- + # Extract label name.
- + label = substr($1, 1, RLENGTH - 1)
- +
- + # Remove label from current line.
- + sub("^" re_label ": ?", "")
- +
- + if (label in functions) {
- + if (in_function) {
- + print ".cfi_endproc"
- + for (l in called)
- + delete called[l]
- + }
- +
- + in_function = label
- + print ".cfi_startproc"
- +
- + for (reg in saved)
- + delete saved[reg]
- + for (reg in dirty)
- + delete dirty[reg]
- + }
- +
- + printf "%s:\n", label
- +
- + # If this label has been jumped to, define the CFA offset to its
- + # value at the location of the jump.
- + if (!(label in functions) && in_function && label in cfa_offset) {
- + if (cfa_offset[in_function] != cfa_offset[label]) {
- + printf ".cfi_def_cfa_offset %d\n", cfa_offset[label]
- + cfa_offset[in_function] = cfa_offset[label]
- + }
- + delete cfa_offset[label]
- + }
- +
- + # If this label has been called, possibly invalidate LR.
- + if (label in called && !(label in functions)) {
- + trashed("lr")
- + delete called[label]
- + }
- + }
- + # An instruction may follow on the same line, so continue processing.
- +}
- +
- +# Skip empty line.
- +/^$/ { next }
- +
- +# Issue source line number.
- +{
- + printf ".loc 1 %d\n", line_number
- + print
- +}
- +
- +# Process jumps to label (using B*).
- +/^b[^xl]/ {
- + jump_to_label($2)
- +}
- +
- +# Process jumps to label (using CBNZ?).
- +/^cbnz? / {
- + if (match($2, /,.*$/))
- + jump_to_label(substr($2, RSTART + 1, RLENGTH - 1))
- +}
- +
- +# Adjust CFA offset and issue relative offsets of pushed registers using PUSH.
- +/^push / {
- + if (in_function) {
- + numregs = split_reglist($2, regs)
- + push_regs(regs, numregs);
- + }
- +}
- +
- +# Adjust CFA offset and Issue relative offsets of pushed registers using STMFD.
- +/^stm(fd|db)(al)?(\.[nw])? (sp|r13)!,/ {
- + if (in_function) {
- + numregs = split_reglist(substr($2, index($2, ",") + 1), regs)
- + push_regs(regs, numregs);
- + }
- +}
- +
- +/^stm(ia|ea)?(al)?(\.[nw])? (sp|r13),/ {
- + if (in_function) {
- + numregs = split_reglist(substr($2, index($2, ",") + 1), regs)
- + save_regs(regs, numregs);
- + }
- +}
- +
- +# Adjust CFA offset and invalidate unsaved registers popped using POP.
- +/^pop / {
- + if (in_function) {
- + numregs = split_reglist($2, regs)
- + pop_regs(regs, numregs)
- + }
- +}
- +
- +# Adjust CFA offset and invalidate unsaved registers popped using LDMFD.
- +/^ldm(fd|ia)(al)?(\.[nw])? (sp|r13)!,/ {
- + if (in_function) {
- + numregs = split_reglist(substr($2, index($2, ",") + 1), regs)
- + pop_regs(regs, numregs)
- + }
- +}
- +
- +# Issue relative offsets of registers stored in SP-relative locations.
- +/^str[a-z.]* .*,\[(sp|r13)[,\]]/ {
- + if (in_function && !match($1, /^str(ex)?[bh]/)) {
- + if (match($2, /(,#[+-]?(0x[0-9a-fA-F]+|[0-9]+))?\]$/)) {
- + # Offset with no write-back.
- + if (RLENGTH == 1)
- + offset = 0
- + else
- + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 3))
- + split($2, operands, ",")
- + if (match($1, /^str(ex)?d/)) {
- + save_reg(operands[1], offset)
- + save_reg(operands[2], offset + 4)
- + } else
- + save_reg(operands[1], offset)
- + } else if (match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)\]!$/)) {
- + # Pre-index with write-back.
- + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 4))
- + adjust_sp_offset(-offset)
- + split($2, operands, ",")
- + if (match($1, /^str(ex)?d/)) {
- + save_reg(operands[1], 0)
- + save_reg(operands[2], 4)
- + } else
- + save_reg(operands[1], 0)
- + } else if (match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/)) {
- + # Post-index
- + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 2))
- + split($2, operands, ",")
- + if (match($1, /^str(ex)?d/)) {
- + save_reg(operands[1], 0)
- + save_reg(operands[2], 4)
- + } else
- + save_reg(operands[1], 0)
- + adjust_sp_offset(-offset)
- + }
- + }
- +}
- +
- +# Adjust CFA offset when decreasing SP.
- +/subs?(al)?(\.[nw])? (sp|r13),(sp|r13),/ {
- + if (in_function && match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/))
- + adjust_sp_offset(parse_const(substr($2, RSTART + 2, RLENGTH - 2)))
- +}
- +
- +# Adjust CFA offset when increasing SP.
- +/adds?(al)?(\.[nw])? (sp|r13),(sp|r13),/ {
- + if (in_function && match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/))
- + adjust_sp_offset(-parse_const(substr($2, RSTART + 2, RLENGTH - 2)))
- +}
- +
- +# Process calls to labels.
- +/bl[a-z]* / {
- + if (match($2, /^[0-9]+f$/)) # "forward" label
- + called[substr($2, 1, RLENGTH - 1)] = 1
- + else if (match($2, /^[a-zA-Z_][0-9a-zA-Z_]*$/))
- + called[$2] = 1
- +}
- +
- +# Invalidate unsaved registers being written to.
- +/^((adc|add|and|asr|adr|bic|eor|lsl|lsr|mla|mov|mul|mvn|orn|orr|ror|rrx|rsb|rsc|sbc|sub)s?|bfc|bfi|clz|cpy|ldr[a-z]*|mls||movt|mrs|neg|pkh(bt|tb)|qadd(8|16)?|qasx|qdadd|qdsub|qsax|qsub(8|16)?|rbit|rev(16)?|revsh|sadd(16|8)|sasx|sbfx|sdiv|sel|shadd(16|8)|shasx|shsax|shsub(16|8)|smla(bb|bt|tb|tt)|smladx?|smlaw[tb]|smlsdx?|smmlar?|smlsr?|smmulr?|smuadx?|smul(bb|bt|tb|tt)|smulw[bt]|smusdx?|ssat(16)?|ssax|ssub(16|8)|swpb?|sxtab(16)?|sxtah|sxtb(16)?|sxth|sxtb(16)?|sxth|uadd(16|8)|uasx|ubfx|udiv|uhadd(16|8)|uhasx|uhsax|uhsub(16|8)|uqadd(16|8)|uqasx|uqsax|uqsub(16|8)|usada?8|usat(16)?|usax|usub(16|8)|uxtab(16)?|uxtah|uxtb(16)?|uxth)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al)? (r([0-9]|1[0-5])|ip|sp|lr|pc),/ {
- + split($2, args, ",")
- + reg = args[1]
- + if (reg != "sp")
- + trashed(regname[reg])
- +}
- +
- +# Invalidate unsaved registers being written to by long instructions.
- +/^(smlals?|smlal(bb|bt|tb|tt)|smlaldx?|smlsldx?|smull|umaal|umlal|umulls?)/ {
- + split($2, args, ",")
- + trashed(regname[args[1]])
- + trashed(regname[args[2]])
- +}
- +
- +# Invalidate unsaved register being modified by write-back on store multiple.
- +/^stm[a-z.]* [^,]+!,/ {
- + first_arg = substr($2, 1, index($2, ",") - 1)
- + if (!match(first_arg, /^(sp|r13)/))
- + trashed(regname[substr(first_arg, 1, length(first_arg) - 1)])
- +}
- +
- +# Invalidate unsaved registers being modified by load multiple.
- +/^ldm[a-z.]* [^,]+,{.*}$/ {
- + comma = index($2, ",")
- + first_arg = substr($2, 1, comma - 1)
- + other_args = substr($2, comma + 1)
- + if (!match(first_arg, /^(sp|r13)/)) {
- + if (match(first_arg, /!$/))
- + trashed(regname[substr(first_arg, 1, RSTART - 1)])
- + numregs = split_reglist(other_args, regs)
- + for (i = 1; i <= numregs; ++i)
- + trashed(regname[regs[i]])
- + }
- +}
- +
- +END {
- + # Issue end of function if still inside one.
- + if (in_function)
- + print ".cfi_endproc"
- +}
- --- a/tools/add-cfi.common.awk
- +++ b/tools/add-cfi.common.awk
- @@ -1,26 +1,46 @@
- -function hex2int(str, i) {
- +function hex2int(str, i) {
- str = tolower(str)
-
- for (i = 1; i <= 16; i++) {
- char = substr("0123456789abcdef", i, 1)
- - lookup[char] = 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]
- + result *= 16
- + char = substr(str, i, 1)
- + result += lookup[char]
- + }
- + return result
- +}
- +
- +function oct2int(str, i) {
- + str = tolower(str)
- +
- + for (i = 1; i <= 8; ++i) {
- + char = substr("01234567", i, 1)
- + lookup[char] = i - 1
- + }
- +
- + result = 0
- + for (i = 1; i <= length(str); ++i) {
- + result *= 8
- + char = substr(str, i, 1)
- + result += lookup[char]
- }
- return result
- }
-
- function parse_const(str) {
- - sign = sub(/^-/, "", str)
- - hex = sub(/^0x/, "", str)
- + neg = sub(/^-/, "", str)
- + oct = match(str, /^0[0-7]/)
- + hex = sub(/^0x/, "", str)
- if (hex)
- n = hex2int(str)
- + else if (oct)
- + n = oct2int(str)
- else
- n = str+0
- - return sign ? -n : n
- + return neg? -n: n
- }
- --- a/tools/add-cfi.i386.awk
- +++ b/tools/add-cfi.i386.awk
- @@ -1,123 +1,179 @@
- -# Insert GAS CFI directives ("control frame information") into x86-32 asm input
- +# Insert GAS CFI directives ("control frame information") into x86-32 asm input.
- #
- -# CFI directives tell the assembler how to generate "stack frame" debug info
- +# 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
- +# 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
- +# 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)
- + # 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
- + # Only emit CFI directives inside a function.
- + in_function = ""
-
- - # emit .loc directives with line numbers from original source
- + # Emit .loc directives with line numbers from original source.
- printf ".file 1 \"%s\"\n", ARGV[1]
- line_number = 0
-
- - # used to detect "call label; label:" trick
- - called = ""
- + re_label = "([0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)"
- +
- + for (i = 1; i <= 4; ++i) {
- + letter = substr("abcd", i, 1)
- + regname[letter "l"] = regname[letter "h"] = regname[letter "x"] = \
- + regname["e" letter "x"] = "e" letter "x"
- + }
- +
- + regname["si"] = regname["esi"] = "esi"
- + regname["di"] = regname["edi"] = "edi"
- + regname["bp"] = regname["ebp"] = "ebp"
- + regname["sp"] = regname["esp"] = "esp"
- }
-
- +# For instructions with 2 operands, get 1st operand (assuming it is constant).
- 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))
- + match($2, /^\$[+-]?(0x[0-9a-fA-F]+|[0-9]+),/)
- + return parse_const(substr($2, 2, RLENGTH - 2))
- }
-
- -function canonicalize_reg(register) {
- - if (match(register, /^e/))
- - return register
- - else if (match(register, /[hl]$/)) # AH, AL, BH, BL, etc
- - return "e" substr(register, 1, 1) "x"
- - else # AX, BX, CX, etc
- - return "e" register
- -}
- +# Only use if you already know there is 1 and only 1 register.
- function get_reg() {
- - # only use if you already know there is 1 and only 1 register
- - match($0, /%e?([abcd][hlx]|si|di|bp)/)
- - return canonicalize_reg(substr($0, RSTART+1, RLENGTH-1))
- + return regname[substr($2, 2, length($2) - 1)]
- }
- +
- +# For instructions with 2 operands, get 1st operand (assuming it is register).
- function get_reg1() {
- - # for instructions with 2 operands, get 1st operand (assuming it is register)
- - match($0, /%e?([abcd][hlx]|si|di|bp),/)
- - return canonicalize_reg(substr($0, RSTART+1, RLENGTH-2))
- + match($2, /^%e?([abcd][hlx]|si|di|bp),/)
- + return regname[substr($2, 2, RLENGTH - 2)]
- }
- +
- +# For instructions with 2 operands, get 2nd operand (assuming it is register).
- function get_reg2() {
- - # for instructions with 2 operands, get 2nd operand (assuming it is register)
- - match($0, /,%e?([abcd][hlx]|si|di|bp)/)
- - return canonicalize_reg(substr($0, RSTART+2, RLENGTH-2))
- + match($2, /,%e?([abcd][hlx]|si|di|bp)$/)
- + return regname[substr($2, RSTART + 2, RLENGTH - 2)]
- }
-
- +# Helper to adjust CFA offset.
- function adjust_sp_offset(delta) {
- - if (in_function)
- + if (in_function) {
- printf ".cfi_adjust_cfa_offset %d\n", delta
- + cfa_offset[in_function] += delta
- + }
- +}
- +
- +function save_reg(reg, offset) {
- + if (!(reg in saved) && !(reg in dirty)) {
- + printf ".cfi_rel_offset %s,%d\n", reg, offset
- + saved[reg] = 1
- + }
- +}
- +
- +# Helper to process jumps to labels by saving the current CFA offset.
- +function jump_to_label(label) {
- + if (in_function) {
- + if (match(label, /^[0-9]+f$/)) # "forward" label
- + cfa_offset[substr(label, 1, RLENGTH - 1)] = cfa_offset[in_function]
- + else if (match(label, /^[a-zA-Z_][a-zA-Z0-9_]*$/))
- + cfa_offset[label] = cfa_offset[in_function]
- + }
- }
-
- {
- - line_number = line_number + 1
- + ++line_number
-
- - # clean the input up before doing anything else
- - # delete comments
- - gsub(/(#|\/\/).*/, "")
- + # Clean the input up before doing anything else.
- + # Delete comments.
- + gsub(/#.*|\/\*.*\*\//, "")
-
- - # canonicalize whitespace
- - gsub(/[ \t]+/, " ") # mawk doesn't understand \s
- + # Canonicalize whitespace.
- + gsub(/[ \t]+/, " ") # Mawk doesn't understand \s.
- gsub(/ *, */, ",")
- gsub(/ *: */, ": ")
- + gsub(/%cs: */, "%cs:")
- + gsub(/%ds: */, "%ds:")
- + gsub(/%ss: */, "%ss:")
- + gsub(/%es: */, "%es:")
- + gsub(/%fs: */, "%fs:")
- + gsub(/%gs: */, "%gs:")
- gsub(/ $/, "")
- gsub(/^ /, "")
- }
-
- -# check for assembler directives which we care about
- +# Check for assembler directives which we care about.
- /^\.(section|data|text)/ {
- - # a .cfi_startproc/.cfi_endproc pair should be within the same section
- - # otherwise, clang will choke when generating ELF output
- + # A .cfi_startproc/.cfi_endproc pair should be within the same section.
- + # Otherwise, clang will choke when generating ELF output.
- if (in_function) {
- print ".cfi_endproc"
- - in_function = 0
- + in_function = ""
- }
- }
- -/^\.type [a-zA-Z0-9_]+,@function/ {
- - functions[substr($2, 1, length($2)-10)] = 1
- +
- +# Record each function name.
- +/^\.type [a-zA-Z0-9_]+( STT_FUNCTION|,[#@%"]function)/ {
- + functions[substr($2, 1, length($2) - 10)] = 1
- }
- -# not interested in assembler directives beyond this, just pass them through
- +
- +# Not interested in assembler directives beyond this, just pass them through.
- /^\./ {
- print
- next
- }
-
- -/^[a-zA-Z0-9_]+:/ {
- - label = substr($1, 1, length($1)-1) # drop trailing :
- -
- - if (called == label) {
- - # note adjustment of stack pointer from "call label; label:"
- - adjust_sp_offset(4)
- - }
- +$0 ~ "^" re_label ":" {
- + # Parse each leading label.
- + while (match($0, "^" re_label ":")) {
- +
- + # Extract label name.
- + label = substr($1, 1, RLENGTH - 1)
- +
- + # Remove label from current line.
- + sub("^" re_label ": ?", "")
- +
- + if (label in functions) {
- + if (in_function) {
- + print ".cfi_endproc"
- + for (l in called)
- + delete called[l]
- + }
- +
- + in_function = label
- + print ".cfi_startproc"
- +
- + for (reg in saved)
- + delete saved[reg]
- + for (reg in dirty)
- + delete dirty[reg]
- + }
-
- - if (functions[label]) {
- - if (in_function)
- - print ".cfi_endproc"
- + printf "%s:\n", label
-
- - in_function = 1
- - print ".cfi_startproc"
- + # If this label has been jumped to, define the CFA offset to its
- + # value at the location of the jump.
- + if (!(label in functions) && in_function && label in cfa_offset) {
- + if (cfa_offset[in_function] != cfa_offset[label]) {
- + printf ".cfi_def_cfa_offset %d\n", cfa_offset[label]
- + cfa_offset[in_function] = cfa_offset[label]
- + }
- + delete cfa_offset[label]
- + }
-
- - for (register in saved)
- - delete saved[register]
- - for (register in dirty)
- - delete dirty[register]
- + # If this label has been called, adjust CFA offset.
- + if (label in called && !(label in functions)) {
- + adjust_sp_offset(4);
- + delete called[label]
- + }
- }
- -
- - # an instruction may follow on the same line, so continue processing
- + # An instruction may follow on the same line, so continue processing.
- }
-
- +# Skip empty line.
- /^$/ { next }
-
- +# Issue source line number.
- {
- - called = ""
- printf ".loc 1 %d\n", line_number
- print
- }
- @@ -126,82 +182,145 @@ function adjust_sp_offset(delta) {
- # 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
- +# %esp should only be adjusted by pushing/popping or adding/subtracting
- +# constants.
- #
- -/pushl?/ {
- - if (match($0, / %(ax|bx|cx|dx|di|si|bp|sp)/))
- +/^push[wl]? / {
- + if ($1 == "pushw" || match($2, /^%([abcd]x|di|si|bp|sp)$/))
- adjust_sp_offset(2)
- else
- adjust_sp_offset(4)
- }
- -/popl?/ {
- - if (match($0, / %(ax|bx|cx|dx|di|si|bp|sp)/))
- +
- +/^pop[wl]? / {
- + if ($1 == "popw" || match($2, /^%([abcd]x|di|si|bp|sp)$/))
- adjust_sp_offset(-2)
- else
- adjust_sp_offset(-4)
- }
- -/addl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%esp/ { adjust_sp_offset(-get_const1()) }
- -/subl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%esp/ { adjust_sp_offset(get_const1()) }
-
- -/call/ {
- - if (match($0, /call [0-9]+f/)) # "forward" label
- - called = substr($0, RSTART+5, RLENGTH-6)
- - else if (match($0, /call [0-9a-zA-Z_]+/))
- - called = substr($0, RSTART+5, RLENGTH-5)
- +/^pushal?$/ {
- + adjust_sp_offset(32)
- + if (in_function) {
- + save_reg("eax", 28)
- + save_reg("ecx", 24)
- + save_reg("edx", 20)
- + save_reg("ebx", 16)
- + save_reg("esp", 12)
- + save_reg("ebp", 8)
- + save_reg("esi", 4)
- + save_reg("edi", 0)
- + }
- +}
- +
- +/^pushaw$/ {
- + adjust_sp_offset(16)
- +}
- +
- +/^popal?$/ {
- + adjust_sp_offset(-32)
- +}
- +
- +/^popaw$/ {
- + adjust_sp_offset(-16)
- +}
- +
- +/^pushfl?$/ {
- + adjust_sp_offset(4)
- +}
- +
- +/^pushfw$/ {
- + adjust_sp_offset(2)
- +}
- +
- +/^popfl?$/ {
- + adjust_sp_offset(-4)
- +}
- +
- +/^popfw$/ {
- + adjust_sp_offset(-2)
- +}
- +
- +/^addl? \$[+-]?(0x[0-9a-fA-F]+|[0-9]+),%esp/ {
- + adjust_sp_offset(-get_const1())
- +}
- +
- +/^subl? \$[+-]?(0x[0-9a-fA-F]+|[0-9]+),%esp/ {
- + adjust_sp_offset(get_const1())
- +}
- +
- +/^call / {
- + if (match($2, /^[0-9]+f$/)) # "forward" label
- + called[substr($2, 1, RLENGTH - 1)] = 1
- + else if (match($2, /^[a-zA-Z_][0-9a-zA-Z_]*$/))
- + called[$2] = 1
- +}
- +
- +/^j/ {
- + jump_to_label($2)
- }
-
- # TRACKING REGISTER VALUES FROM THE PREVIOUS STACK FRAME
- #
- -/pushl? %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
- +/^pushl? %e([abcd]x|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
- + # 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
- - }
- - }
- + if (in_function)
- + save_reg(get_reg(), 0)
- }
-
- -/movl? %e(ax|bx|cx|dx|si|di|bp),-?(0x[0-9a-fA-F]+|[0-9]+)?\(%esp\)/ {
- +/^movl? %e(ax|bx|cx|dx|si|di|bp),[+-]?(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))
- + if (match($2, /,[+-]?(0x[0-9a-fA-F]+|[0-9]+)\(%esp\)$/)) {
- + offset = parse_const(substr($2, RSTART + 1, RLENGTH - 7))
- } else {
- offset = 0
- }
- - if (!saved[register] && !dirty[register]) {
- - printf ".cfi_rel_offset %s,%d\n", register, offset
- - saved[register] = 1
- - }
- + save_reg(get_reg1(), offset)
- }
- }
-
- # 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?([abcd][hlx]|si|di|bp)$/ { trashed(get_reg2()) }
- -/(add|addl|sub|subl|and|or|xor|lea|sal|sar|shl|shr).*,%e?([abcd][hlx]|si|di|bp)$/ {
- +function trashed(reg) {
- + if (in_function && !(reg in saved) && !(reg in dirty)) {
- + printf ".cfi_undefined %s\n", reg
- + dirty[reg] = 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?([abcd][hlx]|si|di|bp)$/ {
- + trashed(get_reg2())
- +}
- +/^(add|sub|and|x?or|lea|s[ah][lr])[bwl]? [^,]+,%e?([abcd][hlx]|si|di|bp)$/ {
- trashed(get_reg2())
- }
- -/^i?mul [^,]*$/ { trashed("eax"); trashed("edx") }
- -/^i?mul.*,%e?([abcd][hlx]|si|di|bp)$/ { trashed(get_reg2()) }
- -/^i?div/ { trashed("eax"); trashed("edx") }
- -/(dec|inc|not|neg|pop) %e?([abcd][hlx]|si|di|bp)/ { trashed(get_reg()) }
- -/cpuid/ { trashed("eax"); trashed("ebx"); trashed("ecx"); trashed("edx") }
- +/^i?mul[bwl] [^,]+$/ {
- + trashed("eax")
- + trashed("edx")
- +}
- +/^i?mul[bwl]? [^,]+,%e?([abcd][hlx]|si|di|bp)$/ {
- + trashed(get_reg2())
- +}
- +/^i?div / {
- + trashed("eax")
- + trashed("edx")
- +}
- +/^(dec|inc|not|neg|pop)[bwl]? %e?([abcd][hlx]|si|di|bp)$/ {
- + trashed(get_reg())
- +}
- +/^cpuid/ {
- + trashed("eax")
- + trashed("ebx")
- + trashed("ecx")
- + trashed("edx")
- +}
-
- END {
- if (in_function)
- --- a/tools/add-cfi.x86_64.awk
- +++ b/tools/add-cfi.x86_64.awk
- @@ -1,169 +1,247 @@
- -# Insert GAS CFI directives ("control frame information") into x86-64 asm input
- +# Insert GAS CFI directives ("control frame information") into x86-64 asm input.
-
- BEGIN {
- - # don't put CFI data in the .eh_frame ELF section (which we don't keep)
- + # 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
- + # Only emit CFI directives inside a function.
- + in_function = ""
-
- - # emit .loc directives with line numbers from original source
- + # Emit .loc directives with line numbers from original source.
- printf ".file 1 \"%s\"\n", ARGV[1]
- line_number = 0
-
- - # used to detect "call label; label:" trick
- - called = ""
- + re_label = "([0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)"
- +
- + for (i = 1; i <= 4; ++i) {
- + letter = substr("abcd", i, 1)
- + regname[letter "l"] = regname[letter "h"] = regname[letter "x"] = \
- + regname["e" letter "x"] = regname["r" letter "x"] = "r" letter "x"
- + }
- +
- + regname["si"] = regname["esi"] = regname["rsi"] = "rsi"
- + regname["di"] = regname["edi"] = regname["rdi"] = "rdi"
- + regname["bp"] = regname["ebp"] = regname["rbp"] = "rbp"
- + regname["sp"] = regname["esp"] = regname["rsp"] = "rsp"
- +
- + for (i = 8; i <= 15; ++i)
- + regname["r" i] = "r" i
- }
-
- +# For instructions with 2 operands, get 1st operand (assuming it is constant).
- 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))
- + match($2, /^\$[+-]?(0x[0-9a-fA-F]+|[0-9]+),/)
- + return parse_const(substr($2, 2, RLENGTH - 2))
- }
-
- -function canonicalize_reg(register) {
- - if (match(register, /^r/))
- - return register
- - else if (match(register, /^e/))
- - return "r" substr(register, 2, length(register)-1)
- - else if (match(register, /[hl]$/)) # AH, AL, BH, BL, etc
- - return "r" substr(register, 1, 1) "x"
- - else # AX, BX, CX, etc
- - return "r" register
- -}
- +# Only use if you already know there is 1 and only 1 register.
- function get_reg() {
- - # only use if you already know there is 1 and only 1 register
- - match($0, /%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)/)
- - return canonicalize_reg(substr($0, RSTART+1, RLENGTH-1))
- + return regname[substr($2, 2, length($2) - 1)]
- }
- +
- +# For instructions with 2 operands, get 1st operand (assuming it is register).
- function get_reg1() {
- - # for instructions with 2 operands, get 1st operand (assuming it is register)
- - match($0, /%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15),/)
- - return canonicalize_reg(substr($0, RSTART+1, RLENGTH-2))
- + match($2, /^%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5]),/)
- + return regname[substr($2, 2, RLENGTH - 2)]
- }
- +
- +# For instructions with 2 operands, get 2nd operand (assuming it is register).
- function get_reg2() {
- - # for instructions with 2 operands, get 2nd operand (assuming it is register)
- - match($0, /,%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)/)
- - return canonicalize_reg(substr($0, RSTART+2, RLENGTH-2))
- + match($2, /,%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/)
- + return regname[substr($2, RSTART + 2, RLENGTH - 2)]
- }
-
- +# Helper to adjust CFA offset.
- function adjust_sp_offset(delta) {
- - if (in_function)
- + if (in_function) {
- printf ".cfi_adjust_cfa_offset %d\n", delta
- + cfa_offset[in_function] += delta
- + }
- +}
- +
- +# Helper to process jumps to labels by saving the current CFA offset.
- +function jump_to_label(label) {
- + if (in_function) {
- + if (match(label, /^[0-9]+f$/)) # "forward" label
- + cfa_offset[substr(label, 1, RLENGTH - 1)] = cfa_offset[in_function]
- + else if (match(label, /^[a-zA-Z_][a-zA-Z0-9_]*$/))
- + cfa_offset[label] = cfa_offset[in_function]
- + }
- }
-
- {
- - line_number = line_number + 1
- + ++line_number
-
- - # clean the input up before doing anything else
- - # delete comments
- - gsub(/(#|\/\/).*/, "")
- + # Clean the input up before doing anything else.
- + # Delete comments.
- + gsub(/#.*|\/\*.*\*\//, "")
-
- - # canonicalize whitespace
- - gsub(/[ \t]+/, " ") # mawk doesn't understand \s
- + # Canonicalize whitespace.
- + gsub(/[ \t]+/, " ") # Mawk doesn't understand \s.
- gsub(/ *, */, ",")
- - gsub(/ *: */, ": ")
- + if (match(":", $1))
- + sub(/ *: */, ": ")
- gsub(/ $/, "")
- gsub(/^ /, "")
- }
-
- -# check for assembler directives which we care about
- +# Check for assembler directives which we care about.
- /^\.(section|data|text)/ {
- - # a .cfi_startproc/.cfi_endproc pair should be within the same section
- - # otherwise, clang will choke when generating ELF output
- + # A .cfi_startproc/.cfi_endproc pair should be within the same section.
- + # Otherwise, clang will choke when generating ELF output.
- if (in_function) {
- print ".cfi_endproc"
- - in_function = 0
- + in_function = ""
- }
- }
- -/^\.type [a-zA-Z0-9_]+,@function/ {
- - functions[substr($2, 1, length($2)-10)] = 1
- +
- +# Record each function name.
- +/^\.type [a-zA-Z0-9_]+( STT_FUNCTION|,[#@%"]function)/ {
- + functions[substr($2, 1, length($2) - 10)] = 1
- }
- -# not interested in assembler directives beyond this, just pass them through
- +# Not interested in assembler directives beyond this, just pass them through.
- /^\./ {
- print
- next
- }
-
- -/^[a-zA-Z0-9_]+:/ {
- - label = substr($1, 1, length($1)-1) # drop trailing :
- -
- - if (called == label) {
- - # note adjustment of stack pointer from "call label; label:"
- - adjust_sp_offset(8)
- - }
- +$0 ~ "^" re_label ":" {
- + # Parse each leading label.
- + while (match($0, "^" re_label ":")) {
- +
- + # Extract label name.
- + label = substr($1, 1, RLENGTH - 1)
- +
- + # Remove label from current line.
- + sub("^" re_label ": ?", "")
- +
- + if (label in functions) {
- + if (in_function) {
- + print ".cfi_endproc"
- + for (l in called)
- + delete called[l]
- + }
- +
- + in_function = label
- + print ".cfi_startproc"
- +
- + for (reg in saved)
- + delete saved[reg]
- + for (reg in dirty)
- + delete dirty[reg]
- + }
-
- - if (functions[label]) {
- - if (in_function)
- - print ".cfi_endproc"
- + printf "%s:\n", label
-
- - in_function = 1
- - print ".cfi_startproc"
- + # If this label has been jumped to, define the CFA offset to its
- + # value at the location of the jump.
- + if (!(label in functions) && in_function && label in cfa_offset) {
- + if (cfa_offset[in_function] != cfa_offset[label]) {
- + printf ".cfi_def_cfa_offset %d\n", cfa_offset[label]
- + cfa_offset[in_function] = cfa_offset[label]
- + }
- + delete cfa_offset[label]
- + }
-
- - for (register in saved)
- - delete saved[register]
- - for (register in dirty)
- - delete dirty[register]
- + # If this label has been called, adjust CFA offset.
- + if (label in called && !(label in functions)) {
- + adjust_sp_offset(8);
- + delete called[label]
- + }
- }
- -
- - # an instruction may follow on the same line, so continue processing
- + # An instruction may follow on the same line, so continue processing.
- }
-
- +# Skip empty line.
- /^$/ { next }
-
- +# Issue source line number.
- {
- - called = ""
- printf ".loc 1 %d\n", line_number
- print
- }
-
- # KEEPING UP WITH THE STACK POINTER
- -# %rsp should only be adjusted by pushing/popping or adding/subtracting constants
- +# %rsp should only be adjusted by pushing/popping or adding/subtracting
- +# constants.
- #
- -/pushl?/ {
- +/^push[wq]? / {
- + if ($1 == "pushw" || match($2, /^%([abcd]x|di|si|bp|sp)$/))
- + adjust_sp_offset(2)
- + else
- + adjust_sp_offset(8)
- +}
- +
- +/^pop[wq]? / {
- + if ($1 == "popw" || match($2, /^%([abcd]x|di|si|bp|sp)$/))
- + adjust_sp_offset(-2)
- + else
- + adjust_sp_offset(-8)
- +}
- +
- +/^pushfq?$/ {
- adjust_sp_offset(8)
- }
- -/popl?/ {
- +
- +/^pushfw$/ {
- + adjust_sp_offset(2)
- +}
- +
- +/^popfq?$/ {
- adjust_sp_offset(-8)
- }
- -/addl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%rsp/ { adjust_sp_offset(-get_const1()) }
- -/subl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%rsp/ { adjust_sp_offset(get_const1()) }
-
- -/call/ {
- - if (match($0, /call [0-9]+f/)) # "forward" label
- - called = substr($0, RSTART+5, RLENGTH-6)
- - else if (match($0, /call [0-9a-zA-Z_]+/))
- - called = substr($0, RSTART+5, RLENGTH-5)
- +/^popfw$/ {
- + adjust_sp_offset(-2)
- +}
- +
- +/^addq? \$[+-]?(0x[0-9a-fA-F]+|[0-9]+),%rsp$/ {
- + adjust_sp_offset(-get_const1())
- +}
- +/^subq? \$[+-]?(0x[0-9a-fA-F]+|[0-9]+),%rsp$/ {
- + adjust_sp_offset(get_const1())
- +}
- +
- +/^call / {
- + if (match($2, /^[0-9]+f$/)) # "forward" label
- + called[substr($2, 1, RLENGTH - 1)] = 1
- + else if (match($2, /^[a-zA-Z_][0-9a-zA-Z_]*$/))
- + called[$2] = 1
- +}
- +
- +/^j/ {
- + jump_to_label($2)
- }
-
- # TRACKING REGISTER VALUES FROM THE PREVIOUS STACK FRAME
- #
- -/pushl? %r(ax|bx|cx|dx|si|di|bp|8|9|10|11|12|13|14|15)/ { # don't match "push (%reg)"
- - # if a register is being pushed, and its value has not changed since the
- +/^pushq? %r([abcd]x|si|di|bp|[89]|1[0-5])$/ {
- + # 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
- + # 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
- + reg = get_reg()
- + if (!(reg in saved) && !(reg in dirty)) {
- + printf ".cfi_rel_offset %s,0\n", reg
- + saved[reg] = 1
- }
- }
- }
-
- -/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\)/ {
- +/^movq? %r([abcd]x|si|di|bp|[89]|1[0-5]),[+-]?(0x[0-9a-fA-F]+|[0-9]+)?\(%rsp\)$/ {
- if (in_function) {
- - register = get_reg()
- - if (match($0, /-?(0x[0-9a-fA-F]+|[0-9]+)\(%rsp\)/)) {
- - offset = parse_const(substr($0, RSTART, RLENGTH-6))
- + if (match($2, /,[+-]?(0x[0-9a-fA-F]+|[0-9]+)\(%rsp\)$/)) {
- + offset = parse_const(substr($2, RSTART, RLENGTH - 7))
- } else {
- offset = 0
- }
- - if (!saved[register] && !dirty[register]) {
- - printf ".cfi_rel_offset %s,%d\n", register, offset
- - saved[register] = 1
- + reg = get_reg1()
- + if (!(reg in saved) && !(reg in dirty)) {
- + printf ".cfi_rel_offset %s,%d\n", reg, offset
- + saved[reg] = 1
- }
- }
- }
- @@ -171,24 +249,41 @@ function adjust_sp_offset(delta) {
- # 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.*,%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)$/ { trashed(get_reg2()) }
- -/(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)$/ {
- +function trashed(reg) {
- + if (in_function && !(reg in saved) && !(reg in dirty)) {
- + printf ".cfi_undefined %s\n", reg
- + }
- + dirty[reg] = 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.*,%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/ {
- + trashed(get_reg2())
- +}
- +/^(add|sub|and|x?or|lea|s[ah][lr])[bwlq]? [^,]+,%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/ {
- trashed(get_reg2())
- }
- -/^i?mul [^,]*$/ { trashed("rax"); trashed("rdx") }
- -/^i?mul.*,%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)$/ { trashed(get_reg2()) }
- -/^i?div/ { trashed("rax"); trashed("rdx") }
- +/^i?mul[bwlq]? [^,]+$/ {
- + trashed("rax")
- + trashed("rdx")
- +}
- +/^i?mul[bwlq] [^,]+,%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/ {
- + trashed(get_reg2())
- +}
- +/^i?div[bwlq]? / {
- + trashed("rax")
- + trashed("rdx")
- +}
-
- -/(dec|inc|not|neg|pop) %[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)/ { trashed(get_reg()) }
- -/cpuid/ { trashed("rax"); trashed("rbx"); trashed("rcx"); trashed("rdx") }
- +/^(dec|inc|not|neg|pop)[bwlq]? %[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/ {
- + trashed(get_reg())
- +}
- +/^cpuid$/ {
- + trashed("rax")
- + trashed("rbx")
- + trashed("rcx")
- + trashed("rdx")
- +}
-
- END {
- if (in_function)
|