From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id kYBdDwwLu2nyRy8AWB0awg (envelope-from ) for ; Wed, 18 Mar 2026 16:29:00 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=polymtl.ca header.i=@polymtl.ca header.a=rsa-sha256 header.s=oct2025 header.b=GRi02JwL; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 399511E08C; Wed, 18 Mar 2026 16:29:00 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id E99AE1E08C for ; Wed, 18 Mar 2026 16:28:58 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id A24F14B7A1F7 for ; Wed, 18 Mar 2026 20:28:57 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A24F14B7A1F7 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=polymtl.ca header.i=@polymtl.ca header.a=rsa-sha256 header.s=oct2025 header.b=GRi02JwL Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id 871934BBCDD2 for ; Wed, 18 Mar 2026 20:28:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 871934BBCDD2 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=polymtl.ca Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=polymtl.ca ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 871934BBCDD2 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=132.207.4.11 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773865696; cv=none; b=BO7LJteTQIfCSjJZFRphfOECl2OpAk54tQpeBX+6iy2jEPPrWz3GDZ452i2K9Q23Mo2qW5w4JbRqiFPZN2/6R7lUJTgqwPxbs2W64RB9gU4Q44mCr7MeKksSx7hg0OeShK97MSAnSYKg9xtDdrOfii0aZk/ukWqYtNvXq3J1peI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773865696; c=relaxed/simple; bh=RJ2LU2+m/nniMDrBalFy9pEG3fNMXko8iOvjgvheEcQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=kavXJ/SqB0/vreqplc+n5Z+zNT/bVSjo7wNxylWoxkM+nsZZcp1oAbKFUYWs5cmzsqFRCtAtmQjCKn7SOgCW+V5p6PxzMgikFqVlUF7F4J+7akufIiOsnKvkfKo+XXQM7Bakq2KNNvP7NPF9cXig4IBTp3JX9OFnZF1qfpp174s= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 871934BBCDD2 Received: from simark.ca (simark.ca [158.69.221.121]) (authenticated bits=0) by smtp.polymtl.ca (8.14.7/8.14.7) with ESMTP id 62IKS4P6134542 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Mar 2026 16:28:08 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62IKS4P6134542 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773865689; bh=vspUa/6xl0iHb6Qctpna3fulSN6fRxbpLp4TKqW+j90=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=GRi02JwLFDd7y54kQyXF+0XKX72XQvNTabXYw3Tz6bmRu0UO6Enn1rtUyK4PKa1PF 0ii+RXQENMXLq41CQZELw2qvu3NKo45h9/lvEgorpdmt7n9F1Sx76gOTO+P9P8VUsq pgh8n6igHZpk+8mK82GLMN/g6S9I4/hh25RNx/dNVO197MUIM5XkG/8hZneJ+QBzE6 gxEuJ2AsKcJqnaARJsz+8wca+VOEmG38+az9+/qi7V+d/a0RLg7A+8t1nE+Y+1NGi7 g0qz10QaniRR9eBewShyqLq86ePrht0bXrS/bBrDb6kM23LxGlZQejBeKYY2SOePGA lxe3L1G6UmYuA== Received: by simark.ca (Postfix) id A3B351E08C; Wed, 18 Mar 2026 16:28:02 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH v2 1/2] gdb/testsuite: add .debug_frame support in DWARF assembler Date: Wed, 18 Mar 2026 16:27:22 -0400 Message-ID: <20260318202801.2030268-1-simon.marchi@polymtl.ca> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260316172239.349677-2-simon.marchi@efficios.com> References: <20260316172239.349677-2-simon.marchi@efficios.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Wed, 18 Mar 2026 20:28:04 +0000 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: Simon Marchi Add support to the DWARF assembler for generating .debug_frame sections. My initial use case is to reproduce a crash happening when encountering an empty FDE, but I suppose that other use cases will pop up in the future. - Generate procs for the `DW_CFA_*` constants, similar to how the DW_OP_* constants are handled. These `DW_CFA_*` procs are expected to be used in the CIE and FDE bodies, described below. - Add handlers for `DW_CFA_*` operations that take arguments. I tried to cover everything that is in DWARF 5. - Add the `frame` proc, used to generate one .debug_frame section. - Add the `_frame_CIE` proc (available as `CIE` in the context of the frame proc), used to generate one Common Information Entry. - Add the `_frame_FDE` proc (available as `FDE` in the context of the frame proc), used to generate one Frame Description Entry. Due to the nature of the .debug_frame contents (it describes how specific machine registers get saved), I expect that most of the tests written using this will be arch-specific. But I think it will still be useful, as it will let us craft .debug_frame sections to look exactly how we want. I included a test (gdb.dwarf2/debug-frame.exp), which is more like a proof that we can build something useful using this, and can serve as an example for whoever wants to write a test case using this in the future. Change-Id: I048568ded53883abf52d70139e5cd3e7b4ac3841 --- gdb/testsuite/gdb.dwarf2/debug-frame.S | 101 ++++++ gdb/testsuite/gdb.dwarf2/debug-frame.exp | 130 ++++++++ gdb/testsuite/lib/dwarf.exp | 394 ++++++++++++++++++++++- 3 files changed, 624 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.dwarf2/debug-frame.S create mode 100644 gdb/testsuite/gdb.dwarf2/debug-frame.exp diff --git a/gdb/testsuite/gdb.dwarf2/debug-frame.S b/gdb/testsuite/gdb.dwarf2/debug-frame.S new file mode 100644 index 000000000000..231e2dc23704 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/debug-frame.S @@ -0,0 +1,101 @@ +/* Copyright 2026 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Hand-written x86-64 assembly with no .cfi directives. The .debug_frame + section is supplied by the companion -dw.S file, generated by the DWARF + assembler. + + The call chain: main -> caller -> callee. + + caller sets some known register values, and callee saves those registers in + different way. */ + + .text + +/* main */ + .globl main + .type main, @function +main: + pushq %rbp + .globl main_after_push_rbp +main_after_push_rbp: + movq %rsp, %rbp + .globl main_after_set_rbp +main_after_set_rbp: + call caller + xorl %eax, %eax + popq %rbp + ret + .size main, . - main + .globl main_end +main_end: + .globl main_len + .set main_len, main_end - main + +/* caller */ + .globl caller + .type caller, @function +caller: + pushq %rbp + .globl caller_after_push_rbp +caller_after_push_rbp: + movq %rsp, %rbp + .globl caller_after_set_rbp +caller_after_set_rbp: + movq $0x11223344, %r12 + movq $0x55667788, %r13 + .globl caller_call_callee +caller_call_callee: + call callee + popq %rbp + ret + .size caller, . - caller + .globl caller_end +caller_end: + .globl caller_len + .set caller_len, caller_end - caller + +/* callee */ + .globl callee + .type callee, @function +callee: + pushq %rbp + .globl callee_after_push_rbp +callee_after_push_rbp: + movq %rsp, %rbp + .globl callee_after_set_rbp +callee_after_set_rbp: + /* Save r12 in the stack, then clobber it. */ + pushq %r12 + xorq %r12, %r12 + /* Save r13 in rax, then clobber it. */ + movq %r13, %rax + xorq %r13, %r13 + /* Clobber r14. This one is described with a DWARF expression. */ + xorq %r14, %r14 + .globl callee_body +callee_body: + nop + movq %rax, %r13 + popq %r12 + popq %rbp + ret + .size callee, . - callee + .globl callee_end +callee_end: + .globl callee_len + .set callee_len, callee_end - callee + + .section .note.GNU-stack,"",@progbits diff --git a/gdb/testsuite/gdb.dwarf2/debug-frame.exp b/gdb/testsuite/gdb.dwarf2/debug-frame.exp new file mode 100644 index 000000000000..ddadab7566ef --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/debug-frame.exp @@ -0,0 +1,130 @@ +# Copyright 2026 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test that GDB can unwind using a .debug_frame section generated by +# the DWARF assembler. +# +# This test is amd64-specific, but could be ported to other +# architectures if needed. + +load_lib dwarf.exp + +require dwarf2_support is_x86_64_m64_target + +standard_testfile .S -dw.S + +# AMD64 DWARF register numbers. +set rax 0 +set rbp 6 +set rsp 7 +set r12 12 +set r13 13 +set r14 14 +set rip 16 + +foreach_with_prefix is_64 { false true } { + set asm_file [standard_output_file ${testfile}-${is_64}-dw.S] + + Dwarf::assemble $asm_file { + frame { + declare_labels cie_label + + cie_label: CIE { + return_address_register $::rip + data_alignment_factor -8 + is_64 $::is_64 + } { + DW_CFA_def_cfa $::rsp 8 + DW_CFA_offset $::rip 1 + } + + # FDE for main + FDE $cie_label main main_len { + is_64 $::is_64 + } { + DW_CFA_set_loc main_after_push_rbp + DW_CFA_def_cfa_offset 16 + DW_CFA_offset $::rbp 2 + DW_CFA_set_loc main_after_set_rbp + DW_CFA_def_cfa_register $::rbp + } + + # FDE for caller + FDE $cie_label caller caller_len { + is_64 $::is_64 + } { + DW_CFA_set_loc caller_after_push_rbp + DW_CFA_def_cfa_offset 16 + DW_CFA_offset $::rbp 2 + DW_CFA_set_loc caller_after_set_rbp + DW_CFA_def_cfa_register $::rbp + } + + # FDE for callee + FDE $cie_label callee callee_len { + is_64 $::is_64 + } { + DW_CFA_set_loc callee_after_push_rbp + DW_CFA_def_cfa_offset 16 + DW_CFA_offset $::rbp 2 + DW_CFA_set_loc callee_after_set_rbp + DW_CFA_def_cfa_register $::rbp + + DW_CFA_set_loc callee_body + DW_CFA_offset $::r12 3 + DW_CFA_register $::r13 $::rax + + # r14's value is computed by an arbitrary expression. + DW_CFA_val_expression $::r14 { + DW_OP_constu 0x99aabbcc + } + } + } + } + + if { [prepare_for_testing "failed to prepare" ${testfile}-${is_64} \ + [list $srcfile $asm_file] {nodebug}] } { + continue + } + + # Stop in caller before the call, to capture rbp. + if { ![runto caller_call_callee] } { + continue + } + + set caller_rbp [get_hexadecimal_valueof "\$rbp" "UNKNOWN"] + + # Stop inside callee. + gdb_breakpoint callee_body + gdb_continue_to_breakpoint "callee_body" + + # Verify backtrace shows the full call chain. + gdb_test "bt" "#0.*callee.*\r\n#1.*caller.*\r\n#2.*main.*" + + # Select caller's frame and check saved registers. + gdb_test "frame 1" "#1.*caller.*" + + # r12 was saved on the stack by callee. + gdb_test "p/x \$r12" "= 0x11223344" + + # r13 was saved in rax by callee. + gdb_test "p/x \$r13" "= 0x55667788" + + # r14's value is computed by a DWARF expression. + gdb_test "p/x \$r14" "= 0x99aabbcc" + + # rbp should match what caller had. + gdb_test "p/x \$rbp" "= ${caller_rbp}" +} diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index 9fa5c4e297da..e545019e065b 100644 --- a/gdb/testsuite/lib/dwarf.exp +++ b/gdb/testsuite/lib/dwarf.exp @@ -602,6 +602,10 @@ namespace eval Dwarf { variable _loc_addr_size variable _loc_offset_size + # Variables used when generating a .debug_frame section. + variable _frame_addr_size + variable _frame_offset_size + proc _process_one_constant {name value} { variable _constants variable _FORM @@ -618,7 +622,6 @@ namespace eval Dwarf { } # We only try to shorten some very common things. - # FIXME: CFA? switch -exact -- $prefix { TAG { # Create two procedures for the tag. These call @@ -667,6 +670,57 @@ namespace eval Dwarf { } $name $name $handler] } + CFA { + # Create procs for DW_CFA_* instructions, used in + # .debug_frame CIE/FDE bodies. + + # DW_CFA_advance_loc, DW_CFA_offset and + # DW_CFA_restore encode the operand in the low 6 + # bits of the opcode byte. They need special + # handling. + switch -exact -- $name { + DW_CFA_advance_loc { + proc DW_CFA_advance_loc {delta} { + _op .byte \ + "$Dwarf::_constants(DW_CFA_advance_loc) + $delta" \ + DW_CFA_advance_loc + } + } + + DW_CFA_offset { + proc DW_CFA_offset {register offset} { + _op .byte \ + "$Dwarf::_constants(DW_CFA_offset) + $register" \ + DW_CFA_offset + _op .uleb128 $offset "offset" + } + } + + DW_CFA_restore { + proc DW_CFA_restore {register} { + _op .byte \ + "$Dwarf::_constants(DW_CFA_restore) + $register" \ + DW_CFA_restore + } + } + + default { + # Standard CFA instruction: emit opcode + # byte then delegate to handler. + set handler _handle_default_CFA + if {[llength [info procs _handle_$name]] > 0} { + set handler _handle_$name + } + + # tclint-disable-next-line command-args + proc $name {args} [format { + _op .byte $Dwarf::_constants(%s) %s + %s {*}$args + } $name $name $handler] + } + } + } + default { return } @@ -1456,6 +1510,127 @@ namespace eval Dwarf { # error. } + # Helper to emit a DWARF expression block (ULEB128 length followed + # by the expression bytes) inside a .debug_frame CIE or FDE body. + # BODY is a Tcl code containing DW_OP_* calls. + proc _emit_cfa_expression {body} { + set start [new_label "cfa_expr_start"] + set end [new_label "cfa_expr_end"] + _op .uleb128 "$end - $start" "expression length" + define_label $start + + # Pass 5 as the DWARF version, since we need to pass something, but it + # doesn't matter. The DWARF version is checked only for DW_OP_* ops + # that don't make sense in CFI. + _location $body 5 $Dwarf::_frame_addr_size $Dwarf::_frame_offset_size + define_label $end + } + + # + # Handlers for DW_CFA_* instructions. + # + # A handler is only needed if the instruction requires operands. + # Generic code handles emitting the opcode byte itself, so a + # handler should not do this. + # + # Handlers are found by name when processing the .def file. If a + # handler isn't found, the default (_handle_default_CFA) is used. + # + + proc _handle_default_CFA {} { + # Do nothing; if arguments are passed, Tcl will cause an + # error. + } + + proc _handle_DW_CFA_set_loc {address} { + _op .${Dwarf::_frame_addr_size}byte $address "address" + } + + proc _handle_DW_CFA_advance_loc1 {delta} { + _op .byte $delta "delta" + } + + proc _handle_DW_CFA_advance_loc2 {delta} { + _op .2byte $delta "delta" + } + + proc _handle_DW_CFA_advance_loc4 {delta} { + _op .4byte $delta "delta" + } + + proc _handle_DW_CFA_offset_extended {register offset} { + _op .uleb128 $register "register" + _op .uleb128 $offset "offset" + } + + proc _handle_DW_CFA_restore_extended {register} { + _op .uleb128 $register "register" + } + + proc _handle_DW_CFA_undefined {register} { + _op .uleb128 $register "register" + } + + proc _handle_DW_CFA_same_value {register} { + _op .uleb128 $register "register" + } + + proc _handle_DW_CFA_register {register1 register2} { + _op .uleb128 $register1 "register" + _op .uleb128 $register2 "register" + } + + proc _handle_DW_CFA_def_cfa {register offset} { + _op .uleb128 $register "register" + _op .uleb128 $offset "offset" + } + + proc _handle_DW_CFA_def_cfa_register {register} { + _op .uleb128 $register "register" + } + + proc _handle_DW_CFA_def_cfa_offset {offset} { + _op .uleb128 $offset "offset" + } + + proc _handle_DW_CFA_def_cfa_expression {body} { + _emit_cfa_expression $body + } + + proc _handle_DW_CFA_expression {register body} { + _op .uleb128 $register "register" + _emit_cfa_expression $body + } + + proc _handle_DW_CFA_offset_extended_sf {register offset} { + _op .uleb128 $register "register" + _op .sleb128 $offset "offset" + } + + proc _handle_DW_CFA_def_cfa_sf {register offset} { + _op .uleb128 $register "register" + _op .sleb128 $offset "offset" + } + + proc _handle_DW_CFA_def_cfa_offset_sf {offset} { + _op .sleb128 $offset "offset" + } + + proc _handle_DW_CFA_val_offset {register offset} { + _op .uleb128 $register "register" + _op .uleb128 $offset "offset" + } + + proc _handle_DW_CFA_val_offset_sf {register offset} { + _op .uleb128 $register "register" + _op .sleb128 $offset "offset" + } + + proc _handle_DW_CFA_val_expression {register body} { + _op .uleb128 $register "register" + _emit_cfa_expression $body + } + # This is a miniature assembler for location expressions. It is # suitable for use in the attributes to a DIE. # @@ -3554,6 +3729,223 @@ namespace eval Dwarf { debug_str_offsets_end: } + # Emit a DWARF .debug_frame section. + # + # BODY is Tcl code that emits the CIEs and FDEs which make up the + # section. It is evaluated in the caller's context. + # + # Within BODY, the following commands are available: + # + # CIE options body + # -- emit a Common Information Entry. See _frame_CIE for details. + # + # FDE cie_label initial_location address_range body + # -- emit a Frame Description Entry. See _frame_FDE for details. + proc frame { body } { + _section .debug_frame + + with_override Dwarf::CIE Dwarf::_frame_CIE { + with_override Dwarf::FDE Dwarf::_frame_FDE { + uplevel $Dwarf::_level $body + } + } + } + + # Available as proc CIE when in the body of proc debug_frame. + # + # OPTIONS is a list of option-name/option-value pairs. Supported + # options are (default values are shown in parentheses): + # + # is_64 (false) + # -- if true, emit a 64-bit CIE. + # + # cie_id (default) + # -- the CIE id value. When "default", uses 0xffffffff for + # 32-bit and 0xffffffffffffffff for 64-bit. Should typically not be + # used unless trying to craft an invalid CIE. + # + # version (4) + # -- the CIE version number. Note that this is version independent + # from the DWARF version. DWARF 4 and 5 both use .debug_frame + # version 4. + # + # augmentation ("") + # -- the augmentation string. + # + # addr_size (default) + # -- the address size in bytes. When "default", use 8 for 64-bit + # targets and 4 for 32-bit targets. + # + # segment_selector_size (0) + # -- the segment selector size in bytes. + # + # code_alignment_factor (1) + # -- the code alignment factor. + # + # data_alignment_factor (1) + # -- the data alignment factor. + # + # return_address_register (0) + # -- the number of the "column" containing the return address. + # + # BODY is Tcl code that emits the CIE's initial instructions using + # DW_CFA_* operations. It is evaluated in the caller's context. + proc _frame_CIE {options body} { + parse_options { + { is_64 false } + { cie_id default } + { version 4 } + { augmentation "" } + { addr_size default } + { segment_selector_size 0 } + { code_alignment_factor 1 } + { data_alignment_factor 1 } + { return_address_register 0 } + } + + if { $is_64 } { + set Dwarf::_frame_offset_size 8 + } else { + set Dwarf::_frame_offset_size 4 + } + + if { $cie_id == "default" } { + if { $is_64 } { + set cie_id 0xffffffffffffffff + } else { + set cie_id 0xffffffff + } + } + + if {$addr_size == "default"} { + if {[is_64_target]} { + set Dwarf::_frame_addr_size 8 + } else { + set Dwarf::_frame_addr_size 4 + } + } else { + set Dwarf::_frame_addr_size $addr_size + } + + declare_labels cie_post_length cie_end + + # Length. + if { $is_64 } { + _op .4byte 0xffffffff "length 1/2" + _op .8byte "$cie_end - $cie_post_length" "length 2/2" + } else { + _op .4byte "$cie_end - $cie_post_length" "length" + } + + define_label $cie_post_length + + # CIE_id + _op .${Dwarf::_frame_offset_size}byte $cie_id "CIE_id" + + # Version. + _op .byte $version "version" + + # Augmentation string. + _op .ascii [_quote $augmentation] "augmentation" + + # Address size. + _op .byte $Dwarf::_frame_addr_size "address_size" + + # Segment selector size. + _op .byte 0 "segment_size" + + # Code alignment factor. + _op .uleb128 $code_alignment_factor "code_alignment_factor" + + # Data alignment factor. + _op .sleb128 $data_alignment_factor "data_alignment_factor" + + # Return address register. + _op .uleb128 $return_address_register "return_address_register" + + # Initial instructions. + uplevel $Dwarf::_level $body + + # Padding up to the address size. Fill with DW_CFA_nop (zeroes). + _op .align $Dwarf::_frame_addr_size "padding" + + define_label $cie_end + } + + # Available as proc FDE when in the body of proc debug_frame. + # + # CIE_LABEL is the label of the CIE this FDE refers to. + # + # INITIAL_LOCATION is the address of the first instruction covered + # by this FDE. + # + # ADDRESS_RANGE is the number of bytes of instructions covered by + # this FDE. + # + # OPTIONS is a list of option-name/option-value pairs. Supported + # options are (default values are shown in parentheses): + # + # is_64 (false) + # -- if true, emit a 64-bit CIE. + # + # addr_size (default) + # -- the address size in bytes. When "default", use 8 for 64-bit + # targets and 4 for 32-bit targets. + # + # BODY is Tcl code that emits the FDE's call frame instructions using + # DW_CFA_* operations. It is evaluated in the caller's context. + proc _frame_FDE { cie_label initial_location address_range options + body } { + parse_options { + { is_64 false } + { addr_size default } + } + + if { $is_64 } { + set Dwarf::_frame_offset_size 8 + } else { + set Dwarf::_frame_offset_size 4 + } + + if {$addr_size == "default"} { + if {[is_64_target]} { + set Dwarf::_frame_addr_size 8 + } else { + set Dwarf::_frame_addr_size 4 + } + } else { + set Dwarf::_frame_addr_size $addr_size + } + + declare_labels fde_post_length fde_end + + # Length. + if { $is_64 } { + _op .4byte 0xffffffff "length 1/2" + _op .8byte "$fde_end - $fde_post_length" "length 2/2" + } else { + _op .4byte "$fde_end - $fde_post_length" "length" + } + define_label $fde_post_length + + # CIE pointer, offset of the CIE into the .debug_frame section. + _op .${Dwarf::_frame_offset_size}byte $cie_label "CIE pointer" + + # Initial location. + _op .${Dwarf::_frame_addr_size}byte $initial_location "initial_location" + + # Address range. + _op .${Dwarf::_frame_addr_size}byte $address_range "address_range" + + # Instructions. + uplevel $Dwarf::_level $body + + # Padding up to the address size. Fill with DW_CFA_nop (zeroes). + _op .align $Dwarf::_frame_addr_size "padding" + + define_label $fde_end + } + # The top-level interface to the DWARF assembler. # OPTIONS is a list with an even number of elements containing # option-name and option-value pairs. base-commit: bd40fc073ae0c30f8ad40623e9f40da1be09b0e5 -- 2.53.0