From patchwork Tue Jan 9 14:26:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83639 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7C9113858298 for ; Tue, 9 Jan 2024 14:27:22 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 767533858C74 for ; Tue, 9 Jan 2024 14:26:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 767533858C74 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 767533858C74 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=1704810408; cv=none; b=qCkxmdavcQtged+3Hj8nkK3/Ma+lh314rjlyeQZ4iR1KrYzMJpaNT1laCtnnthn587Y4jaVmqvzFRPHJf4IRK7KvrDO6ufwhaMGki94Vc+o00m2IE+oQxKO9PIZsaWETVSN8A/vy2+TnLcwLWsqgcDUcz4C/iiIZU563sf/VFZM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810408; c=relaxed/simple; bh=8VeJEcs+pOHv9lUHkcfDv6zWGnNtuHQ/JZHEsCeapr0=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=xi6EGDoESEumNI4YlsVZNj1X8dkgjWfY+HxUOxVh/BB4c6LnCALa/FbD4XM76D5WebhF7Q1LwsgGHHVu3hh+sQtBF1nn9HZoW0u6vrmMe1WKvQGeXi+K7ZqdpLrtttftUrIe9NN3TVHDGun4a6NwH/T9W3DcEVyFf2/D6TiZPC4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810406; 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: in-reply-to:in-reply-to:references:references; bh=g38xUe9ECml6Nh56EQ3c6JMDrgfpsQ4fisuckU5t+Js=; b=L0P8GLnKhP+sqntpdI9b4hGU5UFLE5mqj5pnr1e3c0oSVgJd/eIhvjYz1cNSEE1QuvZy7G 7/E+9t2U7Xu6srIzrLCW2lslSCsrQ8anZmiIvt0kJkdPb8lsSuaNUKA4Z4ItaqhvvEaw3i bK8lSaT1p/mmk0zQX36hoWD8b+HuLuc= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-541-cpeaVr-qOWOSIsZNvAasjA-1; Tue, 09 Jan 2024 09:26:44 -0500 X-MC-Unique: cpeaVr-qOWOSIsZNvAasjA-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-40e49c3c124so11291095e9.1 for ; Tue, 09 Jan 2024 06:26:44 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810403; x=1705415203; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=g38xUe9ECml6Nh56EQ3c6JMDrgfpsQ4fisuckU5t+Js=; b=k+eDdMGm0WsvioKlL7tU9P0JPblJW3RWisD2Ac0yboPuYukQzBpJpDJaZwV1X53oKF XlciC0w1A/gcoOWzGT1EpXEI90xrm0fndvhL4j9/CRiePU0LSZ8KBcIf7A9I8efyPfw0 A8O/Ms9b4CytYLxjR6aejY2QkLZVgbaww2CGxuc9mePOBofKRiGes7BFgYgyMbSFC5QU aj9yD+llwUixu1y/s4BcMzb9sIkb0/V0Hxj12e7e7T3jcwwsd0c+fR4GZIYkW/I6muMK tJ5Cb0xW7SwgHRLM9XHLb5kjLa+dUpY4Ipwhl2n/sVpQyrO4HpQzaDvCoK8tNjiQUDx6 O6Iw== X-Gm-Message-State: AOJu0Yzdw7UUQmO8lb2QMxRUxxWwS9J/tJmz7BUWnyw0yKBWAA8LfGrv 8UQVSqHsVvX5WytOGbr3KtYx2DBlID81fXHMMPiCvdKa2vgmu0zkBsYvWgMmE3SKH04rfO7tU+X vzltiCrZNURFBcbd4Dy7iZoOIOdqctoAnhlwYD2QQsIjMaqe/mNEYdP7Y6CqXuE1d2s0Mr2VgKL 3qpgLjsiY/GtmYtw== X-Received: by 2002:a05:600c:3ba9:b0:40e:3663:88b6 with SMTP id n41-20020a05600c3ba900b0040e366388b6mr2818389wms.2.1704810403669; Tue, 09 Jan 2024 06:26:43 -0800 (PST) X-Google-Smtp-Source: AGHT+IGgPer1nbz6ta/jEFgKykLuPa6NPGOCmq4kL+Eb5lcfMEnUPfCPn85IfzZ+KulG2sRkDtS7CA== X-Received: by 2002:a05:600c:3ba9:b0:40e:3663:88b6 with SMTP id n41-20020a05600c3ba900b0040e366388b6mr2818378wms.2.1704810403237; Tue, 09 Jan 2024 06:26:43 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id f13-20020a05600c154d00b0040e3635ca65sm15109838wmg.2.2024.01.09.06.26.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:42 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 01/16] libiberty/buildargv: POSIX behaviour for backslash handling Date: Tue, 9 Jan 2024 14:26:24 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org This is a libiberty patch. I've posted this to the gcc-patches list here: https://inbox.sourceware.org/gcc-patches/24a8d878590403540bc9b579ba58805985a4d2f7.1701881419.git.aburgess@redhat.com/ However, GCC is currently in stage 4 of its release cycle. Based on the timing of previous releases, I'm not expecting this patch to be merged before April. One option is clearly to just wait until GCC hits stage 1, and then try to get this patch merged. Another option would be to create a GDB only fork of buildargv which includes this patch. In April if/when I manage to get this patch merged I would remove out GDB local copy. Of course, there's a risk that this patch isn't accepted into GCC, in which case we might be stuck with a GDB only fork. Either way, I figure the first step is to address any issues that are raised with the rest of this series, this could well take until April anyway, in which case GCC might be back in stage 1. Thanks, Andrew --- GDB makes use of the libiberty function buildargv for splitting the inferior (program being debugged) argument string in some situations. I have recently been working to fix some edge cases issues in this area of GDB, and have tracked done some of the unexpected behaviour to the libiberty function buildargv, and how it handles backslash escapes. For reference, I've been mostly reading: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html The issues that I would like to fix are: 1. Backslashes within single quotes should not be treated as an escape, thus: '\a' should split to \a, retaining the backslash. 2. Backslashes within double quotes should only act as an escape if they are immediately before one of the characters $ (dollar), ` (backtick), " (double quote), ` (backslash), or \n (newline). In all other cases a backslash should not be treated as an escape character. Thus: "\a" should split to \a, but "\$" should split to $. 3. A backslash-newline sequence should be treated as a line continuation, both the backslash and the newline should be removed. I've updated libiberty and also added some tests. All the existing libiberty tests continue to pass, but I'm not sure if there is additional testing that should be done. --- libiberty/argv.c | 8 +++++-- libiberty/testsuite/test-expandargv.c | 34 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/libiberty/argv.c b/libiberty/argv.c index c2823d3e4ba..6bae4ca2ee9 100644 --- a/libiberty/argv.c +++ b/libiberty/argv.c @@ -224,9 +224,13 @@ char **buildargv (const char *input) if (bsquote) { bsquote = 0; - *arg++ = *input; + if (*input != '\n') + *arg++ = *input; } - else if (*input == '\\') + else if (*input == '\\' + && !squote + && (!dquote + || strchr ("$`\"\\\n", *(input + 1)) != NULL)) { bsquote = 1; } diff --git a/libiberty/testsuite/test-expandargv.c b/libiberty/testsuite/test-expandargv.c index 30f2337ef77..b8dcc6a269a 100644 --- a/libiberty/testsuite/test-expandargv.c +++ b/libiberty/testsuite/test-expandargv.c @@ -142,6 +142,40 @@ const char *test_data[] = { "b", 0, + /* Test 7 - No backslash removal within single quotes. */ + "'a\\$VAR' '\\\"'", /* Test 7 data */ + ARGV0, + "@test-expandargv-7.lst", + 0, + ARGV0, + "a\\$VAR", + "\\\"", + 0, + + /* Test 8 - Remove backslash / newline pairs. */ + "\"ab\\\ncd\" ef\\\ngh", /* Test 8 data */ + ARGV0, + "@test-expandargv-8.lst", + 0, + ARGV0, + "abcd", + "efgh", + 0, + + /* Test 9 - Backslash within double quotes. */ + "\"\\$VAR\" \"\\`\" \"\\\"\" \"\\\\\" \"\\n\" \"\\t\"", /* Test 9 data */ + ARGV0, + "@test-expandargv-9.lst", + 0, + ARGV0, + "$VAR", + "`", + "\"", + "\\", + "\\n", + "\\t", + 0, + 0 /* Test done marker, don't remove. */ }; From patchwork Tue Jan 9 14:26:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83644 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 40FFC385803E for ; Tue, 9 Jan 2024 14:30:04 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id D4A7838582B5 for ; Tue, 9 Jan 2024 14:26:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D4A7838582B5 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 D4A7838582B5 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=1704810417; cv=none; b=f9VETIQDMQLnBao5qyxD9n3BUZkPR8iQFxpbOLWMSDdsSe/4KiIJXuaLR9Di44DT8jSgIHHRM1nhu6J+qpYVB0XezbNNYd1HuRCQlbiGGmN5RhdEYjS35IZ2yagMk9s24HjO8oImy4SmXgrIg1n5gRJdEem11dUV/wDFrUI5d5U= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810417; c=relaxed/simple; bh=9e3YG20+Bg/QD7ahk4+iY7SqS+p/dwWcM2ptkPRsTxw=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=koGUyvRiFVviPAc+r7d7Lt5Pqptxid7cZWgMLelnXsYf4WMxPuxnBDS9caLGRCvcjOUiLI+ebVJz6PpUfyDq3zTynsFYyaPAvMgeKRWEgtAIi0cd1WPEmWLAn5gJkuluKUEuO3zGEF9rtT1LqErJXAGti3yF0HspurOSbFw/2w4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810414; 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: in-reply-to:in-reply-to:references:references; bh=hm8OAfOZDKD3hRcj8lXuncqR9EduzO4VR18/S8IjDrA=; b=ABlDm2grAO/TNsLFK+PNCVtR7na4yA7J1hqRtSaXaZvD9w/Zcm97tHAjBaAfNgn99hFz2C 9M10sm6qhYkQZTVammPrWfv3wt8lKcuiUnvPm1eA8//gTLyOzRdHp8J/POtQy6B2vmo+MJ /Jmp3b4jOHGJUKvKLxYfCiy59y1TjzQ= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-561-L3AmIVJbMWmougZj9WS1SQ-1; Tue, 09 Jan 2024 09:26:47 -0500 X-MC-Unique: L3AmIVJbMWmougZj9WS1SQ-1 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-40d8032afffso23031445e9.1 for ; Tue, 09 Jan 2024 06:26:47 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810405; x=1705415205; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hm8OAfOZDKD3hRcj8lXuncqR9EduzO4VR18/S8IjDrA=; b=j6UE9vOrTdEdHr6+eqhQEI+shZixO0lAkzqgL6lNwDcK3vlFzpF/UASunwu/Sj85a4 UGwDg5bTbuL6YuMc5TKDd+KwZXNiSflw0rLuHwjXIeSXInzLClxnT5eLXeuoO7Ate3wD 6IoOqsCUnupg2oywhV8LtW3asAMWk6UWrlQKzS9ElTKEZ0EcNwVB8Ve1HzA+xcpooBPB 4bW/vdK/PhmITSOHFhm/tDSnYsTacUljf9yhIqfubzscbjyfyjwA85vhCA1TVJPtMsR+ qXTb7yj8f6cn6sS9B+Q0Q5GDMNcIYElrcXvI6Z6kUIAlXG8AWenmpA7Yr3h+tgdSXgtv rP7g== X-Gm-Message-State: AOJu0Yy2v9uJ6j5nRSYrLSaYU7GIiuyo8zA8Dm4GumwwCn4Fx3/hWyXQ vbYRPzgWecdTCS7N/biWkcEoic0T6CsXDuecZ+3VrHfswFdXNUuQsRXfXgkobRK/BKcMGqYa9UU MCVSkY+KwZfP9yJ1kWDRNE+k/C01S8/uA6PCs98+7A6Ngeb6iQI0DxmSJ0LFAoto/uRMxALSOgb kpLpjKVoShjHrmSQ== X-Received: by 2002:a05:600c:19d3:b0:40d:8a22:cc7 with SMTP id u19-20020a05600c19d300b0040d8a220cc7mr1364479wmq.175.1704810405229; Tue, 09 Jan 2024 06:26:45 -0800 (PST) X-Google-Smtp-Source: AGHT+IFl3jXc1eRfjaDoCrp1NvHke+ctZz6DKkvSXuhSwm9Cdi4RA4ewd3/y72K6DwJdYZKtbDWo7A== X-Received: by 2002:a05:600c:19d3:b0:40d:8a22:cc7 with SMTP id u19-20020a05600c19d300b0040d8a220cc7mr1364474wmq.175.1704810404890; Tue, 09 Jan 2024 06:26:44 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id g11-20020a05600c4ecb00b0040d8810efc9sm15067233wmq.17.2024.01.09.06.26.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:44 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 02/16] gdb/testsuite: add some xfail in gdb.base/startup-with-shell.exp Date: Tue, 9 Jan 2024 14:26:25 +0000 Message-Id: <5808a3f3b76fc376d908b820a28151553462a40f.1704809585.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org There are two tests that fail in gdb.base/startup-with-shell.exp when using the native-extended-remote board. I plan to fix these issues in this series, but not straight away. It is easier to test each patch if I can just check for no unexpected failures, so first lets mark the currently failing tests as xfail. This change will be removed by the end of this series, at which point the tests will be passing. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 --- gdb/testsuite/gdb.base/startup-with-shell.exp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gdb/testsuite/gdb.base/startup-with-shell.exp b/gdb/testsuite/gdb.base/startup-with-shell.exp index 51b1e4fa922..f3ad4ec4cbb 100644 --- a/gdb/testsuite/gdb.base/startup-with-shell.exp +++ b/gdb/testsuite/gdb.base/startup-with-shell.exp @@ -55,10 +55,19 @@ proc initial_setup_simple { startup_with_shell run_args } { } } +# Are we using 'remote' or 'extended-remote' protocol? +set is_remote_p [expr [string equal [target_info gdb_protocol] \ + "remote"] \ + || [string equal [target_info gdb_protocol] \ + "extended-remote"]] + ## Run the actual tests with_test_prefix "startup_with_shell = on; run_args = *.unique-extension" { initial_setup_simple "on" "$unique_file_dir/*.unique-extension" + if { $is_remote_p } { + setup_xfail "*-*-*" gdb/28392 + } gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"$unique_file\"" \ "first argument expanded" } @@ -72,6 +81,9 @@ with_test_prefix "startup_with_shell = off; run_args = *.unique-extension" { with_test_prefix "startup_with_shell = on; run_args = \$TEST" { set env(TEST) "1234" initial_setup_simple "on" "\$TEST" + if { $is_remote_p } { + setup_xfail "*-*-*" gdb/28392 + } gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"1234\"" \ "testing first argument" unset env(TEST) From patchwork Tue Jan 9 14:26:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83640 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 29BE03857C53 for ; Tue, 9 Jan 2024 14:28:06 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 26DA73858283 for ; Tue, 9 Jan 2024 14:26:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 26DA73858283 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 26DA73858283 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810416; cv=none; b=Uwa4TTSX5ILen1RPyeb/XftuBZL7xuM3CXrCk4aknuLvWD/hdATXNAdN3ZCcyhC/T67V2M6HyZSumEkKUOUSLAEXS9lHQ3iFU05aB3gL3rUfg6mMLdL9CwWFYSBLBezGaQlpGO205MFamkPvukBuAVsxGuDWfsOffj3FdX10jdc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810416; c=relaxed/simple; bh=ThbhS+AJ6HOojHYlEhyivoDuCrylnNMIAi9xtjd+ptk=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=Z+hKO3e++ebfd67Cc4mTMtqvpciuvGfwcfk/+IVe0fXuwSaBtVQaGcJFqgG3k/MwO+NCurqLwwa2ple7pt3okymYwAxxlP9IVowOh9+NduTtn+aTj/G22jAOvI2FyRv9OWd8pORC0w2qRH/Svk2HRXnflfuoOkCSeCVwpEFbNtM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810410; 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: in-reply-to:in-reply-to:references:references; bh=l5PJrGok25VMszbu6euxdUZg1sujHglq2lQfN31CPhM=; b=GgJT5bcaDEnRpFHNtl4/ri3yGy2RNlBaiApN1R+X5iNZd9X7jR10Pn/+pAhxdt7oZPj7pU Fxl24b76jb5nA3zVaRMku4Twoe70vrCXIWUASnT6JJ2N1Nq66pE+m7BHotw6eZ4+zskFAE dyHH72jB+5xWi0mCGhXI0nsQwxRPg30= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-144-53KT7Kk5OziiZGMcuKFZCw-1; Tue, 09 Jan 2024 09:26:49 -0500 X-MC-Unique: 53KT7Kk5OziiZGMcuKFZCw-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-40d8032afffso23031635e9.1 for ; Tue, 09 Jan 2024 06:26:49 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810408; x=1705415208; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=l5PJrGok25VMszbu6euxdUZg1sujHglq2lQfN31CPhM=; b=vLEZtflPR5NkmKgMDINpyRFgrwnr8CyXPzkqbaZ/cifpFEb0ogAIE0ubzGCO1fMDip 3r6oPWSwwnRO8/dzdYDv47507qEjhDq5bIg/hH94WwgHIJasayAWrvAhz2cqaK1KUGV7 +E6g/2ZpKKgCFbf/2qqdRZBiVxD442lKYt2RXo8T0IUIqA8KMNgGP2p4jsSjtwI4UZgm Y2aCjM/CDq+xDwOpLtdw9UhV7GXO7mj/QjRXbX6wtF3sc29cE7NqcFHpRWsyQexJGQlF mrbyQpdwk73CtlGjn15jnIwogIQ9whqDXztQtJfRfFj50GZ1hdvUe01vyNkgJYJRzHO6 UBfw== X-Gm-Message-State: AOJu0YwKsoPTkPV6ZExXyZLG70ReoW4DEqxIH2zbYs0TQP9utpUdk+TD XjR9XsxMhnZi3Cw6qkDWnAbM2Jl4BSmctBmuAkbcb3f9jnym0w0ebj5O1d6lZnvPw9yIA0gMK2B QCzYfEpfh5qH2hpXQCYto/w2sg0QfF71RCwF7dpSXLgG6QEtzPzGlfFq6E7pB6QVkH75pFS2+6x 1yXUYOmAl0LYZ7cA== X-Received: by 2002:a05:600c:1c04:b0:40d:949c:37 with SMTP id j4-20020a05600c1c0400b0040d949c0037mr1485395wms.178.1704810407531; Tue, 09 Jan 2024 06:26:47 -0800 (PST) X-Google-Smtp-Source: AGHT+IECiBAm8gT83BqJPUngrX2ryK8L5Xy16Q2OkPkeRky49W/OU6KX9F7lXFjQ1xMp9/5Ul00L2w== X-Received: by 2002:a05:600c:1c04:b0:40d:949c:37 with SMTP id j4-20020a05600c1c0400b0040d949c0037mr1485386wms.178.1704810406818; Tue, 09 Jan 2024 06:26:46 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id u21-20020a05600c00d500b0040e47071200sm2559090wmm.0.2024.01.09.06.26.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:45 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Michael Weghorn , Andrew Burgess Subject: [PATCH 03/16] gdb: Support some escaping of args with startup-with-shell being off Date: Tue, 9 Jan 2024 14:26:26 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org From: Michael Weghorn I (Andrew Burgess) have taken this patch from this series: https://inbox.sourceware.org/gdb-patches/20211022071933.3478427-1-m.weghorn@posteo.de/ I started off reviewing that series, but wanted to explore some alternative strategies for solving the problems this series addresses. However, this patch I think is super useful, so I've taken it mostly as it was in the original series. I have made a few minor cleanups, and I've also added some more tests. Any bugs should be considered mine (Andrew's), but I've left the original author (Michael Weghorn) in place as the GDB side changes are mostly their work. The function execv_argv::init_for_no_shell (gdb/nat/fork-inferior.c), is passed a single string ALLARGS containing all of the inferior arguments, and contains some custom code for splitting this argument string into a vector of separate arguments. This function is used when startup-with-shell is off (which is not the default). The algorithm in this function was just splitting on whitespace characters, and ignoring any quoting, so for example: (gdb) set startup-with-shell off (gdb) set args "first arg" second_arg would result in three arguments ("first), (arg"), and (second_arg) being passed to the inferior (the parenthesis are not part of the parsed arguments). This commit replaces this custom argument splitting with a use of the existing gdb_argv class (which uses the libiberty buildargv function). This does a better job of supporting quoting and escaping, so for the example given above we now pass two arguments (first arg) and (second_arg), which is certainly what I would have expected as a GDB user. This commit changes the 'execv_argv' class accordingly and drops the optimization to have all the 'char *' in 'm_argv' point to a single string rather than allocating a separate string for each arg. This is needed because we are now going to be stripping some escaping from the arguments, for example: (gdb) set startup-with-shell off (gdb) set args "literal \$" In this case we will pass the single argument (literal $) to the inferior, the escaping backslash will be removed. This might seem strange as usually the backslash would be stripped by the shell, and now we have no shell. However, I think the consistent behaviour is a good thing; whether we start with a shell or not the escaping will be removed. Using gdb_argv will mean that quote characters are also stripped. If we consider the first example again: (gdb) set startup-with-shell off (gdb) set args "first arg" second_arg This is now going to pass (first arg) and (second_arg), the quotes have been removed. If the user did want the original behaviour then they are going to have to now do this: (gdb) set startup-with-shell off (gdb) set args \"first arg\" second_arg or they could do this: (gdb) set startup-with-shell off (gdb) set args '"first' 'arg"' second_arg This commit also extends the three tests that cover inferior argument passing to cover the case where 'startup-with-shell' is off. All of these new tests pass for native targets, but there are still problems when using remote targets. For now I have (rather crudely) disabled these tests for remote targets. Don't worry too much, the iffy testsuite code will be removed by the end of this series, by which point remote targets will be as functional as native targets (for argument passing at least). Co-Authored-By: Andrew Burgess Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 --- gdb/nat/fork-inferior.c | 84 +++------- gdb/testsuite/gdb.base/args.exp | 65 +++++--- gdb/testsuite/gdb.base/inferior-args.exp | 106 +++++++++++-- gdb/testsuite/gdb.base/startup-with-shell.exp | 146 ++++++++++++++---- 4 files changed, 273 insertions(+), 128 deletions(-) diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c index 968983b2021..30310bdd3ee 100644 --- a/gdb/nat/fork-inferior.c +++ b/gdb/nat/fork-inferior.c @@ -27,6 +27,7 @@ #include "gdbsupport/pathstuff.h" #include "gdbsupport/signals-state-save-restore.h" #include "gdbsupport/gdb_tilde_expand.h" +#include "gdbsupport/buildargv.h" #include extern char **environ; @@ -42,6 +43,11 @@ class execv_argv execv_argv (const char *exec_file, const std::string &allargs, const char *shell_file); + ~execv_argv () + { + free_vector_argv (m_argv); + } + /* Return a pointer to the built argv, in the type expected by execv. The result is (only) valid for as long as this execv_argv object is live. We return a "char **" because that's the type @@ -50,7 +56,7 @@ class execv_argv strings to which the array point. */ char **argv () { - return const_cast (&m_argv[0]); + return m_argv.data (); } private: @@ -69,66 +75,28 @@ class execv_argv const std::string &allargs, const char *shell_file); - /* The argument vector built. Holds non-owning pointers. Elements - either point to the strings passed to the execv_argv ctor, or - inside M_STORAGE. */ - std::vector m_argv; - - /* Storage. In the no-shell case, this contains a copy of the - arguments passed to the ctor, split by '\0'. In the shell case, - this contains the quoted shell command. I.e., SHELL_COMMAND in - {"$SHELL" "-c", SHELL_COMMAND, NULL}. */ - std::string m_storage; + /* The argument vector. This owns the strings within it. */ + std::vector m_argv; }; -/* Create argument vector for straight call to execvp. Breaks up - ALLARGS into an argument vector suitable for passing to execvp and - stores it in M_ARGV. E.g., on "run a b c d" this routine would get - as input the string "a b c d", and as output it would fill in - M_ARGV with the four arguments "a", "b", "c", "d". Each argument - in M_ARGV points to a substring of a copy of ALLARGS stored in - M_STORAGE. */ +/* Create argument vector for straight call to execvp. Breaks up ALLARGS + into an argument vector suitable for passing to execvp and stores it in + M_ARGV. EXEC_FILE is the executable to be run. + + E.g., if EXEC_FILE is "foo", and the user does "run a b c d" then + ALLARGS would be "a b c d", and this function would fill M_ARGV with + give arguments "foo", "a", "b", "c", and "d". */ void execv_argv::init_for_no_shell (const char *exec_file, const std::string &allargs) { + m_argv.push_back (xstrdup (exec_file)); - /* Save/work with a copy stored in our storage. The pointers pushed - to M_ARGV point directly into M_STORAGE, which is modified in - place with the necessary NULL terminators. This avoids N heap - allocations and string dups when 1 is sufficient. */ - std::string &args_copy = m_storage = allargs; + gdb_argv argv (allargs.c_str ()); - m_argv.push_back (exec_file); - - for (size_t cur_pos = 0; cur_pos < args_copy.size ();) - { - /* Skip whitespace-like chars. */ - std::size_t pos = args_copy.find_first_not_of (" \t\n", cur_pos); - - if (pos != std::string::npos) - cur_pos = pos; - - /* Find the position of the next separator. */ - std::size_t next_sep = args_copy.find_first_of (" \t\n", cur_pos); - - if (next_sep == std::string::npos) - { - /* No separator found, which means this is the last - argument. */ - next_sep = args_copy.size (); - } - else - { - /* Replace the separator with a terminator. */ - args_copy[next_sep++] = '\0'; - } - - m_argv.push_back (&args_copy[cur_pos]); - - cur_pos = next_sep; - } + for (const auto &a : argv) + m_argv.push_back (xstrdup (a)); /* NULL-terminate the vector. */ m_argv.push_back (NULL); @@ -182,11 +150,7 @@ execv_argv::init_for_shell (const char *exec_file, /* We're going to call a shell. */ bool escape_bang = escape_bang_in_quoted_argument (shell_file); - /* We need to build a new shell command string, and make argv point - to it. So build it in the storage. */ - std::string &shell_command = m_storage; - - shell_command = "exec "; + std::string shell_command = "exec "; /* Add any exec wrapper. That may be a program name with arguments, so the user must handle quoting. */ @@ -256,9 +220,9 @@ execv_argv::init_for_shell (const char *exec_file, "-c" says to interpret the next arg as a shell command to execute, and this command is "exec ". */ m_argv.reserve (4); - m_argv.push_back (shell_file); - m_argv.push_back ("-c"); - m_argv.push_back (shell_command.c_str ()); + m_argv.push_back (xstrdup (shell_file)); + m_argv.push_back (xstrdup ("-c")); + m_argv.push_back (xstrdup (shell_command.c_str ())); m_argv.push_back (NULL); } diff --git a/gdb/testsuite/gdb.base/args.exp b/gdb/testsuite/gdb.base/args.exp index cb50a4872b5..f97f1089d69 100644 --- a/gdb/testsuite/gdb.base/args.exp +++ b/gdb/testsuite/gdb.base/args.exp @@ -29,30 +29,55 @@ if {[build_executable $testfile.exp $testfile $srcfile] == -1} { return -1 } +set startup_with_shell_modes { "on" } +if {!([target_info gdb_protocol] == "remote" + || [target_info gdb_protocol] == "extended-remote")} { + lappend startup_with_shell_modes "off" +} else { + # Some of these tests will not work when using the remote protocol + # due to bug PR gdb/28392. + unsupported "gdbserver 'startup-with-shell off' broken PR gdb/28392" +} + # NAME is the name to use for the tests and ARGLIST is the list of # arguments that are passed to GDB when it is started. +# +# The optional RE_LIST is the list of patterns to check the arguments +# against, these patterns should match ARGLIST. If the arguments are +# expected to show up unmodified in the test output then RE_LIST can +# be dropped, and this proc will reuse ARGLIST. + +proc args_test { name arglist {re_list {}} } { + + # If RE_LIST is not supplied then we can reuse ARGLIST, this + # implies that the arguments will appear unmodified in the test + # output. + if {[llength $re_list] == 0} { + set re_list $arglist + } -proc args_test { name arglist } { - save_vars { ::GDBFLAGS } { - set ::GDBFLAGS "$::GDBFLAGS --args $::binfile $arglist" + foreach_with_prefix startup_with_shell $::startup_with_shell_modes { + save_vars { ::GDBFLAGS } { + set ::GDBFLAGS "$::GDBFLAGS --args $::binfile $arglist" - clean_restart $::binfile + clean_restart $::binfile - runto_main - gdb_breakpoint [gdb_get_line_number "set breakpoint here"] - gdb_continue_to_breakpoint "breakpoint for $name" + gdb_test_no_output "set startup-with-shell ${startup_with_shell}" \ + "set startup-with-shell for $name" - set expected_len [expr 1 + [llength $arglist]] - gdb_test "print argc" "\\\$$::decimal = $expected_len" "argc for $name" + runto_main + gdb_breakpoint [gdb_get_line_number "set breakpoint here"] + gdb_continue_to_breakpoint "breakpoint for $name" - set i 1 - foreach arg $arglist { - if { $arg eq "\n" } { - set arg {\\n} + set expected_len [expr 1 + [llength $re_list]] + gdb_test "print argc" "\\\$$::decimal = $expected_len" "argc for $name" + + set i 1 + foreach arg $re_list { + gdb_test "print argv\[$i\]" "\\\$$::decimal = $::hex \"$arg\"" \ + "argv\[$i\] for $name" + set i [expr $i + 1] } - gdb_test "print argv\[$i\]" "\\\$$::decimal = $::hex \"$arg\"" \ - "argv\[$i\] for $name" - set i [expr $i + 1] } } } @@ -78,6 +103,10 @@ args_test "two empty with single quotes" {{1} {''} {''} {3}} # Try with arguments containing literal newlines. -args_test "one newline" {{1} "\n" {3}} +args_test "one newline" {{1} "\n" {3}} {1 \\\\n 3} + +args_test "two newlines" {{1} "\n" "\n" {3}} {1 \\\\n \\\\n 3} + +args_test "lone single quote" {{1} \' {3}} -args_test "two newlines" {{1} "\n" "\n" {3}} +args_test "lone double quote" {{1} \" {3}} {1 \\\\\" 3} diff --git a/gdb/testsuite/gdb.base/inferior-args.exp b/gdb/testsuite/gdb.base/inferior-args.exp index 2c920ab14ec..bffbcf1862d 100644 --- a/gdb/testsuite/gdb.base/inferior-args.exp +++ b/gdb/testsuite/gdb.base/inferior-args.exp @@ -25,16 +25,27 @@ if {[build_executable "failed to prepare" $testfile $srcfile \ return } -proc do_test { method } { +# STARTUP_WITH_SHELL is either 'on' or 'off' and determines if the +# inferior is started under a shell or not. INFERIOR_ARGS is the list +# of inferior arguments. EXPECTED_RESULTS is the list of expected +# results, one for each argument. +# +# When STUB_SUITABLE is true this test is suitable for use with +# gdbserver, i.e. INFERIOR_ARGS can be passed through to +# gdbserver_start via gdb_run_cmd. Some of the weird quoting used in +# some of the tests doesn't seem to play well with gdbserver_start. +# This is a TCL issue, not a gdbserver issue. Manually testing with +# gdbserver shows no problems. It's just that when we try to invoke +# gdbserver from TCL the argument quoting gets messed up. For tests +# that are problematic, STUB_SUITABLE is false. +proc do_test { method startup_with_shell inferior_args expected_results \ + stub_suitable } { global binfile hex - # The second arg is an empty string on purpose. The last argument - # must be the empty argument -- we once had a bug where that - # wouldn't work! - set inferior_args { "first arg" "" "third-arg" "'" "\"" " " "" } - clean_restart $binfile + gdb_test_no_output "set startup-with-shell $startup_with_shell" + if { $method == "start" } { # The start command does not make sense for a stub. if { [use_gdb_stub] } { @@ -80,6 +91,10 @@ proc do_test { method } { return -1 } + if { [use_gdb_stub] && !$stub_suitable } { + return + } + # The run command does not make sense for a stub, but GDB_RUN_CMD # does the right thing when the target is a stub (start the stub, # connect to it, and "continue"). @@ -110,18 +125,75 @@ proc do_test { method } { error "invalid method $method" } + set argc [expr [llength $expected_results] + 1] + # Now that we are stopped at main, inspect argc/argv. - gdb_test "print argc" " = 8" - gdb_test "print argv\[0\]" " = $hex \".*\"" - gdb_test "print argv\[1\]" " = $hex \"first arg\"" - gdb_test "print argv\[2\]" " = $hex \"\"" - gdb_test "print argv\[3\]" " = $hex \"third-arg\"" - gdb_test "print argv\[4\]" " = $hex \"'\"" - gdb_test "print argv\[5\]" " = $hex \"\\\\\"\"" - gdb_test "print argv\[6\]" " = $hex \" \"" - gdb_test "print argv\[7\]" " = $hex \"\"" + gdb_test "print argc" " = $argc" + gdb_test "print argv\[0\]" " = $hex \"\[^\r\n\]+\"" + for { set i 1 } { $i < $argc } { incr i } { + set idx [expr $i - 1] + gdb_test "print argv\[$i\]" " = [lindex $expected_results $idx]" + } +} + +set test_desc_list [] + +# test one +# -------- +# +# The second arg is an empty string on purpose. The last argument +# must be the empty argument -- we once had a bug where that wouldn't +# work! +lappend test_desc_list [list "test one" \ + true \ + { "first arg" "" "third-arg" "'" "\"" " " "" } \ + [list "$hex \"first arg\"" \ + "$hex \"\"" \ + "$hex \"third-arg\"" \ + "$hex \"'\"" \ + "$hex \"\\\\\"\"" \ + "$hex \" \"" \ + "$hex \"\"" ]] + +# test two +# -------- +# +# The argument being passed here is '"', that is a single double quote +# contained within single quotes. +# +# I build the test descriptor using this mess of code to avoid having +# unbalanced quotes, which messes up indentation and syntax +# highlighting within (at least) emacs. The 'format' of ascii code 34 +# gives us the double quote character. Then I have to jump through +# the rest of this mess in order to avoid TCL escaping the quote for +# me. It's super important that what we send to GDB is '"' not '\"'. +set item [list "test two" false] +set cmd [format "lappend item \{ '%c' '\\%c' \}" 34 34] +eval $cmd +set bs "\\\\" +lappend item [list "$hex \"$bs\"\"" "$hex \"$bs$bs$bs\"\""] +lappend test_desc_list $item + +set startup_with_shell_modes { "on" } +if {!([target_info gdb_protocol] == "remote" + || [target_info gdb_protocol] == "extended-remote")} { + lappend startup_with_shell_modes "off" +} else { + # Due to PR gdb/28392 gdbserver doesn't currently support having + # startup-with-shell off, and then attempting to pass arguments + # containing whitespace. + unsupported "bug gdb/28392: gdbserver doesn't support this" } -foreach_with_prefix method { "start" "starti" "run" "set args" } { - do_test $method + +foreach desc $test_desc_list { + lassign $desc name stub_suitable args re_list + with_test_prefix $name { + foreach_with_prefix set_method { "start" "starti" "run" "set args" } { + foreach_with_prefix startup_with_shell $startup_with_shell_modes { + do_test $set_method $startup_with_shell $args $re_list \ + $stub_suitable + } + } + } } diff --git a/gdb/testsuite/gdb.base/startup-with-shell.exp b/gdb/testsuite/gdb.base/startup-with-shell.exp index f3ad4ec4cbb..62bb5c9c882 100644 --- a/gdb/testsuite/gdb.base/startup-with-shell.exp +++ b/gdb/testsuite/gdb.base/startup-with-shell.exp @@ -47,14 +47,57 @@ proc initial_setup_simple { startup_with_shell run_args } { gdb_test_no_output "set args $run_args" \ "set args \$run_args" - set test "inferior started" - if { [runto_main] } { - pass $test - } else { - fail $test + return [runto_main] +} + +# Start GDB, set the inferior arguments to ARGS, and then run to main. +# Once at main, read the first argument from the inferior and compare +# it to ON_RE if startup-with-shell is on, otherwise compare to +# OFF_RE. +# +# If PROBLEMATIC_ON is true then when startup-with-shell is on we +# expect the comparison to fail, so setup an xfail. +# +# If PROBLEMATIC_OFF is true then when startup-with-shell is off we +# expect the comparison to fail, so setup an xfail. +# +# TESTNAME is a string used in the test names. +proc run_test { args on_re off_re testname { problematic_on false } \ + { problematic_off false } } { + foreach startup_with_shell { "on" "off" } { + with_test_prefix "$testname, startup_with_shell: ${startup_with_shell}" { + if {![initial_setup_simple $startup_with_shell $args]} { + return -1 + } + + if { $startup_with_shell } { + set re $on_re + set problematic $problematic_on + } else { + set re $off_re + set problematic $problematic_off + } + + if { $problematic } { + setup_xfail "*-*-*" gdb/28392 + } + + gdb_test "print argv\[1\]" "\\\$$::decimal = $::hex $re" $testname + } } } +# This is like the run_test proc except that RE is used as the +# expected argument regexp when startup-with-shell is both on and off. +# For the other arguments, see run_test. +proc run_test_same { args re testname { problematic_on false } \ + { problematic_off false } } { + run_test $args $re $re $testname $problematic_on $problematic_off +} + +# The regexp to match a single '\' character. +set bs "\\\\" + # Are we using 'remote' or 'extended-remote' protocol? set is_remote_p [expr [string equal [target_info gdb_protocol] \ "remote"] \ @@ -63,36 +106,73 @@ set is_remote_p [expr [string equal [target_info gdb_protocol] \ ## Run the actual tests -with_test_prefix "startup_with_shell = on; run_args = *.unique-extension" { - initial_setup_simple "on" "$unique_file_dir/*.unique-extension" - if { $is_remote_p } { - setup_xfail "*-*-*" gdb/28392 - } - gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"$unique_file\"" \ - "first argument expanded" -} +run_test "$unique_file_dir/*.unique-extension" \ + "\"$unique_file\"" \ + "\"$unique_file_dir/\\\*\.unique-extension\"" \ + "arg is glob" \ + $is_remote_p -with_test_prefix "startup_with_shell = off; run_args = *.unique-extension" { - initial_setup_simple "off" "$unique_file_dir/*.unique-extension" - gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"$unique_file_dir/\\\*\.unique-extension\"" \ - "first argument not expanded" -} +run_test_same "$unique_file_dir/\\*.unique-extension" \ + "\"$unique_file_dir/\\\*\.unique-extension\"" \ + "arg is escaped glob" -with_test_prefix "startup_with_shell = on; run_args = \$TEST" { +save_vars { env(TEST) } { set env(TEST) "1234" - initial_setup_simple "on" "\$TEST" - if { $is_remote_p } { - setup_xfail "*-*-*" gdb/28392 - } - gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"1234\"" \ - "testing first argument" - unset env(TEST) + run_test "\$TEST" \ + "\"1234\"" \ + "\"\\\$TEST\"" \ + "arg is shell variable" \ + $is_remote_p + + run_test_same "\\\$TEST" \ + "\"\\\$TEST\"" \ + "arg is escaped shell variable" } -with_test_prefix "startup_with_shell = off; run_args = \$TEST" { - set env(TEST) "1234" - initial_setup_simple "off" "\$TEST" - gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"\\\$TEST\"" \ - "testing first argument" - unset env(TEST) -} +run_test_same "\"\\a\"" \ + "\"${bs}${bs}a\"" \ + "retain backslash in double quote arg" \ + false $is_remote_p + +run_test_same "'\\a'" \ + "\"${bs}${bs}a\"" \ + "retain backslash in single quote arg" \ + false $is_remote_p + +run_test_same "\"\\\$\"" \ + "\"\\\$\"" \ + "'\$' can be escaped in double quote arg" + +run_test_same "'\\\$'" \ + "\"${bs}${bs}\\\$\"" \ + "'\$' is not escaped in single quote arg" \ + false $is_remote_p + +run_test_same "\"\\`\"" \ + "\"\\`\"" \ + "'`' can be escaped in double quote arg" + +run_test_same "'\\`'" \ + "\"${bs}${bs}`\"" \ + "'`' is not escaped in single quote arg" \ + false $is_remote_p + +run_test_same "\"\\\"\"" \ + "\"${bs}\"\"" \ + "'\"' can be escaped in double quote arg" \ + false $is_remote_p + +run_test_same "'\\\"'" \ + "\"${bs}${bs}${bs}\"\"" \ + "'\"' is not escaped in single quote arg" \ + false $is_remote_p + +run_test_same "\"\\\\\"" \ + "\"${bs}${bs}\"" \ + "'\\' can be escaped in double quote arg" \ + false $is_remote_p + +run_test_same "'\\\\'" \ + "\"${bs}${bs}${bs}${bs}\"" \ + "'\\' is not escaped in single quote arg" \ + false $is_remote_p From patchwork Tue Jan 9 14:26:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83641 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AFEE23857C55 for ; Tue, 9 Jan 2024 14:28:07 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 41F673858286 for ; Tue, 9 Jan 2024 14:26:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 41F673858286 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 41F673858286 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810415; cv=none; b=qJBYBA9x5GB2tdceA469nwncpkt7iiFQDMR1UmTNMMXv4fgTZEam4bpdu3lp7pl4SWjumu36cdYAsY+sVuzovTGtBbgcoKMnzv8rcaW4rKBaFSfWUwkBrYM2PWySLg6OirlsedxxATVO7ufntxXZ7s7AlyKWZm18vE/AVidMrUg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810415; c=relaxed/simple; bh=OVY+4zmSnurMY2yaQAGoLlF1JyjlCybaIEkPwZDK72U=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=vNFeoWZiiN6w7og1NMN9ObCnF7UYOvXhiQY7JM9cRp1hymJ//P201mHgdgNjO7F9+s/CmhydY1S704MKb1uH8zcKIJeQgtV8rMxukLm6qGOKr83kjuCW9V8An4z0H6poeH30scNir60JtY0hWgi4rmxpYQnl2lUDsLQuJCIdxHc= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810410; 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: in-reply-to:in-reply-to:references:references; bh=8SEySpwGSGSDAZpjlti++uAYj+TrB3ZEgJgv+d8plig=; b=P+ImtrD5fXeTFmwzYZ6YGaMdcIklRE8TwIPGoG3BmUsh2qwVCMPVRdAEoFhOQdhJ1kOOug 9WhjwcJwp4w7zLtq1FVdEz8xgl2TwLxM6gkCeORYjeJsAKA2hJDBGhpUbIC/C6gkZO/7hB dkcNc91cnX2ku9RRFTYMzb7roSptqG0= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-648-nym0Mcj5Omy4PARu3VQNtA-1; Tue, 09 Jan 2024 09:26:49 -0500 X-MC-Unique: nym0Mcj5Omy4PARu3VQNtA-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-40e49c3c124so11291505e9.1 for ; Tue, 09 Jan 2024 06:26:49 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810408; x=1705415208; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8SEySpwGSGSDAZpjlti++uAYj+TrB3ZEgJgv+d8plig=; b=isR0fLdBsehMUT3l007wPhVTgX+a2zm2yt1pV5KBJnF8b8sy/ROXv8Z3BsrbmZLwnb VhfCbF8KF5qu+PboOAuAgq5yf6+RxL1Lc374qzwVcR/53NR0aarhLs0jxcTcOPGzHgkj edC4V7rMhaauvwK6p/Ll8pERsQA6bSjYsfzzw+2dtsuFh0im4U891cQssbJTeLo7uJH8 WjSeYSxccWmzimBGzhphXffG7QlySqt+A7s6aQd4nieJbI/mv2O6gHoEdpHIJJsXcQiB RauQ1IA6jhXZty3SrV9wBqmKpYpcTC6V7LzMDCNTi7DlGTV3pCKrZgT73BERNxMGpN1J UHnQ== X-Gm-Message-State: AOJu0YyWtHylHD/Ih5ssNZwfb7lv9AJtuzizw2+SQGVQSR+hElZDDfvh gGPEllP0Tksu2cfeA04CFXaSzqjZCx4mtfMUcQrf9RIh2DshXyA3BS1EaeswhWIyDJVPasB0HWp bU05LLTnPRq8spCNcW6Ch3zYSoNOMQ3zIzuTz5c3X/KlZ2a0A9QH/PK3kgL8OaDpYL0Fgb12Kto QIjeizfhqqBBuQ4g== X-Received: by 2002:a05:600c:4f10:b0:40c:53ba:1720 with SMTP id l16-20020a05600c4f1000b0040c53ba1720mr2850514wmq.76.1704810408226; Tue, 09 Jan 2024 06:26:48 -0800 (PST) X-Google-Smtp-Source: AGHT+IFSB56NmzN8ZN92PMzJY6RL+O4XWrMTI6eFfGWMTXLJogh5agqRMYolLGUIMXELCAUY6xpByg== X-Received: by 2002:a05:600c:4f10:b0:40c:53ba:1720 with SMTP id l16-20020a05600c4f1000b0040c53ba1720mr2850502wmq.76.1704810407712; Tue, 09 Jan 2024 06:26:47 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id w17-20020a05600c475100b0040d802a7619sm15034224wmo.38.2024.01.09.06.26.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:47 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 04/16] gdb: remove the !startup_with_shell path from construct_inferior_arguments Date: Tue, 9 Jan 2024 14:26:27 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org In the previous commit nat/fork-inferior.c was updated such that when we are starting an inferior without a shell we now remove escape characters. The benefits of this are explained in the previous commit, but having made that change we can now make an additional change. Currently, in construct_inferior_arguments, when startup_with_shell is false we construct the inferior argument string differently than when startup_with_shell is true; when true we apply some escaping to special shell character, while when false we don't. This commit removes this special handling, and instead we now apply escaping in all cases. This is fine because, thanks to the previous commit the escaping will be correctly removed when we call into nat/fork-inferior.c (thanks to the previous commit). For GDB's native targets construct_inferior_arguments is reached via two code paths; first when GDB starts and we combine arguments from the command line, and second when the Python API is used to set the arguments from a sequence. Now, it might seem like removing this code path is a bad thing for native targets. This path allowed a "neat" trick to work around a limitation of GDB's command line argument processing. Consider this: $ gdb --args /tmp/exec '$FOO' (gdb) show args Argument list to give program being debugged when it is started is "\$FOO". Notice that the argument has become \$FOO, the '$' is now quoted. This is because, by quoting the argument in the shell command that started GDB, GDB was passed a literal $FOO with no quotes. In order to ensure that the inferior sees this same value, GDB then added the extra escape character. When GDB starts with a shell we pass \$FOO, which results in the inferior seeing a literal $FOO. But what if the user _actually_ wanted to have the shell GDB uses to start the inferior expand $FOO? Well, it appears this can't be done from the command line, but from the GDB prompt we can just do: (gdb) set args $FOO (gdb) show args Argument list to give program being debugged when it is started is "$FOO". And now the inferior will see the shell expanded version of $FOO. But there's no obvious way to achieve this from the GDB command line, except with this trick: $ gdb -eiex 'set startup-with-shell off' --args /tmp/exec '$FOO' (gdb) show args Argument list to give program being debugged when it is started is "$FOO". (gdb) show startup-with-shell Use of shell to start subprocesses is off. And now the $FOO is not escaped, but GDB is no longer using a shell to start the inferior, however, we can extend our command line like this: $ gdb -eiex 'set startup-with-shell off' \ -ex 'set startup-with-shell on' \ --args /tmp/exec '$FOO' (gdb) show args Argument list to give program being debugged when it is started is "$FOO". (gdb) show startup-with-shell Use of shell to start subprocesses is on. We use an early-initialisation command line option to disable startup-with-shell, this is done before command line argument processing, then a normal initialisation option turns startup-with-shell back on after GDB has processed the command line arguments! Is this useful? Yes, absolutely. Is this a good user experience? Absolutely not. And a later patch in this series is going to add a new command line option to GDB (and gdbserver) that will allow users to achieve the same result (this trick doesn't work in gdbserver as there's no early-initialisation there). So, the fact that I plan to remove the ability to do this from GDB is not going to be a problem once this complete series is merged. Now, for remote targets the impact of this change is greater, and is only a good thing. When arguments arrive in gdbserver from GDB we use construct_inferior_arguments to build the argument string. After the previous commit we know that calling nat/fork-inferior.c will always remove one "level" of escapes; either the shell gdbserver spawns will remove the escapes, or nat/fork-inferior.c will manually remove one level of escapes. What this means is that, if we don't add a level of escapes when building the arguments in construct_inferior_arguments, we will end up removing an additional level of escapes in nat/fork-inferior.c. After this commit a whole set of tests that were added as xfail in the previous commit are now passing. A change similar to this one can be found in this series: https://inbox.sourceware.org/gdb-patches/20211022071933.3478427-1-m.weghorn@posteo.de/ which I reviewed before writing this patch. I don't think there's any one patch in that series that exactly corresponds with this patch though, so I've listed the author of the original series as co-author on this patch. Co-Authored-By: Michael Weghorn Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 --- gdb/testsuite/gdb.base/args.exp | 12 +- gdb/testsuite/gdb.base/inferior-args.exp | 49 ++++++-- gdb/testsuite/gdb.base/startup-with-shell.exp | 37 ++---- gdbsupport/common-inferior.cc | 108 +++++++----------- 4 files changed, 91 insertions(+), 115 deletions(-) diff --git a/gdb/testsuite/gdb.base/args.exp b/gdb/testsuite/gdb.base/args.exp index f97f1089d69..8b0047999bf 100644 --- a/gdb/testsuite/gdb.base/args.exp +++ b/gdb/testsuite/gdb.base/args.exp @@ -29,16 +29,6 @@ if {[build_executable $testfile.exp $testfile $srcfile] == -1} { return -1 } -set startup_with_shell_modes { "on" } -if {!([target_info gdb_protocol] == "remote" - || [target_info gdb_protocol] == "extended-remote")} { - lappend startup_with_shell_modes "off" -} else { - # Some of these tests will not work when using the remote protocol - # due to bug PR gdb/28392. - unsupported "gdbserver 'startup-with-shell off' broken PR gdb/28392" -} - # NAME is the name to use for the tests and ARGLIST is the list of # arguments that are passed to GDB when it is started. # @@ -56,7 +46,7 @@ proc args_test { name arglist {re_list {}} } { set re_list $arglist } - foreach_with_prefix startup_with_shell $::startup_with_shell_modes { + foreach_with_prefix startup_with_shell { on off } { save_vars { ::GDBFLAGS } { set ::GDBFLAGS "$::GDBFLAGS --args $::binfile $arglist" diff --git a/gdb/testsuite/gdb.base/inferior-args.exp b/gdb/testsuite/gdb.base/inferior-args.exp index bffbcf1862d..4b51b657326 100644 --- a/gdb/testsuite/gdb.base/inferior-args.exp +++ b/gdb/testsuite/gdb.base/inferior-args.exp @@ -174,23 +174,48 @@ set bs "\\\\" lappend item [list "$hex \"$bs\"\"" "$hex \"$bs$bs$bs\"\""] lappend test_desc_list $item -set startup_with_shell_modes { "on" } -if {!([target_info gdb_protocol] == "remote" - || [target_info gdb_protocol] == "extended-remote")} { - lappend startup_with_shell_modes "off" -} else { - # Due to PR gdb/28392 gdbserver doesn't currently support having - # startup-with-shell off, and then attempting to pass arguments - # containing whitespace. - unsupported "bug gdb/28392: gdbserver doesn't support this" -} - +# test three +# ---------- +# +# This test focuses on sending special shell characters within a +# double quote argument, and each special character is prefixed with a +# backslash. +# +# In a POSIX shell, within a double quoted argument, only $ (dollar), +# ` (backtick), " (double quote), \ (backslash), and newline can be +# escaped. All other backslash characters are literal backslashes. +# +# As with the previous test, the double quotes are lost when the +# arguments are sent through gdbserver_start, as such, this test isn't +# going to work when using the native-gdbserver board, hence we set +# the second arguemnt to 'false'. +lappend test_desc_list [list "test three" \ + false \ + { "\&" "\<" "\#" "\^" "\>" "\$" "\`" } \ + [list "$hex \"\\\\\\\\&\"" \ + "$hex \"\\\\\\\\<\"" \ + "$hex \"\\\\\\\\#\"" \ + "$hex \"\\\\\\\\\\^\"" \ + "$hex \"\\\\\\\\>\"" \ + "$hex \"\\\$\"" \ + "$hex \"`\""]] + +# test four +# --------- +# +# This test passes two arguments, a single and double quote, each +# escaped with a backslash. +lappend test_desc_list [list "test four" \ + true \ + { \' \" } \ + [list "$hex \"'\"" \ + "$hex \"\\\\\"\""]] foreach desc $test_desc_list { lassign $desc name stub_suitable args re_list with_test_prefix $name { foreach_with_prefix set_method { "start" "starti" "run" "set args" } { - foreach_with_prefix startup_with_shell $startup_with_shell_modes { + foreach_with_prefix startup_with_shell { on off } { do_test $set_method $startup_with_shell $args $re_list \ $stub_suitable } diff --git a/gdb/testsuite/gdb.base/startup-with-shell.exp b/gdb/testsuite/gdb.base/startup-with-shell.exp index 62bb5c9c882..0424b20de3a 100644 --- a/gdb/testsuite/gdb.base/startup-with-shell.exp +++ b/gdb/testsuite/gdb.base/startup-with-shell.exp @@ -58,12 +58,8 @@ proc initial_setup_simple { startup_with_shell run_args } { # If PROBLEMATIC_ON is true then when startup-with-shell is on we # expect the comparison to fail, so setup an xfail. # -# If PROBLEMATIC_OFF is true then when startup-with-shell is off we -# expect the comparison to fail, so setup an xfail. -# # TESTNAME is a string used in the test names. -proc run_test { args on_re off_re testname { problematic_on false } \ - { problematic_off false } } { +proc run_test { args on_re off_re testname { problematic_on false } } { foreach startup_with_shell { "on" "off" } { with_test_prefix "$testname, startup_with_shell: ${startup_with_shell}" { if {![initial_setup_simple $startup_with_shell $args]} { @@ -75,7 +71,7 @@ proc run_test { args on_re off_re testname { problematic_on false } \ set problematic $problematic_on } else { set re $off_re - set problematic $problematic_off + set problematic false } if { $problematic } { @@ -90,9 +86,8 @@ proc run_test { args on_re off_re testname { problematic_on false } \ # This is like the run_test proc except that RE is used as the # expected argument regexp when startup-with-shell is both on and off. # For the other arguments, see run_test. -proc run_test_same { args re testname { problematic_on false } \ - { problematic_off false } } { - run_test $args $re $re $testname $problematic_on $problematic_off +proc run_test_same { args re testname } { + run_test $args $re $re $testname } # The regexp to match a single '\' character. @@ -131,13 +126,11 @@ save_vars { env(TEST) } { run_test_same "\"\\a\"" \ "\"${bs}${bs}a\"" \ - "retain backslash in double quote arg" \ - false $is_remote_p + "retain backslash in double quote arg" run_test_same "'\\a'" \ "\"${bs}${bs}a\"" \ - "retain backslash in single quote arg" \ - false $is_remote_p + "retain backslash in single quote arg" run_test_same "\"\\\$\"" \ "\"\\\$\"" \ @@ -145,8 +138,7 @@ run_test_same "\"\\\$\"" \ run_test_same "'\\\$'" \ "\"${bs}${bs}\\\$\"" \ - "'\$' is not escaped in single quote arg" \ - false $is_remote_p + "'\$' is not escaped in single quote arg" run_test_same "\"\\`\"" \ "\"\\`\"" \ @@ -154,25 +146,20 @@ run_test_same "\"\\`\"" \ run_test_same "'\\`'" \ "\"${bs}${bs}`\"" \ - "'`' is not escaped in single quote arg" \ - false $is_remote_p + "'`' is not escaped in single quote arg" run_test_same "\"\\\"\"" \ "\"${bs}\"\"" \ - "'\"' can be escaped in double quote arg" \ - false $is_remote_p + "'\"' can be escaped in double quote arg" run_test_same "'\\\"'" \ "\"${bs}${bs}${bs}\"\"" \ - "'\"' is not escaped in single quote arg" \ - false $is_remote_p + "'\"' is not escaped in single quote arg" run_test_same "\"\\\\\"" \ "\"${bs}${bs}\"" \ - "'\\' can be escaped in double quote arg" \ - false $is_remote_p + "'\\' can be escaped in double quote arg" run_test_same "'\\\\'" \ "\"${bs}${bs}${bs}${bs}\"" \ - "'\\' is not escaped in single quote arg" \ - false $is_remote_p + "'\\' is not escaped in single quote arg" diff --git a/gdbsupport/common-inferior.cc b/gdbsupport/common-inferior.cc index 55149ec1f13..076ddc73d51 100644 --- a/gdbsupport/common-inferior.cc +++ b/gdbsupport/common-inferior.cc @@ -32,92 +32,66 @@ construct_inferior_arguments (gdb::array_view argv) { std::string result; - if (startup_with_shell) - { #ifdef __MINGW32__ - /* This holds all the characters considered special to the - Windows shells. */ - static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n"; - static const char quote = '"'; + /* This holds all the characters considered special to the + Windows shells. */ + static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n"; + static const char quote = '"'; #else - /* This holds all the characters considered special to the - typical Unix shells. We include `^' because the SunOS - /bin/sh treats it as a synonym for `|'. */ - static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n"; - static const char quote = '\''; + /* This holds all the characters considered special to the + typical Unix shells. We include `^' because the SunOS + /bin/sh treats it as a synonym for `|'. */ + static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n"; + static const char quote = '\''; #endif - for (int i = 0; i < argv.size (); ++i) + for (int i = 0; i < argv.size (); ++i) + { + if (i > 0) + result += ' '; + + /* Need to handle empty arguments specially. */ + if (argv[i][0] == '\0') { - if (i > 0) - result += ' '; + result += quote; + result += quote; + } + else + { +#ifdef __MINGW32__ + bool quoted = false; - /* Need to handle empty arguments specially. */ - if (argv[i][0] == '\0') + if (strpbrk (argv[i], special)) { - result += quote; + quoted = true; result += quote; } - else +#endif + for (char *cp = argv[i]; *cp; ++cp) { -#ifdef __MINGW32__ - bool quoted = false; - - if (strpbrk (argv[i], special)) + if (*cp == '\n') { - quoted = true; + /* A newline cannot be quoted with a backslash (it + just disappears), only by putting it inside + quotes. */ + result += quote; + result += '\n'; result += quote; } -#endif - for (char *cp = argv[i]; *cp; ++cp) + else { - if (*cp == '\n') - { - /* A newline cannot be quoted with a backslash (it - just disappears), only by putting it inside - quotes. */ - result += quote; - result += '\n'; - result += quote; - } - else - { #ifdef __MINGW32__ - if (*cp == quote) + if (*cp == quote) #else - if (strchr (special, *cp) != NULL) + if (strchr (special, *cp) != NULL) #endif - result += '\\'; - result += *cp; - } + result += '\\'; + result += *cp; } + } #ifdef __MINGW32__ - if (quoted) - result += quote; + if (quoted) + result += quote; #endif - } - } - } - else - { - /* In this case we can't handle arguments that contain spaces, - tabs, or newlines -- see breakup_args(). */ - for (char *arg : argv) - { - char *cp = strchr (arg, ' '); - if (cp == NULL) - cp = strchr (arg, '\t'); - if (cp == NULL) - cp = strchr (arg, '\n'); - if (cp != NULL) - error (_("can't handle command-line " - "argument containing whitespace")); - } - - for (int i = 0; i < argv.size (); ++i) - { - if (i > 0) - result += " "; - result += argv[i]; } } From patchwork Tue Jan 9 14:26:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83643 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id F1C3138582A8 for ; Tue, 9 Jan 2024 14:29:56 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 158A13858016 for ; Tue, 9 Jan 2024 14:26:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 158A13858016 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 158A13858016 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810421; cv=none; b=MDSVB0xLB9LQ5uaYHkYtotDBEgz0VHfshgsU1MMHsraMhW1IZXh0an36jwpFP3cOKZlH+i+Qp52/qCMz5D3JKpFgv3P2cO3ius43Edi4roXZv1POLemnzudjdLQHisoMkLJFZRG3SkYM0W+NNtBxL4oijLjiG7k0OH0ukyHmy84= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810421; c=relaxed/simple; bh=B/YgFAg3XCAhvfiBWu0tp9VwyaKgaCuKaUWLgzbrVYg=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=hGPNRH2Z1YiuAMdvdP0terJy2o5kw8TBAufLkqRtDF7BN4KcE+4GDrd3SdoI/2CLup1FL9pNW49KR8RFLkj5F45XTvCiqFhRSm6boFLza1FNmv6mMjfOepgDtdEC88xZfnRJP+28gV6fezDr4v1zJy60xnuhBDWXmjKsTAJ7x1Q= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810417; 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: in-reply-to:in-reply-to:references:references; bh=lOh7qo/gql9Xr3IgNHSse0+UZKYXDbo2mAZtm45aU2Y=; b=K+Rkfj98gN3dCIcKwrXbb1h7j+5aqWJ+/CIxezPVRx2HPAjMdcMh/sZD/IeE6gCfg9BwEE oInxfq0PFPg4iyjch/ogKMmvxNxZaodxdX+C5WBhLL7caVQN80DQxh/pUhv1Rcv6Ilc5Dr KlUf6/d2mIELgaugUzgHr1NnBYhyubc= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-168-dpa2_MO4NHGqRRFoDPzVEw-1; Tue, 09 Jan 2024 09:26:51 -0500 X-MC-Unique: dpa2_MO4NHGqRRFoDPzVEw-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-3367c893deeso1788223f8f.2 for ; Tue, 09 Jan 2024 06:26:51 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810409; x=1705415209; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=lOh7qo/gql9Xr3IgNHSse0+UZKYXDbo2mAZtm45aU2Y=; b=T83YXe2H1OhkTP1AHxLR7aYRIO+XAgUWV+oSS/x2HPj3IDJbJRCQUyG8bjKjk1m8Ga ONVznnuwmr7/hVbcZOlgSKt3rG6vBXTX5PenKilrI2Cer1tWc/Fymp54FjFHdWYp5ae6 lPC7bNs93rXA5H+rwSBHvGmdE4ayQdQWLFw7oyNsQLFoqtiGcfqByo/AeDbp+ymtlOi+ GxDWztcWfArPDN13Yt822D4+fpWnHJMYcFYdaLYPljSFPd6KIHTsJVbGBkzWpRHs3+mf /iTwbUK+L7anqR+SFI2xR+8wQMNqCrEpYCMesHUk6OdRYXNh1zX/0pORIdeiXno5ZL98 Vc0A== X-Gm-Message-State: AOJu0YwLHGhTBsMoaTSYZuWTEGW0PmQlUl4qa+U1+Fj8RD/r/wfb+2AP 3HJLE6nieuFhRIU2Cr88UkAJKQPe56wyRZa0mgVDvBlezq52ocjrcO3GDaIsfyB+Zv8cy9JrJi6 Am03aT4dSoh7Q8wEt1W/UBpD8vJNcPwBURPxUV8cKICb9S3F2r6xka/BfidCXszrySOsc4Qnm0N liGyHprAsUAMFjXQ== X-Received: by 2002:a5d:4386:0:b0:336:8638:b3f5 with SMTP id i6-20020a5d4386000000b003368638b3f5mr537973wrq.93.1704810409548; Tue, 09 Jan 2024 06:26:49 -0800 (PST) X-Google-Smtp-Source: AGHT+IFxGzdhodfzueFVGFltvQno8oNLSQ6pqkt9B0aIR01Swh8tVTkOwlVAV3foqHFIVgFw1fbg+w== X-Received: by 2002:a5d:4386:0:b0:336:8638:b3f5 with SMTP id i6-20020a5d4386000000b003368638b3f5mr537962wrq.93.1704810409166; Tue, 09 Jan 2024 06:26:49 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id h16-20020a05600004d000b003367ff4aadasm2555328wri.31.2024.01.09.06.26.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:48 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 05/16] gdbserver: convert program_args to a single string Date: Tue, 9 Jan 2024 14:26:28 +0000 Message-Id: <7f1f0fbb51a1b0284548f6ad62ac53692a388f2d.1704809585.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org This commit changes how gdbserver stores the inferior arguments from being a vector of separate arguments into a single string with all of the arguments combined together. Making this change might feel a little strange; intuitively it feels like we would be better off storing the arguments as a vector, but in the context of the upcoming patches, this change makes things easier. First, GDB already stores the inferior arguments as a single string, so doing this moves gdbserver into line with GDB. The common code into which gdbserver calls requires the arguments to be a single string, so currently each target's create_inferior implementation merged the arguments anyway, so all this commit really does is move the merging up the call stack, and store the merged result rather than storing the separate parts. However, the biggest reason for why this commit is needed, is an issue with passing arguments from GDB to gdbserver when starting a new inferior. Consider: (gdb) set args $VAR (gdb) run ... When using a native target the inferior will see the value of $VAR expanded by the shell GDB uses to start the inferior. However, if using an extended-remote target the inferior will see literally $VAR, the unexpanded name of the variable, the reason for this is that, although GDB sends '$VAR' to gdbserver, when gdbserver receives this, it converts this to '\$VAR', which prevents the variable from being expanded by the shell. The reason for this is that construct_inferior_arguments escapes all special shell characters within its arguments, and it is construct_inferior_arguments that is used to combine the separate arguments into a single string. In a later commit I will change construct_inferior_arguments so that it can apply different escaping strategies. When this happens we will want to escape arguments coming from the gdbserver command line differently than arguments coming from GDB (via a vRun packet), which means we need to call construct_inferior_arguments earlier, at the point where we know if the arguments came from the gdbserver command line, or from the vRun packet. This argument escaping issue is discussed in PR gdb/28392. This commit doesn't fix any issues, nor does it change construct_inferior_arguments to actually do different escaping, that will all come in a later commit. This is purely a restructuring. There should be no user visible changes after this commit. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 --- gdbserver/linux-low.cc | 5 ++--- gdbserver/linux-low.h | 2 +- gdbserver/netbsd-low.cc | 6 ++---- gdbserver/netbsd-low.h | 2 +- gdbserver/server.cc | 24 +++++++++++++++++++----- gdbserver/target.h | 6 +++--- gdbserver/win32-low.cc | 7 +++---- gdbserver/win32-low.h | 2 +- 8 files changed, 32 insertions(+), 22 deletions(-) diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc index 4aa011c14ec..640c9f330c7 100644 --- a/gdbserver/linux-low.cc +++ b/gdbserver/linux-low.cc @@ -986,7 +986,7 @@ linux_ptrace_fun () int linux_process_target::create_inferior (const char *program, - const std::vector &program_args) + const std::string &program_args) { client_state &cs = get_client_state (); struct lwp_info *new_lwp; @@ -996,10 +996,9 @@ linux_process_target::create_inferior (const char *program, { maybe_disable_address_space_randomization restore_personality (cs.disable_randomization); - std::string str_program_args = construct_inferior_arguments (program_args); pid = fork_inferior (program, - str_program_args.c_str (), + program_args.c_str (), get_environ ()->envp (), linux_ptrace_fun, NULL, NULL, NULL, NULL); } diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h index 51d1899893a..a8d7a93aaeb 100644 --- a/gdbserver/linux-low.h +++ b/gdbserver/linux-low.h @@ -141,7 +141,7 @@ class linux_process_target : public process_stratum_target public: int create_inferior (const char *program, - const std::vector &program_args) override; + const std::string &program_args) override; void post_create_inferior () override; diff --git a/gdbserver/netbsd-low.cc b/gdbserver/netbsd-low.cc index 10d8d280b98..0fce43299ac 100644 --- a/gdbserver/netbsd-low.cc +++ b/gdbserver/netbsd-low.cc @@ -79,11 +79,9 @@ netbsd_ptrace_fun () int netbsd_process_target::create_inferior (const char *program, - const std::vector &program_args) + const std::string &program_args) { - std::string str_program_args = construct_inferior_arguments (program_args); - - pid_t pid = fork_inferior (program, str_program_args.c_str (), + pid_t pid = fork_inferior (program, program_args.c_str (), get_environ ()->envp (), netbsd_ptrace_fun, nullptr, nullptr, nullptr, nullptr); diff --git a/gdbserver/netbsd-low.h b/gdbserver/netbsd-low.h index 050b43fc54f..d62fbcf4c7a 100644 --- a/gdbserver/netbsd-low.h +++ b/gdbserver/netbsd-low.h @@ -42,7 +42,7 @@ class netbsd_process_target : public process_stratum_target public: int create_inferior (const char *program, - const std::vector &program_args) override; + const std::string &program_args) override; void post_create_inferior () override; diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 2ec97d7ade4..508e42ee097 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -122,7 +122,20 @@ private: /* The program name, adjusted if needed. */ std::string m_path; } program_path; -static std::vector program_args; + +/* All program arguments are merged into a single string. This is similar + to how GDB manages the inferior arguments, and actually makes our lives + easier; the rules for how arguments are merged into a single string + differ depending on where the arguments come from. Arguments arriving + form the gdbserver command line are quoted, while arguments arriving + from GDB (via a vRun packet) are already quoted. + + NOTE: The comment above is ahead of its time. The differences between + how the PROGRAM_ARGS string is built up have not yet been implemented. + A later patch in this series will make this change, and remove this + note. */ +static std::string program_args; + static std::string wrapper_argv; /* The PID of the originally created or attached inferior. Used to @@ -3424,9 +3437,8 @@ handle_v_run (char *own_buf) else program_path.set (new_program_name.get ()); - /* Free the old argv and install the new one. */ - free_vector_argv (program_args); - program_args = new_argv; + program_args = construct_inferior_arguments (new_argv); + free_vector_argv (new_argv); target_create_inferior (program_path.get (), program_args); @@ -4304,8 +4316,10 @@ captured_main (int argc, char *argv[]) n = argc - (next_arg - argv); program_path.set (next_arg[0]); + std::vector temp_arg_vector; for (i = 1; i < n; i++) - program_args.push_back (xstrdup (next_arg[i])); + temp_arg_vector.push_back (next_arg[i]); + program_args = construct_inferior_arguments (temp_arg_vector); /* Wait till we are at first instruction in program. */ target_create_inferior (program_path.get (), program_args); diff --git a/gdbserver/target.h b/gdbserver/target.h index 28d134e7915..5757aa6b84e 100644 --- a/gdbserver/target.h +++ b/gdbserver/target.h @@ -77,13 +77,13 @@ class process_stratum_target /* Start a new process. PROGRAM is a path to the program to execute. - PROGRAM_ARGS is a standard NULL-terminated array of arguments, - to be passed to the inferior as ``argv'' (along with PROGRAM). + PROGRAM_ARGS is a string containing all of the arguments that will be + used to start the inferior. Returns the new PID on success, -1 on failure. Registers the new process with the process list. */ virtual int create_inferior (const char *program, - const std::vector &program_args) = 0; + const std::string &program_args) = 0; /* Do additional setup after a new process is created, including exec-wrapper completion. */ diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc index 90d9510c7b9..e991152a29e 100644 --- a/gdbserver/win32-low.cc +++ b/gdbserver/win32-low.cc @@ -512,12 +512,12 @@ create_process (const char *program, char *args, /* Start a new process. PROGRAM is the program name. - PROGRAM_ARGS is the vector containing the inferior's args. + PROGRAM_ARGS is a string containing all the inferior's arguments. Returns the new PID on success, -1 on failure. Registers the new process with the process list. */ int win32_process_target::create_inferior (const char *program, - const std::vector &program_args) + const std::string &program_args) { client_state &cs = get_client_state (); #ifndef USE_WIN32API @@ -528,8 +528,7 @@ win32_process_target::create_inferior (const char *program, DWORD flags; PROCESS_INFORMATION pi; DWORD err; - std::string str_program_args = construct_inferior_arguments (program_args); - char *args = (char *) str_program_args.c_str (); + char *args = (char *) program_args.c_str (); /* win32_wait needs to know we're not attaching. */ windows_process.attaching = 0; diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h index 6a0b8cf9c84..1526567bfad 100644 --- a/gdbserver/win32-low.h +++ b/gdbserver/win32-low.h @@ -94,7 +94,7 @@ class win32_process_target : public process_stratum_target public: int create_inferior (const char *program, - const std::vector &program_args) override; + const std::string &program_args) override; int attach (unsigned long pid) override; From patchwork Tue Jan 9 14:26:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83646 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 180F43857BBC for ; Tue, 9 Jan 2024 14:30:51 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id B5B7D3858291 for ; Tue, 9 Jan 2024 14:26:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B5B7D3858291 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 B5B7D3858291 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=1704810418; cv=none; b=LpbtroWc3MRLJ/Bx61fkKy7tvlHaG8YZWJmhNWOG3h5/cmITUtgNszcvEnP4+22NqECEJGetKXzPHg0sbJbMW26WRTbHYA0L2KGapS5uH82wM2CjzVHcwg7p5w95BJeNCmWBqK1/X2Vv7l5wrRl6nol+SAnYsV6TJBQ6nryhZxI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810418; c=relaxed/simple; bh=AU5jWBPNeMcAOSKBQwc9QvNlx2xXjxW9Z1RfZUJZK60=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=BqpMXMXQjtveTwi0Xm/t69cohv00V9ZU8DJFjlITszm31XG6LzL7cJ809VKTW8OJLktgO0SR3Gh+YsIO+r4YUtA+tztpbrineLiIl63fun1IY+TdSW74Gu449DppxSpinkJu4t7TXE4X+lSn1/x6e5O6aIk06ua9mybH8p8Ml2Y= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810415; 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: in-reply-to:in-reply-to:references:references; bh=do3L8q1b9TPWiVBLQcvd4iOH85pLjTaUHQfEj9rw4is=; b=h8OKOR6dTkttg5iQVMbENAkpCMQ76hYrH8+SDmmJ+DEkdMYMijw6JzoxAJyCjzoIbypfeg oPvVnYqKs7+sDfgFSNogm7EgwybRExKnJaVNElwdOZcTOLT6AdeMN9tOK+nrQoIGGHq2lS f93LAeu+ES1ML4iNTlkeSHBjwoiJYYw= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-433-Po2sHzFFOzW_Bu4DyWmCZQ-1; Tue, 09 Jan 2024 09:26:53 -0500 X-MC-Unique: Po2sHzFFOzW_Bu4DyWmCZQ-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-40d5d0de143so24976125e9.1 for ; Tue, 09 Jan 2024 06:26:53 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810411; x=1705415211; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=do3L8q1b9TPWiVBLQcvd4iOH85pLjTaUHQfEj9rw4is=; b=QwJbKcQZq3FtwV/Ov6H39fHH6EsERdmOgsd4f11RO1eXvbBHC2MEvV7D9afTjuMkzg bxB8l8C6OSFMBe+8QCuH+HrB0zUpqfPhnVdDD8cGM+9RrHdUk3Tr0YeM9KeFhTrstAOH bcvkI+5I/7wnbj7WzXVO9JctB12aKb25s9NHp/KgHEegn6YOGFqFB0jrDWcN8sZ18jQP vWYPQC2VriAIUDHw2Sg4D2BTBhUqb8Edp07ZaXQ3M89yfW8wA7d5nNkr8nrnTikk12JZ N586eNa58iHQdBFXZD9b0mggkP2tZ9E41HIOjh+K/8ywBHgNZhhuxhsv9ZV1UDRS82iG KNSA== X-Gm-Message-State: AOJu0Yw5VFU4Mtv8VClkxhEDAmFF3iF6y3jPTL8sZHUznaIKdaaF9DYy 3ohiwyKVMUEji18Fbc+a8IIaazyl5Q53mIB59RqhA6C00G3IfACDFmxT0qeBG1Yghlw/DbVF3F1 jDAsLayZ9L+ccsHZxA2l/TBGxzAHxAQUE8qduqzuJteizEjKABRkhlEm7JbvRNXJWWKpbme1q1q Yy0YFQAY9uQhu2Mg== X-Received: by 2002:a1c:4b15:0:b0:40d:5f62:773e with SMTP id y21-20020a1c4b15000000b0040d5f62773emr1929384wma.184.1704810411252; Tue, 09 Jan 2024 06:26:51 -0800 (PST) X-Google-Smtp-Source: AGHT+IHUUNb9LkX3sU5FYkfOo1PgLdHkOK+cr3dL6T6y3O0xBeytVJudr34OCwqXcXdMzdYOrZ4Oig== X-Received: by 2002:a1c:4b15:0:b0:40d:5f62:773e with SMTP id y21-20020a1c4b15000000b0040d5f62773emr1929377wma.184.1704810410802; Tue, 09 Jan 2024 06:26:50 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id d4-20020adfe884000000b003367a5b6b69sm2543934wrm.106.2024.01.09.06.26.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:50 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 06/16] gdbsupport: have construct_inferior_arguments take an escape function Date: Tue, 9 Jan 2024 14:26:29 +0000 Message-Id: <1ed825102ecd1f7582659d48d1ac0d5ecdf571e5.1704809585.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org This commit is a refactor in order to make later commits in this series easier, and continues the work started in the previous commit. The construct_inferior_arguments function takes a vector of strings and does two things: 1. Applies escaping to each string, and 2. Combines all the individual strings into a single string, placing a space between each. The construct_inferior_arguments function is used in a couple of places within GDB and gdbserver, and in each place, the same escaping algorithm is used; it has to be, the algorithm is hard-coded into the function construct_inferior_arguments. However, in later commits I will propose that in different situations we should actually apply different escaping algorithms, depending on whether the incoming arguments are already escaped or not. As a simple concrete example, if we invoke current GDB like this: $ gdb -q --args /tmp/application '$SHELL' ... (gdb) show args Argument list to give program being debugged when it is started is "\$SHELL". Here you can see an example of the default escaping used by construct_inferior_arguments, special shell characters are escaped (the '$' becomes '\$'). The downside of this is that the application will see a literal '$SHELL' string, not the shell expanded $SHELL variable value (e.g. /bin/bash). There's currently no way to get the second behaviour from the GDB command line. This is discussed in PR gdb/28392. If we wanted to add a new GDB command line flag, maybe like this: $ gdb -q --no-escape-args /tmp/application '$SHELL' ... (gdb) show args Argument list to give program being debugged when it is started is "$SHELL". Now, the argument string contains '$SHELL', with no escaping. When the inferior is run (with startup-with-shell on), the application will see the shell expanded value of $SHELL. To achieve this we need construct_inferior_arguments to apply a different escaping algorithm. Another example of where the current construct_inferior_arguments behaviour is not correct is in gdbserver. Like GDB, arguments from the command line are escaped, however, we currently also escape arguments arriving in the vRun packet. Thus, if in GDB we do this: (gdb) set args $SHELL And then run an extended-remote target, the $SHELL will be escaped when it is passed to gdbserver, gdbserver will then hold \$SHELL, which means the inferior will see a literal '$SHELL' string, rather than the shell expanded value of the variable. This is a real bug that exists today in GDB/gdbserver. Given the upcoming patches, I think there is more than a boolean choice between two escaping algorithms, so, at least for now, I propose that construct_inferior_arguments should take a function pointer for a function that will perform the argument escaping. If it turns out that this is excessive then it is easy enough to fold the algorithm selecting back into construct_inferior_arguments and instead pass a bool or enum to choose the correct algorithm, but I think this approach is simple enough. After this commit construct_inferior_arguments merges all the arguments into a single string, using the worker function to escape each argument in turn. However, this commit doesn't actually fix any of the above issues. This is a restructuring commit. All this commit does is change construct_inferior_arguments to take the escaping function, I've split the existing escaping function out from construct_inferior_arguments, and I've updated every call to construct_inferior_arguments to pass in the one and only escaping function. As a result there should be no change in behaviour after this commit. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 --- gdb/inferior.c | 5 ++- gdb/inferior.h | 3 +- gdb/main.c | 4 +- gdb/python/py-inferior.c | 2 +- gdbserver/server.cc | 6 ++- gdbsupport/common-inferior.cc | 81 ++++++++++++++++++++--------------- gdbsupport/common-inferior.h | 17 ++++++-- 7 files changed, 73 insertions(+), 45 deletions(-) diff --git a/gdb/inferior.c b/gdb/inferior.c index 076801db51b..ed138888024 100644 --- a/gdb/inferior.c +++ b/gdb/inferior.c @@ -168,9 +168,10 @@ inferior::tty () /* See inferior.h. */ void -inferior::set_args (gdb::array_view args) +inferior::set_args (gdb::array_view args, + escape_args_func escape_func) { - set_args (construct_inferior_arguments (args)); + set_args (construct_inferior_arguments (args, escape_func)); } void diff --git a/gdb/inferior.h b/gdb/inferior.h index e5173e0adac..d3824236ef6 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -526,7 +526,8 @@ class inferior : public refcounted_object, }; /* Set the argument string from some strings. */ - void set_args (gdb::array_view args); + void set_args (gdb::array_view args, + escape_args_func escape_func); /* Get the argument string to use when running this inferior. diff --git a/gdb/main.c b/gdb/main.c index 688db7655a9..015ed396f58 100644 --- a/gdb/main.c +++ b/gdb/main.c @@ -1084,8 +1084,8 @@ captured_main_1 (struct captured_main_args *context) symarg = argv[optind]; execarg = argv[optind]; ++optind; - current_inferior ()->set_args - (gdb::array_view (&argv[optind], argc - optind)); + gdb::array_view arg_view (&argv[optind], argc - optind); + current_inferior ()->set_args (arg_view, escape_shell_characters); } else { diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c index ed153d668ac..8641d8a068b 100644 --- a/gdb/python/py-inferior.c +++ b/gdb/python/py-inferior.c @@ -919,7 +919,7 @@ infpy_set_args (PyObject *self, PyObject *value, void *closure) for (const auto &arg : args) argvec.push_back (arg.get ()); gdb::array_view view (argvec.data (), argvec.size ()); - inf->inferior->set_args (view); + inf->inferior->set_args (view, escape_shell_characters); } else { diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 508e42ee097..52ce9240ca3 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -3437,7 +3437,8 @@ handle_v_run (char *own_buf) else program_path.set (new_program_name.get ()); - program_args = construct_inferior_arguments (new_argv); + program_args = construct_inferior_arguments (new_argv, + escape_shell_characters); free_vector_argv (new_argv); target_create_inferior (program_path.get (), program_args); @@ -4319,7 +4320,8 @@ captured_main (int argc, char *argv[]) std::vector temp_arg_vector; for (i = 1; i < n; i++) temp_arg_vector.push_back (next_arg[i]); - program_args = construct_inferior_arguments (temp_arg_vector); + program_args = construct_inferior_arguments (temp_arg_vector, + escape_shell_characters); /* Wait till we are at first instruction in program. */ target_create_inferior (program_path.get (), program_args); diff --git a/gdbsupport/common-inferior.cc b/gdbsupport/common-inferior.cc index 076ddc73d51..f5620ec89aa 100644 --- a/gdbsupport/common-inferior.cc +++ b/gdbsupport/common-inferior.cc @@ -28,7 +28,26 @@ bool startup_with_shell = true; /* See common-inferior.h. */ std::string -construct_inferior_arguments (gdb::array_view argv) +construct_inferior_arguments (gdb::array_view argv, + escape_args_func escape_func) +{ + std::string result; + + for (const char *a : argv) + { + if (!result.empty ()) + result += " "; + + result += escape_func (a); + } + + return result; +} + +/* See common-inferior.h. */ + +std::string +escape_shell_characters (const char *arg) { std::string result; @@ -44,55 +63,49 @@ construct_inferior_arguments (gdb::array_view argv) static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n"; static const char quote = '\''; #endif - for (int i = 0; i < argv.size (); ++i) + + /* Need to handle empty arguments specially. */ + if (arg[0] == '\0') + { + result += quote; + result += quote; + } + else { - if (i > 0) - result += ' '; +#ifdef __MINGW32__ + bool quoted = false; - /* Need to handle empty arguments specially. */ - if (argv[i][0] == '\0') + if (strpbrk (arg, special)) { - result += quote; + quoted = true; result += quote; } - else +#endif + for (const char *cp = arg; *cp; ++cp) { -#ifdef __MINGW32__ - bool quoted = false; - - if (strpbrk (argv[i], special)) + if (*cp == '\n') { - quoted = true; + /* A newline cannot be quoted with a backslash (it just + disappears), only by putting it inside quotes. */ + result += quote; + result += '\n'; result += quote; } -#endif - for (char *cp = argv[i]; *cp; ++cp) + else { - if (*cp == '\n') - { - /* A newline cannot be quoted with a backslash (it - just disappears), only by putting it inside - quotes. */ - result += quote; - result += '\n'; - result += quote; - } - else - { #ifdef __MINGW32__ - if (*cp == quote) + if (*cp == quote) #else - if (strchr (special, *cp) != NULL) + if (strchr (special, *cp) != NULL) #endif - result += '\\'; - result += *cp; - } + result += '\\'; + result += *cp; } + } #ifdef __MINGW32__ - if (quoted) - result += quote; + if (quoted) + result += quote; #endif - } } return result; diff --git a/gdbsupport/common-inferior.h b/gdbsupport/common-inferior.h index c468c7bf6a8..92d1954c3fe 100644 --- a/gdbsupport/common-inferior.h +++ b/gdbsupport/common-inferior.h @@ -57,9 +57,20 @@ extern const std::string &get_inferior_cwd (); the target is started up with a shell. */ extern bool startup_with_shell; -/* Compute command-line string given argument vector. This does the - same shell processing as fork_inferior. */ +/* The type for a function used by construct_inferior_arguments to add any + quoting needed to an individual argument before combining all the + arguments into a single string. */ +using escape_args_func = std::string (*) (const char *arg); + +/* Return a version of ARG that has special shell characters escaped. */ +extern std::string escape_shell_characters (const char *arg); + +/* Pass each element of ARGS through ESCAPE_FUNC and combine the results + into a single string, separating each element with a single space + character. */ + extern std::string -construct_inferior_arguments (gdb::array_view); +construct_inferior_arguments (gdb::array_view args, + escape_args_func escape_func); #endif /* COMMON_COMMON_INFERIOR_H */ From patchwork Tue Jan 9 14:26:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83642 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D1CA9385782D for ; Tue, 9 Jan 2024 14:29:27 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 63ABF38582B0 for ; Tue, 9 Jan 2024 14:26:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 63ABF38582B0 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 63ABF38582B0 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=1704810418; cv=none; b=DwnNrJz6dEixWAMTdEyMDAGRuMVTmSn+HzAkPplBX8wzBt7wYUZLaKEpwSo1IgLAUhOAkemuv2tknmYnoa+g8iotMT8GhnOc6dHVer04SDK9U4/IL6SnIbGtsYRcOD/jprvuOX+YsMayGf/DPvMNHH1pvjP/+x1cKggnvNwBzYU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810418; c=relaxed/simple; bh=dJE7p7PX34zVVYrp8Q+50Ibi6E7eWyJLZRXx53S7DUE=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=o8SqHJoIrGF8eGILpKUzP6uDRKhNl6n8AcoUp4i2O9+GRFSReYjh1s8SE0/inKCCUvebL0vFICRXy8P7XumS3f28Ob8cke8czlQO4pRcZkAe48Ms6EJUdKEixILvuQbtYyO68Qvzy8U/jZw44UNJF9UuVNmcNTthDl9KiPhVbqs= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810416; 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: in-reply-to:in-reply-to:references:references; bh=T1p2z0cZ55nb7B40+fQX+qArCu9REZLI9TCzfUxWfeI=; b=KnjZHUO+bn9T/75pDS2rq3mpCWWFD0KWBZBeKG+iFXgo+GZ1S0pjvXGcQkRcxJbemei6Um DnBtJ3aSs1oSlO96wefx8IdMne2lBohUTvoD2I/xD36jujpvkauIRknjy2Kqtbi71k3NFc nYeStGTX3MXusa4/HGjWVdlVqrAqX4Y= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-689-0MjcbojfNtOWiThh0EWcdQ-1; Tue, 09 Jan 2024 09:26:54 -0500 X-MC-Unique: 0MjcbojfNtOWiThh0EWcdQ-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-33768a5aea7so1519773f8f.3 for ; Tue, 09 Jan 2024 06:26:54 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810413; x=1705415213; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=T1p2z0cZ55nb7B40+fQX+qArCu9REZLI9TCzfUxWfeI=; b=Hoe84fnMO+T7Ii7VcxXLdXe3Z+60hQX3iibcdGHbLXJ/H9mpNZn3vxN4WzB8NjVdUD ixQDfCzqT9LPFb8cGKLvKMTpMz8+/oyQjcsWyLhYHc0GVJFkbWqBNlPnemR4FGAb753I nvoVyJstJjg7gsz4I57azhhqZf924zCgH0Q5P3xYWSnbClEfK03Bx1HbUXSWwVH6wNId CY4p56waylKtbg8H2OZ8zyAZTkcZ4L4RpXM5NKW0iwT7TubAvV8kxWYFVEhBmBDGPBH4 ywRh8qecI+z55/SYpewsnZOWNQNW6xIxgqVxRtXM7BpjYa6nmB0oOvaYDFwLfFgXwuIp vFTg== X-Gm-Message-State: AOJu0Yzo8XRiG2j714RonU5FvkIzkmx9ldb+33wiAVllTfUmJdhqJPt9 y1XBUW5qRftpyNdvdEwq+2BBYF4wvY2f82u5Qiemd2p/9MvYsOmreZqtymdoNYN6pYzhUwu8QwR lW+U07H3ZcdH6tnrbJbQjkz/dAmAOWLjjuw5VniNQPZ0sviiVonDxBsdVikJDBQM+u5ZBsBDi0s 9gl1Wd6L7xduqHLA== X-Received: by 2002:adf:f343:0:b0:336:7632:cdbb with SMTP id e3-20020adff343000000b003367632cdbbmr663355wrp.22.1704810412841; Tue, 09 Jan 2024 06:26:52 -0800 (PST) X-Google-Smtp-Source: AGHT+IFvNfpDGcuTySfMHC/LGsPTUEAYGj7t9NgY/s8Hkg4JJGVUlZkiuFwGapCxbNVXKtforguWEw== X-Received: by 2002:adf:f343:0:b0:336:7632:cdbb with SMTP id e3-20020adff343000000b003367632cdbbmr663348wrp.22.1704810412540; Tue, 09 Jan 2024 06:26:52 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id d7-20020adfe887000000b0033672971fabsm2528406wrm.115.2024.01.09.06.26.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:51 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 07/16] gdbsupport: split escape_shell_characters in two Date: Tue, 9 Jan 2024 14:26:30 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org Building on the previous commit, this commit performs further refactoring. The escape_shell_characters function is split into two. We have a worker core (the escape_characters function) which takes a set of "special" characters that need escaping, and applies that escaping. Then we have escape_shell_characters, which just calls escape_characters with the correct set of special characters. There should be no user visible changes after this commit. This commit is similar to some of the changes made in this series: https://inbox.sourceware.org/gdb-patches/20211022071933.3478427-1-m.weghorn@posteo.de/ Though I don't think there's one commit that is exactly the same as this one. But I've listed the author of the original series as a Co-Author, because I feel the work is similar enough. Co-Authored-By: Michael Weghorn --- gdbsupport/common-inferior.cc | 49 +++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/gdbsupport/common-inferior.cc b/gdbsupport/common-inferior.cc index f5620ec89aa..6717f7d5c08 100644 --- a/gdbsupport/common-inferior.cc +++ b/gdbsupport/common-inferior.cc @@ -44,23 +44,23 @@ construct_inferior_arguments (gdb::array_view argv, return result; } -/* See common-inferior.h. */ - -std::string -escape_shell_characters (const char *arg) +/* Escape characters in ARG and return an updated string. The string + SPECIAL contains the set of characters that must be escaped. SPECIAL + must not be nullptr, and it is assumed that SPECIAL contains the newline + '\n' character. It is assumed that ARG is not nullptr, but ARG can + be the empty string. */ + +static std::string +escape_characters (const char *arg, const char *special) { + gdb_assert (special != nullptr); + gdb_assert (arg != nullptr); + std::string result; #ifdef __MINGW32__ - /* This holds all the characters considered special to the - Windows shells. */ - static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n"; static const char quote = '"'; #else - /* This holds all the characters considered special to the - typical Unix shells. We include `^' because the SunOS - /bin/sh treats it as a synonym for `|'. */ - static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n"; static const char quote = '\''; #endif @@ -70,12 +70,16 @@ escape_shell_characters (const char *arg) result += quote; result += quote; } + /* The special character handling code here assumes that if SPECIAL is + not nullptr, then SPECIAL will contain '\n'. This is true for all our + current usages, but if this ever changes in the future the following + might need reworking. */ else { #ifdef __MINGW32__ bool quoted = false; - if (strpbrk (arg, special)) + if (strpbrk (argv[i], special)) { quoted = true; result += quote; @@ -96,7 +100,7 @@ escape_shell_characters (const char *arg) #ifdef __MINGW32__ if (*cp == quote) #else - if (strchr (special, *cp) != NULL) + if (strchr (special, *cp) != nullptr) #endif result += '\\'; result += *cp; @@ -110,3 +114,22 @@ escape_shell_characters (const char *arg) return result; } + +/* See common-inferior.h. */ + +std::string +escape_shell_characters (const char *arg) +{ +#ifdef __MINGW32__ + /* This holds all the characters considered special to the + Windows shells. */ + static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n"; +#else + /* This holds all the characters considered special to the + typical Unix shells. We include `^' because the SunOS + /bin/sh treats it as a synonym for `|'. */ + static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n"; +#endif + + return escape_characters (arg, special); +} From patchwork Tue Jan 9 14:26:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83650 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id BDC263857B92 for ; Tue, 9 Jan 2024 14:31:32 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id D627F385843E for ; Tue, 9 Jan 2024 14:26:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D627F385843E 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 D627F385843E Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810421; cv=none; b=ry3M93sTX14QkLAleKsqc5jpk79zklbvjgKBzs45ztx+A8O4trgcFHlVbVgUhzmCfkfyFAapdESxbc7XVGd4z3ECTbt8IfTcbgdCFyAfcNmUBFXmpyIVkqntZRPv9VIJqGL5P5//uWAbd2qwF5c51NSNGa3yOeitAojl7XcsFCU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810421; c=relaxed/simple; bh=cZX1I+OEGikbzD1fTAV6PsOPF7O3yiHurx1mYuRWDvI=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=sCLp0Bh8so/reY73Jsa6+3ydVxfzJukWDIZzkeDXuLwWAu0y8GeWlV6PRLzma+Zok769r9+1ApZrgtH8JIxhJeTm29iDRDzF0JZRN9VJI+L8BSiKVM94x9FEI/Pe/pRGoFPz/Uigz6VADIOEPJqTp8Gj+JBvhf3scYqdV/8Jry4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810417; 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: in-reply-to:in-reply-to:references:references; bh=ZmhUqlnwj7sOwcBEJcy9Nz4mNrNDX8wDgNmcIIQo85I=; b=G4A8EiFhHBfyg56iTbRSiCHXdMydXPNWJHV5s/EEc69SdepaRQbhmC4rw4i/wwha1q97aS artArxF3Mrk68FlcxG/ckkWKt9lov/QlZYdCtEI+DDadM0H4dYFn15vOeMPTdUrw/57gBu dUPBTKY1HqmQHxdjdIP2nHxRLkdhBTk= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-90-CHtmejjVNhilo__aAQXX0g-1; Tue, 09 Jan 2024 09:26:56 -0500 X-MC-Unique: CHtmejjVNhilo__aAQXX0g-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-40e4576b7e6so17451735e9.0 for ; Tue, 09 Jan 2024 06:26:55 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810414; x=1705415214; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ZmhUqlnwj7sOwcBEJcy9Nz4mNrNDX8wDgNmcIIQo85I=; b=sxs65GJaOWOLvDqPJeUmKHtAxiwa9KpwEIBDVXb98ZD9P198Rv8qjijbv2MUBVmwf1 6D2DykvCUao47U8fFA4vENHnsizP0Zq9AyIZaa6tqLuldYrK7J82KlGoJjyasF/MTmwS V8+2MNGyIZux5IR7cv+G2F5O1qQf/MYxpmkBqxfwHHBh+cWmxzp1sfdd5KB+bYvkVOdw jwg66Bf+hfeey16p6QSiG2VDyBfaRDjMGT4qtgGX5IH82f/4QjmT7nKySPnd7/utzmuX AMJqOQEdtF56cEYGpwKk66tsS+n9a3iQ44JT3POhzz7jIsv2gAEIsFhYPnlbz1K2ryss VVDQ== X-Gm-Message-State: AOJu0YzJ0ccHbqPjSX0EDjTkYaWgwdPLcbRIiSUcCvTxfwi1OzMaG8gk 2XfEMvOGhNwoVINBBFlpNQum8swtbADYqLSjGSxOmkS0LCarZC9kF3D9S1DcBElxwnXO8W3Yiwx bjtSWgSJzYxPp2zYIzHiR8H5gUl/DGMR5FoWfDhQRC1PG/CqjLIEfTsm6B1F86UdHrH/EJzKUG/ kuXCmN91I4pTvZfA== X-Received: by 2002:a05:600c:3590:b0:40d:8dee:26fb with SMTP id p16-20020a05600c359000b0040d8dee26fbmr1868238wmq.251.1704810414408; Tue, 09 Jan 2024 06:26:54 -0800 (PST) X-Google-Smtp-Source: AGHT+IGBrrfMmj9VuTHYTITt2rklw6zcsO2MfBlYDVj1mY9bRpiO2eXKmeWpmc/p8Ann1EBJweyZcQ== X-Received: by 2002:a05:600c:3590:b0:40d:8dee:26fb with SMTP id p16-20020a05600c359000b0040d8dee26fbmr1868230wmq.251.1704810414053; Tue, 09 Jan 2024 06:26:54 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id v11-20020a05600c444b00b0040e34835a58sm3796808wmn.22.2024.01.09.06.26.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:53 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 08/16] gdb: move remote arg splitting and joining into gdbsupport/ Date: Tue, 9 Jan 2024 14:26:31 +0000 Message-Id: <0b19b3f83eb1df36d1d4bfa2ccdb5ca9754f095f.1704809585.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org This is a refactoring commit. When passing inferior arguments to gdbserver we have two actions that need to be performed, splitting and joining. On the GDB side, we take the inferior arguments, a single string, and split the string into a list of individual arguments. These are then sent to gdbserver over the remote protocol. On the gdbserver side we receive the list of individual arguments and join these back together into a single inferior argument string. In a later commit I would like to add some unit testing for this remote argument passing process. Ideally, for unit testing, we want to use the exact same code for splitting and joining the arguments as is actually used -- we could duplicate the code within the unit test, and this would validate the algorithm as it is today, but there is always the risk that a future change would not be mirrored within the tests, which makes the tests useless. So in this commit I propose to move the splitting and joining logic out into a separate file, we can then use this within GDB and gdbserver when passing arguments between GDB and gdbserver, but we can also use the exact same code for some unit tests. In this commit I'm not adding the unit tests, they will be added later in this series, so for now there should be no user visible changes after this commit. --- gdb/remote.c | 12 +++++------ gdbserver/server.cc | 4 ++-- gdbsupport/Makefile.am | 1 + gdbsupport/Makefile.in | 13 +++++++----- gdbsupport/remote-args.cc | 43 ++++++++++++++++++++++++++++++++++++++ gdbsupport/remote-args.h | 44 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 gdbsupport/remote-args.cc create mode 100644 gdbsupport/remote-args.h diff --git a/gdb/remote.c b/gdb/remote.c index dcc1a0d0639..ebef409ffed 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -79,6 +79,7 @@ #include #include "async-event.h" #include "gdbsupport/selftest.h" +#include "gdbsupport/remote-args.h" /* The remote target. */ @@ -10625,16 +10626,15 @@ remote_target::extended_remote_run (const std::string &args) if (!args.empty ()) { - int i; + std::vector split_args = gdb::remote_args::split (args); - gdb_argv argv (args.c_str ()); - for (i = 0; argv[i] != NULL; i++) + for (const auto &a : split_args) { - if (strlen (argv[i]) * 2 + 1 + len >= get_remote_packet_size ()) + if (a.size () * 2 + 1 + len >= get_remote_packet_size ()) error (_("Argument list too long for run packet")); rs->buf[len++] = ';'; - len += 2 * bin2hex ((gdb_byte *) argv[i], rs->buf.data () + len, - strlen (argv[i])); + len += 2 * bin2hex ((gdb_byte *) a.c_str (), rs->buf.data () + len, + a.size ()); } } diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 52ce9240ca3..13abb0b7636 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -51,6 +51,7 @@ #include "gdbsupport/gdb_select.h" #include "gdbsupport/scoped_restore.h" #include "gdbsupport/search.h" +#include "gdbsupport/remote-args.h" /* PBUFSIZ must also be at least as big as IPA_CMD_BUF_SIZE, because the client state data is passed directly to some agent @@ -3437,8 +3438,7 @@ handle_v_run (char *own_buf) else program_path.set (new_program_name.get ()); - program_args = construct_inferior_arguments (new_argv, - escape_shell_characters); + program_args = gdb::remote_args::join (new_argv); free_vector_argv (new_argv); target_create_inferior (program_path.get (), program_args); diff --git a/gdbsupport/Makefile.am b/gdbsupport/Makefile.am index f1a641308fe..6f5a5ef36c6 100644 --- a/gdbsupport/Makefile.am +++ b/gdbsupport/Makefile.am @@ -72,6 +72,7 @@ libgdbsupport_a_SOURCES = \ pathstuff.cc \ print-utils.cc \ ptid.cc \ + remote-args.cc \ rsp-low.cc \ run-time-clock.cc \ safe-strerror.cc \ diff --git a/gdbsupport/Makefile.in b/gdbsupport/Makefile.in index 9fdc23c39a9..5d860302baa 100644 --- a/gdbsupport/Makefile.in +++ b/gdbsupport/Makefile.in @@ -163,11 +163,12 @@ am_libgdbsupport_a_OBJECTS = agent.$(OBJEXT) btrace-common.$(OBJEXT) \ gdb_tilde_expand.$(OBJEXT) gdb_wait.$(OBJEXT) \ gdb_vecs.$(OBJEXT) job-control.$(OBJEXT) netstuff.$(OBJEXT) \ new-op.$(OBJEXT) pathstuff.$(OBJEXT) print-utils.$(OBJEXT) \ - ptid.$(OBJEXT) rsp-low.$(OBJEXT) run-time-clock.$(OBJEXT) \ - safe-strerror.$(OBJEXT) scoped_mmap.$(OBJEXT) search.$(OBJEXT) \ - signals.$(OBJEXT) signals-state-save-restore.$(OBJEXT) \ - tdesc.$(OBJEXT) thread-pool.$(OBJEXT) xml-utils.$(OBJEXT) \ - $(am__objects_1) $(am__objects_2) + ptid.$(OBJEXT) remote-args.$(OBJEXT) rsp-low.$(OBJEXT) \ + run-time-clock.$(OBJEXT) safe-strerror.$(OBJEXT) \ + scoped_mmap.$(OBJEXT) search.$(OBJEXT) signals.$(OBJEXT) \ + signals-state-save-restore.$(OBJEXT) tdesc.$(OBJEXT) \ + thread-pool.$(OBJEXT) xml-utils.$(OBJEXT) $(am__objects_1) \ + $(am__objects_2) libgdbsupport_a_OBJECTS = $(am_libgdbsupport_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -429,6 +430,7 @@ libgdbsupport_a_SOURCES = \ pathstuff.cc \ print-utils.cc \ ptid.cc \ + remote-args.cc \ rsp-low.cc \ run-time-clock.cc \ safe-strerror.cc \ @@ -538,6 +540,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pathstuff.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-utils.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote-args.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsp-low.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run-time-clock.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe-strerror.Po@am__quote@ diff --git a/gdbsupport/remote-args.cc b/gdbsupport/remote-args.cc new file mode 100644 index 00000000000..96c12ffac67 --- /dev/null +++ b/gdbsupport/remote-args.cc @@ -0,0 +1,43 @@ +/* Copyright (C) 2023 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +#include "gdbsupport/common-defs.h" +#include "gdbsupport/remote-args.h" +#include "gdbsupport/common-inferior.h" +#include "gdbsupport/buildargv.h" + +/* See remote-args.h. */ + +std::vector +gdb::remote_args::split (std::string args) +{ + std::vector results; + + gdb_argv argv (args.c_str ()); + for (int i = 0; argv[i] != nullptr; i++) + results.emplace_back (argv[i]); + + return results; +} + +/* See remote-args.h. */ + +std::string +gdb::remote_args::join (std::vector &args) +{ + return construct_inferior_arguments (args, escape_shell_characters); +} diff --git a/gdbsupport/remote-args.h b/gdbsupport/remote-args.h new file mode 100644 index 00000000000..c0acce9b7c4 --- /dev/null +++ b/gdbsupport/remote-args.h @@ -0,0 +1,44 @@ +/* Functions to help when passing arguments between GDB and gdbserver. + + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +namespace gdb +{ + +namespace remote_args +{ + +/* ARGS is an inferior argument string. This function splits ARGS into + individual arguments and returns a vector containing each argument. */ + +extern std::vector split (std::string args); + +/* Join together the separate arguments in ARGS and build a single + inferior argument string. The string returned by this function will be + equivalent, but not necessarily identical to the string passed to + ::split, for example passing the string '"a b"' (without the single + quotes, but including the double quotes) to ::split, will return an + argument of 'a b' (without the single quotes). When this argument is + passed through ::join we will get back the string 'a\ b' (without the + single quotes), that is, we choose to escape the white space, rather + than wrap the argument in quotes. */ +extern std::string join (std::vector &args); + +} /* namespace remote_args */ + +} /* namespac gdb */ From patchwork Tue Jan 9 14:26:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83645 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 25F22385829F for ; Tue, 9 Jan 2024 14:30:22 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 504BC3858422 for ; Tue, 9 Jan 2024 14:27:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 504BC3858422 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 504BC3858422 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810425; cv=none; b=REsMmS8PwnvkOn6/zuL7JRJg4iTCdrrHbZp7a2nNnw0Uz8PQX5PjOC1e1qYOPWUQffdJCLbuqv96b8SzOeeaDHKPucQUsXkAt7GCnES4pRyKdgdqxSjaEx7tmIXb3qnCI8h1TIlKgp0911aD6MByw2VRwW6rMVZIbtRCrqCCAE0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810425; c=relaxed/simple; bh=jtJWle8OHynSnjbt8CuExylvba3X5MUhpQYqQlNlWWw=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=ChPLfb5JpQaNKd7bdWVe5QHw1zErDJbgpCyItXQbB/xhJF8D14mFTps9oBWJYzmlPBXsXBC5GyOwPsDdl/ocozuyQdRS4OGIGwjvtJV0/0PygXRCKaIcCgRhPYwcyYfQf2WbPOCz3HwgQmmntmn3KMNB7332cFnK58UeV6FosXo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810420; 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: in-reply-to:in-reply-to:references:references; bh=s4A9G0oL4n5HgXpfnhGHTZbgokBsVuUb3vgL2fzsAx0=; b=KqGQYLAnjsox+YGJzwuiBTY3RjOFgd351BDCPp/MnsUhRgqaCUNBRk9R2AW1bIJf5uNiWZ vBGM0kdyMBfLQEnBIyHWeW4p4YoS5bePDqUgWuZD0dhFUHa2nWDru46+EXUungNdgL2KFU IrRnBVUxj9FtPg98js0zJmKoNjnsJEk= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-222-_-tnzgCkMI6fJKSo6H-Jyg-1; Tue, 09 Jan 2024 09:26:58 -0500 X-MC-Unique: _-tnzgCkMI6fJKSo6H-Jyg-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-40e417aa684so21927025e9.3 for ; Tue, 09 Jan 2024 06:26:58 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810416; x=1705415216; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=s4A9G0oL4n5HgXpfnhGHTZbgokBsVuUb3vgL2fzsAx0=; b=C/AlLZhFoO8eL/FdHPdoY2NGpjdzNJpiALKmpKNwz5AMdlbSPz8ejaEVxNVzNQvfWP jqvW7RfaZsjQL4ugIl/+VrNrmsNIx04+dWXml+4TXjYhjs+5lz4WQujGn7Gv3q/s5/Ft htZmeILq6WI2Ak1agMaL25agmof4Q7kYnoTdd9YypMkC/WkFeZ4vsUQFhkS2TB38REzc QXtLGZEcsHQAUvXfGHXrpdn5OUoOK82ZS/Ni4eZbp4Z1E+8Ww4MdouRXOv1XHnoMExxW inBVq9OkoIh6lslug8czLRiMnOrsdr/TE5U0t/gbWh/8oOKBkXZrAbS5LwnazhBmmfh4 OFMw== X-Gm-Message-State: AOJu0YyUh9+rJWFkxeHtwinkLGhcjSLPGKbK28I7+GQcnyxsfmLzWGwr lAToK3SzEwos9RWgX9Nypr9i97U31MKJRCudVZqfLY25uw5HhMBZRzxepjCgyLfj1Udh7C3fwZk AV0QmSbKMFdLBmwp8qb0+2vwjAqYOiFUdq6UpFWB58SCzdWkwLjt8yxPGtPpmnW7Gp3GO9FAMYG pF1dPKVwuu39WJHg== X-Received: by 2002:a05:600c:2909:b0:40e:4e0c:ecdd with SMTP id i9-20020a05600c290900b0040e4e0cecddmr673912wmd.50.1704810416002; Tue, 09 Jan 2024 06:26:56 -0800 (PST) X-Google-Smtp-Source: AGHT+IFM85/vQBEE1uTNLtMYFYUvhm4GjnAABMzFvsn5D2WW8MWS7GQdjY7xR3kh6kOwYCfJGMPyvw== X-Received: by 2002:a05:600c:2909:b0:40e:4e0c:ecdd with SMTP id i9-20020a05600c290900b0040e4e0cecddmr673900wmd.50.1704810415582; Tue, 09 Jan 2024 06:26:55 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id h4-20020a05600c314400b0040d7c3d5454sm14911429wmo.3.2024.01.09.06.26.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:54 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 09/16] gdb/python: change escaping rules when setting arguments Date: Tue, 9 Jan 2024 14:26:32 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org It is possible to set an inferior's arguments through the Python API by assigning to the gdb.Inferior.arguments attribute. This attribute can be assigned a string, in which case the string is taken verbatim as the inferior's argument string. Or this attribute can be assigned a sequence, in which case the members of the sequence are combined (with some escaping applied) to create the inferior's argument string. Currently, the when the arguments from a Python list are escaped, we use escape_shell_characters. I suspect the reasons for this are mostly accidental. When the gdb.Inferior.arguments attribute was introduced in commit: commit 3153113252f3b949a159439a17e88af8ff0dce30 Date: Mon May 1 13:53:59 2023 -0600 Add attributes and methods to gdb.Inferior GDB's inferior::set_args method called construct_inferior_arguments, and construct_inferior_arguments didn't take an escaping function as a parameter, the only option was escape_shell_characters as that was the escaping hard-coded into construct_inferior_arguments. The commit message makes no comments for or against escaping of special shell characters, and no tests were added that checked this behaviour. All of this leads me to think that the handling of special shell characters wasn't really considered (an understandable oversight). But I'd like to consider it, and I think the current behaviour is not ideal. Consider this case: (gdb) python gdb.selected_inferior().arguments = ['$VAR'] (gdb) show args Argument list to give program being debugged when it is started is "\$VAR". This means that when the inferior is run it will see literal '$VAR' as its argument. If instead, the user wants to pass the shell expanded value of $VAR to the inferior, there's no way to achieve this result using the list assignment method. In this commit I propose that we change this behaviour so that we instead see this: (gdb) python gdb.selected_inferior().arguments = ['$VAR'] (gdb) show args Argument list to give program being debugged when it is started is "$VAR". Now the '$' character is not escaped. If the inferior is started under a shell then the user will see the shell expanded value of '$VAR'. Of course, if the user wants to pass a literal '$VAR' (with no expansion) then they can do: (gdb) python gdb.selected_inferior().arguments = ['\$VAR'] This actually feels more natural to me, the user writes the argument as they would present it to a shell. So, after this commit, GDB only escapes quote characters and white space characters. This keeps some level of backward compatibility with the existing behaviour (for things other than shell special characters), but also seems natural, from the user's point of view, the arguments they are providing are already quoted (by Python's string quotes) so there's no need to quote white space. It's only when GDB converts the Python sequence into a single string that the white space actually needs quoting. There are tests for the updated functionality, and I've updated the docs and added a NEWS entry. Reviewed-By: Eli Zaretskii --- gdb/NEWS | 4 +++ gdb/doc/python.texi | 7 +++-- gdb/python/py-inferior.c | 2 +- gdb/testsuite/gdb.python/py-inferior.exp | 36 ++++++++++++++++++++---- gdbsupport/common-inferior.cc | 14 +++++++++ gdbsupport/common-inferior.h | 5 ++++ 6 files changed, 60 insertions(+), 8 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 11cd6c0663e..b72ba3d87e8 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -87,6 +87,10 @@ show remote thread-options-packet ** New function gdb.interrupt(), that interrupts GDB as if the user typed control-c. + ** When assigning a sequence to gdb.Inferior.arguments, only quote + and whitespace characters will be escaped. Everything else will + be left unmodified. + * Debugger Adapter Protocol changes ** GDB now emits the "process" event. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index d74defeec0c..e04e79cafa5 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -3580,8 +3580,11 @@ Either a string or a sequence of strings can be assigned to this attribute. When a string is assigned, it is assumed to have any -necessary quoting for the shell; when a sequence is assigned, the -quoting is applied by @value{GDBN}. +necessary quoting for the shell; when a sequence is assigned, quoting +is applied by @value{GDBN} so that the individual strings can be +concatenated into a single string, with a single space between each +argument. This means that shell quote characters and whitespace +characters will be escaped. @end defvar A @code{gdb.Inferior} object has the following methods: diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c index 8641d8a068b..5b7c7fb9365 100644 --- a/gdb/python/py-inferior.c +++ b/gdb/python/py-inferior.c @@ -919,7 +919,7 @@ infpy_set_args (PyObject *self, PyObject *value, void *closure) for (const auto &arg : args) argvec.push_back (arg.get ()); gdb::array_view view (argvec.data (), argvec.size ()); - inf->inferior->set_args (view, escape_shell_characters); + inf->inferior->set_args (view, escape_quotes_and_white_space); } else { diff --git a/gdb/testsuite/gdb.python/py-inferior.exp b/gdb/testsuite/gdb.python/py-inferior.exp index 6fbcdd6822f..108c85c0165 100644 --- a/gdb/testsuite/gdb.python/py-inferior.exp +++ b/gdb/testsuite/gdb.python/py-inferior.exp @@ -410,11 +410,37 @@ gdb_test "show args" \ [string_to_regexp "Argument list to give program being debugged when it is started is \"a b c\"."] \ "show args from string" -gdb_test_no_output "python gdb.selected_inferior().arguments = \['a', 'b c'\]" \ - "set arguments from list" -gdb_test "show args" \ - [string_to_regexp "Argument list to give program being debugged when it is started is \"a b\\ c\"."] \ - "show args from list" +# Test setting inferior arguments from a Python list. INPUT is a +# single string that contains the Python list, this is inserted into +# the argument setting command, and should include the surrouning +# square brackets. +# +# The OUTPUT is the string that describes the arguments as GDB will +# have stored them within the inferior, as seen in the 'show args' +# command output. OUTPUT should include the surrounding quotes. +# OUTPUT will be passed through string_to_regexp, so should be a plain +# string, not a regexp. +proc test_setting_arguments_from_list { input output } { + with_test_prefix "input: ${input}" { + gdb_test_no_output "python gdb.selected_inferior().arguments = ${input}" \ + "set arguments from list" + gdb_test "show args" \ + [string_to_regexp \ + "Argument list to give program being debugged when it is started is ${output}."] \ + "show args from list" + } +} + +# Test setting inferior arguments from a list. Try to hit all the +# potentially problematic cases. Notice that shell characters are not +# automatically quoted, if a user wants a shell character quoted then +# they must do that themselves. +test_setting_arguments_from_list "\['a', 'b c'\]" "\"a b\\ c\"" +test_setting_arguments_from_list "\[' ', '\\t', '\\n']" "\"\\ \\\t '\r\n'\"" +test_setting_arguments_from_list "\['', '']" "\"'' ''\"" +test_setting_arguments_from_list "\['\"']" "\"\\\"\"" +test_setting_arguments_from_list "\[\"'\"]" "\"\\\'\"" +test_setting_arguments_from_list "\[\"\$VAR\", \";\"]" "\"\$VAR ;\"" gdb_test_no_output "python gdb.selected_inferior().clear_env()" \ "clear environment" diff --git a/gdbsupport/common-inferior.cc b/gdbsupport/common-inferior.cc index 6717f7d5c08..cf2cd9a090a 100644 --- a/gdbsupport/common-inferior.cc +++ b/gdbsupport/common-inferior.cc @@ -133,3 +133,17 @@ escape_shell_characters (const char *arg) return escape_characters (arg, special); } + +/* See common-inferior.h. */ + +std::string +escape_quotes_and_white_space (const char * arg) +{ +#ifdef __MINGW32__ + static const char special[] = "\" \t\n"; +#else + static const char special[] = "\"' \t\n"; +#endif + + return escape_characters (arg, special); +} diff --git a/gdbsupport/common-inferior.h b/gdbsupport/common-inferior.h index 92d1954c3fe..7cc01fb2f28 100644 --- a/gdbsupport/common-inferior.h +++ b/gdbsupport/common-inferior.h @@ -65,6 +65,11 @@ using escape_args_func = std::string (*) (const char *arg); /* Return a version of ARG that has special shell characters escaped. */ extern std::string escape_shell_characters (const char *arg); +/* Return a version of ARG that has quote characters and white space + characters escaped. No other special shell characters will have been + escaped though. */ +extern std::string escape_quotes_and_white_space (const char *arg); + /* Pass each element of ARGS through ESCAPE_FUNC and combine the results into a single string, separating each element with a single space character. */ From patchwork Tue Jan 9 14:26:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83648 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 85A703857C4C for ; Tue, 9 Jan 2024 14:31:06 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 785B23858407 for ; Tue, 9 Jan 2024 14:27:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 785B23858407 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 785B23858407 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810425; cv=none; b=scqSfsM/xOnGkaoomj7sqJqMeNx5cGZnVvGPlCg3r1hvDr3HhFvTipc2yqCNBvH3AavcgfTd/CB+8Db7/DCUyAzSwWaQc1c03WVgFw9c2gt7M7dCLfrndKpzfGGRREeEx6abe4rXr39+uu1ioF52D4WjIWXdcxrVmj+7UQHV9dg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810425; c=relaxed/simple; bh=FFaScsYEUMvtc9anUVSA/b6LDQIIHQl72cSvM6M5Xi4=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=dmRrPAaa4OUL16vDGAkNI00QsKGZ0STY63TfG8Jh1Kr4+37QrxGVNXrTMXFSoTl7UdeFx3DgvTBVIK1OgugPn9McidxyzfrMELIh1kS9NmPyowy96bV44LOAGKD+74/gA++8HISL88onxzWOrCXkoMAVw21H9+1TtJ/c2WKLxIs= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810422; 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: in-reply-to:in-reply-to:references:references; bh=HiMrBDD6o+vCyE1qKqHNkSyjJdKgNsOeWsulZ9P3Nc0=; b=LnGuwnCd4Nb5wJTcnrUzQYIVrTrHEva85NiRykkWTV7kkeKnfr24UuOYsF/TV65MZ29emL 1cz6vRXBBYN6SgUARQ98DmpSJbGBocQ2nL/8bJ+AKIKSGe+htAchWCxg9I/DMTTP+aN8wi xfRXvxdMgczYwT26bzDk0zmHj95jLuE= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-346-XlrS-YH4M3CD7l6uc5t74Q-1; Tue, 09 Jan 2024 09:27:00 -0500 X-MC-Unique: XlrS-YH4M3CD7l6uc5t74Q-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-3367657a290so1880817f8f.3 for ; Tue, 09 Jan 2024 06:26:59 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810418; x=1705415218; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=HiMrBDD6o+vCyE1qKqHNkSyjJdKgNsOeWsulZ9P3Nc0=; b=JLvu/a5ZAEOdYGR6hRZg+vlSEn1JHroRvANJB0ir/FK0DKsQvdMeIRsxv5LuS87vj+ Vj8juQk5EfajHGtcUe+VkXYgxaBvtEL5VZYxHtPJLxrfpMZbUVytL4Z9LmwLlXiPp6px 3hgu8SkrBIOctnNSb5gLZhQQ2O898Bvimtzb77l+IePzUvdiyhcc9PRhNdS85SQBcjhk WLs5PEtUU0eMcFwngMNQqlSlWmbP7caK4rgxQGCyMNOtZtk8XBu1hQ5zNqvk930YFet8 1UAeD6BOjCdL2P97BhZ/W2cXmCoAC5B8ujLcnVZrIghyF+bfw+q2p2xle7lqA9IogD3/ AQmQ== X-Gm-Message-State: AOJu0YwIA/O8r1h4/gwAXdq7haiJnf/9OLNjEW3Wuc/HjeFlvNlVsROo zE+VTcxyIHWexxRM/vH4xi0Juhs0AfbOFscT1Jnv+ABGJNh7OyZVhYID+mmoCLMyPHNTgl6c/R5 d5w/6rqN/xTMjnclwpzc2CQjGq3KXiLMAAVerNi3LHAxghrVhEkjIDaepyCuibNaDSw0hsItFcf S68HS+dUoTAvOuxA== X-Received: by 2002:adf:e2d2:0:b0:337:6001:9ff5 with SMTP id d18-20020adfe2d2000000b0033760019ff5mr565238wrj.60.1704810417960; Tue, 09 Jan 2024 06:26:57 -0800 (PST) X-Google-Smtp-Source: AGHT+IFOeBkflydzj3T7q4TNfbSmMbmqcmOwMgdlgMV3jrrruAXYnbN7k77hXuVUQxSLCfCKuX6pcQ== X-Received: by 2002:adf:e2d2:0:b0:337:6001:9ff5 with SMTP id d18-20020adfe2d2000000b0033760019ff5mr565229wrj.60.1704810417653; Tue, 09 Jan 2024 06:26:57 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id p11-20020a5d59ab000000b00336ebf93416sm2587435wrr.17.2024.01.09.06.26.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:56 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 10/16] gdb: add remote argument passing self tests Date: Tue, 9 Jan 2024 14:26:33 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org This commit adds some remote argument passing self-tests. There are not many tests right now -- there are known bugs in the remote argument passing mechanism (see PR gdb/28392) -- but some simple cases are covered here, and I plan to add additional tests once I've fixed some of the problems with the existing code. The tests take an inferior argument string, this is the string that GDB would carry around as inferior::m_args. This string is then split using gdb::remote_args::split, this gives a vector of strings, these are the strings that are passed over the remote protocol. These split strings are validated as part of the test. The split strings are then combined using gdb::remote_args::join which gives the inferior argument string that gdbserver will use, this is held in server.cc, program_args, this joined string is then checked as part of the test. There are no changes to GDB's behaviour as part of this commit, other than adding the new tests. --- gdb/Makefile.in | 1 + gdb/unittests/remote-arg-selftests.c | 189 +++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 gdb/unittests/remote-arg-selftests.c diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 195f3a2e2d1..cc2e62b6f3c 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -476,6 +476,7 @@ SELFTESTS_SRCS = \ unittests/ptid-selftests.c \ unittests/main-thread-selftests.c \ unittests/mkdir-recursive-selftests.c \ + unittests/remote-arg-selftests.c \ unittests/rsp-low-selftests.c \ unittests/scoped_fd-selftests.c \ unittests/scoped_ignore_signal-selftests.c \ diff --git a/gdb/unittests/remote-arg-selftests.c b/gdb/unittests/remote-arg-selftests.c new file mode 100644 index 00000000000..3240ab9aeea --- /dev/null +++ b/gdb/unittests/remote-arg-selftests.c @@ -0,0 +1,189 @@ +/* Self tests for GDB's argument splitting and merging. + + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +#include "defs.h" +#include "gdbsupport/selftest.h" +#include "gdbsupport/buildargv.h" +#include "gdbsupport/common-inferior.h" +#include "gdbsupport/remote-args.h" + +namespace selftests { +namespace remote_args_tests { + +/* The data needed to perform a single remote argument test. */ +struct arg_test_desc +{ + /* The original inferior argument string. */ + std::string input; + + /* The individual arguments once they have been split. */ + std::vector split; + + /* The new inferior argument string, created by joining SPLIT. */ + std::string joined; +}; + +/* The list of tests. */ +arg_test_desc desc[] = { + { "abc", { "abc" }, "abc" }, + { "a b c", { "a", "b", "c" }, "a b c" }, + { "\"a b\" 'c d'", { "a b", "c d" }, "a\\ b c\\ d" }, + { "\\' \\\"", { "'", "\"" }, "\\' \\\"" }, + { "'\\'", { "\\" }, "\\\\" }, + { "\"\\\\\" \"\\\\\\\"\"", { "\\", "\\\"" }, "\\\\ \\\\\\\"" }, + { "\\ \" \" ' '", { " ", " ", " "}, "\\ \\ \\ " }, + { "\"'\"", { "'" }, "\\'" }, + { "'\"' '\\\"'", { "\"", "\\\"" } , "\\\" \\\\\\\""}, + { "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \"\\\\\\\"\" \" \" \"\"", + { "first arg", "", "third-arg", "'", "\"", "\\\""," ", "" }, + "first\\ arg '' third-arg \\' \\\" \\\\\\\" \\ ''"}, + { "\"\\a\" \"\\&\" \"\\#\" \"\\<\" \"\\^\"", + { "\\a", "\\&", "\\#" , "\\<" , "\\^"}, + "\\\\a \\\\\\& \\\\\\# \\\\\\< \\\\\\^" }, + { "1 '\n' 3", { "1", "\n", "3" }, "1 '\n' 3" }, +}; + +/* Convert a std::vector into std::vector. This + requires copying all of the string content. This class takes care of + freeing the memory once we are done with it. */ + +struct args_as_c_strings +{ + args_as_c_strings (std::vector args) + { + for (const auto & a : args) + m_data.push_back (xstrdup (a.c_str ())); + } + + ~args_as_c_strings () + { + free_vector_argv (m_data); + } + + std::vector &get () + { + return m_data; + } + +private: + std::vector m_data; +}; + +/* Run the remote argument passing self tests. */ + +static void +self_test () +{ + int failure_count = 0; + for (const auto &d : desc) + { + if (run_verbose ()) + { + if (&d != &desc[0]) + debug_printf ("------------------------------\n"); + debug_printf ("Input (%s)\n", d.input.c_str ()); + } + + /* Split argument string into individual arguments. */ + std::vector split_args = gdb::remote_args::split (d.input); + + if (run_verbose ()) + { + debug_printf ("Split:\n"); + + size_t len = std::max (split_args.size (), d.split.size ()); + for (size_t i = 0; i < len; ++i) + { + const char *got = "N/A"; + const char *expected = got; + + if (i < split_args.size ()) + got = split_args[i].c_str (); + + if (i < d.split.size ()) + expected = d.split[i].c_str (); + + debug_printf (" got (%s), expected (%s)\n", got, expected); + } + } + + if (split_args != d.split) + { + ++failure_count; + if (run_verbose ()) + debug_printf ("FAIL\n"); + continue; + } + + /* Now join the arguments. */ + args_as_c_strings split_args_c_str (split_args); + std::string joined_args + = gdb::remote_args::join (split_args_c_str.get ()); + + if (run_verbose ()) + debug_printf ("Joined (%s), expected (%s)\n", + joined_args.c_str (), d.joined.c_str ()); + + if (joined_args != d.joined) + { + ++failure_count; + if (run_verbose ()) + debug_printf ("FAIL\n"); + continue; + } + + /* The contents of JOINED_ARGS will not be identical to D.INPUT. + There are multiple ways that an argument can be escaped, and out + join function just picks one. However, if we split JOINED_ARGS + again then each individual argument should be the same as those in + SPLIT_ARGS. So lets test that next. */ + std::vector split_args_v2 + = gdb::remote_args::split (joined_args); + + if (split_args_v2 != split_args) + { + ++failure_count; + if (run_verbose ()) + { + debug_printf ("Re-split:\n"); + for (const auto &a : split_args_v2) + debug_printf (" got (%s)\n", a.c_str ()); + debug_printf ("FAIL\n"); + } + continue; + } + + if (run_verbose ()) + debug_printf ("PASS\n"); + } + + SELF_CHECK (failure_count == 0); +} + +} /* namespace remote_args_tests */ +} /* namespace selftests */ + +void _initialize_remote_arg_selftests (); + +void +_initialize_remote_arg_selftests () +{ + selftests::register_test ("remote-args", + selftests::remote_args_tests::self_test); +} From patchwork Tue Jan 9 14:26:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83652 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 93A6E385829B for ; Tue, 9 Jan 2024 14:32:15 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 877E838582AD for ; Tue, 9 Jan 2024 14:27:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 877E838582AD 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 877E838582AD 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=1704810428; cv=none; b=chQRqgA0OJzXzVAWYgNCC2gITnuIdbylyp4m4I0RRumZidhd+j6CHSPnTWir6emRMHsNy6iB+BDx3xTOFNZMiG1w1I7buZ2EwMigv2jUmEuoHhf8X6EDVyZ+BMcJaFjTh2RUoChrbxBPzJDowz10fpEq2cpuMvY4VOVDxM8XSH8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810428; c=relaxed/simple; bh=ztzsU5tTMM4ftD0Lp/Ss0iz3j4sU9h5YiOdnk/0jmJI=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=Dx+ObI/nhfYWnt37aynnoI/O8rPWE6/TG0eG623aCiNU3AAX6lVZ7d11qrq+ub+/sqDY4V8eSZeuo3L/qMcwa7dIUq0rBSCHFp3ETVrxcTcnEk/5rjePfn2sdUWGW/MhNpRQhMJtDn6hblhUjKr1o4Z2j79kQH7w89FavauXn2k= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810423; 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: in-reply-to:in-reply-to:references:references; bh=W9vUFYwOwBYS5/0xD2EDgAwFlZcoqF1rAV6PjNeOgEU=; b=WY4IxQAY9FAeu/U+2zKOb8NI37dISuZPDBMgQPBtqmhx53vAZxYLRbGHxF6kG0nx9hU7Gv Bf8L+67VM0VFY6/5+8KUaKLKcL/ivq4z+isjt3neU+zx+yFwmaKHxbGkfCKxL8U6sfhf8b j2iCtENAVmh+VJFW7by3Cj0NwmfSLVI= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-197-auHfecQPPBSg5vH1zI1iHw-1; Tue, 09 Jan 2024 09:27:02 -0500 X-MC-Unique: auHfecQPPBSg5vH1zI1iHw-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-40e435a606aso16761465e9.3 for ; Tue, 09 Jan 2024 06:27:01 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810420; x=1705415220; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=W9vUFYwOwBYS5/0xD2EDgAwFlZcoqF1rAV6PjNeOgEU=; b=u+UXV3N1sy/66gUV4q9WEQe4xpIDSkVETq51YZ4uw25YyNoznyql/Fa5GUd8FYZQrl o6TS9XEP0K8GaSFr/vJcxYh4iMuDtccv8mgvcabgbXEqLtTdTWRKls6Xk/o3zqTB2yFm L/guj3cuYugFf3tG5o9Mk8eP7x0nSwR6Zr9Suhw/0q5EsnegUVGmvQ10FkNhfvWSYg9M oO8rLWHydrO3w1JwI4nLxgus/mRth3Ye2tZRH74hZbnyirdiu1p1PLtaad1tcR/RkzG2 RzF2sOD2kCdZrhovZGjY+v0kycuXaIESdNB3EfUhwobBsQCKVOfcIVm4dBIFPGxd7a1O 7hoA== X-Gm-Message-State: AOJu0Yyi5NqrsVR5/oXdHq2tANgFD3p9MWL6aNxd4T3AtIagIMkCnUMU ZWI9JOnVrWMNVonfiYSGSlwi7X81fjtm4bhRL8BrkqB8fOn70z0/+PyGDmoy/ZrPlE/8EaiPklz ya4KP6ryjEoPZhuePXpoyirpd8G4e7JXWB1jqaB3AkrLlQiHYodciaYxTN+p5nIxlHnYszNE751 UcGxtidWbrkWUDQQ== X-Received: by 2002:a05:600c:1c02:b0:40e:3bf4:3a92 with SMTP id j2-20020a05600c1c0200b0040e3bf43a92mr3086399wms.45.1704810420176; Tue, 09 Jan 2024 06:27:00 -0800 (PST) X-Google-Smtp-Source: AGHT+IHV76CiGvqH3EVE0dRrKMOyQuUqdSJWB74NBtF+sCXYlRqpKn2UYLmarEjLsLoolwjgsfAl/g== X-Received: by 2002:a05:600c:1c02:b0:40e:3bf4:3a92 with SMTP id j2-20020a05600c1c0200b0040e3bf43a92mr3086388wms.45.1704810419514; Tue, 09 Jan 2024 06:26:59 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id bg11-20020a05600c3c8b00b0040d6ffae526sm15047391wmb.39.2024.01.09.06.26.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:26:58 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 11/16] gdb/gdbserver: pass inferior arguments as a single string Date: Tue, 9 Jan 2024 14:26:34 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org GDB holds the inferior arguments as a single string. Currently when GDB needs to pass the inferior arguments to a remote target as part of a vRun packet, this is done by splitting the single argument string into its component arguments by calling gdb::remote_args::split, which uses the gdb_argv class to split the arguments for us. The same gdb_argv class is used when the user has asked GDB/gdbserver to start the inferior without first invoking a shell; the gdb_argv class is used to split the argument string into it component arguments, and each is passed as a separate argument to the execve call which spawns the inferior. There is however, a problem with using gdb_argv to split the arguments before passing them to a remote target. To understand this problem we must first understand how gdb_argv is used when invoking an inferior without a shell. And to understand how gdb_argv is used to start an inferior without a shell, I feel we need to first look at an example of starting an inferior with a shell. Consider these two cases: (a) (gdb) set args \$VAR (b) (gdb) set args $VAR When starting with a shell, in case (a) the user expects the inferior to receive a literal '$VAR' string as an argument, while in case (b) the user expects to see the shell expanded value of the variable $VAR. If the user does 'set startup-with-shell off', then in (a) GDB will strip the '\' while splitting the arguments, and the inferior will be passed a literal '$VAR'. In (b) there is no '\' to strip, so also in this case the inferior will receive a literal '$VAR', remember startup-with-shell is off, so there is no shell than can ever expand $VAR. Notice, that when startup-with-shell is off, we end up with a many to one mapping, both (a) and (b) result in the literal string $VAR being passed to the inferior. I think this is the correct behaviour in this case. However, as we use gdb_argv to split the remote arguments we have the same many to one mapping within the vRun packet. But the vRun packet will be used when startup-with-shell is both on and off. What this means is that when gdbserver receives a vRun packet containing '$VAR' it doesn't know if GDB actually had '$VAR', or if GDB had '\$VAR'. And this is a huge problem. We can try to address this by making the argument splitting for remote targets smarter. And later in this series I will do that. However, I think that splitting and joining the arguments as we do was a mistake. The later patch in this series handles unquoted, single quoted, and double quoted strings. But doesn't really address parameter substitution, command substitution, or arithmetic expansion. And even if we did try to address these cases, what rules exactly would we implement? Probably POSIX shell rules, but what if the remote target doesn't have a POSIX shell? Why do we need to pick an particular rule set? Clearly, for backward compatibility we need to maintain some degree of argument splitting and joining as we currently have; and that's why I have a later patch in this series that tries to improve that splitting and joining a little. But I think, what we should really do, is add a new feature flag (as used by the qSupported packet) and, if GDB and the remote target agree, we should pass the inferior arguments as a single string. This solves all our problems. In the startup with shell case, we no longer need to worry about splitting at all. The arguments are passed unmodified to the remote target, who can then pass the arguments to the shell directly. In the 'startup-with-shell off' case it is now up to the remote target to split the arguments, though in gdbserver we already did this as we always joined the arguments, so for gdbserver it's no significant hardship. And if the remote target doesn't have a POSIX shell, well GDB just doesn't need to worry about it! Something similar to this was originally suggested in this series: https://inbox.sourceware.org/gdb-patches/20211022071933.3478427-1-m.weghorn@posteo.de/ though this series didn't try to maintain backward compatibility, which I think is an issue that my patch solves. Additionally, this series only passed the arguments as a single string in some cases, I've simplified this so that when GDB and the remote agree, the arguments are always passed as a single string. I think this makes my version a little cleaner. I've also added documentation and some tests with this commit, including ensuring that we test both the new single string approach, and the fallback split/join approach. I've credited the author of the referenced series as co-author as they did come to a similar conclusion, though I think my implementation is different enough that I'm happy to list myself as primary author. Co-Authored-By: Michael Weghorn Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 Reviewed-By: Eli Zaretskii --- gdb/NEWS | 7 + gdb/doc/gdb.texinfo | 23 +++ gdb/remote.c | 25 ++- gdb/testsuite/gdb.base/args.exp | 47 +++-- gdb/testsuite/gdb.base/inferior-args.exp | 31 +++- gdb/testsuite/gdb.base/startup-with-shell.exp | 161 ++++++++++-------- gdbserver/server.cc | 21 ++- gdbserver/server.h | 5 + 8 files changed, 226 insertions(+), 94 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index b72ba3d87e8..65a082808e4 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -120,6 +120,13 @@ QThreadOptions in qSupported QThreadOptions packet, and the qSupported response can contain the set of thread options the remote stub supports. +single-inf-arg in qSupported + The new single-inf-arg feature within the qSupported packet allows + GDB to inform the stub that it would like to send the inferior + arguments as a single string within the vRun packet. The stub can + reply with the single-inf-arg feature to indicate that it is able to + accept arguments as a single string. + *** Changes in GDB 14 * GDB now supports the AArch64 Scalable Matrix Extension 2 (SME2), which diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 4ada257d256..9dbe53384e1 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -43041,6 +43041,12 @@ (e.g.@: the last program run). The program is created in the stopped state. +If @value{GDBN} sent the @samp{single-inf-arg+} feature in the +@samp{qSupported} packet, and the stub replied with +@samp{single-inf-arg+}, then there will only be a single +@var{argument} string, which includes all inferior arguments, +separated with whitespace. + @c FIXME: What about non-stop mode? This packet is only available in extended mode (@pxref{extended mode}). @@ -44471,6 +44477,13 @@ @item vContSupported This feature indicates whether @value{GDBN} wants to know the supported actions in the reply to @samp{vCont?} packet. + +@item single-inf-arg +This feature indicates that @value{GDBN} would like to send the +inferior arguments as a single string within the @samp{vRun} packet. +@value{GDBN} will not send the arguments as a single string unless the +stub also reports that is supports this behaviour by including +@samp{single-inf-arg+} in its @samp{qSupported} reply. @end table Stubs should ignore any unknown values for @@ -44754,6 +44767,11 @@ @tab @samp{-} @tab No +@item @samp{single-inf-arg} +@tab No +@tab @samp{-} +@tab No + @end multitable These are the currently defined stub features, in more detail: @@ -44985,6 +45003,11 @@ @file{/proc/@var{pid}/smaps} file so memory mapping page flags can be inspected. This is done via the @samp{vFile} requests. +@item single-inf-arg +The remote stub would like to receive the inferior arguments as a +single string within the @samp{vRun} packet. The stub should only +send this feature if @value{GDBN} sent @samp{single-inf-arg+} in the +@samp{qSupported} packet. @end table @item qSymbol:: diff --git a/gdb/remote.c b/gdb/remote.c index ebef409ffed..75d275d38df 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -306,6 +306,10 @@ enum { packets and the tag violation stop replies. */ PACKET_memory_tagging_feature, + /* Not really a packet; this indicates support for sending the vRun + inferior arguments as a single string. */ + PACKET_vRun_single_argument, + PACKET_MAX }; @@ -727,6 +731,11 @@ struct remote_features bool remote_memory_tagging_p () const { return packet_support (PACKET_memory_tagging_feature) == PACKET_ENABLE; } + /* Returns true if there is support for sending vRun inferior arguments + as a single string. */ + bool remote_vrun_single_arg_p () const + { return packet_support (PACKET_vRun_single_argument) == PACKET_ENABLE; } + /* Reset all packets back to "unknown support". Called when opening a new connection to a remote target. */ void reset_all_packet_configs_support (); @@ -5724,6 +5733,8 @@ static const struct protocol_feature remote_protocol_features[] = { { "no-resumed", PACKET_DISABLE, remote_supported_packet, PACKET_no_resumed }, { "memory-tagging", PACKET_DISABLE, remote_supported_packet, PACKET_memory_tagging_feature }, + { "single-inf-arg", PACKET_DISABLE, remote_supported_packet, + PACKET_vRun_single_argument }, }; static char *remote_support_xml; @@ -5835,6 +5846,10 @@ remote_target::remote_query_supported () != AUTO_BOOLEAN_FALSE) remote_query_supported_append (&q, "memory-tagging+"); + if (m_features.packet_set_cmd_state (PACKET_vRun_single_argument) + != AUTO_BOOLEAN_FALSE) + remote_query_supported_append (&q, "single-inf-arg+"); + /* Keep this one last to work around a gdbserver <= 7.10 bug in the qSupported:xmlRegisters=i386 handling. */ if (remote_support_xml != NULL @@ -10626,7 +10641,11 @@ remote_target::extended_remote_run (const std::string &args) if (!args.empty ()) { - std::vector split_args = gdb::remote_args::split (args); + std::vector split_args; + if (!m_features.remote_vrun_single_arg_p ()) + split_args = gdb::remote_args::split (args); + else + split_args.push_back (args); for (const auto &a : split_args) { @@ -15990,6 +16009,10 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (PACKET_memory_tagging_feature, "memory-tagging-feature", "memory-tagging-feature", 0); + add_packet_config_cmd (PACKET_vRun_single_argument, + "single-inferior-argument-feature", + "single-inferior-argument-feature", 0); + /* Assert that we've registered "set remote foo-packet" commands for all packet configs. */ { diff --git a/gdb/testsuite/gdb.base/args.exp b/gdb/testsuite/gdb.base/args.exp index 8b0047999bf..7c123e36404 100644 --- a/gdb/testsuite/gdb.base/args.exp +++ b/gdb/testsuite/gdb.base/args.exp @@ -72,31 +72,48 @@ proc args_test { name arglist {re_list {}} } { } } -# Test that the --args are processed correctly. +# Run all the tests. +proc run_all_tests {} { + # Test that the --args are processed correctly. -args_test basic {{1} {3}} + args_test basic {{1} {3}} -# Test that the --args are processed correctly even if one of them is -# empty. + # Test that the --args are processed correctly even if one of them is + # empty. -args_test "one empty" {{1} {} {3}} + args_test "one empty" {{1} {} {3}} -# Try with 2 empty args. + # Try with 2 empty args. -args_test "two empty" {{1} {} {} 3} + args_test "two empty" {{1} {} {} 3} -# Try with arguments containing literal single quotes. + # Try with arguments containing literal single quotes. -args_test "one empty with single quotes" {{1} {''} {3}} + args_test "one empty with single quotes" {{1} {''} {3}} -args_test "two empty with single quotes" {{1} {''} {''} {3}} + args_test "two empty with single quotes" {{1} {''} {''} {3}} -# Try with arguments containing literal newlines. + # Try with arguments containing literal newlines. -args_test "one newline" {{1} "\n" {3}} {1 \\\\n 3} + args_test "one newline" {{1} "\n" {3}} {1 \\\\n 3} -args_test "two newlines" {{1} "\n" "\n" {3}} {1 \\\\n \\\\n 3} + args_test "two newlines" {{1} "\n" "\n" {3}} {1 \\\\n \\\\n 3} -args_test "lone single quote" {{1} \' {3}} + args_test "lone single quote" {{1} \' {3}} -args_test "lone double quote" {{1} \" {3}} {1 \\\\\" 3} + args_test "lone double quote" {{1} \" {3}} {1 \\\\\" 3} +} + +run_all_tests + +# For extended-remote targets, disable the packet which passes +# inferior arguments as a single string. This changes how the vRun +# (extended-remote only) packet works. +if {[target_info gdb_protocol] == "extended-remote"} { + with_test_prefix "single-inferior-arg disabled" { + save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set remote single-inferior-argument-feature-packet off\"" + run_all_tests + } + } +} diff --git a/gdb/testsuite/gdb.base/inferior-args.exp b/gdb/testsuite/gdb.base/inferior-args.exp index 4b51b657326..6c22ecb3c54 100644 --- a/gdb/testsuite/gdb.base/inferior-args.exp +++ b/gdb/testsuite/gdb.base/inferior-args.exp @@ -211,14 +211,31 @@ lappend test_desc_list [list "test four" \ [list "$hex \"'\"" \ "$hex \"\\\\\"\""]] -foreach desc $test_desc_list { - lassign $desc name stub_suitable args re_list - with_test_prefix $name { - foreach_with_prefix set_method { "start" "starti" "run" "set args" } { - foreach_with_prefix startup_with_shell { on off } { - do_test $set_method $startup_with_shell $args $re_list \ - $stub_suitable +# Run all tests in the global TEST_DESC_LIST. +proc run_all_tests {} { + foreach desc $::test_desc_list { + lassign $desc name stub_suitable args re_list + with_test_prefix $name { + foreach_with_prefix set_method { "start" "starti" "run" "set args" } { + foreach_with_prefix startup_with_shell { on off } { + do_test $set_method $startup_with_shell $args $re_list \ + $stub_suitable + } } } } } + +run_all_tests + +# For extended-remote targets, disable the packet which passes +# inferior arguments as a single string. This changes how the vRun +# (extended-remote only) packet works. +if {[target_info gdb_protocol] == "extended-remote"} { + with_test_prefix "single-inferior-arg disabled" { + save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set remote single-inferior-argument-feature-packet off\"" + run_all_tests + } + } +} diff --git a/gdb/testsuite/gdb.base/startup-with-shell.exp b/gdb/testsuite/gdb.base/startup-with-shell.exp index 0424b20de3a..f467774dc5f 100644 --- a/gdb/testsuite/gdb.base/startup-with-shell.exp +++ b/gdb/testsuite/gdb.base/startup-with-shell.exp @@ -90,76 +90,97 @@ proc run_test_same { args re testname } { run_test $args $re $re $testname } -# The regexp to match a single '\' character. -set bs "\\\\" - -# Are we using 'remote' or 'extended-remote' protocol? -set is_remote_p [expr [string equal [target_info gdb_protocol] \ - "remote"] \ - || [string equal [target_info gdb_protocol] \ - "extended-remote"]] - -## Run the actual tests - -run_test "$unique_file_dir/*.unique-extension" \ - "\"$unique_file\"" \ - "\"$unique_file_dir/\\\*\.unique-extension\"" \ - "arg is glob" \ - $is_remote_p - -run_test_same "$unique_file_dir/\\*.unique-extension" \ - "\"$unique_file_dir/\\\*\.unique-extension\"" \ - "arg is escaped glob" - -save_vars { env(TEST) } { - set env(TEST) "1234" - run_test "\$TEST" \ - "\"1234\"" \ - "\"\\\$TEST\"" \ - "arg is shell variable" \ - $is_remote_p - - run_test_same "\\\$TEST" \ - "\"\\\$TEST\"" \ - "arg is escaped shell variable" -} - -run_test_same "\"\\a\"" \ - "\"${bs}${bs}a\"" \ - "retain backslash in double quote arg" - -run_test_same "'\\a'" \ - "\"${bs}${bs}a\"" \ - "retain backslash in single quote arg" - -run_test_same "\"\\\$\"" \ - "\"\\\$\"" \ - "'\$' can be escaped in double quote arg" - -run_test_same "'\\\$'" \ - "\"${bs}${bs}\\\$\"" \ - "'\$' is not escaped in single quote arg" - -run_test_same "\"\\`\"" \ - "\"\\`\"" \ - "'`' can be escaped in double quote arg" - -run_test_same "'\\`'" \ - "\"${bs}${bs}`\"" \ - "'`' is not escaped in single quote arg" - -run_test_same "\"\\\"\"" \ - "\"${bs}\"\"" \ - "'\"' can be escaped in double quote arg" +# Run the actual tests +proc run_all_tests { { is_remote_with_split_args false } } { + # The regexp to match a single '\' character. + set bs "\\\\" + + run_test "$::unique_file_dir/*.unique-extension" \ + "\"$::unique_file\"" \ + "\"$::unique_file_dir/\\\*\.unique-extension\"" \ + "arg is glob" \ + $is_remote_with_split_args + + run_test_same "$::unique_file_dir/\\*.unique-extension" \ + "\"$::unique_file_dir/\\\*\.unique-extension\"" \ + "arg is escaped glob" + + save_vars { ::env(TEST) } { + set ::env(TEST) "1234" + run_test "\$TEST" \ + "\"1234\"" \ + "\"\\\$TEST\"" \ + "arg is shell variable" \ + $is_remote_with_split_args + + run_test_same "\\\$TEST" \ + "\"\\\$TEST\"" \ + "arg is escaped shell variable" + } -run_test_same "'\\\"'" \ - "\"${bs}${bs}${bs}\"\"" \ - "'\"' is not escaped in single quote arg" + run_test "\$(echo foo)" \ + "\"foo\"" \ + "\"\\\$\\(echo\"" \ + "arg is parameter expansion, command execution" \ + $is_remote_with_split_args + + run_test "\$((2 + 3))" \ + "\"5\"" \ + "\"\\\$\\(\\(2\"" \ + "arg is parameter expansion, expression evaluation" \ + $is_remote_with_split_args + + run_test_same "\"\\a\"" \ + "\"${bs}${bs}a\"" \ + "retain backslash in double quote arg" + + run_test_same "'\\a'" \ + "\"${bs}${bs}a\"" \ + "retain backslash in single quote arg" + + run_test_same "\"\\\$\"" \ + "\"\\\$\"" \ + "'\$' can be escaped in double quote arg" + + run_test_same "'\\\$'" \ + "\"${bs}${bs}\\\$\"" \ + "'\$' is not escaped in single quote arg" + + run_test_same "\"\\`\"" \ + "\"\\`\"" \ + "'`' can be escaped in double quote arg" + + run_test_same "'\\`'" \ + "\"${bs}${bs}`\"" \ + "'`' is not escaped in single quote arg" + + run_test_same "\"\\\"\"" \ + "\"${bs}\"\"" \ + "'\"' can be escaped in double quote arg" + + run_test_same "'\\\"'" \ + "\"${bs}${bs}${bs}\"\"" \ + "'\"' is not escaped in single quote arg" + + run_test_same "\"\\\\\"" \ + "\"${bs}${bs}\"" \ + "'\\' can be escaped in double quote arg" + + run_test_same "'\\\\'" \ + "\"${bs}${bs}${bs}${bs}\"" \ + "'\\' is not escaped in single quote arg" +} -run_test_same "\"\\\\\"" \ - "\"${bs}${bs}\"" \ - "'\\' can be escaped in double quote arg" +run_all_tests -run_test_same "'\\\\'" \ - "\"${bs}${bs}${bs}${bs}\"" \ - "'\\' is not escaped in single quote arg" +# For extended-remote targets, disable the packet which passes +# inferior arguments as a single string. This changes how the vRun +# (extended-remote only) packet works. +if {[target_info gdb_protocol] == "extended-remote"} { + with_test_prefix "single-inferior-arg disabled" { + save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set remote single-inferior-argument-feature-packet off\"" + run_all_tests true + } + } +} diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 13abb0b7636..65df03ef309 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -2725,6 +2725,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (target_supports_memory_tagging ()) cs.memory_tagging_feature = true; } + else if (feature == "single-inf-arg+") + cs.single_inferior_argument = true; else { /* Move the unknown features all together. */ @@ -2854,6 +2856,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (target_supports_memory_tagging ()) strcat (own_buf, ";memory-tagging+"); + if (cs.single_inferior_argument) + strcat (own_buf, ";single-inf-arg+"); + /* Reinitialize components as needed for the new connection. */ hostio_handle_new_gdb_connection (); target_handle_new_gdb_connection (); @@ -3438,7 +3443,21 @@ handle_v_run (char *own_buf) else program_path.set (new_program_name.get ()); - program_args = gdb::remote_args::join (new_argv); + if (cs.single_inferior_argument) + { + if (new_argv.size () > 1) + { + write_enn (own_buf); + return; + } + else if (new_argv.size () == 1) + program_args = std::string (new_argv[0]); + else + program_args.clear (); + } + else + program_args = gdb::remote_args::join (new_argv); + free_vector_argv (new_argv); target_create_inferior (program_path.get (), program_args); diff --git a/gdbserver/server.h b/gdbserver/server.h index 2bca4718941..93914098728 100644 --- a/gdbserver/server.h +++ b/gdbserver/server.h @@ -192,6 +192,11 @@ struct client_state /* If true, memory tagging features are supported. */ bool memory_tagging_feature = false; + /* If true then we've agreed that the debugger will send all inferior + arguments as a single string. When false the debugger will attempt + to split the inferior arguments before sending them. */ + bool single_inferior_argument = false; + }; client_state &get_client_state (); From patchwork Tue Jan 9 14:26:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83654 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D45933858407 for ; Tue, 9 Jan 2024 14:32:50 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 828073858CDB for ; Tue, 9 Jan 2024 14:27:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 828073858CDB 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 828073858CDB Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810431; cv=none; b=EdnoB4jLm5JzufAoHWiV3NU5jGl7pHJttaE0oPU/MZABXZvsbVPt2rf+hi30XnhWojRlDnPQXsGq0ld9qQgcAVGkwI1eCs8gEKIblXlGDi1zUR2wccjLYWjnby7tVCCLPiSBLB3Ztrn14OpSM73ZMkK2dhZ5H5+qRZZvvn+9Uew= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810431; c=relaxed/simple; bh=g6ACxEyGo5MQVH0f+Mkxa5WLwKRjx9W+/xeoGSGxPNc=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=yFNLuhmxTAvHzV46h2ABlsaxfk5AGEEhNax/tFPvjijCH6gME8dhFx4wiwuprU/jGKtozXSqekflDFvWfozLyLi6wUfiNxZlEDYRYeXeUb7PqjrB7y4bNfXME/d1ANpwVsqPXQ+zDaCkx78z02ihKiOelcNrDl5FD2tEaxNFYr0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810425; 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: in-reply-to:in-reply-to:references:references; bh=rL4p13QpFaFjZKjfeUnpU44cAF7n/8oufum9jxEwzSg=; b=NUUFyxpwEZkp9B+vTWJyxB3kHryEFzjoqXiPbZgLVd00ksPimSATbFyXx2ia0l2snmsrn5 rJ/Jp+2OxB2o6ZphylLcjqaxhl0c3a6rpB7u5swqoHlhuP+ykHralGtanCtSGBajWdOph0 kP7Ne4fd5Hkv/4vHcfe0N/UsO2+P1bQ= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-534--1QGZOyhNPSL8mgPel0IJg-1; Tue, 09 Jan 2024 09:27:03 -0500 X-MC-Unique: -1QGZOyhNPSL8mgPel0IJg-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-40e47b2f6b8so14014285e9.2 for ; Tue, 09 Jan 2024 06:27:03 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810422; x=1705415222; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=rL4p13QpFaFjZKjfeUnpU44cAF7n/8oufum9jxEwzSg=; b=jHMB6Dh/kBifxvxuvK5kIDd4mXj+mc83VkGhwoafOD6NQqlFK/Y3z8nJ7DqKW2i+0t cDrIelAF9dzxI4lbbn5uK9jZdTbpEgnJx3AXFH2phyC7vah90o8N9Q95RqyQ6PNdjuV5 bUtDYppBvllHG41GMM4irxjWhCgBZ4IQzcff2C8oS0TDJkPgOtK6Reetdfk7L0OKFYce 9F7Ix6YLrjxvZFf2UdV+nPq9wvq6ZuJK4BgbDyaZUw9GSUYb2PqAS3SaQBIWhKrKio/C zcBNoOumVeilXAund0cwbYY94KEzQz9jAbO7rCWUPzSvBKja7OlZmULOe0179cge5X7W 4G5Q== X-Gm-Message-State: AOJu0YwOVqu/ssjybq7B3+dtaKW1MIhY/t51EDZ593OY3gUcFCOioiGJ pW4/eMxF34qfTZ2gRcujELNPDQHyjkX/zSOL0RoRI4GD2jMt3gZ1cX9ShrN73SKYouiz3hug2Yw oaRayx75D6n2Wi0fLmBPgWFH1xp8W5pbfpbeVLGdWA2WH3tqwf7A03drdOYHE748t8fkDP0Hdqv aGcp0J2e7sRLpwMA== X-Received: by 2002:a05:600c:a007:b0:40e:5333:2024 with SMTP id jg7-20020a05600ca00700b0040e53332024mr29859wmb.53.1704810421388; Tue, 09 Jan 2024 06:27:01 -0800 (PST) X-Google-Smtp-Source: AGHT+IFC3PA8oXHov9l8MdSVDNYpV6L5r9304Hhb3fD1nr3h1Zs22vbr12gKbATBa2QFTesEA7Zryg== X-Received: by 2002:a05:600c:a007:b0:40e:5333:2024 with SMTP id jg7-20020a05600ca00700b0040e53332024mr29844wmb.53.1704810420666; Tue, 09 Jan 2024 06:27:00 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id p3-20020a05600c358300b0040d91930f93sm3733546wmq.11.2024.01.09.06.27.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:27:00 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Michael Weghorn , Andrew Burgess Subject: [PATCH 12/16] gdb/gdbserver: add a '--no-escape-args' command line option Date: Tue, 9 Jan 2024 14:26:35 +0000 Message-Id: <47fe194ee2e88d8ed68192abc31ac8e3df626fe1.1704809585.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org From: Michael Weghorn This introduces a new '--no-escape-args' option for gdb and gdbserver. I (Andrew Burgess) have based this patch from work done in this series: https://inbox.sourceware.org/gdb-patches/20211022071933.3478427-1-m.weghorn@posteo.de/ I have changed things slightly from the original series, however, I think this work is close enough that I've left the original author (Michael) in place and added myself as co-author. Any bugs introduced by my modifications to the original patch should be considered mine. I've also added documentation and tests which were missing from the originally proposed patch. When the startup-with-shell option is enabled, arguments passed directly as 'gdb --args ' or 'gdbserver ', are by default escaped so that they are passed to the inferior as passed on the command line, no globbing or variable substitution happens within the shell GDB uses to start the inferior. For gdbserver, this is the case since commit: commit bea571ebd78ee29cb94adf648fbcda1e109e1be6 Date: Mon May 25 11:39:43 2020 -0400 Use construct_inferior_arguments which handles special chars Only arguments set via 'set args ', 'run ', or through the Python API are not escaped in standard upstream GDB right now. For the 'gdb --args' case, directly settings unescaped args on gdb invocation is possible e.g. by using the "--eval-command='set args '", while this possibility does not exist for gdbserver. This commit adds a new '--no-escape-args' command line option for GDB and gdbserver. This option is used with GDB as a replacement for the current '--args' option, and for gdbserver this new option is a flag which changes how gdbserver handles inferior arguments on the command line. When '--no-escape-args' is used inferior arguments passed on the command line will not have escaping added by GDB or gdbserver. For gdbserver, using this new option allows having the behaviour from before commit bea571ebd78ee29cb94adf648fbcda1e109e1be6, while keeping the default behaviour unified between GDB and GDBserver. For GDB the --no-escape-args option can be used as a replacement for --args, like this: shell> gdb --no-escape-args my-program arg1 arg2 arg3 While for gdbserver, the --no-escape-args option is a flag, which can be used like: shell> gdbserver --no-escape-args --once localhost:54321 \ my-program arg1 arg2 arg3 Co-Authored-By: Andrew Burgess Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 Reviewed-By: Eli Zaretskii --- gdb/NEWS | 8 ++ gdb/doc/gdb.texinfo | 114 ++++++++++++++- gdb/main.c | 28 +++- gdb/testsuite/gdb.base/args.exp | 101 ++++++++----- gdb/testsuite/gdb.server/inferior-args.c | 27 ++++ gdb/testsuite/gdb.server/inferior-args.exp | 157 +++++++++++++++++++++ gdbserver/server.cc | 21 ++- 7 files changed, 407 insertions(+), 49 deletions(-) create mode 100644 gdb/testsuite/gdb.server/inferior-args.c create mode 100644 gdb/testsuite/gdb.server/inferior-args.exp diff --git a/gdb/NEWS b/gdb/NEWS index 65a082808e4..80c766eeeda 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -9,6 +9,14 @@ * GDB index now contains information about the main function. This speeds up startup when it is being used for some large binaries. +* GDB now accepts --no-escape-args as an alternative to --args on the + command line. GDB will not escape special shell characters within + arguments after --no-escape-args. + +* gdbserver now accepts --no-escape-args as a command line flag. When + this flag is used gdbserver will not escape special shell characters + within the inferior arguments. + * Changed commands disassemble diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 9dbe53384e1..abb07d74baf 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -898,9 +898,9 @@ ``process'', and there is often no way to get a core dump. @value{GDBN} will warn you if it is unable to attach or to read core dumps. -You can optionally have @code{@value{GDBP}} pass any arguments after the -executable file to the inferior using @code{--args}. This option stops -option processing. +You can optionally have @code{@value{GDBP}} pass any arguments after +the executable file to the inferior using @code{--args} or +@code{--no-escape-args}. These options stops option processing. @smallexample @value{GDBP} --args gcc -O2 -c foo.c @end smallexample @@ -1246,6 +1246,56 @@ executable file are passed as command line arguments to the inferior. This option stops option processing. +Argument supplied using @code{--args} will have backslashes applied to +escape any special shell characters. This ensures that when the +inferior starts it is passed arguments exactly as @value{GDBN} +receives them. + +For example, consider the following command run under a shell: +@smallexample +@value{GDBP} --args ls *.c +@end smallexample +@noindent +In this case the shell will expand @kbd{*.c} at the time @value{GDBN} +is invoked, not at the time that the inferior is invoked. As a +result, if an additional @kbd{.c} file is created after @value{GDBN} +is started, but before the inferior is started, then the inferior will +not show the file in its output; the list of matching files was +resolved at the time @value{GDBN} was started. + +If you quote the @kbd{*} character used in the @value{GDBN} command +line argument then this will prevent the shell that starts +@value{GDBN} from expanding the @kbd{*.c} pattern, however, this +quoting will also be passed to the shell that @value{GDBN} invokes in +order to start the inferior (@pxref{set startup-with-shell}), and this +will prevent the @kbd{*.c} pattern being expanded at this point either: +@smallexample +@value{GDBP} --args ls '*.c' +(@value{GDBP}) show args +Argument list to give program being debugged when it is started is "\*.log". +@end smallexample +@noindent +If this quoting behaviour does not meet your needs, then you could use +@code{--no-escape-args} instead, which is described below. + +@item --no-escape-args +@cindex @code{--no-escape-args} +Change interpretation of command line so that arguments following the +executable file are passed as command line arguments to the inferior. +This option stops option processing. + +Unlike @code{--args}, arguments after the executable name will not +have any escaping applied to them. As a result, any special shell +characters that are not expanded by the shell that invokes +@value{GDBN} will be expanded by the shell that @value{GDBN} uses to +start the inferior. + +@smallexample +@value{GDBP} --no-escape-args ls '*.c' +(@value{GDBP}) show args +Argument list to give program being debugged when it is started is "*.log". +@end smallexample + @item -baud @var{bps} @itemx -b @var{bps} @cindex @code{--baud} @@ -50498,9 +50548,10 @@ directly to @code{stdout}, will also be made silent. @item --args @var{prog} [@var{arglist}] -Change interpretation of command line so that arguments following this -option are passed as arguments to the inferior. As an example, take -the following command: +@itemx --no-escape-args @var{prog} [@var{arglist}] +Change interpretation of command line so that arguments following +either of these options are passed as arguments to the inferior. As +an example, take the following command: @smallexample gdb ./a.out -q @@ -50515,7 +50566,44 @@ @end smallexample @noindent -starts @value{GDBN} with the introductory message, and passes the option to the inferior. +starts @value{GDBN} with the introductory message, and passes the +option @code{-q} to the inferior. + +The difference between @option{--args} and @option{--no-escape-args} +is whether @value{GDBN} applies escapes to the argument it sees: + +@smallexample +gdb --args ./a.out *.c +@end smallexample + +@noindent +in this case the @code{*.c} is expanded by the shell that invokes +@value{GDBN}, the list of matching files will be fixed in the inferior +argument list. If instead this is used: + +@smallexample +gdb --args ./a.out '*.c' +@end smallexample + +@noindent +then the shell that invokes @value{GDBN} will not expand @code{*.c}, +but instead @value{GDBN} will escape the @code{*} character so when +a.out is invoked it will be passed a literal @code{*.c}. If instead +this is used: + +@smallexample +gdb --no-escape-args ./a.out '*.c' +@end smallexample + +@noindent +now @value{GDBN} will not escape the @code{*} character. When the +inferior is invoked the @code{*.c} will be expanded, and the inferior +will be passed the list of files as present at the time the inferior +is invoked. + +This change of behaviour can be important if the list of matching +files could change between the time that @value{GDBN} is started, and +the time the inferior is started. @item --pid=@var{pid} Attach @value{GDBN} to an already running program, with the PID @var{pid}. @@ -50857,6 +50945,18 @@ with the @option{--once} option, it will stop listening for any further connection attempts after connecting to the first @value{GDBN} session. +@item --no-escape-args +By default, inferior arguments passed on the @command{gdbserver} +command line will have any special shell characters escaped by +@command{gdbserver}. This ensures that when @command{gdbserver} +invokes the inferior, the arguments passed to the inferior are +identical to the arguments passed to @command{gdbserver}. + +To disable this escaping, use @option{--no-escape-args}. With this +option special shell characters will not be escaped. When +@command{gdbserver} starts a new shell in order to invoke the +inferior, this new shell will expand any special shell characters. + @c --disable-packet is not documented for users. @c --disable-randomization and --no-disable-randomization are superseded by diff --git a/gdb/main.c b/gdb/main.c index 015ed396f58..e9cd4172e4a 100644 --- a/gdb/main.c +++ b/gdb/main.c @@ -622,9 +622,10 @@ captured_main_1 (struct captured_main_args *context) char **argv = context->argv; static int quiet = 0; - static int set_args = 0; static int inhibit_home_gdbinit = 0; + enum { NO_ARGS, SET_ESC_ARGS, SET_NO_ESC_ARGS } set_args = NO_ARGS; + /* Pointers to various arguments from command line. */ char *symarg = NULL; char *execarg = NULL; @@ -773,7 +774,9 @@ captured_main_1 (struct captured_main_args *context) OPT_EIX, OPT_EIEX, OPT_READNOW, - OPT_READNEVER + OPT_READNEVER, + OPT_SET_ESC_ARGS, + OPT_SET_NO_ESC_ARGS, }; /* This struct requires int* in the struct, but write_files is a bool. So use this temporary int that we write back after argument parsing. */ @@ -846,7 +849,8 @@ captured_main_1 (struct captured_main_args *context) {"windows", no_argument, NULL, OPT_WINDOWS}, {"statistics", no_argument, 0, OPT_STATISTICS}, {"write", no_argument, &write_files_1, 1}, - {"args", no_argument, &set_args, 1}, + {"args", no_argument, nullptr, OPT_SET_ESC_ARGS}, + {"no-escape-args", no_argument, nullptr, OPT_SET_NO_ESC_ARGS}, {"l", required_argument, 0, 'l'}, {"return-child-result", no_argument, &return_child_result, 1}, {0, no_argument, 0, 0} @@ -858,7 +862,7 @@ captured_main_1 (struct captured_main_args *context) c = getopt_long_only (argc, argv, "", long_options, &option_index); - if (c == EOF || set_args) + if (c == EOF || set_args != NO_ARGS) break; /* Long option that takes an argument. */ @@ -939,6 +943,12 @@ captured_main_1 (struct captured_main_args *context) case OPT_EIEX: cmdarg_vec.emplace_back (CMDARG_EARLYINIT_COMMAND, optarg); break; + case OPT_SET_ESC_ARGS: + set_args = SET_ESC_ARGS; + break; + case OPT_SET_NO_ESC_ARGS: + set_args = SET_NO_ESC_ARGS; + break; case 'B': batch_flag = batch_silent = 1; gdb_stdout = new null_file (); @@ -1072,7 +1082,7 @@ captured_main_1 (struct captured_main_args *context) /* Now that gdb_init has created the initial inferior, we're in position to set args for that inferior. */ - if (set_args) + if (set_args != NO_ARGS) { /* The remaining options are the command-line options for the inferior. The first one is the sym/exec file, and the rest @@ -1084,8 +1094,11 @@ captured_main_1 (struct captured_main_args *context) symarg = argv[optind]; execarg = argv[optind]; ++optind; + escape_args_func escape_func + = ((set_args == SET_ESC_ARGS) ? escape_shell_characters + : escape_quotes_and_white_space); gdb::array_view arg_view (&argv[optind], argc - optind); - current_inferior ()->set_args (arg_view, escape_shell_characters); + current_inferior ()->set_args (arg_view, escape_func); } else { @@ -1398,7 +1411,8 @@ This is the GNU debugger. Usage:\n\n\ gdb_puts (_("\ Selection of debuggee and its files:\n\n\ --args Arguments after executable-file are passed to inferior.\n\ - --core=COREFILE Analyze the core dump COREFILE.\n\ + --no-escape-args Like --args, but arguments are not escaped.\n \ + --core=COREFILE Analyze the core dump COREFILE.\n \ --exec=EXECFILE Use EXECFILE as the executable.\n\ --pid=PID Attach to running process PID.\n\ --directory=DIR Search for source files in DIR.\n\ diff --git a/gdb/testsuite/gdb.base/args.exp b/gdb/testsuite/gdb.base/args.exp index 7c123e36404..9ff9e7ee6d1 100644 --- a/gdb/testsuite/gdb.base/args.exp +++ b/gdb/testsuite/gdb.base/args.exp @@ -32,41 +32,73 @@ if {[build_executable $testfile.exp $testfile $srcfile] == -1} { # NAME is the name to use for the tests and ARGLIST is the list of # arguments that are passed to GDB when it is started. # -# The optional RE_LIST is the list of patterns to check the arguments -# against, these patterns should match ARGLIST. If the arguments are -# expected to show up unmodified in the test output then RE_LIST can -# be dropped, and this proc will reuse ARGLIST. - -proc args_test { name arglist {re_list {}} } { - - # If RE_LIST is not supplied then we can reuse ARGLIST, this - # implies that the arguments will appear unmodified in the test - # output. - if {[llength $re_list] == 0} { - set re_list $arglist +# The optional RE_ESC_LIST is the list of patterns to check the +# inferior arguments against when GDB is started using --args. If +# RE_ESC_LIST is not given then ARGLIST is reused, this implies that +# the inferior arguments appear unchanged in the test output. +# +# The optional RE_NO_ESC_LIST is the list of patterns to check the +# inferior arguments against when GDB is started using +# --no-escape-args. If RE_NO_ESC_LIST is not given then RE_ESC_LIST +# is reused, this implies that there's no difference between the test +# output when the arguments are escaped or not. + +proc args_test { name arglist {re_esc_list {}} {re_no_esc_list {}} } { + + # If either of the two regexp lists are not specificed then we can + # use an earlier argument value instead. + # + # For the first regexp list, if this is missing then we use the + # argument list, this assumes that the arguments will appear + # unmodified in the output. + if {[llength $re_esc_list] == 0} { + set re_esc_list $arglist } - foreach_with_prefix startup_with_shell { on off } { - save_vars { ::GDBFLAGS } { - set ::GDBFLAGS "$::GDBFLAGS --args $::binfile $arglist" - - clean_restart $::binfile - - gdb_test_no_output "set startup-with-shell ${startup_with_shell}" \ - "set startup-with-shell for $name" - - runto_main - gdb_breakpoint [gdb_get_line_number "set breakpoint here"] - gdb_continue_to_breakpoint "breakpoint for $name" - - set expected_len [expr 1 + [llength $re_list]] - gdb_test "print argc" "\\\$$::decimal = $expected_len" "argc for $name" + # If the second regexp list is missing then we reuse the first + # regexp list. This assumes there's no difference between escaped + # and unescaped arguments in the output. + if {[llength $re_no_esc_list] == 0} { + set re_no_esc_list $re_esc_list + } - set i 1 - foreach arg $re_list { - gdb_test "print argv\[$i\]" "\\\$$::decimal = $::hex \"$arg\"" \ - "argv\[$i\] for $name" - set i [expr $i + 1] + foreach_with_prefix startup_with_shell { on off } { + foreach_with_prefix arg_flag { args no-escape-args } { + save_vars { ::GDBFLAGS } { + set ::GDBFLAGS "$::GDBFLAGS --${arg_flag} $::binfile $arglist" + + clean_restart $::binfile + + gdb_test_no_output \ + "set startup-with-shell ${startup_with_shell}" \ + "set startup-with-shell for $name" + + runto_main + gdb_breakpoint [gdb_get_line_number "set breakpoint here"] + gdb_continue_to_breakpoint "breakpoint for $name" + + if { $arg_flag eq "args" || $startup_with_shell eq "off" } { + set re_list $re_esc_list + } else { + set re_list $re_no_esc_list + } + + set expected_len [expr 1 + [llength $re_list]] + gdb_test "print argc" \ + "\\\$$::decimal = $expected_len" "argc for $name" + + set i 1 + foreach arg $re_list { + if { $arg eq "\n" } { + set arg {\\n} + } elseif { $arg eq "\"" } { + set arg {\\\"} + } + gdb_test "print argv\[$i\]" \ + "\\\$$::decimal = $::hex \"$arg\"" \ + "argv\[$i\] for $name" + set i [expr $i + 1] + } } } } @@ -102,6 +134,11 @@ proc run_all_tests {} { args_test "lone single quote" {{1} \' {3}} args_test "lone double quote" {{1} \" {3}} {1 \\\\\" 3} + + save_vars { ::env(TEST) } { + set ::env(TEST) "ABCD" + args_test "shell variable" {{$TEST}} {\\$TEST} {{ABCD}} + } } run_all_tests diff --git a/gdb/testsuite/gdb.server/inferior-args.c b/gdb/testsuite/gdb.server/inferior-args.c new file mode 100644 index 00000000000..5fd215f50a8 --- /dev/null +++ b/gdb/testsuite/gdb.server/inferior-args.c @@ -0,0 +1,27 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2023 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 . */ + +#include + +int +main (int argc, char **argv) +{ + for (int i = 0; i < argc; i++) + printf ("[%d] %s\n", i, argv[i]); + + return 0; +} diff --git a/gdb/testsuite/gdb.server/inferior-args.exp b/gdb/testsuite/gdb.server/inferior-args.exp new file mode 100644 index 00000000000..9b2aeb249e0 --- /dev/null +++ b/gdb/testsuite/gdb.server/inferior-args.exp @@ -0,0 +1,157 @@ +# Copyright 2023 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 passing inferior arguments on the gdbserver command line. Tests the +# flags --no-startup-with-shell and --no-escape-args that change how GDB +# interprets the arguments being passed. + +# This test relies on starting gdbserver using the pipe syntax. Not sure +# how well this will run if part of this test is being run elsewhere. +require {!is_remote target} {!is_remote host} + +load_lib gdbserver-support.exp + +standard_testfile + +require allow_gdbserver_tests + +set gdbserver [find_gdbserver] +if { $gdbserver == "" } { + unsupported "could not find gdbserver" + return +} + +standard_testfile +if {[build_executable "failed to prepare" $testfile $srcfile]} { + return -1 +} + +# EXTENDED_P is a boolean, when true gdbserver is started with --multi, and +# GDB connects using extended-remote protocol. Otherwise, no --multi flag +# is passed, and GDB connects with the remote protocol. +# +# WITH_SHELL_P is a boolean, when true gdbserver starts the inferior using a +# shell, when false gdbserver is passed the --no-startup-with-shell command +# line option, and should not start the inferior through a shell. +# +# ESCAPE_P is a boolean, when true gdbserver applies escapes to the inferior +# arguments, when false gdbserver is passed the --no-escape-args command +# line option, and should not apply escaping to the inferior arguments. +# +# ARGLIST is a list of inferior arguments to add to the gdbserver command +# line. +# +# RE_LIST is a list of patterns to match, one for each of ARGLIST. Once the +# inferior is started we check that each argument matches its corresponding +# entry in RE_LIST. +proc do_test_inner { extended_p with_shell_p escape_p arglist re_list } { + + clean_restart ${::binfile} + + gdb_test_no_output "set sysroot" + + # Make sure we're disconnected, in case we're testing with an + # extended-remote board, therefore already connected. + gdb_test "disconnect" ".*" + + if { $extended_p } { + set protocol "extended-remote" + } else { + set protocol "remote" + } + + if { $escape_p } { + set esc_opt "" + } else { + set esc_opt "--no-escape-args" + } + + if { $with_shell_p } { + set shell_opt "" + } else { + set shell_opt "--no-startup-with-shell" + } + + gdb_test "target ${protocol} | ${::gdbserver} --once ${esc_opt} ${shell_opt} - ${::binfile} ${arglist}" \ + ".*" \ + "start gdbserver over stdin" + + gdb_breakpoint main + gdb_continue_to_breakpoint main + + set expected_len [expr 1 + [llength $re_list]] + gdb_test "print argc" \ + "\\\$$::decimal = $expected_len" "check argc" + + set i 1 + foreach arg $re_list { + verbose -log "APB ($arg)" + gdb_test "print argv\[$i\]" \ + "\\\$$::decimal = $::hex \"$arg\"" \ + "check argv\[$i\]" + set i [expr $i + 1] + } +} + +# Wrapper around do_test_inner. NAME is the name of this test, used to make +# the test names unique. ARGLIST is the list of inferior arguments to add +# to the gdbserver command line. +# +# The optional RE_ESC_LIST is a list of patterns to match against the +# inferior arguments once the inferior is started, one pattern for each +# argument. If RE_ESC_LIST is not given then ARGLIST is reused, which +# implies the arguments appear unmodified in the test output. +# +# The optional RE_NO_ESC_LIST is a list of patterns ot match against the +# inferior arguments when gdbserver is started with --no-escape-args or +# --no-startup-with-shell. There should be one pattern for each argument. +# If RE_NO_ESC_LIST is not given then RE_ESC_LIST is resused, which implies +# there's no difference in how the arguments are printed. +proc args_test { name arglist {re_esc_list {}} {re_no_esc_list {}} } { + if {[llength $re_esc_list] == 0} { + set re_esc_list $arglist + } + + if {[llength $re_no_esc_list] == 0} { + set re_no_esc_list $re_esc_list + } + + foreach_with_prefix extended_p { yes no } { + foreach_with_prefix startup_with_shell { on off } { + foreach_with_prefix escape_p { yes no } { + if { $escape_p || !$startup_with_shell } { + set re_list $re_esc_list + } else { + set re_list $re_no_esc_list + } + + with_test_prefix "$name" { + do_test_inner $extended_p $startup_with_shell \ + $escape_p $arglist $re_list + } + } + } + } +} + +args_test "basic" {a b c} +args_test "one empty" {1 "" 3} +args_test "two empty" {1 "" "" 3} +args_test "one with single quotes" {1 "''" 3} +args_test "lone double quote" {"1" \" 3} {1 \\\\\" 3} +save_vars { env(TEST) } { + set env(TEST) "ABCD" + args_test "shell variable" {\$TEST} {\\$TEST} {ABCD} +} diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 65df03ef309..0445fa0237f 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -3837,10 +3837,20 @@ gdbserver_usage (FILE *stream) " --startup-with-shell\n" " Start PROG using a shell. I.e., execs a shell that\n" " then execs PROG. (default)\n" + " To make use of globbing and variable subsitution for\n" + " arguments passed directly on gdbserver invocation,\n" + " see the --no-escape-args command line option in\n" + " addition\n" " --no-startup-with-shell\n" " Exec PROG directly instead of using a shell.\n" - " Disables argument globbing and variable substitution\n" - " on UNIX-like systems.\n" + " --no-escape-args\n" + " If PROG is started using a shell (see the\n" + " --[no-]startup-with-shell option),\n" + " ARGS passed directly on gdbserver invocation are\n" + " escaped, so no globbing or variable substitution\n" + " happens for those. This option disables escaping, so\n" + " globbing and variable substituation in the shell\n" + " are done for ARGS on UNIX-like systems.\n" "\n" "Debug options:\n" "\n" @@ -4074,6 +4084,7 @@ captured_main (int argc, char *argv[]) volatile int attach = 0; int was_running; bool selftest = false; + bool escape_args = true; #if GDB_SELF_TEST std::vector selftest_filters; @@ -4230,6 +4241,8 @@ captured_main (int argc, char *argv[]) startup_with_shell = true; else if (strcmp (*next_arg, "--no-startup-with-shell") == 0) startup_with_shell = false; + else if (strcmp (*next_arg, "--no-escape-args") == 0) + escape_args = false; else if (strcmp (*next_arg, "--once") == 0) run_once = true; else if (strcmp (*next_arg, "--selftest") == 0) @@ -4339,8 +4352,10 @@ captured_main (int argc, char *argv[]) std::vector temp_arg_vector; for (i = 1; i < n; i++) temp_arg_vector.push_back (next_arg[i]); + escape_args_func escape_func = (escape_args ? escape_shell_characters + : escape_quotes_and_white_space); program_args = construct_inferior_arguments (temp_arg_vector, - escape_shell_characters); + escape_func); /* Wait till we are at first instruction in program. */ target_create_inferior (program_path.get (), program_args); From patchwork Tue Jan 9 14:26:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83655 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C451B3858C74 for ; Tue, 9 Jan 2024 14:33:18 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id E1431385842F for ; Tue, 9 Jan 2024 14:27:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E1431385842F 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 E1431385842F 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=1704810435; cv=none; b=kc7Zzeomt9F1PwSfQQc3tJHlBK3h4Paff+9MuRXJgEk6OzJvRNAhYc/6idniOleapl8g7kZLj/6Exd2k+aJE+Xa49MTe9M/sHJrD8pW1OoxgHLvzlkTE9y4K4KHEOMVKfLq5f5xxMJl6UlBINyQUN3NtO72Ey3xNTWF7p0ATYbI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810435; c=relaxed/simple; bh=hboOMOSpj3b+INaK91JOGX6/Hp+wyvGCiickiijumz8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=moTicol4JboZID+dQ+9VWtUZjIgVsCHrTz2WLetPQiQjzfAREassze1dJRTedbfuuSgwP9/HflNRhS0QSYh5uVtwcdhCral2A1txGcHg3/h3xxD/Hb7kUo7tGY3pkm5xNEdPx5Rp35BlkIep8ychb7EwL6ywAsJk7jVhhhHI9fY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810430; 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: in-reply-to:in-reply-to:references:references; bh=XwCI5z9M+cmGxRyGE+5iA8boBZSoiPOGIHJMIQRfk6c=; b=I1h9qP11kLEvEehSFMQ+gM1EX3yMkKrBj4m6g94aSxzGHI9tq3u7tMQmScciaNBbQE8ylB xbBxgNx4DIN1IUgjpW93D9fIuJju11+ccR9tbTjJaxxda98vR5OMwHBdWtoJKTEHrFGebR Y+KZjKWhrRHzuU9eV/SiG0uZIKTOWpg= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-117-dxJZM3vwPHiGb-pt4GzQJg-1; Tue, 09 Jan 2024 09:27:04 -0500 X-MC-Unique: dxJZM3vwPHiGb-pt4GzQJg-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-40e53200380so661655e9.3 for ; Tue, 09 Jan 2024 06:27:04 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810422; x=1705415222; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=XwCI5z9M+cmGxRyGE+5iA8boBZSoiPOGIHJMIQRfk6c=; b=JUyrY0M2dI9WE9+KRJRgNcA8GoKpqvwbHBZKJZ8dUTjvMBFdr/d/RdTDVSRmOpM4e3 R6UvgOGEtgimKgTqEzrLV9f1zKS47S1Lht2NPOx2G62R5CEitoxsM+ZYgiq7y1PwtqRh CBRFx4n7Ys1bQHQ7/gGCFb3MdEkyC82usY9S64enQ1ryHvfnwEtYG4besQmpwSR2/VBA LBQy7+v1+cls6CHDrqiFkkAAiQ1o1VTagCIWwKwT3CIh/kY87RUOX29hsL4VOny/07Yz D2uRkHM2VZqCdEChbpYzpibuoGJqR0wPEVC81Vm7BVVuKp14IpW2aX12LB2ZfK78LE1L a5OA== X-Gm-Message-State: AOJu0Yy9vyZFJhnlnyv1qq1CSE04yxM9YgbFi03zM44w+ZLUv0i6bLpJ /j5DCTRhBJa6kfAEEU1JrQWQmVf0jvfIVthV7hVmr9cyn1UkA1YZsdJgPwZhzkc7v7CjTErBzi0 Z9o3bKLrc5qLFwEzjaxnqCsX3NO+ySFtQJoSCWRAgxPUwipF9NZTgIA34FSI3wKpZNvz09PNRNk NxG97M4yMrIqWVLg== X-Received: by 2002:a05:600c:468b:b0:40c:366a:598a with SMTP id p11-20020a05600c468b00b0040c366a598amr2603748wmo.107.1704810422285; Tue, 09 Jan 2024 06:27:02 -0800 (PST) X-Google-Smtp-Source: AGHT+IF9SElVpd6UbB+Ebs86V2i7/Z8ufe46O0ikP7YlAiJemqRLdFCh5D6NxRg9tbazhy4hDq1agQ== X-Received: by 2002:a05:600c:468b:b0:40c:366a:598a with SMTP id p11-20020a05600c468b00b0040c366a598amr2603742wmo.107.1704810421790; Tue, 09 Jan 2024 06:27:01 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id d7-20020adfe887000000b0033672971fabsm2528766wrm.115.2024.01.09.06.27.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:27:01 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 13/16] gdb: allow 'set args' and run commands to contain newlines Date: Tue, 9 Jan 2024 14:26:36 +0000 Message-Id: <4b226e07edabf10a704aef70fd9ef4a3f2a5bd34.1704809585.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org When starting GDB it is possible to set an inferior argument that contains a newline, for example: shell> gdb --args my.app "abc > def" ... (gdb) show args Argument list to give program being debugged when it is started is "abc' 'def". However, once GDB is started, the only way to install an argument containing a newline is to use the Python API. This commit changes that. After this commit 'set args' as well as 'run', 'start', and 'starti', will now accept multi-line inferior arguments, e.g.: (gdb) set args "abc > def" (gdb) show args Argument list to give program being debugged when it is started is ""abc def"". And also: (gdb) run "abc > def" ... etc ... Once GDB has presented the secondary prompt to gather the remaining inferior arguments then it is possible for the user to quit argument entry by sending SIGINT (usually, Ctrl-c). For the 'set args' case this will abort the argument change, leaving the arguments as they were previously. For the run style commands, this aborts the run command completely, the inferior is not changed, and the partially collected arguments are not installed. Reviewed-By: Eli Zaretskii --- gdb/NEWS | 10 ++ gdb/doc/gdb.texinfo | 20 ++++ gdb/infcmd.c | 128 +++++++++++++++++++++-- gdb/testsuite/gdb.base/inferior-args.exp | 71 ++++++++++++- 4 files changed, 219 insertions(+), 10 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 80c766eeeda..2ba1899f78c 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -51,6 +51,16 @@ set remote thread-options-packet show remote thread-options-packet Set/show the use of the thread options packet. +* Changed commands + +set args +run +start +starti + These commands now all allow for entering inferior arguments that + contain a newline character. The newline must be contained within a + single or double quoted argument. + * New features in the GDB remote stub, GDBserver ** The --remote-debug and --event-loop-debug command line options diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index abb07d74baf..2015293ee0d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -2893,6 +2893,26 @@ using @code{set args} before the next @code{run} is the only way to run it again without arguments. +It is possible to set arguments containing a newline character. This +can be done by enclosing an argument within a single or double quote. +For example, start by entering: + +@smallexample +(@value{GDBP}) set args "ab +@end smallexample + +@noindent +and then enter the newline, @value{GDBN} gives a secondary prompt +@code{>} and allows you to continue entering the arguments: + +@smallexample +(@value{GDBP}) set args "ab +>cd" +(@value{GDBP}) show args +Argument list to give program being debugged when it is started is ""ab +cd"". +@end smallexample + @kindex show args @item show args Show the arguments to give your program when it is started. diff --git a/gdb/infcmd.c b/gdb/infcmd.c index a3c32792491..bad8736aad2 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -120,11 +120,112 @@ show_inferior_tty_command (struct ui_file *file, int from_tty, "is \"%s\".\n"), inferior_tty.c_str ()); } +/* Return true if the inferior argument string ARGS represents a "complete" + set of arguments. Arguments are considered complete so long as they + don't contain unbalanced single or double quoted strings. Unbalanced + means that a single or double quoted argument is started, but not + finished. */ + +static bool +args_complete_p (const std::string &args) +{ + const char *input = args.c_str (); + bool squote = false, dquote = false; + + while (*input != '\0') + { + input = skip_spaces (input); + + if (squote) + { + /* Inside a single quoted argument, look for the closing single + quote. */ + if (*input == '\'') + squote = false; + } + else if (dquote) + { + /* If we see either '\"' or '\\' within a double quoted argument + then skip both characters (one is skipped here, and the other + at the end of the loop). We need to skip the '\"' so that we + don't consider the '"' as closing the double quoted argument, + and we don't skip the entire '\\' then we'll only skip the + first '\', in which case we might see the second '\' as a '\"' + sequence, which would be wrong. */ + if (*input == '\\' && strchr ("\"\\", *(input + 1)) != nullptr) + ++input; + /* Otherwise, just look for the closing double quote. */ + else if (*input == '"') + dquote = false; + } + else + { + /* Outside of either a single or double quoted argument, we need + to check for '\"', '\'', and '\\'. The escaped quotes we + obviously need to skip so we don't think that we have started + a quoted argument. The '\\' we need to skip so we don't just + skip the first '\' and then incorrectly consider the second + '\' are part of a '\"' or '\'' sequence. */ + if (*input == '\\' && strchr ("\"\\'", *(input + 1)) != nullptr) + ++input; + /* Otherwise, check for the start of a single or double quoted + argument. */ + else if (*input == '\'') + squote = true; + else if (*input == '"') + dquote = true; + } + + ++input; + } + + return (!dquote && !squote); +} + +/* ... */ + +static std::string +get_complete_args (std::string args) +{ + /* If the user wants an argument containing a newline then they need to + do so within quotes. Use args_complete_p to check if the ARGS string + contains balanced double and single quotes. If not then prompt the + user for additional arguments and append this to ARGS. */ + const char *prompt = nullptr; + while (!args_complete_p (args)) + { + if (prompt == nullptr) + { + prompt = getenv ("PS2"); + if (prompt == nullptr) + prompt = "> "; + } + + std::string buffer; + const char *content = command_line_input (buffer, prompt, "set_args"); + if (content == nullptr) + return {}; + + args += "\n" + buffer; + } + + return args; +} + /* Store the new value passed to 'set args'. */ static void -set_args_value (const std::string &args) +set_args_value (const std::string &args_in) { + std::string args; + + if (!args_in.empty ()) + { + args = get_complete_args (args_in); + if (args.empty ()) + return; + } + current_inferior ()->set_args (args); } @@ -376,6 +477,21 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) dont_repeat (); + gdb::unique_xmalloc_ptr stripped = strip_bg_char (args, &async_exec); + args = stripped.get (); + + std::string inf_args; + /* If there were other args, beside '&', process them. */ + if (args != nullptr) + { + /* If ARGS is only a partial argument string then this call will + interactively read more arguments from the user. If the user + quits then we shouldn't start the inferior. */ + inf_args = get_complete_args (args); + if (inf_args.empty ()) + return; + } + scoped_disable_commit_resumed disable_commit_resumed ("running"); kill_if_already_running (from_tty); @@ -397,9 +513,6 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) reopen_exec_file (); reread_symbols (from_tty); - gdb::unique_xmalloc_ptr stripped = strip_bg_char (args, &async_exec); - args = stripped.get (); - /* Do validation and preparation before possibly changing anything in the inferior. */ @@ -412,6 +525,9 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) /* Done. Can now set breakpoints, change inferior args, etc. */ + if (!inf_args.empty ()) + current_inferior ()->set_args (inf_args); + /* Insert temporary breakpoint in main function if requested. */ if (run_how == RUN_STOP_AT_MAIN) { @@ -433,10 +549,6 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) the user has to manually nuke all symbols between runs if they want them to go away (PR 2207). This is probably reasonable. */ - /* If there were other args, beside '&', process them. */ - if (args != nullptr) - current_inferior ()->set_args (args); - if (from_tty) { uiout->field_string (nullptr, "Starting program"); diff --git a/gdb/testsuite/gdb.base/inferior-args.exp b/gdb/testsuite/gdb.base/inferior-args.exp index 6c22ecb3c54..b259e4a4217 100644 --- a/gdb/testsuite/gdb.base/inferior-args.exp +++ b/gdb/testsuite/gdb.base/inferior-args.exp @@ -211,12 +211,15 @@ lappend test_desc_list [list "test four" \ [list "$hex \"'\"" \ "$hex \"\\\\\"\""]] -# Run all tests in the global TEST_DESC_LIST. +# Run all tests in the global TEST_DESC_LIST, as well as some tests of +# inferior arguments containing newlines. proc run_all_tests {} { + set all_methods { "start" "starti" "run" "set args" } + foreach desc $::test_desc_list { lassign $desc name stub_suitable args re_list with_test_prefix $name { - foreach_with_prefix set_method { "start" "starti" "run" "set args" } { + foreach_with_prefix set_method $all_methods { foreach_with_prefix startup_with_shell { on off } { do_test $set_method $startup_with_shell $args $re_list \ $stub_suitable @@ -224,6 +227,70 @@ proc run_all_tests {} { } } } + + # Check the multi-line argument entry. This isn't going to work when + # using the gdbstub, as the only way to set arguments in this case is + # via the gdbserver command line, which isn't what we're testing here. + if { ![use_gdb_stub] } { + foreach_with_prefix set_method $all_methods { + clean_restart $::binfile + + # First check that we can abort entering multi-line arguments. + set saw_prompt false + gdb_test_multiple "$set_method \"ab" "abort argument entry" { + -re "^$set_method \"ab\r\n" { + exp_continue + } + -re "^> $" { + set saw_prompt true + send_gdb "\004" + exp_continue + } + -re "quit\r\n$::gdb_prompt $" { + gdb_assert {$saw_prompt} \ + $gdb_test_name + } + } + + # Now place a breakpoint on main. + if { ![gdb_breakpoint "main" message] } { + fail "could not set breakpoint on main" + continue + } + + # And actually enter some multi-line arguments. + set saw_prompt false + gdb_test_multiple "$set_method \"xy" "complete argument entry" { + -re "^$set_method \"xy\r\n" { + exp_continue + } + -re "^> $" { + set saw_prompt true + send_gdb "12\"\n" + exp_continue + } + + -re "$::gdb_prompt $" { + gdb_assert { $saw_prompt } \ + $gdb_test_name + } + } + + # For the two methods that don't automatically run to main, + # poke the inferior along to main. + if { $set_method == "set args" } { + if { ![runto_main] } { + continue + } + } elseif { $set_method == "starti" } { + gdb_continue_to_breakpoint "b/p in main" + } + + # And check we correctly see the argument containing a newline. + gdb_test "print argc" " = 2" + gdb_test "print argv\[1\]" " = $::hex \"xy\\\\n12\"" + } + } } run_all_tests From patchwork Tue Jan 9 14:26:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83647 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E17113857B92 for ; Tue, 9 Jan 2024 14:30:56 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id B64683857C74 for ; Tue, 9 Jan 2024 14:27:07 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B64683857C74 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 B64683857C74 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810431; cv=none; b=oGl4ojHlDZiOoJzkQx/R7kPC1NEe6juLXG3nHvsPcbNG8dRaZ5MXUxXJ+vy8t3wyEb+BDJgGNy2681S4ZW7DsiSPZaPnqvM0a/+krZ9Ns4IF6Up8C5rgrQooQ3HH/3ZolgzTHJ590GGqUGXFzsXlE/BQrs00lDJ91iF+aVf8QuM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810431; c=relaxed/simple; bh=WYvl/rYIYBX+DHvtpaSHRby7Oa3qN7lrA3Uik6xB31k=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=vSedezjk8dkX3AjP0k2o0MLgVPqEAXuKIZlygHjRA9VQodxvm2l3I+6416MWMzrrvZhjDbU3+pWFpDJI5/u7GoR6+em3XlmSza9L3se/F6/eAUg5dKEb7lxF0Dz9Leckl4tQ/7vYmZfP7V1ePwJweTxxDgcrQWcT+58iarcVvk8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810427; 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: in-reply-to:in-reply-to:references:references; bh=X9Zb1iWSZmiYm4hiz+CxKeb4FtK9fhl9UF0yLQpKelY=; b=D1RU1r5mxC6nFLHStuQLBC4xAOfN/E60lmcfsGFMCdxbQo8HuIznwX/TCk4QWn7znVMsmI ZLmERRoPH7SstUhToDxPVKPBrYPauIblk+tq4w+ckcul3MdGgUhuvzKOmNdzBtLAR1CPcp XI1t/lvEUmeSQXUR3aaJ/ql6oKColn8= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-34-k5McOR5EM1mAmAj_YDZUDw-1; Tue, 09 Jan 2024 09:27:05 -0500 X-MC-Unique: k5McOR5EM1mAmAj_YDZUDw-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-33769c5db4aso1488623f8f.0 for ; Tue, 09 Jan 2024 06:27:05 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810424; x=1705415224; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=X9Zb1iWSZmiYm4hiz+CxKeb4FtK9fhl9UF0yLQpKelY=; b=m3iWFFncUU3mmmzyg2h71TIp+m8/MqJ4AeEqWGKXIq9ChtlZEkaIQF8Di4OwsaqXTf s8MMJh4iKziQoaI/aRC4CS9ujZse67slfII32g2rEUAYQN2Z/hm8cpP4OoK5GGgRDxin YYGiPuXDlHZuXditRZdo+R8sOCecoHB4WgXtvTyf/KodE4jAPKm91VtWT2JEdSGHesyB DIo9DaaSsIHhbct8IWLphouJOMNuLhh3W8qu/Ru4uoIygld5JNpwjYtmCaESlokIuFuz VbkhbELgoSLVymDBnkVyR8SNlpsAY40eR+/Y1cKaiD+oBgDhmHBQ1sFq0kB85LfObQb7 IAzQ== X-Gm-Message-State: AOJu0Yxzojk0f31Bg1MuzLdaFDED3fZ8qs+irT9ames2Y9EQ0tabC32X x+rj/bCRG8A/0UF3UguUkKA4cUwbhUqtkTaRAefs24MV+Sr3TSLbQc8umlRWHHYKhD0mesEt5fu yx/my+mZT2r5cwgj2Gw6RNI9V/zZ4QkD9Yw69j0seIYCVJYhL+EMz8bxMWW7PBBOpp4yqWJtylo a7B5FNaJK8pYdaaw== X-Received: by 2002:a05:6000:543:b0:336:ec62:ea8b with SMTP id b3-20020a056000054300b00336ec62ea8bmr618568wrf.63.1704810424193; Tue, 09 Jan 2024 06:27:04 -0800 (PST) X-Google-Smtp-Source: AGHT+IEU2fBrAVDqgVucxoeEP4IWltlwmftafMkFYY/ay3uzRRHEUMUR/578pCNQO3eBa8C4sRJkgA== X-Received: by 2002:a05:6000:543:b0:336:ec62:ea8b with SMTP id b3-20020a056000054300b00336ec62ea8bmr618555wrf.63.1704810423805; Tue, 09 Jan 2024 06:27:03 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id s15-20020a5d424f000000b0033743c2d17dsm2556500wrr.28.2024.01.09.06.27.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:27:02 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 14/16] gdb/gdbserver: remove some uses of free_vector_argv Date: Tue, 9 Jan 2024 14:26:37 +0000 Message-Id: <530cf2c6789513d079c3a3817c00e9625ba18c49.1704809585.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org This commit removes some of the uses of free_vector_argv and instead makes use of gdb::unique_xmalloc_ptr to manage memory. Most of this patch is about changing various APIs to accept the gdb::unique_xmalloc_ptr vectors instead of raw 'char *'. However, there are a couple of places, one in gdbserver, and one in GDB, where retaining the old API will remove the need to malloc copies of all the incoming arguments -- this is for the initial set of arguments delivered from the OS. To support these I've added an overload of construct_inferior_arguments that retains the old API (taking a gdb::array_view), and we make use of this to avoid unnecessary malloc/free calls. There is a place in gdb/nat/fork-inferior.c where it seems unavoidable that free_vector_argv is needed. In this case we need to build an array of 'char *' to pass to an exec call, so using gdb::unique_xmalloc_ptr is out of the question I think. There should be no user visible changes after this commit. --- gdb/inferior.c | 9 ----- gdb/inferior.h | 8 +++-- gdb/python/py-inferior.c | 7 ++-- gdb/unittests/remote-arg-selftests.c | 33 +++---------------- gdbserver/server.cc | 22 ++++--------- gdbsupport/common-inferior.cc | 49 ++++++++++++++++++++++++---- gdbsupport/common-inferior.h | 7 ++++ gdbsupport/remote-args.cc | 2 +- gdbsupport/remote-args.h | 2 +- 9 files changed, 71 insertions(+), 68 deletions(-) diff --git a/gdb/inferior.c b/gdb/inferior.c index ed138888024..d2cdf31551a 100644 --- a/gdb/inferior.c +++ b/gdb/inferior.c @@ -165,15 +165,6 @@ inferior::tty () return m_terminal; } -/* See inferior.h. */ - -void -inferior::set_args (gdb::array_view args, - escape_args_func escape_func) -{ - set_args (construct_inferior_arguments (args, escape_func)); -} - void inferior::set_arch (gdbarch *arch) { diff --git a/gdb/inferior.h b/gdb/inferior.h index d3824236ef6..3dad403b244 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -526,8 +526,12 @@ class inferior : public refcounted_object, }; /* Set the argument string from some strings. */ - void set_args (gdb::array_view args, - escape_args_func escape_func); + template + void set_args (gdb::array_view args, + escape_args_func escape_func) + { + this->set_args (construct_inferior_arguments (args, escape_func)); + } /* Get the argument string to use when running this inferior. diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c index 5b7c7fb9365..c7a47e92ded 100644 --- a/gdb/python/py-inferior.c +++ b/gdb/python/py-inferior.c @@ -915,11 +915,8 @@ infpy_set_args (PyObject *self, PyObject *value, void *closure) return -1; args.push_back (std::move (str)); } - std::vector argvec; - for (const auto &arg : args) - argvec.push_back (arg.get ()); - gdb::array_view view (argvec.data (), argvec.size ()); - inf->inferior->set_args (view, escape_quotes_and_white_space); + gdb::array_view const> arg_view (args); + inf->inferior->set_args (arg_view, escape_quotes_and_white_space); } else { diff --git a/gdb/unittests/remote-arg-selftests.c b/gdb/unittests/remote-arg-selftests.c index 3240ab9aeea..b732d94792d 100644 --- a/gdb/unittests/remote-arg-selftests.c +++ b/gdb/unittests/remote-arg-selftests.c @@ -59,32 +59,6 @@ arg_test_desc desc[] = { { "1 '\n' 3", { "1", "\n", "3" }, "1 '\n' 3" }, }; -/* Convert a std::vector into std::vector. This - requires copying all of the string content. This class takes care of - freeing the memory once we are done with it. */ - -struct args_as_c_strings -{ - args_as_c_strings (std::vector args) - { - for (const auto & a : args) - m_data.push_back (xstrdup (a.c_str ())); - } - - ~args_as_c_strings () - { - free_vector_argv (m_data); - } - - std::vector &get () - { - return m_data; - } - -private: - std::vector m_data; -}; - /* Run the remote argument passing self tests. */ static void @@ -132,9 +106,10 @@ self_test () } /* Now join the arguments. */ - args_as_c_strings split_args_c_str (split_args); - std::string joined_args - = gdb::remote_args::join (split_args_c_str.get ()); + std::vector> temp_args; + for (const auto & a : split_args) + temp_args.push_back (make_unique_xstrdup (a.c_str ())); + std::string joined_args = gdb::remote_args::join (temp_args); if (run_verbose ()) debug_printf ("Joined (%s), expected (%s)\n", diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 0445fa0237f..9c5d8ee4f54 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -3385,7 +3385,7 @@ handle_v_run (char *own_buf) { client_state &cs = get_client_state (); char *p, *next_p; - std::vector new_argv; + std::vector> new_argv; gdb::unique_xmalloc_ptr new_program_name; int i; @@ -3405,7 +3405,7 @@ handle_v_run (char *own_buf) else if (p == next_p) { /* Empty argument. */ - new_argv.push_back (xstrdup ("")); + new_argv.push_back (make_unique_xstrdup ("")); } else { @@ -3416,14 +3416,13 @@ handle_v_run (char *own_buf) if (arg == nullptr) { write_enn (own_buf); - free_vector_argv (new_argv); return; } if (i == 0) new_program_name = std::move (arg); else - new_argv.push_back (arg.release ()); + new_argv.push_back (std::move (arg)); } if (*next_p == '\0') break; @@ -3436,7 +3435,6 @@ handle_v_run (char *own_buf) if (program_path.get () == nullptr) { write_enn (own_buf); - free_vector_argv (new_argv); return; } } @@ -3451,15 +3449,13 @@ handle_v_run (char *own_buf) return; } else if (new_argv.size () == 1) - program_args = std::string (new_argv[0]); + program_args = std::string (new_argv[0].get ()); else program_args.clear (); } else program_args = gdb::remote_args::join (new_argv); - free_vector_argv (new_argv); - target_create_inferior (program_path.get (), program_args); if (cs.last_status.kind () == TARGET_WAITKIND_STOPPED) @@ -4345,16 +4341,12 @@ captured_main (int argc, char *argv[]) if (pid == 0 && *next_arg != NULL) { - int i, n; - - n = argc - (next_arg - argv); program_path.set (next_arg[0]); - std::vector temp_arg_vector; - for (i = 1; i < n; i++) - temp_arg_vector.push_back (next_arg[i]); escape_args_func escape_func = (escape_args ? escape_shell_characters : escape_quotes_and_white_space); - program_args = construct_inferior_arguments (temp_arg_vector, + int arg_count = argc - (next_arg - argv) - 1; + gdb::array_view arg_view (&next_arg[1], arg_count); + program_args = construct_inferior_arguments (arg_view, escape_func); /* Wait till we are at first instruction in program. */ diff --git a/gdbsupport/common-inferior.cc b/gdbsupport/common-inferior.cc index cf2cd9a090a..458d78295ab 100644 --- a/gdbsupport/common-inferior.cc +++ b/gdbsupport/common-inferior.cc @@ -20,30 +20,67 @@ #include "gdbsupport/common-defs.h" #include "gdbsupport/common-inferior.h" +#include "gdbsupport/function-view.h" /* See common-inferior.h. */ bool startup_with_shell = true; -/* See common-inferior.h. */ +/* Helper function for the two construct_inferior_arguments overloads + below. Accept a gdb::array_view over objects of type T. Convert each T + to a std::string by calling CB, and join all the resulting strings + together with a single space between each. */ -std::string -construct_inferior_arguments (gdb::array_view argv, - escape_args_func escape_func) +template +static std::string +construct_inferior_arguments_1 (gdb::array_view argv, + gdb::function_view cb) { std::string result; - for (const char *a : argv) + for (const T &a : argv) { if (!result.empty ()) result += " "; - result += escape_func (a); + result += cb (a); } return result; } +/* See common-inferior.h. */ + +std::string +construct_inferior_arguments + (gdb::array_view const> argv, + escape_args_func escape_func) +{ + /* Convert ARG to a std::string by applying ESCAPE_FUNC. */ + auto escape_cb = [&] (const gdb::unique_xmalloc_ptr &arg) + { + return escape_func (arg.get ()); + }; + + return construct_inferior_arguments_1> + (argv, escape_cb); +} + +/* See common-inferior.h. */ + +std::string +construct_inferior_arguments (gdb::array_view argv, + escape_args_func escape_func) +{ + /* Convert ARG to a std::string by applying ESCAPE_FUNC. */ + auto escape_cb = [&] (const char * const &arg) + { + return escape_func (arg); + }; + + return construct_inferior_arguments_1 (argv, escape_cb); +} + /* Escape characters in ARG and return an updated string. The string SPECIAL contains the set of characters that must be escaped. SPECIAL must not be nullptr, and it is assumed that SPECIAL contains the newline diff --git a/gdbsupport/common-inferior.h b/gdbsupport/common-inferior.h index 7cc01fb2f28..c607ab6d98e 100644 --- a/gdbsupport/common-inferior.h +++ b/gdbsupport/common-inferior.h @@ -74,6 +74,13 @@ extern std::string escape_quotes_and_white_space (const char *arg); into a single string, separating each element with a single space character. */ +extern std::string +construct_inferior_arguments + (gdb::array_view const> args, + escape_args_func escape_func); + +/* An overload of the above that takes an array of raw pointers. */ + extern std::string construct_inferior_arguments (gdb::array_view args, escape_args_func escape_func); diff --git a/gdbsupport/remote-args.cc b/gdbsupport/remote-args.cc index 96c12ffac67..b808a64efce 100644 --- a/gdbsupport/remote-args.cc +++ b/gdbsupport/remote-args.cc @@ -37,7 +37,7 @@ gdb::remote_args::split (std::string args) /* See remote-args.h. */ std::string -gdb::remote_args::join (std::vector &args) +gdb::remote_args::join (std::vector> &args) { return construct_inferior_arguments (args, escape_shell_characters); } diff --git a/gdbsupport/remote-args.h b/gdbsupport/remote-args.h index c0acce9b7c4..1397a4d16d9 100644 --- a/gdbsupport/remote-args.h +++ b/gdbsupport/remote-args.h @@ -37,7 +37,7 @@ extern std::vector split (std::string args); passed through ::join we will get back the string 'a\ b' (without the single quotes), that is, we choose to escape the white space, rather than wrap the argument in quotes. */ -extern std::string join (std::vector &args); +extern std::string join (std::vector > &args); } /* namespace remote_args */ From patchwork Tue Jan 9 14:26:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83649 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 525CE3857C70 for ; Tue, 9 Jan 2024 14:31:32 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id E4CCD3857C5A for ; Tue, 9 Jan 2024 14:27:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E4CCD3857C5A 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 E4CCD3857C5A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810431; cv=none; b=v+R40z85rBj4TIDBTu4VDmRbfdfefeiaWKgwFFPPWk+b2m3vSu8rieY7ICYkEv90oYWa2rnXo43vg/El1L+vu6N6eg+5+94pKvoNKE0/ipV91ItRPzG1Is1WCbTUDeqD0bM83BWJmFQRWmsMO20iHUGfBJBsV5boVhtl9E3mOxY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810431; c=relaxed/simple; bh=JGn7oOCpRgHExvPgnyrz2JIBlQBNWK10REZxnZCHcfc=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=ngjqqLtyqWCsdTE8v7J7gp5jNUPFB76nXSjvtApIY/zM30B6mkKTWNerINjjIzw+ik/TnOVvSlI45vU3i45LVZuYfRDY/4GqHEDWHz7kfSzOYWhEcKbe+drnz2qSgQUBPQHdqZL65RN0fpINUkxPoPmr0bi73D9rIrnDuQVq3a4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810428; 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: in-reply-to:in-reply-to:references:references; bh=yomtnGXzR5y/RK5a0HonjNymvkiDuTb4bB2ViShqU70=; b=XmXPUa5Xc/cCgjvE6XyYLYj9lUG/zhPgN4FGhUgH95OCRf4ObrbyUcNNfzOlaj4MDWyLLj DF0K29/0ENvUt+pw7KYOVKswOWr9IPNLihFY5jhSiZXPnPDA/vuZN/dveS9lDS3T/ocbaj CWP46V7QpNeg7GIT3Q1UX8c8OdSbOys= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-624-5dsr5v8OPM2RyFYf28A0WQ-1; Tue, 09 Jan 2024 09:27:07 -0500 X-MC-Unique: 5dsr5v8OPM2RyFYf28A0WQ-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-336937b3cf5so1952346f8f.3 for ; Tue, 09 Jan 2024 06:27:07 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810426; x=1705415226; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=yomtnGXzR5y/RK5a0HonjNymvkiDuTb4bB2ViShqU70=; b=KWktTgP1TL+xjvaIplND85mRHIf+gSa98EXXTo8NQy/QfVmHmp7RG4tveGbnjiNWvS 3uLAgXxw6EquPr0PXteC5ERIscSfW9QckjdqY3mVNOQ8+Kb2kyKi+fYt0Xcg12TJa0e7 hIg7yBFuQHBcWVPyYbAUVc/PeE6T1RvDWzAFVD1wFYn3IB4btgnqe36JqEI7uqQv0s2A GzCKyGkF1jTuFYg1/Lose2M3GRQt0XgH5DbYTvaX5HbL+c2nc+nmth82uf4uA3incufO McGZVEtsUMxCSMHCclvFbobRzqmL+q6hKnJEkHeGhm47JaTPr9U6raizZBgqAVaqK10W OL5A== X-Gm-Message-State: AOJu0YyvR/Fgdj917KHwY5v8Y7q/2K/9QqkuD+sNf2WbPvHVsFR2gBDV YCd7xYGbir/4f7bjw7KlXYWC5oWhsBdKme45dlRACbaKcFdBdq/p7wfWvclcw/i3XpebS+3Qe9i 5apipX0WHnRrEZc1NDDVZw+6luCF3dNO9y4K4xRrmS7v3NepL/zyHTozL/G7Y2lIvL0ua9vEgs5 S50GFUFRRGWfa9uA== X-Received: by 2002:a5d:6448:0:b0:336:6422:708d with SMTP id d8-20020a5d6448000000b003366422708dmr401988wrw.116.1704810425808; Tue, 09 Jan 2024 06:27:05 -0800 (PST) X-Google-Smtp-Source: AGHT+IG3I9wWgf0pOpfzjifFmxKmoXWcfync/SEqOkTux6eFxfpqt4mqXbeM0mGZhxA8tqzo0pcdCQ== X-Received: by 2002:a5d:6448:0:b0:336:6422:708d with SMTP id d8-20020a5d6448000000b003366422708dmr401982wrw.116.1704810425452; Tue, 09 Jan 2024 06:27:05 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id e30-20020adfa45e000000b003373ece28efsm2582389wra.29.2024.01.09.06.27.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:27:04 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 15/16] gdb: new maintenance command to help debug remote argument issues Date: Tue, 9 Jan 2024 14:26:38 +0000 Message-Id: <4d76e490c2285dd7858b853f03465a1d798d6240.1704809585.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org Add a new maintenance command 'maint test-remote-args', this command takes an argument string and splits it using gdb::remote_args::split and then joins the result using gdb::remote_args::join and prints all of the results. This is useful for diagnosing problems with remote argument passing. This new command is identical to what the remote argument self-tests do, I found it easier to have a maintenance command available for testing rather than having to add a new selftest and recompile GDB. Reviewed-By: Eli Zaretskii --- gdb/NEWS | 4 ++ gdb/doc/gdb.texinfo | 21 +++++++ gdb/remote.c | 59 +++++++++++++++++++ .../gdb.base/maint-test-remote-args.exp | 40 +++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 gdb/testsuite/gdb.base/maint-test-remote-args.exp diff --git a/gdb/NEWS b/gdb/NEWS index 2ba1899f78c..8f0bf34fccc 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -51,6 +51,10 @@ set remote thread-options-packet show remote thread-options-packet Set/show the use of the thread options packet. +maintenance test-remote-args ARGS + Test splitting and joining of inferior arguments ARGS as they would + be split and joined when being passed to a remote target. + * Changed commands set args diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 2015293ee0d..6ff059d14ed 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -42204,6 +42204,26 @@ @value{GDBN} supports. They are used by the testsuite for exercising the settings infrastructure. +@kindex maint test-remote-args +@item maint test-remote-args @var{args} +For targets that don't support passing inferior arguments as a single +string (@pxref{single-inf-arg}), @value{GDBN} will attempt to split +the inferior arguments before passing them to the remote target, and +the remote target might choose to join the inferior arguments upon +receipt. Historically gdbserver did join inferior arguments, but now +it will request inferior arguments be passed as a single string if +@value{GDBN} supports this feature. + +This maintenance command splits @var{args} as @value{GDBN} would +normally split such an argument string before passing the arguments to +a remote target, the split arguments are then printed. + +The split arguments are then joined together as gdbserver would join +them, and the result is printed. + +This command is intended to help diagnose issues passing inferior +arguments to remote targets. + @kindex maint set backtrace-on-fatal-signal @kindex maint show backtrace-on-fatal-signal @item maint set backtrace-on-fatal-signal [on|off] @@ -44548,6 +44568,7 @@ This feature indicates whether @value{GDBN} wants to know the supported actions in the reply to @samp{vCont?} packet. +@anchor{single-inf-arg} @item single-inf-arg This feature indicates that @value{GDBN} would like to send the inferior arguments as a single string within the @samp{vRun} packet. diff --git a/gdb/remote.c b/gdb/remote.c index 75d275d38df..9b4d31dc633 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -12029,6 +12029,51 @@ cli_packet_command (const char *args, int from_tty) send_remote_packet (view, &cb); } +/* Implement 'maint test-remote-args' command. + + Treat ARGS as an argument string. Split the remote arguments using + gdb::remote_args::split, and then join using gdb::remote_args::join. + The split and joined arguments are printed out. Additionally, the + joined arguments are split and joined a second time, and compared to the + result of the first join, this provides some basic validation that GDB + sess the joined arguments as equivalent to the original argument + string. */ + +static void +test_remote_args_command (const char *args, int from_tty) +{ + std::vector split_args = gdb::remote_args::split (args); + + gdb_printf ("Input (%s)\n", args); + for (const auto & a : split_args) + gdb_printf (" (%s)\n", a.c_str ()); + + std::vector> tmp_split_args; + for (const auto & a : split_args) + tmp_split_args.emplace_back (xstrdup (a.c_str ())); + + std::string joined_args = gdb::remote_args::join (tmp_split_args); + + gdb_printf ("Output (%s)\n", joined_args.c_str ()); + + std::vector resplit = gdb::remote_args::split (joined_args); + + tmp_split_args.clear (); + for (const auto & a : resplit) + tmp_split_args.emplace_back (xstrdup (a.c_str ())); + + std::string rejoined = gdb::remote_args::join (tmp_split_args); + + if (joined_args != rejoined || split_args != resplit) + { + gdb_printf ("FAILURE ON REJOINING\n"); + gdb_printf ("Resplit args:\n"); + for (const auto & a : resplit) + gdb_printf (" (%s)\n", a.c_str ()); + gdb_printf ("Rejoined (%s)\n", rejoined.c_str ()); + } +} + #if 0 /* --------- UNIT_TEST for THREAD oriented PACKETS ------------------- */ @@ -16145,6 +16190,20 @@ from the target."), /* Eventually initialize fileio. See fileio.c */ initialize_remote_fileio (&remote_set_cmdlist, &remote_show_cmdlist); + add_cmd ("test-remote-args", class_maintenance, + test_remote_args_command, _("\ +Test remote argument splitting and joining.\n \ + maintenance test-remote-args ARGS\n\ +For remote targets that don't support passing inferior arguments as a\n\ +single string, GDB needs to split the inferior arguments before passing\n\ +them, and gdbserver needs to join the arguments it receives.\n\ +This command splits ARGS just as GDB would before passing them to a\n\ +remote target, and prints the result. This command then joins the\n\ +arguments just as gdbserver would, and prints the results.\n\ +This command is useful in diagnosing problems when passing arguments\n\ +between GDB and a remote target."), + &maintenancelist); + #if GDB_SELF_TEST selftests::register_test ("remote_memory_tagging", selftests::test_memory_tagging_functions); diff --git a/gdb/testsuite/gdb.base/maint-test-remote-args.exp b/gdb/testsuite/gdb.base/maint-test-remote-args.exp new file mode 100644 index 00000000000..3daf0725932 --- /dev/null +++ b/gdb/testsuite/gdb.base/maint-test-remote-args.exp @@ -0,0 +1,40 @@ +# 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 . + +# Test the 'maint test-remote-args' command. +# +# We do minimal testing in here. If you are thinking of adding a new +# test here then you are most likely adding the test in the wrong +# place. Remote argument testing is checked in the following test +# scripts: gdb.base/args.exp, gdb.base/inferior-args.exp, +# gdb.base/startup-with-shell.exp, and gdb.python/py-inferior.exp. +# The test gdb.gdb/unittest.exp also runs 'maint selftest +# remote-args', which are the remote argument self tests. +# +# If you have a new test for an argument that was being passed +# incorrectly, then add the test to one of those scripts. +# +# This file is ONLY for validating that the 'maint test-remote-args' +# command itself is working. + +gdb_start + +gdb_test "maint test-remote-args a b c" \ + [multi_line \ + "Input \\(a b c\\)" \ + " \\(a\\)" \ + " \\(b\\)" \ + " \\(c\\)" \ + "Output \\(a b c\\)"] From patchwork Tue Jan 9 14:26:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 83653 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1CAF43858017 for ; Tue, 9 Jan 2024 14:32:16 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id A39E338582A6 for ; Tue, 9 Jan 2024 14:27:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A39E338582A6 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 A39E338582A6 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810437; cv=none; b=LNhGuNsIS9LzxG/2NUELDUxqv0Y+zNTJCIDekUNCb2L7aQjA+AQ8uc6ZuEYEv+5szZFFFvzYlhYTjIwDyR/dvYVT6M0kPNQmqDGG86YeFpihHSWjSS2MxhbkKqYyM8sbujezQi2nbh2R8MXSQb9iS/kr5Tci2GcHVqCaAigjiIU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810437; c=relaxed/simple; bh=zKsBISShAypINWlhHHMQ9H60uO3wgqTHNVdIqZ0dpZ4=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=S1DJ7BnadElO2V4LHsC5UM6xSI9ToPTQCe/zTWztV6/tGO/DDE3UH2vUu8uM6rdNFkIdzSF5D4Z+WZzTkHOIefFCoCOwc2WZiZ99UyBUcKJYzYKn81gCK9tujV3wduCMyh/xTt4O2oZSaGyAs6mamVs19imED4PcPj52TVUKRDI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810431; 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: in-reply-to:in-reply-to:references:references; bh=zspPI7YTZ0pTjx72nJ7v7ED2fPgMu9oxtxDs2mm42n8=; b=O78R1cK3nAxCRxANOgfl05p2SfF6xC4SlbLV31SRI5NE1nnXRXbpNpmtxXFP1Ij2lSlvoN suDR5KSa1o/wkA7QfKcbBiZyDdaTNcKQ9BEd4ksuZbyfT1rZu9UN7AAl5D20vw5IMNC/GU Fe57MFclYKFjlEuLeQTtkdWV2k+XAbs= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-338-TqAR-h4ENfejCsOa79gq0w-1; Tue, 09 Jan 2024 09:27:10 -0500 X-MC-Unique: TqAR-h4ENfejCsOa79gq0w-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-40d3c533083so24721655e9.1 for ; Tue, 09 Jan 2024 06:27:09 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810427; x=1705415227; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zspPI7YTZ0pTjx72nJ7v7ED2fPgMu9oxtxDs2mm42n8=; b=DiI0t08Q26tzZYfXUHS3liHf5AYYRvb/SdkBSe3oMYSZ8F71/czMTORpmVrWFFT7G8 nk5PKYg4pj1Cw5DXtihr1YrvwVoupscw906cPFRzriadIyRdwjfQo6FUHWZ+emAYlxLl fSXu3cY3p1tf3YrhdrPNIiPZW8b0ivEs+BMDtdIH7jKY112vsvU917PaBv+lOq+8l8fz 29wXwYf2rfX+gO4FPyZYK8+MqiWpwFk8Hq77vWGAH73zXac6FVjNj/NqXlHcxWRvdq6F tHMD1lM7hvoyTUOffZRUVcVCjA4nuHFbRtveiEWHfhv+xTApBsfX4Xslif1/7F+beuh4 8L9g== X-Gm-Message-State: AOJu0Yx+WW9pX/Pb87dSRD50elRU0IYsP300XxhOJw8yyDrKm7qdiZFC 1aXWiT1++zaAepU5a3b72AZ/KbbbzcH2NKPUp4U3HsY/HI2+ZmGx2rILxEAQvEDklOgvw9cg6xC 6gO4GrYmhZymMjFaXXp3gGMlgY8DUN+fa9eOgdQ8S6u8FLpIq70u73JIq2zdih3C2gpO7FiM6Mw Bm1ylAWL3+u+tZbA== X-Received: by 2002:a05:600c:510d:b0:40d:8557:8d85 with SMTP id o13-20020a05600c510d00b0040d85578d85mr2883101wms.80.1704810427061; Tue, 09 Jan 2024 06:27:07 -0800 (PST) X-Google-Smtp-Source: AGHT+IE3KSzjRXpe6+0mwA1lfkmUB6Yq8fXKweZYMP8lnmwv2Kt/n6YY6ZOgP27+9MpvCFoTM7ckuQ== X-Received: by 2002:a05:600c:510d:b0:40d:8557:8d85 with SMTP id o13-20020a05600c510d00b0040d85578d85mr2883086wms.80.1704810426363; Tue, 09 Jan 2024 06:27:06 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id fa7-20020a05600c518700b0040d87100733sm3701785wmb.39.2024.01.09.06.27.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:27:06 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 16/16] gdb/gdbserver: rework argument splitting and joining Date: Tue, 9 Jan 2024 14:26:39 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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+patchwork=sourceware.org@sourceware.org This commit attempts to improve how GDB splits and joins inferior arguments when passing them to a remote target. The splitting and joining is now the legacy approach for passing arguments to a remote target; the preference is to pass arguments as a single string, however, if GDB is connecting to a target that doesn't support passing inferior arguments as a single string then the joining and splitting approach is still used, and this commit tries to improve that approach a little. Currently when GDB passes arguments to a remote target this is done by using the gdb_argv class. This is the same technique uses to split the argument vector in order to start an inferior without a shell. This approach has a problem. Consider a native target, and the user sets the inferior arguments like this: (gdb) set args \$VAR In this case the user has escaped the '$' character, removing it's special meaning. When started under a shell the '\$VAR' will not result in variable expansion, and the inferior will see a literal '$VAR'. When startup-with-shell is off then gdb_argv is used to split the inferior arguments, and gdb_argv also removes the backslash escape, so the inferior again sees a literal '$VAR' string. I think this consistency makes sense. Now consider if the user sets the inferior arguments like this: (gdb) set args $VAR In the startup-with-shell case the shell will expand '$VAR' and the inferior will see whatever the value of '$VAR' was. But if startup-with-shell is off then there is no shell, and the inferior will just be passed a literal '$VAR' string. Given the above, when we consider using gdb_argv for splitting inferior arguments in order to pass them to a remote target, the problem we have is this: there is a many to one between input strings and output strings. Both '\$VAR' and '$VAR' both result in an output string of '$VAR'. The remote target can't know what the actual input was. If the remote adds the escaping then (when startup-with-shell is on) the shell will not substitute variables when it should, and if the remote doesn't add escaping then the shell will try to substitute variables when it shouldn't. So I think we have to admit that the current approach is not ideal, but we cannot change things too much; in simple cases (i.e. without too many special shell characters) the existing approach does work, so if we do something too different then existing targets are going to break even for simple cases. The current approach then is for the remote target to escape any special shell characters within the arguments received from GDB. This is too aggressive. An ideal solution might be to have GDB remove quoting, but to keep, or apply all required escaping, and for the remote target to do nothing but join the incoming arguments with a single whitespace character. This would certainly be easiest all around, but would break a lot of backward compatibility, for example given this: (gdb) set args 'ab cd' GDB could strip the single quotes, and escape the whitespace, sending the argument to the remote target as (ab\ cd) -- without the parenthesis -- then on the remote end the target could use this argument directly. However, if we adopt this approach existing targets, those that escape special shell characters, are likely to do the wrong thing, it's likely that both the backslash and space would be escaped. So, what we are searching for is a solution that sits somewhere half way between GDB performs all the escaping, and the current situation, where the target is expected to apply the escaping. I propose that the rules be something like: + GDB removes the quotes, and escapes all special shell characters except for the following: + Whitespace is not escaped, the remote is expected to escape any whitespace within an argument, + Quote characters are not escaped, the remote is expected to escape any single or double quote characters within an argument. + Any other special shell characters will have been escaped, if needed, by GDB, and the escaping included in the transmitted argument. The solution presented here takes into account unquoted arguments, as well as single and double quoted arguments. **WARNING**: This is a potentially breaking change in the remote protocol. A user, using an existing remote target, that handles inferior arguments in a particular way may find that with this change now causes some cases to behave differently. My hope is that by handling whitespace and quotes differently we can preserve enough behaviour that the problem cases are not too common. As part of this commit I've updated unittests/remote-arg-selftests.c, and it's possible to see examples of when the arguments passed from GDB to the remote will change, and when they remain the same. And there are some tests in gdb.base/startup-with-shell.exp that now pass even when using a remote target board and when the single-inferior-arg feature is turned off. All of the other argument passing tests, e.g. gdb.base/args.exp, and gdb.base/inferior-args.exp continue to pass, and also already run with the single-inferior-arg feature both enabled and disabled, so the changes in this commit should be well tested already. What isn't addressed at all in this commit is parameter expansion, command substitution, or arithmetic expansion. In some way these are similar to quotes, but instead of single or double quotes the argument is surrounded with ${...}, $(...), or $((....)). However, unlike single or double quotes I don't think that we can remove the quoting in these cases, if we did, how would the remote target know what to add back? However, I'm not proposing to even try to address these cases just yet. Ideally, if this whole series is accepted then by this point we will have already merged the patch that passes inferior arguments as a single string, a solution that handles all of the above cases. We can always revisit this later on and look at supporting these cases if needed. This work was inspired by this series: https://inbox.sourceware.org/gdb-patches/20211022071933.3478427-1-m.weghorn@posteo.de/ I originally started reviewing this series, and figured that, surely it must be possible to solve the problem that series tries to solve by just being "smarter" about how we split arguments. It was only when I actually tried to implement this, and started writing tests, that I realised that above series was right, and what we really need to do is pass all arguments as a single string. But I think that series is worth a read, and should get credit for reaching the right answer quicker than me! Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 Reviewed-By: Eli Zaretskii --- gdb/NEWS | 8 + gdb/doc/gdb.texinfo | 13 + gdb/testsuite/gdb.base/startup-with-shell.exp | 27 +-- gdb/unittests/remote-arg-selftests.c | 18 +- gdbsupport/remote-args.cc | 229 +++++++++++++++++- 5 files changed, 264 insertions(+), 31 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 8f0bf34fccc..206f72070bb 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -17,6 +17,14 @@ this flag is used gdbserver will not escape special shell characters within the inferior arguments. +* There have been changes to how inferior arguments are passed to + remote targets. When using the latest GDB and gdbserver the new + single-inf-arg will be used for argument passing. When using remote + targets that don't support single-inf-arg, GDB will now escape + special shell characters (except for whitespace and quotes) within + arguments before passing them to the remote target. Remote targets + will need updating to take this into account. + * Changed commands disassemble diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 6ff059d14ed..cc9c5ef0ccc 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -43137,6 +43137,19 @@ @var{argument} string, which includes all inferior arguments, separated with whitespace. +If @value{GDBN} didn't offer @samp{single-inf-arg+}, or the remote +didn't choose to use this feature, then @value{GDBN} will split the +inferior arguments before passing them to the remote target. Single +and double quotes surrounding any argument will have been removed by +@value{GDBN}. Any special shell characters within each argument will +have been escaped with a backslash character except for quote and +whitespace characters, these will not be escaped within the arguments, +the remote target is expected to escape these characters itself. + +These slightly complex rules exist for backward compatibility reasons. +When implementing a new remote target it is suggested that the +@samp{single-inf-arg} feature be supported. + @c FIXME: What about non-stop mode? This packet is only available in extended mode (@pxref{extended mode}). diff --git a/gdb/testsuite/gdb.base/startup-with-shell.exp b/gdb/testsuite/gdb.base/startup-with-shell.exp index f467774dc5f..3952bd774d4 100644 --- a/gdb/testsuite/gdb.base/startup-with-shell.exp +++ b/gdb/testsuite/gdb.base/startup-with-shell.exp @@ -55,11 +55,8 @@ proc initial_setup_simple { startup_with_shell run_args } { # it to ON_RE if startup-with-shell is on, otherwise compare to # OFF_RE. # -# If PROBLEMATIC_ON is true then when startup-with-shell is on we -# expect the comparison to fail, so setup an xfail. -# # TESTNAME is a string used in the test names. -proc run_test { args on_re off_re testname { problematic_on false } } { +proc run_test { args on_re off_re testname } { foreach startup_with_shell { "on" "off" } { with_test_prefix "$testname, startup_with_shell: ${startup_with_shell}" { if {![initial_setup_simple $startup_with_shell $args]} { @@ -68,14 +65,8 @@ proc run_test { args on_re off_re testname { problematic_on false } } { if { $startup_with_shell } { set re $on_re - set problematic $problematic_on } else { set re $off_re - set problematic false - } - - if { $problematic } { - setup_xfail "*-*-*" gdb/28392 } gdb_test "print argv\[1\]" "\\\$$::decimal = $::hex $re" $testname @@ -91,15 +82,14 @@ proc run_test_same { args re testname } { } # Run the actual tests -proc run_all_tests { { is_remote_with_split_args false } } { +proc run_all_tests { } { # The regexp to match a single '\' character. set bs "\\\\" run_test "$::unique_file_dir/*.unique-extension" \ "\"$::unique_file\"" \ "\"$::unique_file_dir/\\\*\.unique-extension\"" \ - "arg is glob" \ - $is_remote_with_split_args + "arg is glob" run_test_same "$::unique_file_dir/\\*.unique-extension" \ "\"$::unique_file_dir/\\\*\.unique-extension\"" \ @@ -110,8 +100,7 @@ proc run_all_tests { { is_remote_with_split_args false } } { run_test "\$TEST" \ "\"1234\"" \ "\"\\\$TEST\"" \ - "arg is shell variable" \ - $is_remote_with_split_args + "arg is shell variable" run_test_same "\\\$TEST" \ "\"\\\$TEST\"" \ @@ -121,14 +110,12 @@ proc run_all_tests { { is_remote_with_split_args false } } { run_test "\$(echo foo)" \ "\"foo\"" \ "\"\\\$\\(echo\"" \ - "arg is parameter expansion, command execution" \ - $is_remote_with_split_args + "arg is parameter expansion, command execution" run_test "\$((2 + 3))" \ "\"5\"" \ "\"\\\$\\(\\(2\"" \ - "arg is parameter expansion, expression evaluation" \ - $is_remote_with_split_args + "arg is parameter expansion, expression evaluation" run_test_same "\"\\a\"" \ "\"${bs}${bs}a\"" \ @@ -180,7 +167,7 @@ if {[target_info gdb_protocol] == "extended-remote"} { with_test_prefix "single-inferior-arg disabled" { save_vars { GDBFLAGS } { append GDBFLAGS " -ex \"set remote single-inferior-argument-feature-packet off\"" - run_all_tests true + run_all_tests } } } diff --git a/gdb/unittests/remote-arg-selftests.c b/gdb/unittests/remote-arg-selftests.c index b732d94792d..7abc9d46345 100644 --- a/gdb/unittests/remote-arg-selftests.c +++ b/gdb/unittests/remote-arg-selftests.c @@ -45,18 +45,26 @@ arg_test_desc desc[] = { { "a b c", { "a", "b", "c" }, "a b c" }, { "\"a b\" 'c d'", { "a b", "c d" }, "a\\ b c\\ d" }, { "\\' \\\"", { "'", "\"" }, "\\' \\\"" }, - { "'\\'", { "\\" }, "\\\\" }, - { "\"\\\\\" \"\\\\\\\"\"", { "\\", "\\\"" }, "\\\\ \\\\\\\"" }, + { "'\\'", { "\\\\" }, "\\\\" }, + { "\"\\\\\" \"\\\\\\\"\"", { "\\\\", "\\\\\"" }, "\\\\ \\\\\\\"" }, { "\\ \" \" ' '", { " ", " ", " "}, "\\ \\ \\ " }, { "\"'\"", { "'" }, "\\'" }, - { "'\"' '\\\"'", { "\"", "\\\"" } , "\\\" \\\\\\\""}, + { "'\"' '\\\"'", { "\"", "\\\\\"" } , "\\\" \\\\\\\""}, { "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \"\\\\\\\"\" \" \" \"\"", - { "first arg", "", "third-arg", "'", "\"", "\\\""," ", "" }, + { "first arg", "", "third-arg", "'", "\"", "\\\\\""," ", "" }, "first\\ arg '' third-arg \\' \\\" \\\\\\\" \\ ''"}, { "\"\\a\" \"\\&\" \"\\#\" \"\\<\" \"\\^\"", - { "\\a", "\\&", "\\#" , "\\<" , "\\^"}, + { "\\\\a", "\\\\\\&", "\\\\\\#" , "\\\\\\<" , "\\\\\\^"}, + "\\\\a \\\\\\& \\\\\\# \\\\\\< \\\\\\^" }, + { "\"\\\\a\" \"\\\\&\" \"\\\\#\" \"\\\\<\" \"\\\\^\"", + { "\\\\a", "\\\\\\&", "\\\\\\#" , "\\\\\\<" , "\\\\\\^"}, "\\\\a \\\\\\& \\\\\\# \\\\\\< \\\\\\^" }, { "1 '\n' 3", { "1", "\n", "3" }, "1 '\n' 3" }, + { "abc* abc\\* abc\\\\*", { "abc*", "abc\\*", "abc\\\\*" }, + "abc* abc\\* abc\\\\*" }, + { "\"ab\\ cd\" \"ef\\'gh\"", { "ab\\\\ cd", "ef\\\\'gh" }, + "ab\\\\\\ cd ef\\\\\\'gh" }, + { "\"$VAR\" \"ab`foo`cd\"", { "$VAR", "ab`foo`cd" }, "$VAR ab`foo`cd" }, }; /* Run the remote argument passing self tests. */ diff --git a/gdbsupport/remote-args.cc b/gdbsupport/remote-args.cc index b808a64efce..77e4c1a4bc1 100644 --- a/gdbsupport/remote-args.cc +++ b/gdbsupport/remote-args.cc @@ -20,18 +20,235 @@ #include "gdbsupport/common-inferior.h" #include "gdbsupport/buildargv.h" +/* This file contains the function used for splitting an argument string + into separate arguments in preparation for sending the argument over the + remote protocol, as well as the function to merge the separate arguments + back together into a single argument string. + + The logic within these functions is slightly more complex that it should + be. This is in order to maintain some level of backward compatibility. + + In the following test example of command line arguments will be given. + To avoid confusion arguments, and argument strings will be delimited + with '(' and ')', the parenthesis are not part of the argument itself. + This is clearer than using quotes, as some of the examples will include + quotes within the arguments. + + Historically, the algorithm used to split the argument string into + separate arguments removed a level of quoting from the arguments. For + example consider the following argument string: (abc* abc\*). The + historic algorithm would split this into (abc*) and (abc*). Notice that + the two arguments are identical. On the remote end we are now destined + for failure, either we apply an escape to both '*' characters, or we + apply an escape to neither. In either case, we get one of the arguments + wrong. The historic approach was just broken. + + However, the historic approach has been in place for many years. + Clearly not all arguments were corrupted in the manor described above, + so lots of things did work. For example, the string: ("ab cd" "ef") + will be split into (ab cd) and (ef). And the string ('"') will become + just ("). + + What we can observer from all of these examples, is that the historic + approach at the remote end was to simple apply an escape to every + special shell character, quotes, white space, as well as every other + special character (e.g. (*)). The problem with this approach is that + sometimes special shell characters shouldn't be escaped. + + If we could start from scratch, then the simple approach would be to + retain all escaping while splitting the argument string, and, where + quotes are used, convert this into backslash escaping as neeeded. Thus + the argument string ("ab cd" "ef") would become (ab\ cd) and (ef). And + the argument string (abc* abc\*) would become (abc*) (abc\*). On the + remote end, joining these arguments is as simple as concatenation with a + single space character between. + + However, if we took this approach, then consider ("ab cd"). Previously + this was sent as (ab cd), but now it would be (ab\ cd). This breaks + backward compatibility. + + And so, this is where the complexity comes in. + + The strategy here is to split the arguments, removing all double and + single quotes. While removing quotes, special shell characters are + escaped as needed. But, white space characters, and quote characters + are not escaped. These characters must always be escaped, and so we can + safely drop the escape in these cases, this provides some degree of + backward compatibility. */ + +/* Return true if C is a double or single quote character. */ + +static bool +isquote (const char c) +{ + return c == '"' || c == '\''; +} + /* See remote-args.h. */ std::vector gdb::remote_args::split (std::string args) { - std::vector results; + std::vector remote_args; + + const char *input = args.c_str (); + bool squote = false, dquote = false; + +#ifdef __MINGW32__ + /* This holds all the characters considered special to the + Windows shells. */ + static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n"; +#else + /* This holds all the characters considered special to the + typical Unix shells. We include `^' because the SunOS + /bin/sh treats it as a synonym for `|'. */ + static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n"; +#endif + + /* Characters that are special within double quotes. */ + static const char dquote_special[] = "$`\\"; + + input = skip_spaces (input); + + do + { + std::string arg; + + while (*input != '\0') + { + if (isspace (*input) && !squote && !dquote) + break; + else if (*input == '\\' && !squote) + { + if (input[1] == '\0') + arg += input[0]; + else if (input[1] == '\n') + ++input; + else if (dquote && input[1] == '"') + { + arg += input[1]; + ++input; + } + else if (dquote && strchr (dquote_special, input[1]) != nullptr) + { + /* Within double quotes, these characters have special + meaning. If they are escaped with a backslash then we + need to preserve the escape once we remove the + quotes. */ + arg += input[0]; /* Backslash. */ + arg += input[1]; /* Special character. */ + ++input; + } + else if (dquote) + { + /* Within double quotes, none of the remaining characters + have any special meaning, the backslash before the + character is a literal backslash. + + To retain the literal backslash with the quotes + removed, we need to escape the backslash. */ + arg += input[0]; /* Backslash. */ + arg += input[0]; /* Backslash. */ + + /* If the character after the backslash has special + meaning outside of the double quotes, then we need to + escape it now, otherwise it will gain special meaning + as we remove the surrounding quotes. However, as per + the comments at the head of this file; we don't + escape white space or quotes. */ + if (!isspace (input[1]) + && !isquote (input[1]) + && strchr (special, input[1]) != nullptr) + arg += input[0]; /* Backslash. */ + + arg += input[1]; /* Character. */ + ++input; + } + else if (isspace (input[1]) || isquote (input[1])) + { + /* We remove the escaping from white space and quote + characters. */ + arg += input[1]; /* Character. */ + ++input; + } + else + { + /* For everything else, retain the escaping. */ + arg += input[0]; /* Backslash. */ + arg += input[1]; /* Character. */ + ++input; + } + } + else if (squote) + { + /* Inside a single quote argument there are no special + characters. A single quote finishes the argument. */ + if (*input == '\'') + squote = false; + /* Don't add escaping to white space or quotes. We already + handled single quotes above, so the isquote call here will + only find double quotes. */ + else if (isspace (*input) || isquote (*input)) + arg += *input; + /* Any other special shell character needs a backslash adding + to avoid the character gaining special meaning outside of + the single quotes. */ + else if (strchr (special, *input) != NULL) + { + arg += '\\'; + arg += *input; + } + /* Any other character just gets added to the output. */ + else + arg += *input; + } + else if (dquote) + { + /* Inside a double quoted argument. A double quote closes + the argument. An escaped double quote will have been + handled in the backslash handling block above. */ + if (*input == '"') + dquote = false; + /* Don't add escaping for white space or quotes. We already + handled double quotes above, so the isquote call here will + only find single quotes. */ + else if (isspace (*input) || isquote (*input)) + arg += *input; + /* Any character that is not one of the few characters that + retains its special meaning without double quotes, but is + otherwise a special character needs an escape character + adding, to avoid the character gaining special meaning + outside of the quotes. */ + else if (strchr (dquote_special, *input) == nullptr + && strchr (special, *input) != nullptr) + { + arg += '\\'; + arg += *input; + } + /* Anything else just gets passed through to the output. */ + else + arg += *input; + } + else + { + /* Found a character outside of a single or double quoted + argument, and not preceded by a backslash. */ + if (*input == '\'') + squote = true; + else if (*input == '"') + dquote = true; + else + arg += *input; + } + ++input; + } - gdb_argv argv (args.c_str ()); - for (int i = 0; argv[i] != nullptr; i++) - results.emplace_back (argv[i]); + remote_args.push_back (std::move (arg)); + input = skip_spaces (input); + } + while (*input != '\0'); - return results; + return remote_args; } /* See remote-args.h. */ @@ -39,5 +256,5 @@ gdb::remote_args::split (std::string args) std::string gdb::remote_args::join (std::vector> &args) { - return construct_inferior_arguments (args, escape_shell_characters); + return construct_inferior_arguments (args, escape_quotes_and_white_space); }