From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id efnuORj6PWchJDgAWB0awg (envelope-from ) for ; Wed, 20 Nov 2024 10:02:48 -0500 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=fuWj7dxM; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id AD3791E253; Wed, 20 Nov 2024 10:02:48 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-6.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.0 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (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 E80881E1CC for ; Wed, 20 Nov 2024 10:02:46 -0500 (EST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0E905385840F for ; Wed, 20 Nov 2024 15:02:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0E905385840F Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=fuWj7dxM Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 56EDF3858D20 for ; Wed, 20 Nov 2024 15:02:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 56EDF3858D20 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 56EDF3858D20 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1732114920; cv=none; b=QajirC4F5fGdwggBnH6HbzX9yoBJZQABi99Jn586bl1A9mucDCH87qbHTya77+42bEUQ1Z0PecrtO8qylDpRu0DnHW2f43OXsGJT6Y3wjVXolAkPCb1hvVpqRUClf12DTzvYKozMk6Q965eE73O04XxbKpXzBcJIEogMJV+M9XU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1732114920; c=relaxed/simple; bh=37jZSgEMSrlstUjqlt/q2lzbA+gYYE+c661m7C7vhaU=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=VpM/rUC8pEVhG64nUg0LVIqovcrw7gYFpEuf0WYV7vQZWssLSg5HqgCd2Fk0weTCLncgqJNBD7yf10tlxmvg1Xge9eHA+PuEoapkYoPUi5C4DZ1hvLrZoPLEBukA3pm0JMGKlChYe841AxZGPy2WMBv7q2NjKIvLi4IJ49lJxno= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 56EDF3858D20 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1732114920; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=g5hXJpMj05TdBzaue1Hn6s+42d6nsE1QSKVLDqakIq0=; b=fuWj7dxMiG+UeyVAlckI1h73aAPSyHR5AXKp6iaFZTYi7gnzv9F1sCLDDxzP+YshV4T1LW een+9PqZDYF28Y0z4jzA4HhzZR3S9nAcnh7Qn7Lbnqkc6H3MivYVqe3DNv+tbk6vtMlhaV wEQ62OQwAw3bdoGwBwlZA2GeY3g9mNg= Received: from mail-ej1-f71.google.com (mail-ej1-f71.google.com [209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-73-jQXrEJ3TOuyy_wtU_JNXuw-1; Wed, 20 Nov 2024 10:01:58 -0500 X-MC-Unique: jQXrEJ3TOuyy_wtU_JNXuw-1 X-Mimecast-MFC-AGG-ID: jQXrEJ3TOuyy_wtU_JNXuw Received: by mail-ej1-f71.google.com with SMTP id a640c23a62f3a-a9a157d028aso303488266b.2 for ; Wed, 20 Nov 2024 07:01:58 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1732114917; x=1732719717; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=g5hXJpMj05TdBzaue1Hn6s+42d6nsE1QSKVLDqakIq0=; b=iRojzp556b9A/G60Nn2fTxOv3sD6f97D0PjeHlwTx4MZHNt+SkPV673MLUB+D1IcNB zmwAlDBQ29F8CIzWjV0bnyj6x6UePcETLuCi4HbmlvEZs863YBb7DYB7FWucNcdpeDIf yVAPoSKlg3n8vQ3A/ld0B+RJvtHIzSS2bQDNpFLgKdlzaRP0BoweikiZVdGqH/3NZlUD qypJ/MmQjQZXXxT0cYivxTk+NGCHsrRrTBPwJBhf4CpeRZza1LE3RnuSVcxOjqPJu1R0 QiQ/EY4oTIEzozA69C7LNVlAo0qlB4vVTn+qMyHGcLEZXcf5nQ9lItmrD38+5+/P/dha ++0A== X-Gm-Message-State: AOJu0YyMmTi6XGWhxyZzOXdwJwOxU8HIIBSy4671cJyKDwNylsoGPcng 9M2rSMlapSVwLHj4tS3IakDtxjLSBtr7/pTd/4P9uVX077poh0c9Ky0ppywD2+58kcZ3G21Uktd CMbbF63zTli6A0ggfL/fCvXMBWLwraiHbeYbWrWprfa8igwyX341RSo17IDCNP8qQanUVArzLze 7SNgkpY0Xs6vfzxZiGQr7nnKrQWEulkQq8cIqKyk6M00g= X-Received: by 2002:a17:907:31c7:b0:a99:4f40:3e82 with SMTP id a640c23a62f3a-aa4dd547313mr305846166b.7.1732114916572; Wed, 20 Nov 2024 07:01:56 -0800 (PST) X-Google-Smtp-Source: AGHT+IFqqg4bFDqNOojgEWY/RnB6vfBQJFgZSlxKID+USMKgEo2eLtrPzLkAPa/Jp9LanFZmrOpuhA== X-Received: by 2002:a17:907:31c7:b0:a99:4f40:3e82 with SMTP id a640c23a62f3a-aa4dd547313mr305833566b.7.1732114915614; Wed, 20 Nov 2024 07:01:55 -0800 (PST) Received: from localhost ([109.144.27.192]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-aa20e04687asm777236466b.160.2024.11.20.07.01.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Nov 2024 07:01:55 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Bernd Edlinger , Andrew Burgess Subject: [PATCH] gdb: handle DW_AT_entry_pc pointing at an empty sub-range Date: Wed, 20 Nov 2024 15:01:49 +0000 Message-Id: <34cfe440ffd0e53843bfaf92494d29a6951fa9fd.1732114887.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: nXElawpGB4UjlDV8UdWvv3Kw7oYg0TVdEj_sSNDswuc_1732114918 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true 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 The test gdb.cp/step-and-next-inline.exp creates a test binary called step-and-next-inline-no-header. This test includes a function `tree_check` which is inlined 3 times. When testing with some older versions of gcc (I've tried 8.4.0, 9.3.1) we see the following DWARF representing one of the inline instances of tree_check: <2><8d9>: Abbrev Number: 38 (DW_TAG_inlined_subroutine) <8da> DW_AT_abstract_origin: <0x9ee> <8de> DW_AT_entry_pc : 0x401165 <8e6> DW_AT_GNU_entry_view: 0 <8e7> DW_AT_ranges : 0x30 <8eb> DW_AT_call_file : 1 <8ec> DW_AT_call_line : 52 <8ed> DW_AT_call_column : 10 <8ee> DW_AT_sibling : <0x92d> ... <1><9ee>: Abbrev Number: 46 (DW_TAG_subprogram) <9ef> DW_AT_external : 1 <9ef> DW_AT_name : (indirect string, offset: 0xe8): tree_check <9f3> DW_AT_decl_file : 1 <9f4> DW_AT_decl_line : 38 <9f5> DW_AT_decl_column : 1 <9f6> DW_AT_linkage_name: (indirect string, offset: 0x2f2): _Z10tree_checkP4treei <9fa> DW_AT_type : <0x9e8> <9fe> DW_AT_inline : 3 (declared as inline and inlined) <9ff> DW_AT_sibling : <0xa22> ... Contents of the .debug_ranges section: Offset Begin End ... 00000030 0000000000401165 0000000000401165 (start == end) 00000030 0000000000401169 0000000000401173 00000030 0000000000401040 0000000000401045 00000030 ... Notice that one of the sub-ranges of tree-check is empty, this is the line marked 'start == end'. As the end address is the first address after the range, this range cover absolutely no code. But notice too that the DW_AT_entry_pc for the inline instance points at this empty range. Further, notice that despite the ordering of the sub-ranges, the empty range is actually in the middle of the region defined by the lowest address to the highest address. The ordering is not a problem, the DWARF spec doesn't require that ranges be in any particular order. However, this empty range is causing issues with GDB newly acquire DW_AT_entry_pc support. GDB already rejects, and has done for a long time, empty sub-ranges, after all, the DWARF spec is clear that such a range covers no code. The recent DW_AT_entry_pc patch also had GDB reject an entry-pc which was outside of the low/high bounds of a block. But in this case, the entry-pc value is within the bounds of a block, it's just not within any useful sub-range. As a consequence, GDB is storing the entry-pc value, and making use of it, but when GDB stops, and tries to work out which block the inferior is in, it fails to spot that the inferior is within tree_check, and instead reports the function into which tree_check was inlined. I've tested with newer versions of gcc (12.2.0 and 14.2.0) and with these versions gcc is still generating the empty sub-range, but now this empty sub-range is no longer the entry point. Here's the corresponding ranges table from gcc 14.2.0: Contents of the .debug_rnglists section: Table at Offset: 0: Length: 0x56 DWARF version: 5 Address size: 8 Segment size: 0 Offset entries: 0 Offset Begin End ... 00000021 0000000000401165 000000000040116f 0000002b 0000000000401040 (base address) 00000034 0000000000401040 0000000000401040 (start == end) 00000037 0000000000401041 0000000000401046 0000003a ... The DW_AT_entry_pc is 0x401165, but this is not the empty sub-range, as a result, when GDB stops at the entry-pc, GDB will correctly spot that the inferior is in the tree_check function. The fix I propose here is, instead of rejecting entry-pc values that are outside the block's low/high range, instead reject entry-pc values that are not inside any of the block's sub-ranges. Now, on the older versions of GDB, GDB will ignore the prescribed entry-pc, and will instead select a suitable default entry-pc based on either the block's low-pc value, or the first address of the first range. I have extended the gdb.cp/step-and-next-inline.exp test to check this case, but this does depend on the compiler version being used (newer compilers will always pass, even without the fix). So I have also added a DWARF assembler test to cover this case. --- gdb/dwarf2/read.c | 24 ++- gdb/testsuite/gdb.cp/step-and-next-inline.exp | 26 +++ .../gdb.dwarf2/dw2-entry-pc-in-empty-range.c | 57 ++++++ .../dw2-entry-pc-in-empty-range.exp | 186 ++++++++++++++++++ 4 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-entry-pc-in-empty-range.c create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-entry-pc-in-empty-range.exp diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 5f0b0d4e5d6..c178b13d96d 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -11338,6 +11338,28 @@ dwarf2_die_base_address (struct die_info *die, struct block *block, return {}; } +/* Return true if ADDR is within any of the ranges covered by BLOCK. If + there are no sub-ranges then just check against the block's start and + end addresses, otherwise, check each sub-range covered by the block. */ + +static bool +dwarf2_addr_in_block_ranges (CORE_ADDR addr, struct block *block) +{ + if (block->ranges ().size () == 0) + return addr >= block->start () && addr < block->end (); + + /* Check if ADDR is within any of the block's sub-ranges. */ + for (const blockrange &br : block->ranges ()) + { + if (addr >= br.start () && addr < br.end ()) + return true; + } + + /* ADDR is not within any of the block's sub-ranges. */ + return false; +} + + /* Set the entry PC for BLOCK which represents DIE from CU. Relies on the range information (if present) already having been read from DIE and stored into BLOCK. */ @@ -11398,7 +11420,7 @@ dwarf2_record_block_entry_pc (struct die_info *die, struct block *block, To avoid this, ignore entry-pc values that are outside the block's range, GDB will then select a suitable default entry-pc. */ - if (entry_pc >= block->start () && entry_pc < block->end ()) + if (dwarf2_addr_in_block_ranges (entry_pc, block)) block->set_entry_pc (entry_pc); else complaint (_("in %s, DIE %s, DW_AT_entry_pc (%s) outside " diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp index af1719dc53a..e16c2cca82d 100644 --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp @@ -55,6 +55,32 @@ proc do_test { use_header } { set main_location [gdb_get_line_number "Beginning of main" $srcfile] + if {![runto_main]} { + return + } + + gdb_breakpoint tree_check + + # Check that GDB can correctly stop in `tree_check`. On some + # targets. gcc will use DW_AT_ranges to represent the addresses of + # tree_check, and in some cases, will create an empty sub-range + # for some of the tree_check code. To really confuse things, gcc + # will then set the DW_AT_entry_pc to point at the address of the + # empty sub-range. + # + # The result of this is that GDB would stop at the DW_AT_entry_pc, + # but then GDB would fail to realise that this address was inside + # tree_check. + for { set i 1 } { $i < 4 } { incr i } { + gdb_test "continue" \ + [multi_line \ + "Breakpoint $::decimal\\.$i, (?:$::hex in )?tree_check \\(\[^\r\n\]+\\) at \[^\r\n\]+/$hdrfile:$::decimal" \ + "$::decimal\\s+\[^\r\n\]+"] \ + "stop at tree_check, $i" + } + + clean_restart $executable + if ![runto $main_location qualified] { return } diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-pc-in-empty-range.c b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc-in-empty-range.c new file mode 100644 index 00000000000..cf43727f1fb --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc-in-empty-range.c @@ -0,0 +1,57 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 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 . */ + +volatile int global_var = 0; + +void +foo (void) /* foo decl line */ +{ + /* This label is used to find the start of 'foo' when generating the + debug information. Place nothing before it. */ + asm ("foo_label: .globl foo_label"); + ++global_var; + + asm ("foo_0: .globl foo_0"); + ++global_var; /* bar call line */ + + asm ("foo_1: .globl foo_1"); + ++global_var; + + asm ("foo_2: .globl foo_2"); + ++global_var; + + asm ("foo_3: .globl foo_3"); + ++global_var; + + asm ("foo_4: .globl foo_4"); + ++global_var; + + asm ("foo_5: .globl foo_5"); + ++global_var; + + asm ("foo_6: .globl foo_6"); + ++global_var; + + asm ("foo_7: .globl foo_7"); +} + +int +main (void) +{ + asm ("main_label: .globl main_label"); + foo (); +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-pc-in-empty-range.exp b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc-in-empty-range.exp new file mode 100644 index 00000000000..79b1783b2ec --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc-in-empty-range.exp @@ -0,0 +1,186 @@ +# Copyright 2024 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 . + +# Create an inline function which uses DW_AT_ranges and which has a +# DW_AT_entry_pc. +# +# Within the function's ranges, create an empty sub-range, many +# versions of gcc (8.x to at least 14.x) do this, and point the +# DW_AT_entry_pc at this empty sub-range (at last 8.x to 9.x did +# this). +# +# Now place a breakpoint on the inline function and run to the +# breakpoint, check that GDB reports we are inside the inline +# function. +# +# At one point GDB would use the entry-pc value as the breakpoint +# location even though that address is not actually associated with +# the inline function. Now GDB will reject the entry-pc value and +# select a suitable default entry-pc value instead, one which is +# associated with the inline function. + +load_lib dwarf.exp + +require dwarf2_support + +standard_testfile + +# This compiles the source file and starts and stops GDB, so run it +# before calling prepare_for_testing otherwise GDB will have exited. +get_func_info foo + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list ${srcfile}]] } { + return +} + +if ![runto_main] { + return +} + +# Some label addresses, needed to match against the output later. +foreach foo {foo_1 foo_2 foo_3 foo_4 foo_5 foo_6} { + set $foo [get_hexadecimal_valueof "&$foo" "UNKNOWN" \ + "get address for $foo label"] +} + +# Some line numbers needed in the generated DWARF. +set foo_decl_line [gdb_get_line_number "foo decl line"] +set bar_call_line [gdb_get_line_number "bar call line"] + +if [is_ilp32_target] { + set ptr_type "data4" +} else { + set ptr_type "data8" +} + +# Setup the fake DWARF (see comment at top of this file for more +# details). Use DWARF_VERSION (either 4 or 5) to select which type of +# ranges are created. Compile the source and generated DWARF and run +# the test. +# +# The ENTRY_LABEL is the label to use as the entry-pc value. The +# useful choices are 'foo_3', this label is for an empty sub-range, or +# 'foo_4', this label is within the blocks low/high addresses, but is +# not in any sub-range for the block at all. +# +# The 'foo_4' case is not something that has been seen generated by +# any compiler, but it doesn't hurt to test. + +proc run_test { entry_label dwarf_version } { + set dw_testname "${::testfile}-${dwarf_version}" + + set asm_file [standard_output_file "${dw_testname}.S"] + Dwarf::assemble $asm_file { + upvar dwarf_version dwarf_version + upvar entry_label entry_label + + declare_labels lines_table inline_func ranges_label + + cu { version $dwarf_version } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name $::srcfile} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + {low_pc 0 addr} + } { + inline_func: subprogram { + {name bar} + {inline @DW_INL_declared_inlined} + } + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $::foo_decl_line data1} + {decl_column 1 data1} + {low_pc $::foo_start addr} + {high_pc $::foo_len $::ptr_type} + {external 1 flag} + } { + inlined_subroutine { + {abstract_origin %$inline_func} + {call_file 1 data1} + {call_line $::bar_call_line data1} + {entry_pc $entry_label addr} + {ranges ${ranges_label} DW_FORM_sec_offset} + } + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$::srcfile" 1 + } + + if { $dwarf_version == 5 } { + rnglists {} { + table {} { + ranges_label: list_ { + start_end foo_3 foo_3 + start_end foo_1 foo_2 + start_end foo_5 foo_6 + } + } + } + } else { + ranges { } { + ranges_label: sequence { + range foo_3 foo_3 + range foo_1 foo_2 + range foo_5 foo_6 + } + } + } + } + + if {[prepare_for_testing "failed to prepare" "${dw_testname}" \ + [list $::srcfile $asm_file] {nodebug}]} { + return false + } + + if ![runto_main] { + return false + } + + # Place a breakpoint on `bar` and run to the breakpoint. Use + # gdb_test as we want full pattern matching against the stop + # location. + gdb_breakpoint bar + gdb_test "continue" \ + "Breakpoint $::decimal, $::hex in bar \\(\\)" + + # Inspect the block structure of `bar` at this location. We are + # expecting that the empty range (that contained the entry-pc) has + # been removed from the block, and that the entry-pc has its + # default value. + gdb_test "maint info blocks" \ + [multi_line \ + "\\\[\\(block \\*\\) $::hex\\\] $::foo_1\\.\\.$::foo_6" \ + " entry pc: $::foo_1" \ + " inline function: bar" \ + " symbol count: $::decimal" \ + " address ranges:" \ + " $::foo_1\\.\\.$::foo_2" \ + " $::foo_5\\.\\.$::foo_6"] +} + +foreach_with_prefix entry_label { foo_3 foo_4 } { + foreach_with_prefix dwarf_version { 4 5 } { + run_test $entry_label $dwarf_version + } +} base-commit: 82eff6743b77908a502b4cf9030acc93caf69e74 -- 2.25.4