100-tools-Rework-adding-of-CFI-annotations.patch 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568
  1. From: =?UTF-8?q?Ignacy=20Gaw=C4=99dzki?=
  2. <[email protected]>
  3. Date: Thu, 20 Mar 2025 12:07:21 +0100
  4. Subject: [PATCH] tools: Rework adding of CFI annotations.
  5. MIME-Version: 1.0
  6. Content-Type: text/plain; charset=UTF-8
  7. Content-Transfer-Encoding: 8bit
  8. Rework awk scripts used to add CFI annotations to i386 and x86_64
  9. assembly, in order to properly maintain CFA offset across in-function
  10. jumps.
  11. Add arm and aarch64 versions of these scripts.
  12. Signed-off-by: Ignacy Gawędzki <[email protected]>
  13. ---
  14. create mode 100644 tools/add-cfi.aarch64.awk
  15. create mode 100644 tools/add-cfi.arm.awk
  16. --- /dev/null
  17. +++ b/tools/add-cfi.aarch64.awk
  18. @@ -0,0 +1,287 @@
  19. +# Insert GAS CFI directives ("control frame information") into AArch64 asm input.
  20. +#
  21. +# CFI directives tell the assembler how to generate "stack frame" debug info.
  22. +# This information can tell a debugger (like gdb) how to find the current stack
  23. +# frame at any point in the program code, and how to find the values which
  24. +# various registers had at higher points in the call stack.
  25. +# With this information, the debugger can show a backtrace, and you can move up
  26. +# and down the call stack and examine the values of local variables.
  27. +
  28. +BEGIN {
  29. + # Don't put CFI data in the .eh_frame ELF section (which we don't keep).
  30. + print ".cfi_sections .debug_frame"
  31. +
  32. + # Only emit CFI directives inside a function.
  33. + in_function = ""
  34. +
  35. + # Emit .loc directives with line numbers from original source.
  36. + printf ".file 1 \"%s\"\n", ARGV[1]
  37. + line_number = 0
  38. +
  39. + re_label = "([0-9+|[a-zA-Z_][a-zA-Z0-9_]*)"
  40. +
  41. + # Build an associative array of canonical register names.
  42. + for (i = 0; i < 30; ++i)
  43. + regname["x" i] = regname["w" i] = "x" i
  44. + regname["x30"] = regname["w30"] = regname["lr"] = "x30"
  45. + regname["xzr"] = regname["wzr"] = "xzr"
  46. + regname["sp"] = regname["wsp"] = "sp"
  47. +}
  48. +
  49. +{
  50. + ++line_number
  51. +
  52. + # Clean the input up before doing anything else.
  53. + # Delete comments.
  54. + gsub(/^#.*|\/\/.*|\/\*.*\*\//, "")
  55. +
  56. + # Canonicalize whitespace.
  57. + gsub(/[ \t]+/, " ") # Mawk doesn't understand \s.
  58. + gsub(/ *, */, ",")
  59. + gsub(/ *: */, ": ")
  60. + gsub(/ $/, "")
  61. + gsub(/^ /, "")
  62. +}
  63. +
  64. +# Check for assembler directives which we care about.
  65. +/^\.(section|data|text)/ {
  66. + # A .cfi_startproc/.cfi_endproc pair should be within the same section
  67. + # otherwise, clang will choke when generating ELF output.
  68. + if (in_function) {
  69. + print ".cfi_endproc"
  70. + in_function = ""
  71. + }
  72. +}
  73. +
  74. +# Record each function name.
  75. +/^\.type [a-zA-Z0-9_]+( STT_FUNCTION|,[#@%"]function)/ {
  76. + functions[substr($2, 1, length($2) - 10)] = 1
  77. +}
  78. +
  79. +# Not interested in assembler directives beyond this, just pass them through.
  80. +/^\./ {
  81. + print
  82. + next
  83. +}
  84. +
  85. +# Helper to adjust CFA offset.
  86. +function adjust_sp_offset(delta) {
  87. + if (in_function) {
  88. + printf ".cfi_adjust_cfa_offset %d\n", delta
  89. + cfa_offset[in_function] += delta
  90. + }
  91. +}
  92. +
  93. +# Helper to invalidate unsaved register.
  94. +function trashed(reg) {
  95. + if (in_function && !(reg in saved) && !(reg in dirty))
  96. + printf ".cfi_undefined %s\n", reg
  97. + dirty[reg] = 1
  98. +}
  99. +
  100. +# Helper to process jumps to labels by saving the current CFA offset.
  101. +function jump_to_label(label) {
  102. + if (in_function) {
  103. + if (match(label, /^[0-9]+f$/)) # "forward" label
  104. + cfa_offset[substr(label, 1, RLENGTH - 1)] = cfa_offset[in_function]
  105. + else if (match(label, /^[a-zA-Z_][a-zA-Z0-9_]*$/))
  106. + cfa_offset[label] = cfa_offset[in_function]
  107. + }
  108. +}
  109. +
  110. +# Helper to set relative offset of registers pushed on the stack.
  111. +function push_regs(regs, numregs, i) {
  112. + adjust_sp_offset(numregs * 4)
  113. + for (i = 1; i <= numregs; ++i) {
  114. + reg = regname[regs[i]]
  115. + if (!(reg in saved) && !(reg in dirty)) {
  116. + printf ".cfi_rel_offset %s,%i\n", reg, ((i - 1) * 4)
  117. + saved[reg] = 1
  118. + }
  119. + }
  120. +}
  121. +
  122. +# Helper to invalidate unsaved registers popped from the stack.
  123. +function pop_regs(regs, numregs, i) {
  124. + adjust_sp_offset(numregs * -4)
  125. + for (i = 1; i <= numregs; ++i) {
  126. + reg = regname[regs[i]]
  127. + trashed(reg)
  128. + }
  129. +}
  130. +
  131. +# Helper to save a single register saved in SP-relative locations.
  132. +function save_reg(reg, offset) {
  133. + reg = regname[reg]
  134. + if (!(reg in saved) && !(reg in dirty)) {
  135. + printf ".cfi_rel_offset %s,%d\n", reg, offset
  136. + saved[reg] = 1
  137. + }
  138. +}
  139. +
  140. +# Process labels.
  141. +$0 ~ "^" re_label ":" {
  142. + # Parse each leading label.
  143. + while (match($0, "^" re_label ":")) {
  144. +
  145. + # Extract label name.
  146. + label = substr($1, 1, RLENGTH - 1)
  147. +
  148. + # Remove label from current line.
  149. + sub("^" re_label ": ?", "")
  150. +
  151. + if (label in functions) {
  152. + if (in_function) {
  153. + print ".cfi_endproc"
  154. + for (l in called)
  155. + delete called[l]
  156. + }
  157. +
  158. + in_function = label
  159. + print ".cfi_startproc"
  160. +
  161. + for (reg in saved)
  162. + delete saved[reg]
  163. + for (reg in dirty)
  164. + delete dirty[reg]
  165. + }
  166. +
  167. + printf "%s:\n", label
  168. +
  169. + # If this label has been jumped to, define the CFA offset to its
  170. + # value at the location of the jump.
  171. + if (!(label in functions) && in_function && label in cfa_offset) {
  172. + if (cfa_offset[in_function] != cfa_offset[label]) {
  173. + printf ".cfi_def_cfa_offset %d\n", cfa_offset[label]
  174. + cfa_offset[in_function] = cfa_offset[label]
  175. + }
  176. + delete cfa_offset[label]
  177. + }
  178. +
  179. + # If this label has been called, possibly invalidate LR.
  180. + if (label in called && !(label in functions)) {
  181. + trashed("lr")
  182. + delete called[label]
  183. + }
  184. + }
  185. + # An instruction may follow on the same line, so continue processing.
  186. +}
  187. +
  188. +# Skip empty line.
  189. +/^$/ { next }
  190. +
  191. +# Issue source line number.
  192. +{
  193. + printf ".loc 1 %d\n", line_number
  194. + print
  195. +}
  196. +
  197. +# Process jumps to label (using B*).
  198. +/^b[^xrl]/ {
  199. + jump_to_label($2)
  200. +}
  201. +
  202. +# Process jumps to label (using [CT]BN?Z).
  203. +/^[ct]bn?z / {
  204. + if (match($2, /,.+$/))
  205. + jump_to_label(substr($2, RSTART + 1, RLENGTH - 1))
  206. +}
  207. +
  208. +# Issue relative offsets of registers stored in SP-relative locations.
  209. +/^st(n?p|r[bh]?|l[lu]?r|tr|ur) .+,\[(sp|x30)[,\]]/ {
  210. + if (in_function) {
  211. + if (match($2, /(,#?[+-]?(0x[0-9a-fA-F]+|[0-9]+))?\]$/)) {
  212. + # Offset with no write-back.
  213. + if (RLENGTH == 1)
  214. + offset = 0
  215. + else
  216. + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 3))
  217. + split($2, operands, ",")
  218. + if (match($1, /^stn?p$/)) {
  219. + if (match(operands[1], /^x/)) {
  220. + save_reg(operands[1], offset)
  221. + save_reg(operands[2], offset + 8)
  222. + }
  223. + } else if (match(operands[1], /x^/))
  224. + save_reg(operands[1], offset)
  225. + } else if (match($2, /,#?[+-]?(0x[0-9a-fA-F]+|[0-9]+)\]!$/)) {
  226. + # Pre-index with write-back.
  227. + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 4))
  228. + adjust_sp_offset(-offset)
  229. + split($2, operands, ",")
  230. + if ($1 == "stp") {
  231. + if (match(operands[1], /^x/)) {
  232. + save_reg(operands[1], 0)
  233. + save_reg(operands[2], 8)
  234. + }
  235. + } else if (match(operands[1], /^x/))
  236. + save_reg(operands[1], 0)
  237. + } else if (match($2, /,#?[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/)) {
  238. + # Post-index
  239. + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 2))
  240. + split($2, operands, ",")
  241. + if ($1 == "stp") {
  242. + if (match(operands[1], /^x/)) {
  243. + save_reg(operands[1], 0)
  244. + save_reg(operands[2], 8)
  245. + }
  246. + } else if (match(operands[1], /^x/))
  247. + save_reg(operands[1], 0)
  248. + adjust_sp_offset(-offset)
  249. + }
  250. + }
  251. +}
  252. +
  253. +# Adjust CFA offset when decreasing SP.
  254. +/subs?(\.[nw])? sp,sp,/ {
  255. + if (in_function && match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/))
  256. + adjust_sp_offset(parse_const(substr($2, RSTART + 2, RLENGTH - 2)))
  257. +}
  258. +
  259. +# Adjust CFA offset when increasing SP.
  260. +/adds?(\.[nw])? sp,sp,/ {
  261. + if (in_function && match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/))
  262. + adjust_sp_offset(-parse_const(substr($2, RSTART + 2, RLENGTH - 2)))
  263. +}
  264. +
  265. +# Process calls to labels.
  266. +/bl[a-z]* / {
  267. + if (match($2, /^[0-9]+f$/)) # "forward" label
  268. + called[substr($2, 1, RLENGTH - 1)] = 1
  269. + else if (match($2, /^[a-zA-Z_][0-9a-zA-Z_]*$/))
  270. + called[$2] = 1
  271. +}
  272. +
  273. +# Invalidate unsaved registers being written to.
  274. +/^(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),/ {
  275. + split($2, args, ",")
  276. + reg = args[1]
  277. + if (reg != "sp")
  278. + trashed(regname[reg])
  279. +}
  280. +
  281. +# Invalidate unsaved registers being written to by atomic operations in memory.
  282. +/^ld(add|clr|eor|set|[su](max|min))/ {
  283. + split($2, args, ",")
  284. + trashed(regname[args[2]])
  285. +}
  286. +
  287. +# Invalidate unsaved registers being written to by pair loading.
  288. +/^ld[nx]p(sw)? / {
  289. + split($2, args, ",")
  290. + trashed(regname[args[1]])
  291. + trashed(regname[args[2]])
  292. +}
  293. +
  294. +# Invalidate unsaved registers being written to by long instructions.
  295. +/^(smlals?|smlal(bb|bt|tb|tt)|smlaldx?|smlsldx?|smull|umaal|umlal|umulls?) / {
  296. + split($2, args, ",")
  297. + trashed(regname[args[1]])
  298. + trashed(regname[args[2]])
  299. +}
  300. +
  301. +END {
  302. + # Issue end of function if still inside one.
  303. + if (in_function)
  304. + print ".cfi_endproc"
  305. +}
  306. --- /dev/null
  307. +++ b/tools/add-cfi.arm.awk
  308. @@ -0,0 +1,367 @@
  309. +# Insert GAS CFI directives ("control frame information") into ARM asm input.
  310. +#
  311. +# CFI directives tell the assembler how to generate "stack frame" debug info.
  312. +# This information can tell a debugger (like gdb) how to find the current stack
  313. +# frame at any point in the program code, and how to find the values which
  314. +# various registers had at higher points in the call stack.
  315. +# With this information, the debugger can show a backtrace, and you can move up
  316. +# and down the call stack and examine the values of local variables.
  317. +
  318. +BEGIN {
  319. + # Don't put CFI data in the .eh_frame ELF section (which we don't keep).
  320. + print ".cfi_sections .debug_frame"
  321. +
  322. + # Only emit CFI directives inside a function.
  323. + in_function = ""
  324. +
  325. + # Emit .loc directives with line numbers from original source.
  326. + printf ".file 1 \"%s\"\n", ARGV[1]
  327. + line_number = 0
  328. +
  329. + re_label = "([0-9+|[a-zA-Z_][a-zA-Z0-9_]*)"
  330. +
  331. + # Build an associative array of canonical register names.
  332. + for (i = 0; i < 10; ++i) {
  333. + regname["r" i] = "r" i
  334. + regnum["r" i] = i
  335. + }
  336. + regname["r10"] = regname["sl"] = "r10"
  337. + regnum["r10"] = regnum["sl"] = 10
  338. + regname["r11"] = regname["fp"] = "r11"
  339. + regnum["r11"] = regnum["fp"] = 11
  340. + regname["r12"] = regname["ip"] = "r12"
  341. + regnum["r12"] = regnum["ip"] = 12
  342. + regname["r13"] = regname["sp"] = "r13"
  343. + regnum["r13"] = regnum["sp"] = 13
  344. + regname["r14"] = regname["lr"] = "r14"
  345. + regnum["r14"] = regnum["lr"] = 14
  346. + regname["r15"] = regname["pc"] = "r15"
  347. + regnum["r15"] = regnum["pc"] = 15
  348. +}
  349. +
  350. +{
  351. + ++line_number
  352. +
  353. + # Clean the input up before doing anything else.
  354. + # Delete comments.
  355. + gsub(/(^#|@|\/\/).*|\/\*.*\*\//, "")
  356. +
  357. + # Canonicalize whitespace.
  358. + gsub(/[ \t]+/, " ") # Mawk doesn't understand \s.
  359. + gsub(/ *, */, ",")
  360. + gsub(/ *: */, ": ")
  361. + gsub(/ $/, "")
  362. + gsub(/^ /, "")
  363. +}
  364. +
  365. +# Check for assembler directives which we care about.
  366. +/^\.(section|data|text)/ {
  367. + # A .cfi_startproc/.cfi_endproc pair should be within the same section
  368. + # otherwise, clang will choke when generating ELF output.
  369. + if (in_function) {
  370. + print ".cfi_endproc"
  371. + in_function = ""
  372. + }
  373. +}
  374. +
  375. +# Record each function name.
  376. +/^\.type [a-zA-Z0-9_]+( STT_FUNCTION|,[#@%"]function)/ {
  377. + functions[substr($2, 1, length($2) - 10)] = 1
  378. +}
  379. +
  380. +# Not interested in assembler directives beyond this, just pass them through.
  381. +/^\./ {
  382. + print
  383. + next
  384. +}
  385. +
  386. +# Helper to adjust CFA offset.
  387. +function adjust_sp_offset(delta) {
  388. + if (in_function) {
  389. + printf ".cfi_adjust_cfa_offset %d\n", delta
  390. + cfa_offset[in_function] += delta
  391. + }
  392. +}
  393. +
  394. +# Helper to invalidate unsaved register.
  395. +function trashed(reg) {
  396. + if (in_function && !(reg in saved) && !(reg in dirty))
  397. + printf ".cfi_undefined %s\n", reg
  398. + dirty[reg] = 1
  399. +}
  400. +
  401. +# Helper to process jumps to labels by saving the current CFA offset.
  402. +function jump_to_label(label) {
  403. + if (in_function) {
  404. + if (match(label, /^[0-9]+f$/)) # "forward" label
  405. + cfa_offset[substr(label, 1, RLENGTH - 1)] = cfa_offset[in_function]
  406. + else if (match(label, /^[a-zA-Z_][a-zA-Z0-9_]*$/))
  407. + cfa_offset[label] = cfa_offset[in_function]
  408. + }
  409. +}
  410. +
  411. +# Helper to save a single register saved in SP-relative locations.
  412. +function save_reg(reg, offset) {
  413. + reg = regname[reg]
  414. + if (!(reg in saved) && !(reg in dirty)) {
  415. + printf ".cfi_rel_offset %s,%d\n", reg, offset
  416. + saved[reg] = 1
  417. + }
  418. +}
  419. +
  420. +# Helper to save registers relative to SP.
  421. +function save_regs(regs, numregs, i) {
  422. + for (i = 1; i <= numregs; ++i)
  423. + save_reg(regname[regs[i]], (i - 1) * -4)
  424. +}
  425. +
  426. +# Helper to set relative offset of registers pushed on the stack.
  427. +function push_regs(regs, numregs, i) {
  428. + adjust_sp_offset(numregs * 4)
  429. + for (i = 1; i <= numregs; ++i)
  430. + save_reg(regname[regs[i]], (i - 1) * 4)
  431. +}
  432. +
  433. +# Helper to invalidate unsaved registers popped from the stack.
  434. +function pop_regs(regs, numregs, i) {
  435. + adjust_sp_offset(numregs * -4)
  436. + for (i = 1; i <= numregs; ++i) {
  437. + reg = regname[regs[i]]
  438. + trashed(reg)
  439. + }
  440. +}
  441. +
  442. +# Helper to parse register lists.
  443. +function split_reglist(arg, regs, num, toks, tmp, dash, i, j) {
  444. + while (match(arg, /^{[^}]+}/)) {
  445. + num = split(substr(arg, RSTART + 1, RLENGTH - 2), toks, ",")
  446. + for (i = 1; i <= num; ++i)
  447. + if (match(toks[i], /^r([0-9]|1[0-5])-r([0-9]|1[0-5])$/)) {
  448. + dash = index(toks[i], "-")
  449. + first = 0 + substr(toks[i], 2, dash - 2)
  450. + last = 0 + substr(toks[i], dash + 2)
  451. + for (j = first; j <= last; ++j)
  452. + tmp[j]
  453. + } else
  454. + tmp[regnum[toks[i]]]
  455. + arg = substr(arg, RSTART + RLENGTH)
  456. + if (!match(arg, /^[\t ]*[+|][\t ]*/))
  457. + break
  458. + arg = substr(arg, RLENGTH + 1)
  459. + }
  460. + num = 0
  461. + for (i = 0; i < 16; ++i) {
  462. + if (!(i in tmp))
  463. + continue
  464. + regs[++num] = regname["r" i]
  465. + }
  466. + return num
  467. +}
  468. +
  469. +# Process labels.
  470. +$0 ~ "^" re_label ":" {
  471. + # Parse each leading label.
  472. + while (match($0, "^" re_label ":")) {
  473. +
  474. + # Extract label name.
  475. + label = substr($1, 1, RLENGTH - 1)
  476. +
  477. + # Remove label from current line.
  478. + sub("^" re_label ": ?", "")
  479. +
  480. + if (label in functions) {
  481. + if (in_function) {
  482. + print ".cfi_endproc"
  483. + for (l in called)
  484. + delete called[l]
  485. + }
  486. +
  487. + in_function = label
  488. + print ".cfi_startproc"
  489. +
  490. + for (reg in saved)
  491. + delete saved[reg]
  492. + for (reg in dirty)
  493. + delete dirty[reg]
  494. + }
  495. +
  496. + printf "%s:\n", label
  497. +
  498. + # If this label has been jumped to, define the CFA offset to its
  499. + # value at the location of the jump.
  500. + if (!(label in functions) && in_function && label in cfa_offset) {
  501. + if (cfa_offset[in_function] != cfa_offset[label]) {
  502. + printf ".cfi_def_cfa_offset %d\n", cfa_offset[label]
  503. + cfa_offset[in_function] = cfa_offset[label]
  504. + }
  505. + delete cfa_offset[label]
  506. + }
  507. +
  508. + # If this label has been called, possibly invalidate LR.
  509. + if (label in called && !(label in functions)) {
  510. + trashed("lr")
  511. + delete called[label]
  512. + }
  513. + }
  514. + # An instruction may follow on the same line, so continue processing.
  515. +}
  516. +
  517. +# Skip empty line.
  518. +/^$/ { next }
  519. +
  520. +# Issue source line number.
  521. +{
  522. + printf ".loc 1 %d\n", line_number
  523. + print
  524. +}
  525. +
  526. +# Process jumps to label (using B*).
  527. +/^b[^xl]/ {
  528. + jump_to_label($2)
  529. +}
  530. +
  531. +# Process jumps to label (using CBNZ?).
  532. +/^cbnz? / {
  533. + if (match($2, /,.*$/))
  534. + jump_to_label(substr($2, RSTART + 1, RLENGTH - 1))
  535. +}
  536. +
  537. +# Adjust CFA offset and issue relative offsets of pushed registers using PUSH.
  538. +/^push / {
  539. + if (in_function) {
  540. + numregs = split_reglist($2, regs)
  541. + push_regs(regs, numregs);
  542. + }
  543. +}
  544. +
  545. +# Adjust CFA offset and Issue relative offsets of pushed registers using STMFD.
  546. +/^stm(fd|db)(al)?(\.[nw])? (sp|r13)!,/ {
  547. + if (in_function) {
  548. + numregs = split_reglist(substr($2, index($2, ",") + 1), regs)
  549. + push_regs(regs, numregs);
  550. + }
  551. +}
  552. +
  553. +/^stm(ia|ea)?(al)?(\.[nw])? (sp|r13),/ {
  554. + if (in_function) {
  555. + numregs = split_reglist(substr($2, index($2, ",") + 1), regs)
  556. + save_regs(regs, numregs);
  557. + }
  558. +}
  559. +
  560. +# Adjust CFA offset and invalidate unsaved registers popped using POP.
  561. +/^pop / {
  562. + if (in_function) {
  563. + numregs = split_reglist($2, regs)
  564. + pop_regs(regs, numregs)
  565. + }
  566. +}
  567. +
  568. +# Adjust CFA offset and invalidate unsaved registers popped using LDMFD.
  569. +/^ldm(fd|ia)(al)?(\.[nw])? (sp|r13)!,/ {
  570. + if (in_function) {
  571. + numregs = split_reglist(substr($2, index($2, ",") + 1), regs)
  572. + pop_regs(regs, numregs)
  573. + }
  574. +}
  575. +
  576. +# Issue relative offsets of registers stored in SP-relative locations.
  577. +/^str[a-z.]* .*,\[(sp|r13)[,\]]/ {
  578. + if (in_function && !match($1, /^str(ex)?[bh]/)) {
  579. + if (match($2, /(,#[+-]?(0x[0-9a-fA-F]+|[0-9]+))?\]$/)) {
  580. + # Offset with no write-back.
  581. + if (RLENGTH == 1)
  582. + offset = 0
  583. + else
  584. + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 3))
  585. + split($2, operands, ",")
  586. + if (match($1, /^str(ex)?d/)) {
  587. + save_reg(operands[1], offset)
  588. + save_reg(operands[2], offset + 4)
  589. + } else
  590. + save_reg(operands[1], offset)
  591. + } else if (match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)\]!$/)) {
  592. + # Pre-index with write-back.
  593. + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 4))
  594. + adjust_sp_offset(-offset)
  595. + split($2, operands, ",")
  596. + if (match($1, /^str(ex)?d/)) {
  597. + save_reg(operands[1], 0)
  598. + save_reg(operands[2], 4)
  599. + } else
  600. + save_reg(operands[1], 0)
  601. + } else if (match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/)) {
  602. + # Post-index
  603. + offset = parse_const(substr($2, RSTART + 2, RLENGTH - 2))
  604. + split($2, operands, ",")
  605. + if (match($1, /^str(ex)?d/)) {
  606. + save_reg(operands[1], 0)
  607. + save_reg(operands[2], 4)
  608. + } else
  609. + save_reg(operands[1], 0)
  610. + adjust_sp_offset(-offset)
  611. + }
  612. + }
  613. +}
  614. +
  615. +# Adjust CFA offset when decreasing SP.
  616. +/subs?(al)?(\.[nw])? (sp|r13),(sp|r13),/ {
  617. + if (in_function && match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/))
  618. + adjust_sp_offset(parse_const(substr($2, RSTART + 2, RLENGTH - 2)))
  619. +}
  620. +
  621. +# Adjust CFA offset when increasing SP.
  622. +/adds?(al)?(\.[nw])? (sp|r13),(sp|r13),/ {
  623. + if (in_function && match($2, /,#[+-]?(0x[0-9a-fA-F]+|[0-9]+)$/))
  624. + adjust_sp_offset(-parse_const(substr($2, RSTART + 2, RLENGTH - 2)))
  625. +}
  626. +
  627. +# Process calls to labels.
  628. +/bl[a-z]* / {
  629. + if (match($2, /^[0-9]+f$/)) # "forward" label
  630. + called[substr($2, 1, RLENGTH - 1)] = 1
  631. + else if (match($2, /^[a-zA-Z_][0-9a-zA-Z_]*$/))
  632. + called[$2] = 1
  633. +}
  634. +
  635. +# Invalidate unsaved registers being written to.
  636. +/^((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),/ {
  637. + split($2, args, ",")
  638. + reg = args[1]
  639. + if (reg != "sp")
  640. + trashed(regname[reg])
  641. +}
  642. +
  643. +# Invalidate unsaved registers being written to by long instructions.
  644. +/^(smlals?|smlal(bb|bt|tb|tt)|smlaldx?|smlsldx?|smull|umaal|umlal|umulls?)/ {
  645. + split($2, args, ",")
  646. + trashed(regname[args[1]])
  647. + trashed(regname[args[2]])
  648. +}
  649. +
  650. +# Invalidate unsaved register being modified by write-back on store multiple.
  651. +/^stm[a-z.]* [^,]+!,/ {
  652. + first_arg = substr($2, 1, index($2, ",") - 1)
  653. + if (!match(first_arg, /^(sp|r13)/))
  654. + trashed(regname[substr(first_arg, 1, length(first_arg) - 1)])
  655. +}
  656. +
  657. +# Invalidate unsaved registers being modified by load multiple.
  658. +/^ldm[a-z.]* [^,]+,{.*}$/ {
  659. + comma = index($2, ",")
  660. + first_arg = substr($2, 1, comma - 1)
  661. + other_args = substr($2, comma + 1)
  662. + if (!match(first_arg, /^(sp|r13)/)) {
  663. + if (match(first_arg, /!$/))
  664. + trashed(regname[substr(first_arg, 1, RSTART - 1)])
  665. + numregs = split_reglist(other_args, regs)
  666. + for (i = 1; i <= numregs; ++i)
  667. + trashed(regname[regs[i]])
  668. + }
  669. +}
  670. +
  671. +END {
  672. + # Issue end of function if still inside one.
  673. + if (in_function)
  674. + print ".cfi_endproc"
  675. +}
  676. --- a/tools/add-cfi.common.awk
  677. +++ b/tools/add-cfi.common.awk
  678. @@ -1,26 +1,46 @@
  679. -function hex2int(str, i) {
  680. +function hex2int(str, i) {
  681. str = tolower(str)
  682. for (i = 1; i <= 16; i++) {
  683. char = substr("0123456789abcdef", i, 1)
  684. - lookup[char] = i-1
  685. + lookup[char] = i - 1
  686. }
  687. result = 0
  688. for (i = 1; i <= length(str); i++) {
  689. - result = result * 16
  690. - char = substr(str, i, 1)
  691. - result = result + lookup[char]
  692. + result *= 16
  693. + char = substr(str, i, 1)
  694. + result += lookup[char]
  695. + }
  696. + return result
  697. +}
  698. +
  699. +function oct2int(str, i) {
  700. + str = tolower(str)
  701. +
  702. + for (i = 1; i <= 8; ++i) {
  703. + char = substr("01234567", i, 1)
  704. + lookup[char] = i - 1
  705. + }
  706. +
  707. + result = 0
  708. + for (i = 1; i <= length(str); ++i) {
  709. + result *= 8
  710. + char = substr(str, i, 1)
  711. + result += lookup[char]
  712. }
  713. return result
  714. }
  715. function parse_const(str) {
  716. - sign = sub(/^-/, "", str)
  717. - hex = sub(/^0x/, "", str)
  718. + neg = sub(/^-/, "", str)
  719. + oct = match(str, /^0[0-7]/)
  720. + hex = sub(/^0x/, "", str)
  721. if (hex)
  722. n = hex2int(str)
  723. + else if (oct)
  724. + n = oct2int(str)
  725. else
  726. n = str+0
  727. - return sign ? -n : n
  728. + return neg? -n: n
  729. }
  730. --- a/tools/add-cfi.i386.awk
  731. +++ b/tools/add-cfi.i386.awk
  732. @@ -1,123 +1,179 @@
  733. -# Insert GAS CFI directives ("control frame information") into x86-32 asm input
  734. +# Insert GAS CFI directives ("control frame information") into x86-32 asm input.
  735. #
  736. -# CFI directives tell the assembler how to generate "stack frame" debug info
  737. +# CFI directives tell the assembler how to generate "stack frame" debug info.
  738. # This information can tell a debugger (like gdb) how to find the current stack
  739. # frame at any point in the program code, and how to find the values which
  740. -# various registers had at higher points in the call stack
  741. +# various registers had at higher points in the call stack.
  742. # With this information, the debugger can show a backtrace, and you can move up
  743. -# and down the call stack and examine the values of local variables
  744. +# and down the call stack and examine the values of local variables.
  745. BEGIN {
  746. - # don't put CFI data in the .eh_frame ELF section (which we don't keep)
  747. + # Don't put CFI data in the .eh_frame ELF section (which we don't keep).
  748. print ".cfi_sections .debug_frame"
  749. - # only emit CFI directives inside a function
  750. - in_function = 0
  751. + # Only emit CFI directives inside a function.
  752. + in_function = ""
  753. - # emit .loc directives with line numbers from original source
  754. + # Emit .loc directives with line numbers from original source.
  755. printf ".file 1 \"%s\"\n", ARGV[1]
  756. line_number = 0
  757. - # used to detect "call label; label:" trick
  758. - called = ""
  759. + re_label = "([0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)"
  760. +
  761. + for (i = 1; i <= 4; ++i) {
  762. + letter = substr("abcd", i, 1)
  763. + regname[letter "l"] = regname[letter "h"] = regname[letter "x"] = \
  764. + regname["e" letter "x"] = "e" letter "x"
  765. + }
  766. +
  767. + regname["si"] = regname["esi"] = "esi"
  768. + regname["di"] = regname["edi"] = "edi"
  769. + regname["bp"] = regname["ebp"] = "ebp"
  770. + regname["sp"] = regname["esp"] = "esp"
  771. }
  772. +# For instructions with 2 operands, get 1st operand (assuming it is constant).
  773. function get_const1() {
  774. - # for instructions with 2 operands, get 1st operand (assuming it is constant)
  775. - match($0, /-?(0x[0-9a-fA-F]+|[0-9]+),/)
  776. - return parse_const(substr($0, RSTART, RLENGTH-1))
  777. + match($2, /^\$[+-]?(0x[0-9a-fA-F]+|[0-9]+),/)
  778. + return parse_const(substr($2, 2, RLENGTH - 2))
  779. }
  780. -function canonicalize_reg(register) {
  781. - if (match(register, /^e/))
  782. - return register
  783. - else if (match(register, /[hl]$/)) # AH, AL, BH, BL, etc
  784. - return "e" substr(register, 1, 1) "x"
  785. - else # AX, BX, CX, etc
  786. - return "e" register
  787. -}
  788. +# Only use if you already know there is 1 and only 1 register.
  789. function get_reg() {
  790. - # only use if you already know there is 1 and only 1 register
  791. - match($0, /%e?([abcd][hlx]|si|di|bp)/)
  792. - return canonicalize_reg(substr($0, RSTART+1, RLENGTH-1))
  793. + return regname[substr($2, 2, length($2) - 1)]
  794. }
  795. +
  796. +# For instructions with 2 operands, get 1st operand (assuming it is register).
  797. function get_reg1() {
  798. - # for instructions with 2 operands, get 1st operand (assuming it is register)
  799. - match($0, /%e?([abcd][hlx]|si|di|bp),/)
  800. - return canonicalize_reg(substr($0, RSTART+1, RLENGTH-2))
  801. + match($2, /^%e?([abcd][hlx]|si|di|bp),/)
  802. + return regname[substr($2, 2, RLENGTH - 2)]
  803. }
  804. +
  805. +# For instructions with 2 operands, get 2nd operand (assuming it is register).
  806. function get_reg2() {
  807. - # for instructions with 2 operands, get 2nd operand (assuming it is register)
  808. - match($0, /,%e?([abcd][hlx]|si|di|bp)/)
  809. - return canonicalize_reg(substr($0, RSTART+2, RLENGTH-2))
  810. + match($2, /,%e?([abcd][hlx]|si|di|bp)$/)
  811. + return regname[substr($2, RSTART + 2, RLENGTH - 2)]
  812. }
  813. +# Helper to adjust CFA offset.
  814. function adjust_sp_offset(delta) {
  815. - if (in_function)
  816. + if (in_function) {
  817. printf ".cfi_adjust_cfa_offset %d\n", delta
  818. + cfa_offset[in_function] += delta
  819. + }
  820. +}
  821. +
  822. +function save_reg(reg, offset) {
  823. + if (!(reg in saved) && !(reg in dirty)) {
  824. + printf ".cfi_rel_offset %s,%d\n", reg, offset
  825. + saved[reg] = 1
  826. + }
  827. +}
  828. +
  829. +# Helper to process jumps to labels by saving the current CFA offset.
  830. +function jump_to_label(label) {
  831. + if (in_function) {
  832. + if (match(label, /^[0-9]+f$/)) # "forward" label
  833. + cfa_offset[substr(label, 1, RLENGTH - 1)] = cfa_offset[in_function]
  834. + else if (match(label, /^[a-zA-Z_][a-zA-Z0-9_]*$/))
  835. + cfa_offset[label] = cfa_offset[in_function]
  836. + }
  837. }
  838. {
  839. - line_number = line_number + 1
  840. + ++line_number
  841. - # clean the input up before doing anything else
  842. - # delete comments
  843. - gsub(/(#|\/\/).*/, "")
  844. + # Clean the input up before doing anything else.
  845. + # Delete comments.
  846. + gsub(/#.*|\/\*.*\*\//, "")
  847. - # canonicalize whitespace
  848. - gsub(/[ \t]+/, " ") # mawk doesn't understand \s
  849. + # Canonicalize whitespace.
  850. + gsub(/[ \t]+/, " ") # Mawk doesn't understand \s.
  851. gsub(/ *, */, ",")
  852. gsub(/ *: */, ": ")
  853. + gsub(/%cs: */, "%cs:")
  854. + gsub(/%ds: */, "%ds:")
  855. + gsub(/%ss: */, "%ss:")
  856. + gsub(/%es: */, "%es:")
  857. + gsub(/%fs: */, "%fs:")
  858. + gsub(/%gs: */, "%gs:")
  859. gsub(/ $/, "")
  860. gsub(/^ /, "")
  861. }
  862. -# check for assembler directives which we care about
  863. +# Check for assembler directives which we care about.
  864. /^\.(section|data|text)/ {
  865. - # a .cfi_startproc/.cfi_endproc pair should be within the same section
  866. - # otherwise, clang will choke when generating ELF output
  867. + # A .cfi_startproc/.cfi_endproc pair should be within the same section.
  868. + # Otherwise, clang will choke when generating ELF output.
  869. if (in_function) {
  870. print ".cfi_endproc"
  871. - in_function = 0
  872. + in_function = ""
  873. }
  874. }
  875. -/^\.type [a-zA-Z0-9_]+,@function/ {
  876. - functions[substr($2, 1, length($2)-10)] = 1
  877. +
  878. +# Record each function name.
  879. +/^\.type [a-zA-Z0-9_]+( STT_FUNCTION|,[#@%"]function)/ {
  880. + functions[substr($2, 1, length($2) - 10)] = 1
  881. }
  882. -# not interested in assembler directives beyond this, just pass them through
  883. +
  884. +# Not interested in assembler directives beyond this, just pass them through.
  885. /^\./ {
  886. print
  887. next
  888. }
  889. -/^[a-zA-Z0-9_]+:/ {
  890. - label = substr($1, 1, length($1)-1) # drop trailing :
  891. -
  892. - if (called == label) {
  893. - # note adjustment of stack pointer from "call label; label:"
  894. - adjust_sp_offset(4)
  895. - }
  896. +$0 ~ "^" re_label ":" {
  897. + # Parse each leading label.
  898. + while (match($0, "^" re_label ":")) {
  899. +
  900. + # Extract label name.
  901. + label = substr($1, 1, RLENGTH - 1)
  902. +
  903. + # Remove label from current line.
  904. + sub("^" re_label ": ?", "")
  905. +
  906. + if (label in functions) {
  907. + if (in_function) {
  908. + print ".cfi_endproc"
  909. + for (l in called)
  910. + delete called[l]
  911. + }
  912. +
  913. + in_function = label
  914. + print ".cfi_startproc"
  915. +
  916. + for (reg in saved)
  917. + delete saved[reg]
  918. + for (reg in dirty)
  919. + delete dirty[reg]
  920. + }
  921. - if (functions[label]) {
  922. - if (in_function)
  923. - print ".cfi_endproc"
  924. + printf "%s:\n", label
  925. - in_function = 1
  926. - print ".cfi_startproc"
  927. + # If this label has been jumped to, define the CFA offset to its
  928. + # value at the location of the jump.
  929. + if (!(label in functions) && in_function && label in cfa_offset) {
  930. + if (cfa_offset[in_function] != cfa_offset[label]) {
  931. + printf ".cfi_def_cfa_offset %d\n", cfa_offset[label]
  932. + cfa_offset[in_function] = cfa_offset[label]
  933. + }
  934. + delete cfa_offset[label]
  935. + }
  936. - for (register in saved)
  937. - delete saved[register]
  938. - for (register in dirty)
  939. - delete dirty[register]
  940. + # If this label has been called, adjust CFA offset.
  941. + if (label in called && !(label in functions)) {
  942. + adjust_sp_offset(4);
  943. + delete called[label]
  944. + }
  945. }
  946. -
  947. - # an instruction may follow on the same line, so continue processing
  948. + # An instruction may follow on the same line, so continue processing.
  949. }
  950. +# Skip empty line.
  951. /^$/ { next }
  952. +# Issue source line number.
  953. {
  954. - called = ""
  955. printf ".loc 1 %d\n", line_number
  956. print
  957. }
  958. @@ -126,82 +182,145 @@ function adjust_sp_offset(delta) {
  959. # We do NOT attempt to understand foolish and ridiculous tricks like stashing
  960. # the stack pointer and then using %esp as a scratch register, or bitshifting
  961. # it or taking its square root or anything stupid like that.
  962. -# %esp should only be adjusted by pushing/popping or adding/subtracting constants
  963. +# %esp should only be adjusted by pushing/popping or adding/subtracting
  964. +# constants.
  965. #
  966. -/pushl?/ {
  967. - if (match($0, / %(ax|bx|cx|dx|di|si|bp|sp)/))
  968. +/^push[wl]? / {
  969. + if ($1 == "pushw" || match($2, /^%([abcd]x|di|si|bp|sp)$/))
  970. adjust_sp_offset(2)
  971. else
  972. adjust_sp_offset(4)
  973. }
  974. -/popl?/ {
  975. - if (match($0, / %(ax|bx|cx|dx|di|si|bp|sp)/))
  976. +
  977. +/^pop[wl]? / {
  978. + if ($1 == "popw" || match($2, /^%([abcd]x|di|si|bp|sp)$/))
  979. adjust_sp_offset(-2)
  980. else
  981. adjust_sp_offset(-4)
  982. }
  983. -/addl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%esp/ { adjust_sp_offset(-get_const1()) }
  984. -/subl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%esp/ { adjust_sp_offset(get_const1()) }
  985. -/call/ {
  986. - if (match($0, /call [0-9]+f/)) # "forward" label
  987. - called = substr($0, RSTART+5, RLENGTH-6)
  988. - else if (match($0, /call [0-9a-zA-Z_]+/))
  989. - called = substr($0, RSTART+5, RLENGTH-5)
  990. +/^pushal?$/ {
  991. + adjust_sp_offset(32)
  992. + if (in_function) {
  993. + save_reg("eax", 28)
  994. + save_reg("ecx", 24)
  995. + save_reg("edx", 20)
  996. + save_reg("ebx", 16)
  997. + save_reg("esp", 12)
  998. + save_reg("ebp", 8)
  999. + save_reg("esi", 4)
  1000. + save_reg("edi", 0)
  1001. + }
  1002. +}
  1003. +
  1004. +/^pushaw$/ {
  1005. + adjust_sp_offset(16)
  1006. +}
  1007. +
  1008. +/^popal?$/ {
  1009. + adjust_sp_offset(-32)
  1010. +}
  1011. +
  1012. +/^popaw$/ {
  1013. + adjust_sp_offset(-16)
  1014. +}
  1015. +
  1016. +/^pushfl?$/ {
  1017. + adjust_sp_offset(4)
  1018. +}
  1019. +
  1020. +/^pushfw$/ {
  1021. + adjust_sp_offset(2)
  1022. +}
  1023. +
  1024. +/^popfl?$/ {
  1025. + adjust_sp_offset(-4)
  1026. +}
  1027. +
  1028. +/^popfw$/ {
  1029. + adjust_sp_offset(-2)
  1030. +}
  1031. +
  1032. +/^addl? \$[+-]?(0x[0-9a-fA-F]+|[0-9]+),%esp/ {
  1033. + adjust_sp_offset(-get_const1())
  1034. +}
  1035. +
  1036. +/^subl? \$[+-]?(0x[0-9a-fA-F]+|[0-9]+),%esp/ {
  1037. + adjust_sp_offset(get_const1())
  1038. +}
  1039. +
  1040. +/^call / {
  1041. + if (match($2, /^[0-9]+f$/)) # "forward" label
  1042. + called[substr($2, 1, RLENGTH - 1)] = 1
  1043. + else if (match($2, /^[a-zA-Z_][0-9a-zA-Z_]*$/))
  1044. + called[$2] = 1
  1045. +}
  1046. +
  1047. +/^j/ {
  1048. + jump_to_label($2)
  1049. }
  1050. # TRACKING REGISTER VALUES FROM THE PREVIOUS STACK FRAME
  1051. #
  1052. -/pushl? %e(ax|bx|cx|dx|si|di|bp)/ { # don't match "push (%reg)"
  1053. - # if a register is being pushed, and its value has not changed since the
  1054. +/^pushl? %e([abcd]x|si|di|bp)$/ {
  1055. + # Don't match "push (%reg)"
  1056. + # If a register is being pushed, and its value has not changed since the
  1057. # beginning of this function, the pushed value can be used when printing
  1058. - # local variables at the next level up the stack
  1059. - # emit '.cfi_rel_offset' for that
  1060. + # local variables at the next level up the stack.
  1061. + # Emit '.cfi_rel_offset' for that.
  1062. - if (in_function) {
  1063. - register = get_reg()
  1064. - if (!saved[register] && !dirty[register]) {
  1065. - printf ".cfi_rel_offset %s,0\n", register
  1066. - saved[register] = 1
  1067. - }
  1068. - }
  1069. + if (in_function)
  1070. + save_reg(get_reg(), 0)
  1071. }
  1072. -/movl? %e(ax|bx|cx|dx|si|di|bp),-?(0x[0-9a-fA-F]+|[0-9]+)?\(%esp\)/ {
  1073. +/^movl? %e(ax|bx|cx|dx|si|di|bp),[+-]?(0x[0-9a-fA-F]+|[0-9]+)?\(%esp\)$/ {
  1074. if (in_function) {
  1075. - register = get_reg()
  1076. - if (match($0, /-?(0x[0-9a-fA-F]+|[0-9]+)\(%esp\)/)) {
  1077. - offset = parse_const(substr($0, RSTART, RLENGTH-6))
  1078. + if (match($2, /,[+-]?(0x[0-9a-fA-F]+|[0-9]+)\(%esp\)$/)) {
  1079. + offset = parse_const(substr($2, RSTART + 1, RLENGTH - 7))
  1080. } else {
  1081. offset = 0
  1082. }
  1083. - if (!saved[register] && !dirty[register]) {
  1084. - printf ".cfi_rel_offset %s,%d\n", register, offset
  1085. - saved[register] = 1
  1086. - }
  1087. + save_reg(get_reg1(), offset)
  1088. }
  1089. }
  1090. # IF REGISTER VALUES ARE UNCEREMONIOUSLY TRASHED
  1091. # ...then we want to know about it.
  1092. #
  1093. -function trashed(register) {
  1094. - if (in_function && !saved[register] && !dirty[register]) {
  1095. - printf ".cfi_undefined %s\n", register
  1096. - }
  1097. - dirty[register] = 1
  1098. -}
  1099. -# this does NOT exhaustively check for all possible instructions which could
  1100. -# overwrite a register value inherited from the caller (just the common ones)
  1101. -/mov.*,%e?([abcd][hlx]|si|di|bp)$/ { trashed(get_reg2()) }
  1102. -/(add|addl|sub|subl|and|or|xor|lea|sal|sar|shl|shr).*,%e?([abcd][hlx]|si|di|bp)$/ {
  1103. +function trashed(reg) {
  1104. + if (in_function && !(reg in saved) && !(reg in dirty)) {
  1105. + printf ".cfi_undefined %s\n", reg
  1106. + dirty[reg] = 1
  1107. + }
  1108. +}
  1109. +# This does NOT exhaustively check for all possible instructions which could
  1110. +# overwrite a register value inherited from the caller (just the common ones).
  1111. +/^mov.*,%e?([abcd][hlx]|si|di|bp)$/ {
  1112. + trashed(get_reg2())
  1113. +}
  1114. +/^(add|sub|and|x?or|lea|s[ah][lr])[bwl]? [^,]+,%e?([abcd][hlx]|si|di|bp)$/ {
  1115. trashed(get_reg2())
  1116. }
  1117. -/^i?mul [^,]*$/ { trashed("eax"); trashed("edx") }
  1118. -/^i?mul.*,%e?([abcd][hlx]|si|di|bp)$/ { trashed(get_reg2()) }
  1119. -/^i?div/ { trashed("eax"); trashed("edx") }
  1120. -/(dec|inc|not|neg|pop) %e?([abcd][hlx]|si|di|bp)/ { trashed(get_reg()) }
  1121. -/cpuid/ { trashed("eax"); trashed("ebx"); trashed("ecx"); trashed("edx") }
  1122. +/^i?mul[bwl] [^,]+$/ {
  1123. + trashed("eax")
  1124. + trashed("edx")
  1125. +}
  1126. +/^i?mul[bwl]? [^,]+,%e?([abcd][hlx]|si|di|bp)$/ {
  1127. + trashed(get_reg2())
  1128. +}
  1129. +/^i?div / {
  1130. + trashed("eax")
  1131. + trashed("edx")
  1132. +}
  1133. +/^(dec|inc|not|neg|pop)[bwl]? %e?([abcd][hlx]|si|di|bp)$/ {
  1134. + trashed(get_reg())
  1135. +}
  1136. +/^cpuid/ {
  1137. + trashed("eax")
  1138. + trashed("ebx")
  1139. + trashed("ecx")
  1140. + trashed("edx")
  1141. +}
  1142. END {
  1143. if (in_function)
  1144. --- a/tools/add-cfi.x86_64.awk
  1145. +++ b/tools/add-cfi.x86_64.awk
  1146. @@ -1,169 +1,247 @@
  1147. -# Insert GAS CFI directives ("control frame information") into x86-64 asm input
  1148. +# Insert GAS CFI directives ("control frame information") into x86-64 asm input.
  1149. BEGIN {
  1150. - # don't put CFI data in the .eh_frame ELF section (which we don't keep)
  1151. + # Don't put CFI data in the .eh_frame ELF section (which we don't keep).
  1152. print ".cfi_sections .debug_frame"
  1153. - # only emit CFI directives inside a function
  1154. - in_function = 0
  1155. + # Only emit CFI directives inside a function.
  1156. + in_function = ""
  1157. - # emit .loc directives with line numbers from original source
  1158. + # Emit .loc directives with line numbers from original source.
  1159. printf ".file 1 \"%s\"\n", ARGV[1]
  1160. line_number = 0
  1161. - # used to detect "call label; label:" trick
  1162. - called = ""
  1163. + re_label = "([0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)"
  1164. +
  1165. + for (i = 1; i <= 4; ++i) {
  1166. + letter = substr("abcd", i, 1)
  1167. + regname[letter "l"] = regname[letter "h"] = regname[letter "x"] = \
  1168. + regname["e" letter "x"] = regname["r" letter "x"] = "r" letter "x"
  1169. + }
  1170. +
  1171. + regname["si"] = regname["esi"] = regname["rsi"] = "rsi"
  1172. + regname["di"] = regname["edi"] = regname["rdi"] = "rdi"
  1173. + regname["bp"] = regname["ebp"] = regname["rbp"] = "rbp"
  1174. + regname["sp"] = regname["esp"] = regname["rsp"] = "rsp"
  1175. +
  1176. + for (i = 8; i <= 15; ++i)
  1177. + regname["r" i] = "r" i
  1178. }
  1179. +# For instructions with 2 operands, get 1st operand (assuming it is constant).
  1180. function get_const1() {
  1181. - # for instructions with 2 operands, get 1st operand (assuming it is constant)
  1182. - match($0, /-?(0x[0-9a-fA-F]+|[0-9]+),/)
  1183. - return parse_const(substr($0, RSTART, RLENGTH-1))
  1184. + match($2, /^\$[+-]?(0x[0-9a-fA-F]+|[0-9]+),/)
  1185. + return parse_const(substr($2, 2, RLENGTH - 2))
  1186. }
  1187. -function canonicalize_reg(register) {
  1188. - if (match(register, /^r/))
  1189. - return register
  1190. - else if (match(register, /^e/))
  1191. - return "r" substr(register, 2, length(register)-1)
  1192. - else if (match(register, /[hl]$/)) # AH, AL, BH, BL, etc
  1193. - return "r" substr(register, 1, 1) "x"
  1194. - else # AX, BX, CX, etc
  1195. - return "r" register
  1196. -}
  1197. +# Only use if you already know there is 1 and only 1 register.
  1198. function get_reg() {
  1199. - # only use if you already know there is 1 and only 1 register
  1200. - match($0, /%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)/)
  1201. - return canonicalize_reg(substr($0, RSTART+1, RLENGTH-1))
  1202. + return regname[substr($2, 2, length($2) - 1)]
  1203. }
  1204. +
  1205. +# For instructions with 2 operands, get 1st operand (assuming it is register).
  1206. function get_reg1() {
  1207. - # for instructions with 2 operands, get 1st operand (assuming it is register)
  1208. - match($0, /%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15),/)
  1209. - return canonicalize_reg(substr($0, RSTART+1, RLENGTH-2))
  1210. + match($2, /^%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5]),/)
  1211. + return regname[substr($2, 2, RLENGTH - 2)]
  1212. }
  1213. +
  1214. +# For instructions with 2 operands, get 2nd operand (assuming it is register).
  1215. function get_reg2() {
  1216. - # for instructions with 2 operands, get 2nd operand (assuming it is register)
  1217. - match($0, /,%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)/)
  1218. - return canonicalize_reg(substr($0, RSTART+2, RLENGTH-2))
  1219. + match($2, /,%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/)
  1220. + return regname[substr($2, RSTART + 2, RLENGTH - 2)]
  1221. }
  1222. +# Helper to adjust CFA offset.
  1223. function adjust_sp_offset(delta) {
  1224. - if (in_function)
  1225. + if (in_function) {
  1226. printf ".cfi_adjust_cfa_offset %d\n", delta
  1227. + cfa_offset[in_function] += delta
  1228. + }
  1229. +}
  1230. +
  1231. +# Helper to process jumps to labels by saving the current CFA offset.
  1232. +function jump_to_label(label) {
  1233. + if (in_function) {
  1234. + if (match(label, /^[0-9]+f$/)) # "forward" label
  1235. + cfa_offset[substr(label, 1, RLENGTH - 1)] = cfa_offset[in_function]
  1236. + else if (match(label, /^[a-zA-Z_][a-zA-Z0-9_]*$/))
  1237. + cfa_offset[label] = cfa_offset[in_function]
  1238. + }
  1239. }
  1240. {
  1241. - line_number = line_number + 1
  1242. + ++line_number
  1243. - # clean the input up before doing anything else
  1244. - # delete comments
  1245. - gsub(/(#|\/\/).*/, "")
  1246. + # Clean the input up before doing anything else.
  1247. + # Delete comments.
  1248. + gsub(/#.*|\/\*.*\*\//, "")
  1249. - # canonicalize whitespace
  1250. - gsub(/[ \t]+/, " ") # mawk doesn't understand \s
  1251. + # Canonicalize whitespace.
  1252. + gsub(/[ \t]+/, " ") # Mawk doesn't understand \s.
  1253. gsub(/ *, */, ",")
  1254. - gsub(/ *: */, ": ")
  1255. + if (match(":", $1))
  1256. + sub(/ *: */, ": ")
  1257. gsub(/ $/, "")
  1258. gsub(/^ /, "")
  1259. }
  1260. -# check for assembler directives which we care about
  1261. +# Check for assembler directives which we care about.
  1262. /^\.(section|data|text)/ {
  1263. - # a .cfi_startproc/.cfi_endproc pair should be within the same section
  1264. - # otherwise, clang will choke when generating ELF output
  1265. + # A .cfi_startproc/.cfi_endproc pair should be within the same section.
  1266. + # Otherwise, clang will choke when generating ELF output.
  1267. if (in_function) {
  1268. print ".cfi_endproc"
  1269. - in_function = 0
  1270. + in_function = ""
  1271. }
  1272. }
  1273. -/^\.type [a-zA-Z0-9_]+,@function/ {
  1274. - functions[substr($2, 1, length($2)-10)] = 1
  1275. +
  1276. +# Record each function name.
  1277. +/^\.type [a-zA-Z0-9_]+( STT_FUNCTION|,[#@%"]function)/ {
  1278. + functions[substr($2, 1, length($2) - 10)] = 1
  1279. }
  1280. -# not interested in assembler directives beyond this, just pass them through
  1281. +# Not interested in assembler directives beyond this, just pass them through.
  1282. /^\./ {
  1283. print
  1284. next
  1285. }
  1286. -/^[a-zA-Z0-9_]+:/ {
  1287. - label = substr($1, 1, length($1)-1) # drop trailing :
  1288. -
  1289. - if (called == label) {
  1290. - # note adjustment of stack pointer from "call label; label:"
  1291. - adjust_sp_offset(8)
  1292. - }
  1293. +$0 ~ "^" re_label ":" {
  1294. + # Parse each leading label.
  1295. + while (match($0, "^" re_label ":")) {
  1296. +
  1297. + # Extract label name.
  1298. + label = substr($1, 1, RLENGTH - 1)
  1299. +
  1300. + # Remove label from current line.
  1301. + sub("^" re_label ": ?", "")
  1302. +
  1303. + if (label in functions) {
  1304. + if (in_function) {
  1305. + print ".cfi_endproc"
  1306. + for (l in called)
  1307. + delete called[l]
  1308. + }
  1309. +
  1310. + in_function = label
  1311. + print ".cfi_startproc"
  1312. +
  1313. + for (reg in saved)
  1314. + delete saved[reg]
  1315. + for (reg in dirty)
  1316. + delete dirty[reg]
  1317. + }
  1318. - if (functions[label]) {
  1319. - if (in_function)
  1320. - print ".cfi_endproc"
  1321. + printf "%s:\n", label
  1322. - in_function = 1
  1323. - print ".cfi_startproc"
  1324. + # If this label has been jumped to, define the CFA offset to its
  1325. + # value at the location of the jump.
  1326. + if (!(label in functions) && in_function && label in cfa_offset) {
  1327. + if (cfa_offset[in_function] != cfa_offset[label]) {
  1328. + printf ".cfi_def_cfa_offset %d\n", cfa_offset[label]
  1329. + cfa_offset[in_function] = cfa_offset[label]
  1330. + }
  1331. + delete cfa_offset[label]
  1332. + }
  1333. - for (register in saved)
  1334. - delete saved[register]
  1335. - for (register in dirty)
  1336. - delete dirty[register]
  1337. + # If this label has been called, adjust CFA offset.
  1338. + if (label in called && !(label in functions)) {
  1339. + adjust_sp_offset(8);
  1340. + delete called[label]
  1341. + }
  1342. }
  1343. -
  1344. - # an instruction may follow on the same line, so continue processing
  1345. + # An instruction may follow on the same line, so continue processing.
  1346. }
  1347. +# Skip empty line.
  1348. /^$/ { next }
  1349. +# Issue source line number.
  1350. {
  1351. - called = ""
  1352. printf ".loc 1 %d\n", line_number
  1353. print
  1354. }
  1355. # KEEPING UP WITH THE STACK POINTER
  1356. -# %rsp should only be adjusted by pushing/popping or adding/subtracting constants
  1357. +# %rsp should only be adjusted by pushing/popping or adding/subtracting
  1358. +# constants.
  1359. #
  1360. -/pushl?/ {
  1361. +/^push[wq]? / {
  1362. + if ($1 == "pushw" || match($2, /^%([abcd]x|di|si|bp|sp)$/))
  1363. + adjust_sp_offset(2)
  1364. + else
  1365. + adjust_sp_offset(8)
  1366. +}
  1367. +
  1368. +/^pop[wq]? / {
  1369. + if ($1 == "popw" || match($2, /^%([abcd]x|di|si|bp|sp)$/))
  1370. + adjust_sp_offset(-2)
  1371. + else
  1372. + adjust_sp_offset(-8)
  1373. +}
  1374. +
  1375. +/^pushfq?$/ {
  1376. adjust_sp_offset(8)
  1377. }
  1378. -/popl?/ {
  1379. +
  1380. +/^pushfw$/ {
  1381. + adjust_sp_offset(2)
  1382. +}
  1383. +
  1384. +/^popfq?$/ {
  1385. adjust_sp_offset(-8)
  1386. }
  1387. -/addl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%rsp/ { adjust_sp_offset(-get_const1()) }
  1388. -/subl? \$-?(0x[0-9a-fA-F]+|[0-9]+),%rsp/ { adjust_sp_offset(get_const1()) }
  1389. -/call/ {
  1390. - if (match($0, /call [0-9]+f/)) # "forward" label
  1391. - called = substr($0, RSTART+5, RLENGTH-6)
  1392. - else if (match($0, /call [0-9a-zA-Z_]+/))
  1393. - called = substr($0, RSTART+5, RLENGTH-5)
  1394. +/^popfw$/ {
  1395. + adjust_sp_offset(-2)
  1396. +}
  1397. +
  1398. +/^addq? \$[+-]?(0x[0-9a-fA-F]+|[0-9]+),%rsp$/ {
  1399. + adjust_sp_offset(-get_const1())
  1400. +}
  1401. +/^subq? \$[+-]?(0x[0-9a-fA-F]+|[0-9]+),%rsp$/ {
  1402. + adjust_sp_offset(get_const1())
  1403. +}
  1404. +
  1405. +/^call / {
  1406. + if (match($2, /^[0-9]+f$/)) # "forward" label
  1407. + called[substr($2, 1, RLENGTH - 1)] = 1
  1408. + else if (match($2, /^[a-zA-Z_][0-9a-zA-Z_]*$/))
  1409. + called[$2] = 1
  1410. +}
  1411. +
  1412. +/^j/ {
  1413. + jump_to_label($2)
  1414. }
  1415. # TRACKING REGISTER VALUES FROM THE PREVIOUS STACK FRAME
  1416. #
  1417. -/pushl? %r(ax|bx|cx|dx|si|di|bp|8|9|10|11|12|13|14|15)/ { # don't match "push (%reg)"
  1418. - # if a register is being pushed, and its value has not changed since the
  1419. +/^pushq? %r([abcd]x|si|di|bp|[89]|1[0-5])$/ {
  1420. + # Don't match "push (%reg)".
  1421. + # If a register is being pushed, and its value has not changed since the
  1422. # beginning of this function, the pushed value can be used when printing
  1423. - # local variables at the next level up the stack
  1424. - # emit '.cfi_rel_offset' for that
  1425. + # local variables at the next level up the stack.
  1426. + # Emit '.cfi_rel_offset' for that.
  1427. if (in_function) {
  1428. - register = get_reg()
  1429. - if (!saved[register] && !dirty[register]) {
  1430. - printf ".cfi_rel_offset %s,0\n", register
  1431. - saved[register] = 1
  1432. + reg = get_reg()
  1433. + if (!(reg in saved) && !(reg in dirty)) {
  1434. + printf ".cfi_rel_offset %s,0\n", reg
  1435. + saved[reg] = 1
  1436. }
  1437. }
  1438. }
  1439. -/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\)/ {
  1440. +/^movq? %r([abcd]x|si|di|bp|[89]|1[0-5]),[+-]?(0x[0-9a-fA-F]+|[0-9]+)?\(%rsp\)$/ {
  1441. if (in_function) {
  1442. - register = get_reg()
  1443. - if (match($0, /-?(0x[0-9a-fA-F]+|[0-9]+)\(%rsp\)/)) {
  1444. - offset = parse_const(substr($0, RSTART, RLENGTH-6))
  1445. + if (match($2, /,[+-]?(0x[0-9a-fA-F]+|[0-9]+)\(%rsp\)$/)) {
  1446. + offset = parse_const(substr($2, RSTART, RLENGTH - 7))
  1447. } else {
  1448. offset = 0
  1449. }
  1450. - if (!saved[register] && !dirty[register]) {
  1451. - printf ".cfi_rel_offset %s,%d\n", register, offset
  1452. - saved[register] = 1
  1453. + reg = get_reg1()
  1454. + if (!(reg in saved) && !(reg in dirty)) {
  1455. + printf ".cfi_rel_offset %s,%d\n", reg, offset
  1456. + saved[reg] = 1
  1457. }
  1458. }
  1459. }
  1460. @@ -171,24 +249,41 @@ function adjust_sp_offset(delta) {
  1461. # IF REGISTER VALUES ARE UNCEREMONIOUSLY TRASHED
  1462. # ...then we want to know about it.
  1463. #
  1464. -function trashed(register) {
  1465. - if (in_function && !saved[register] && !dirty[register]) {
  1466. - printf ".cfi_undefined %s\n", register
  1467. - }
  1468. - dirty[register] = 1
  1469. -}
  1470. -# this does NOT exhaustively check for all possible instructions which could
  1471. -# overwrite a register value inherited from the caller (just the common ones)
  1472. -/mov.*,%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)$/ { trashed(get_reg2()) }
  1473. -/(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)$/ {
  1474. +function trashed(reg) {
  1475. + if (in_function && !(reg in saved) && !(reg in dirty)) {
  1476. + printf ".cfi_undefined %s\n", reg
  1477. + }
  1478. + dirty[reg] = 1
  1479. +}
  1480. +# This does NOT exhaustively check for all possible instructions which could
  1481. +# overwrite a register value inherited from the caller (just the common ones).
  1482. +/^mov.*,%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/ {
  1483. + trashed(get_reg2())
  1484. +}
  1485. +/^(add|sub|and|x?or|lea|s[ah][lr])[bwlq]? [^,]+,%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/ {
  1486. trashed(get_reg2())
  1487. }
  1488. -/^i?mul [^,]*$/ { trashed("rax"); trashed("rdx") }
  1489. -/^i?mul.*,%[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)$/ { trashed(get_reg2()) }
  1490. -/^i?div/ { trashed("rax"); trashed("rdx") }
  1491. +/^i?mul[bwlq]? [^,]+$/ {
  1492. + trashed("rax")
  1493. + trashed("rdx")
  1494. +}
  1495. +/^i?mul[bwlq] [^,]+,%[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/ {
  1496. + trashed(get_reg2())
  1497. +}
  1498. +/^i?div[bwlq]? / {
  1499. + trashed("rax")
  1500. + trashed("rdx")
  1501. +}
  1502. -/(dec|inc|not|neg|pop) %[er]?([abcd][xlh]|si|di|bp|8|9|10|11|12|13|14|15)/ { trashed(get_reg()) }
  1503. -/cpuid/ { trashed("rax"); trashed("rbx"); trashed("rcx"); trashed("rdx") }
  1504. +/^(dec|inc|not|neg|pop)[bwlq]? %[er]?([abcd][xlh]|si|di|bp|[89]|1[0-5])$/ {
  1505. + trashed(get_reg())
  1506. +}
  1507. +/^cpuid$/ {
  1508. + trashed("rax")
  1509. + trashed("rbx")
  1510. + trashed("rcx")
  1511. + trashed("rdx")
  1512. +}
  1513. END {
  1514. if (in_function)