Message ID | 20744f2c843ca8bffb773634350b8479a58c05e5.1679919937.git.aburgess@redhat.com |
---|---|
State | New |
Headers |
Return-Path: <gdb-patches-bounces+patchwork=sourceware.org@sourceware.org> 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 7CA063850861 for <patchwork@sourceware.org>; Mon, 27 Mar 2023 12:32:56 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7CA063850861 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1679920376; bh=fGGIQu2gSfCMm7ghPG4KSQrXbRkEXvzTVvCjmyRWMgY=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=PhP4JR5MOYGuROtepUUU/QKirUQyryhd8cIIqEFYLxu2cDHA9czmJPjraihj+NhH2 UmqzZ8MCUi25xSoHPy3RASiPchTjdsa6Pkf9dKBNujSsM2qsAh0Ejzl1iiqgtPh2j7 Wuldc9wQlTfh6KXmlQx16+ape8MYNB53cpjzt2nY= 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 E5514385842A for <gdb-patches@sourceware.org>; Mon, 27 Mar 2023 12:32:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E5514385842A 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-589-NDbU19XUONKAYg09vuUipQ-1; Mon, 27 Mar 2023 08:32:27 -0400 X-MC-Unique: NDbU19XUONKAYg09vuUipQ-1 Received: by mail-wm1-f71.google.com with SMTP id k1-20020a05600c1c8100b003ee6dbceb81so4718895wms.5 for <gdb-patches@sourceware.org>; Mon, 27 Mar 2023 05:32:27 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679920346; 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=fGGIQu2gSfCMm7ghPG4KSQrXbRkEXvzTVvCjmyRWMgY=; b=kp8Qgzkxh/a8MM5U8JxRuicqYLS/4bcTNiWkkKkHoQRmcXv82HaPyV6+CMhn9FsDVg fvxhahoIKZoJccSc4KLNLw0Agzpj7u013b1sWU1IeL5lVSNsnMZu6pOZDT57Lp1OMuQb u+6rue6UEUO8Zy9b0W+px/SzDrXYZMgoY/ncDChn+7FSrvcixVg+abvHTUFMXt8oFwyv nSclNGxpzaqSQVctetXEGVrWmMf5TQ/ulFImt9G8yamjzeKtzS90DQsn+2oDizws8uTd //hVVp4jONISO3mlrMJ66jMpmUMnafNOgYAxY7gAahBV0U6cs/y9HNk9uz5GKJLAulkR +s+A== X-Gm-Message-State: AO0yUKXp6VmkWcB6C2seVWl5PfmGbkC7PjZJFaQBM5Z1CcMuOwA4Cf6V idIyb3PTAhC/E9JtchcIIIa6JKMHTQ6TpnpKfa9nTakYNJhMC3S7zFJof1QcAOZij1Z12FDm8D5 vrIXOj/Vw5VIk9DagpepEduhvt+oUu10OKhOq4cng5gb8AeinNAtTaBImX8oDmXdwQWrAGfaz/v y+jUEY5w== X-Received: by 2002:a7b:c7da:0:b0:3ef:1745:518e with SMTP id z26-20020a7bc7da000000b003ef1745518emr9756826wmk.26.1679920346096; Mon, 27 Mar 2023 05:32:26 -0700 (PDT) X-Google-Smtp-Source: AK7set9e10LP+FHuWZJ2yXI6zEfObMLVRAEBbdzGQxKpJym2ZTxUwsRuDN7kE01IYnQSpG6qApd9tw== X-Received: by 2002:a7b:c7da:0:b0:3ef:1745:518e with SMTP id z26-20020a7bc7da000000b003ef1745518emr9756805wmk.26.1679920345769; Mon, 27 Mar 2023 05:32:25 -0700 (PDT) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id m15-20020a05600c3b0f00b003ee91eda67bsm8823481wms.12.2023.03.27.05.32.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Mar 2023 05:32:25 -0700 (PDT) To: gdb-patches@sourceware.org Cc: Pedro Alves <pedro@palves.net>, Simon Marchi <simark@simark.ca>, Andrew Burgess <aburgess@redhat.com> Subject: [PATCHv3 1/3] gdb: more debug output for displaced stepping Date: Mon, 27 Mar 2023 13:32:19 +0100 Message-Id: <20744f2c843ca8bffb773634350b8479a58c05e5.1679919937.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: <cover.1679919937.git.aburgess@redhat.com> References: <cover.1678984664.git.aburgess@redhat.com> <cover.1679919937.git.aburgess@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.7 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_H2, SPF_HELO_NONE, SPF_NONE, TXREP 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.29 Precedence: list List-Id: Gdb-patches mailing list <gdb-patches.sourceware.org> List-Unsubscribe: <https://sourceware.org/mailman/options/gdb-patches>, <mailto:gdb-patches-request@sourceware.org?subject=unsubscribe> List-Archive: <https://sourceware.org/pipermail/gdb-patches/> List-Post: <mailto:gdb-patches@sourceware.org> List-Help: <mailto:gdb-patches-request@sourceware.org?subject=help> List-Subscribe: <https://sourceware.org/mailman/listinfo/gdb-patches>, <mailto:gdb-patches-request@sourceware.org?subject=subscribe> From: Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org> Reply-To: Andrew Burgess <aburgess@redhat.com> Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" <gdb-patches-bounces+patchwork=sourceware.org@sourceware.org> |
Series |
AMD64 Displaced Stepping Fix
|
|
Commit Message
Andrew Burgess
March 27, 2023, 12:32 p.m. UTC
While investigating a displaced stepping issue I wanted an easy way to see what GDB thought the original instruction was, and what instruction GDB replaced that with when performing the displaced step. We do print out the address that is being stepped, so I can track down the original instruction, I just need to go find the information myself. And we do print out the bytes of the new instruction, so I can figure out what the replacement instruction was, but it's not really easy. Also, the code that prints the bytes of the replacement instruction only prints 4 bytes, which clearly isn't always going to be correct. In this commit I remove the existing code that prints the bytes of the replacement instruction, and add two new blocks of code to displaced_step_prepare_throw. This new code prints the original instruction, and the replacement instruction. In each case we print both the bytes that make up the instruction and the completely disassembled instruction. Here's an example of what the output looks like on x86-64 (this is with 'set debug displaced on'). The two interesting lines contain the strings 'original insn' and 'replacement insn': (gdb) step [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00 jmp *0x2fe2(%rip) # 0x404018 <puts@got.plt> [displaced] prepare: selected buffer at 0x401052 [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50 [displaced] fixup_riprel: %rip-relative addressing used. [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036 [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052 [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00 jmp *0x2fe2(%rcx) [displaced] finish: restored 2892655.2892655.0 0x401052 [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ... [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578 0x00007ffff7e402c0 in puts () from /lib64/libc.so.6 (gdb) One final note. For many targets that support displaced stepping (in fact all targets except ARM) the replacement instruction is always a single instruction. But on ARM the replacement could actually be a series of instructions. The debug code tries to handle this by disassembling the entire displaced stepping buffer. Obviously this might actually print more than is necessary, but there's (currently) no easy way to know how many instructions to disassemble; that knowledge is all locked in the architecture specific code. Still I don't think it really hurts, if someone is looking at this debug then hopefully they known what to expect. Obviously we can imagine schemes where the architecture specific displaced stepping code could communicate back how many bytes its replacement sequence was, and then our debug print code could use this to limit the disassembly. But this seems like a lot of effort just to save printing a few additional instructions in some debug output. I'm not proposing to do anything about this issue for now. --- gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 17 deletions(-)
Comments
On 3/27/23 08:32, Andrew Burgess wrote: > While investigating a displaced stepping issue I wanted an easy way to > see what GDB thought the original instruction was, and what > instruction GDB replaced that with when performing the displaced step. > > We do print out the address that is being stepped, so I can track down > the original instruction, I just need to go find the information > myself. > > And we do print out the bytes of the new instruction, so I can figure > out what the replacement instruction was, but it's not really easy. > > Also, the code that prints the bytes of the replacement instruction > only prints 4 bytes, which clearly isn't always going to be correct. > > In this commit I remove the existing code that prints the bytes of the > replacement instruction, and add two new blocks of code to > displaced_step_prepare_throw. This new code prints the original > instruction, and the replacement instruction. In each case we print > both the bytes that make up the instruction and the completely > disassembled instruction. > > Here's an example of what the output looks like on x86-64 (this is > with 'set debug displaced on'). The two interesting lines contain the > strings 'original insn' and 'replacement insn': > > (gdb) step > [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now > [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00 jmp *0x2fe2(%rip) # 0x404018 <puts@got.plt> > [displaced] prepare: selected buffer at 0x401052 > [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50 > [displaced] fixup_riprel: %rip-relative addressing used. > [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036 > [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff > [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052 > [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00 jmp *0x2fe2(%rcx) > [displaced] finish: restored 2892655.2892655.0 0x401052 > [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ... > [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578 > 0x00007ffff7e402c0 in puts () from /lib64/libc.so.6 > (gdb) > > One final note. For many targets that support displaced stepping (in > fact all targets except ARM) the replacement instruction is always a > single instruction. But on ARM the replacement could actually be a > series of instructions. > > The debug code tries to handle this by disassembling the entire > displaced stepping buffer. Obviously this might actually print more > than is necessary, but there's (currently) no easy way to know how > many instructions to disassemble; that knowledge is all locked in the > architecture specific code. Still I don't think it really hurts, if > someone is looking at this debug then hopefully they known what to > expect. > > Obviously we can imagine schemes where the architecture specific > displaced stepping code could communicate back how many bytes its > replacement sequence was, and then our debug print code could use this > to limit the disassembly. But this seems like a lot of effort just to > save printing a few additional instructions in some debug output. > > I'm not proposing to do anything about this issue for now. > --- > gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++----------- > 1 file changed, 68 insertions(+), 17 deletions(-) > > diff --git a/gdb/infrun.c b/gdb/infrun.c > index 5c9babb9104..8c56a9a4dfb 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -74,6 +74,7 @@ > #include "gdbsupport/common-debug.h" > #include "gdbsupport/buildargv.h" > #include "extension.h" > +#include "disasm.h" > > /* Prototypes for local functions */ > > @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp) > CORE_ADDR original_pc = regcache_read_pc (regcache); > CORE_ADDR displaced_pc; > > + /* Display the instruction we are going to displaced step. */ > + if (debug_displaced) > + { > + string_file tmp_stream; > + int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream, > + nullptr); > + > + if (dislen > 0) > + { > + gdb::byte_vector insn_buf (dislen); > + read_memory (original_pc, insn_buf.data (), insn_buf.size ()); > + > + std::string insn_bytes > + = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ()); > + > + displaced_debug_printf ("original insn %s: %s \t %s", > + paddress (gdbarch, original_pc), > + insn_bytes.c_str (), > + tmp_stream.string ().c_str ()); If the bytes disassemble to more than one instruction, does tmp_stream contain new lines characters? Just wondering what the output would look like (not a big deal in any case). Approved-By: Simon Marchi <simon.marchi@efficios.com> Simon
Simon Marchi <simark@simark.ca> writes: > On 3/27/23 08:32, Andrew Burgess wrote: >> While investigating a displaced stepping issue I wanted an easy way to >> see what GDB thought the original instruction was, and what >> instruction GDB replaced that with when performing the displaced step. >> >> We do print out the address that is being stepped, so I can track down >> the original instruction, I just need to go find the information >> myself. >> >> And we do print out the bytes of the new instruction, so I can figure >> out what the replacement instruction was, but it's not really easy. >> >> Also, the code that prints the bytes of the replacement instruction >> only prints 4 bytes, which clearly isn't always going to be correct. >> >> In this commit I remove the existing code that prints the bytes of the >> replacement instruction, and add two new blocks of code to >> displaced_step_prepare_throw. This new code prints the original >> instruction, and the replacement instruction. In each case we print >> both the bytes that make up the instruction and the completely >> disassembled instruction. >> >> Here's an example of what the output looks like on x86-64 (this is >> with 'set debug displaced on'). The two interesting lines contain the >> strings 'original insn' and 'replacement insn': >> >> (gdb) step >> [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now >> [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00 jmp *0x2fe2(%rip) # 0x404018 <puts@got.plt> >> [displaced] prepare: selected buffer at 0x401052 >> [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50 >> [displaced] fixup_riprel: %rip-relative addressing used. >> [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036 >> [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff >> [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052 >> [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00 jmp *0x2fe2(%rcx) >> [displaced] finish: restored 2892655.2892655.0 0x401052 >> [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ... >> [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578 >> 0x00007ffff7e402c0 in puts () from /lib64/libc.so.6 >> (gdb) >> >> One final note. For many targets that support displaced stepping (in >> fact all targets except ARM) the replacement instruction is always a >> single instruction. But on ARM the replacement could actually be a >> series of instructions. >> >> The debug code tries to handle this by disassembling the entire >> displaced stepping buffer. Obviously this might actually print more >> than is necessary, but there's (currently) no easy way to know how >> many instructions to disassemble; that knowledge is all locked in the >> architecture specific code. Still I don't think it really hurts, if >> someone is looking at this debug then hopefully they known what to >> expect. >> >> Obviously we can imagine schemes where the architecture specific >> displaced stepping code could communicate back how many bytes its >> replacement sequence was, and then our debug print code could use this >> to limit the disassembly. But this seems like a lot of effort just to >> save printing a few additional instructions in some debug output. >> >> I'm not proposing to do anything about this issue for now. >> --- >> gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++----------- >> 1 file changed, 68 insertions(+), 17 deletions(-) >> >> diff --git a/gdb/infrun.c b/gdb/infrun.c >> index 5c9babb9104..8c56a9a4dfb 100644 >> --- a/gdb/infrun.c >> +++ b/gdb/infrun.c >> @@ -74,6 +74,7 @@ >> #include "gdbsupport/common-debug.h" >> #include "gdbsupport/buildargv.h" >> #include "extension.h" >> +#include "disasm.h" >> >> /* Prototypes for local functions */ >> >> @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp) >> CORE_ADDR original_pc = regcache_read_pc (regcache); >> CORE_ADDR displaced_pc; >> >> + /* Display the instruction we are going to displaced step. */ >> + if (debug_displaced) >> + { >> + string_file tmp_stream; >> + int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream, >> + nullptr); >> + >> + if (dislen > 0) >> + { >> + gdb::byte_vector insn_buf (dislen); >> + read_memory (original_pc, insn_buf.data (), insn_buf.size ()); >> + >> + std::string insn_bytes >> + = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ()); >> + >> + displaced_debug_printf ("original insn %s: %s \t %s", >> + paddress (gdbarch, original_pc), >> + insn_bytes.c_str (), >> + tmp_stream.string ().c_str ()); > > If the bytes disassemble to more than one instruction, does tmp_stream > contain new lines characters? Just wondering what the output would look > like (not a big deal in any case). No. gdb_print_insn will only disassemble a single instruction and return its length. In this bit of debug, we assume the original instruction is always a single instruction. If that's not true then I've seriously not understood how displaced stepping works. For the replacement instructions the call to gdb_print_insn is placed inside a loop which calls gdb_print_insn multiple times, so you'll see multiple lines like: [displaced] displaced_step_prepare_throw: replacement insn <ADDRESS>: <BYTES> <DISASSEMBLY> I use this trick: CORE_ADDR end = addr + (gdbarch_displaced_step_hw_singlestep (gdbarch) ? 1 : gdbarch_displaced_step_buffer_length (gdbarch)); Which means for targets that do a 1:1 replacement we only disassemble a single instruction. But for everyone else we'll always disassemble the entire displaced step buffer. Currently this is just ARM. > > Approved-By: Simon Marchi <simon.marchi@efficios.com> Thanks, Andrew
On 3/28/23 11:08, Andrew Burgess wrote: > Simon Marchi <simark@simark.ca> writes: > >> On 3/27/23 08:32, Andrew Burgess wrote: >>> While investigating a displaced stepping issue I wanted an easy way to >>> see what GDB thought the original instruction was, and what >>> instruction GDB replaced that with when performing the displaced step. >>> >>> We do print out the address that is being stepped, so I can track down >>> the original instruction, I just need to go find the information >>> myself. >>> >>> And we do print out the bytes of the new instruction, so I can figure >>> out what the replacement instruction was, but it's not really easy. >>> >>> Also, the code that prints the bytes of the replacement instruction >>> only prints 4 bytes, which clearly isn't always going to be correct. >>> >>> In this commit I remove the existing code that prints the bytes of the >>> replacement instruction, and add two new blocks of code to >>> displaced_step_prepare_throw. This new code prints the original >>> instruction, and the replacement instruction. In each case we print >>> both the bytes that make up the instruction and the completely >>> disassembled instruction. >>> >>> Here's an example of what the output looks like on x86-64 (this is >>> with 'set debug displaced on'). The two interesting lines contain the >>> strings 'original insn' and 'replacement insn': >>> >>> (gdb) step >>> [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now >>> [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00 jmp *0x2fe2(%rip) # 0x404018 <puts@got.plt> >>> [displaced] prepare: selected buffer at 0x401052 >>> [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50 >>> [displaced] fixup_riprel: %rip-relative addressing used. >>> [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036 >>> [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff >>> [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052 >>> [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00 jmp *0x2fe2(%rcx) >>> [displaced] finish: restored 2892655.2892655.0 0x401052 >>> [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ... >>> [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578 >>> 0x00007ffff7e402c0 in puts () from /lib64/libc.so.6 >>> (gdb) >>> >>> One final note. For many targets that support displaced stepping (in >>> fact all targets except ARM) the replacement instruction is always a >>> single instruction. But on ARM the replacement could actually be a >>> series of instructions. >>> >>> The debug code tries to handle this by disassembling the entire >>> displaced stepping buffer. Obviously this might actually print more >>> than is necessary, but there's (currently) no easy way to know how >>> many instructions to disassemble; that knowledge is all locked in the >>> architecture specific code. Still I don't think it really hurts, if >>> someone is looking at this debug then hopefully they known what to >>> expect. >>> >>> Obviously we can imagine schemes where the architecture specific >>> displaced stepping code could communicate back how many bytes its >>> replacement sequence was, and then our debug print code could use this >>> to limit the disassembly. But this seems like a lot of effort just to >>> save printing a few additional instructions in some debug output. >>> >>> I'm not proposing to do anything about this issue for now. >>> --- >>> gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++----------- >>> 1 file changed, 68 insertions(+), 17 deletions(-) >>> >>> diff --git a/gdb/infrun.c b/gdb/infrun.c >>> index 5c9babb9104..8c56a9a4dfb 100644 >>> --- a/gdb/infrun.c >>> +++ b/gdb/infrun.c >>> @@ -74,6 +74,7 @@ >>> #include "gdbsupport/common-debug.h" >>> #include "gdbsupport/buildargv.h" >>> #include "extension.h" >>> +#include "disasm.h" >>> >>> /* Prototypes for local functions */ >>> >>> @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp) >>> CORE_ADDR original_pc = regcache_read_pc (regcache); >>> CORE_ADDR displaced_pc; >>> >>> + /* Display the instruction we are going to displaced step. */ >>> + if (debug_displaced) >>> + { >>> + string_file tmp_stream; >>> + int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream, >>> + nullptr); >>> + >>> + if (dislen > 0) >>> + { >>> + gdb::byte_vector insn_buf (dislen); >>> + read_memory (original_pc, insn_buf.data (), insn_buf.size ()); >>> + >>> + std::string insn_bytes >>> + = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ()); >>> + >>> + displaced_debug_printf ("original insn %s: %s \t %s", >>> + paddress (gdbarch, original_pc), >>> + insn_bytes.c_str (), >>> + tmp_stream.string ().c_str ()); >> >> If the bytes disassemble to more than one instruction, does tmp_stream >> contain new lines characters? Just wondering what the output would look >> like (not a big deal in any case). > > No. gdb_print_insn will only disassemble a single instruction and > return its length. In this bit of debug, we assume the original > instruction is always a single instruction. If that's not true then > I've seriously not understood how displaced stepping works. Eh yeah, that's the input instruction, you're right. > > For the replacement instructions the call to gdb_print_insn is placed > inside a loop which calls gdb_print_insn multiple times, so you'll see > multiple lines like: > > [displaced] displaced_step_prepare_throw: replacement insn <ADDRESS>: <BYTES> <DISASSEMBLY> > > I use this trick: > > CORE_ADDR end > = addr + (gdbarch_displaced_step_hw_singlestep (gdbarch) > ? 1 : gdbarch_displaced_step_buffer_length (gdbarch)); > > Which means for targets that do a 1:1 replacement we only disassemble a > single instruction. But for everyone else we'll always disassemble the > entire displaced step buffer. Currently this is just ARM. Ok I missed that, that makes sense. Simon
Simon Marchi <simark@simark.ca> writes: > On 3/28/23 11:08, Andrew Burgess wrote: >> Simon Marchi <simark@simark.ca> writes: >> >>> On 3/27/23 08:32, Andrew Burgess wrote: >>>> While investigating a displaced stepping issue I wanted an easy way to >>>> see what GDB thought the original instruction was, and what >>>> instruction GDB replaced that with when performing the displaced step. >>>> >>>> We do print out the address that is being stepped, so I can track down >>>> the original instruction, I just need to go find the information >>>> myself. >>>> >>>> And we do print out the bytes of the new instruction, so I can figure >>>> out what the replacement instruction was, but it's not really easy. >>>> >>>> Also, the code that prints the bytes of the replacement instruction >>>> only prints 4 bytes, which clearly isn't always going to be correct. >>>> >>>> In this commit I remove the existing code that prints the bytes of the >>>> replacement instruction, and add two new blocks of code to >>>> displaced_step_prepare_throw. This new code prints the original >>>> instruction, and the replacement instruction. In each case we print >>>> both the bytes that make up the instruction and the completely >>>> disassembled instruction. >>>> >>>> Here's an example of what the output looks like on x86-64 (this is >>>> with 'set debug displaced on'). The two interesting lines contain the >>>> strings 'original insn' and 'replacement insn': >>>> >>>> (gdb) step >>>> [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now >>>> [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00 jmp *0x2fe2(%rip) # 0x404018 <puts@got.plt> >>>> [displaced] prepare: selected buffer at 0x401052 >>>> [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50 >>>> [displaced] fixup_riprel: %rip-relative addressing used. >>>> [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036 >>>> [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff >>>> [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052 >>>> [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00 jmp *0x2fe2(%rcx) >>>> [displaced] finish: restored 2892655.2892655.0 0x401052 >>>> [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ... >>>> [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578 >>>> 0x00007ffff7e402c0 in puts () from /lib64/libc.so.6 >>>> (gdb) >>>> >>>> One final note. For many targets that support displaced stepping (in >>>> fact all targets except ARM) the replacement instruction is always a >>>> single instruction. But on ARM the replacement could actually be a >>>> series of instructions. >>>> >>>> The debug code tries to handle this by disassembling the entire >>>> displaced stepping buffer. Obviously this might actually print more >>>> than is necessary, but there's (currently) no easy way to know how >>>> many instructions to disassemble; that knowledge is all locked in the >>>> architecture specific code. Still I don't think it really hurts, if >>>> someone is looking at this debug then hopefully they known what to >>>> expect. >>>> >>>> Obviously we can imagine schemes where the architecture specific >>>> displaced stepping code could communicate back how many bytes its >>>> replacement sequence was, and then our debug print code could use this >>>> to limit the disassembly. But this seems like a lot of effort just to >>>> save printing a few additional instructions in some debug output. >>>> >>>> I'm not proposing to do anything about this issue for now. >>>> --- >>>> gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++----------- >>>> 1 file changed, 68 insertions(+), 17 deletions(-) >>>> >>>> diff --git a/gdb/infrun.c b/gdb/infrun.c >>>> index 5c9babb9104..8c56a9a4dfb 100644 >>>> --- a/gdb/infrun.c >>>> +++ b/gdb/infrun.c >>>> @@ -74,6 +74,7 @@ >>>> #include "gdbsupport/common-debug.h" >>>> #include "gdbsupport/buildargv.h" >>>> #include "extension.h" >>>> +#include "disasm.h" >>>> >>>> /* Prototypes for local functions */ >>>> >>>> @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp) >>>> CORE_ADDR original_pc = regcache_read_pc (regcache); >>>> CORE_ADDR displaced_pc; >>>> >>>> + /* Display the instruction we are going to displaced step. */ >>>> + if (debug_displaced) >>>> + { >>>> + string_file tmp_stream; >>>> + int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream, >>>> + nullptr); >>>> + >>>> + if (dislen > 0) >>>> + { >>>> + gdb::byte_vector insn_buf (dislen); >>>> + read_memory (original_pc, insn_buf.data (), insn_buf.size ()); >>>> + >>>> + std::string insn_bytes >>>> + = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ()); >>>> + >>>> + displaced_debug_printf ("original insn %s: %s \t %s", >>>> + paddress (gdbarch, original_pc), >>>> + insn_bytes.c_str (), >>>> + tmp_stream.string ().c_str ()); >>> >>> If the bytes disassemble to more than one instruction, does tmp_stream >>> contain new lines characters? Just wondering what the output would look >>> like (not a big deal in any case). >> >> No. gdb_print_insn will only disassemble a single instruction and >> return its length. In this bit of debug, we assume the original >> instruction is always a single instruction. If that's not true then >> I've seriously not understood how displaced stepping works. > > Eh yeah, that's the input instruction, you're right. > >> >> For the replacement instructions the call to gdb_print_insn is placed >> inside a loop which calls gdb_print_insn multiple times, so you'll see >> multiple lines like: >> >> [displaced] displaced_step_prepare_throw: replacement insn <ADDRESS>: <BYTES> <DISASSEMBLY> >> >> I use this trick: >> >> CORE_ADDR end >> = addr + (gdbarch_displaced_step_hw_singlestep (gdbarch) >> ? 1 : gdbarch_displaced_step_buffer_length (gdbarch)); >> >> Which means for targets that do a 1:1 replacement we only disassemble a >> single instruction. But for everyone else we'll always disassemble the >> entire displaced step buffer. Currently this is just ARM. > > Ok I missed that, that makes sense. I pushed this patch. Thanks, Andrew
diff --git a/gdb/infrun.c b/gdb/infrun.c index 5c9babb9104..8c56a9a4dfb 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -74,6 +74,7 @@ #include "gdbsupport/common-debug.h" #include "gdbsupport/buildargv.h" #include "extension.h" +#include "disasm.h" /* Prototypes for local functions */ @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp) CORE_ADDR original_pc = regcache_read_pc (regcache); CORE_ADDR displaced_pc; + /* Display the instruction we are going to displaced step. */ + if (debug_displaced) + { + string_file tmp_stream; + int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream, + nullptr); + + if (dislen > 0) + { + gdb::byte_vector insn_buf (dislen); + read_memory (original_pc, insn_buf.data (), insn_buf.size ()); + + std::string insn_bytes + = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ()); + + displaced_debug_printf ("original insn %s: %s \t %s", + paddress (gdbarch, original_pc), + insn_bytes.c_str (), + tmp_stream.string ().c_str ()); + } + else + displaced_debug_printf ("replacement insn %s: invalid length: %d", + paddress (gdbarch, original_pc), dislen); + } + displaced_step_prepare_status status = gdbarch_displaced_step_prepare (gdbarch, tp, displaced_pc); @@ -1845,6 +1871,48 @@ displaced_step_prepare_throw (thread_info *tp) paddress (gdbarch, original_pc), paddress (gdbarch, displaced_pc)); + /* Display the new displaced instruction(s). */ + if (debug_displaced) + { + string_file tmp_stream; + CORE_ADDR addr = displaced_pc; + + /* If displaced stepping is going to use h/w single step then we know + that the replacement instruction can only be a single instruction, + in that case set the end address at the next byte. + + Otherwise the displaced stepping copy instruction routine could + have generated multiple instructions, and all we know is that they + must fit within the LEN bytes of the buffer. */ + CORE_ADDR end + = addr + (gdbarch_displaced_step_hw_singlestep (gdbarch) + ? 1 : gdbarch_displaced_step_buffer_length (gdbarch)); + + while (addr < end) + { + int dislen = gdb_print_insn (gdbarch, addr, &tmp_stream, nullptr); + if (dislen <= 0) + { + displaced_debug_printf + ("replacement insn %s: invalid length: %d", + paddress (gdbarch, addr), dislen); + break; + } + + gdb::byte_vector insn_buf (dislen); + read_memory (addr, insn_buf.data (), insn_buf.size ()); + + std::string insn_bytes + = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ()); + std::string insn_str = tmp_stream.release (); + displaced_debug_printf ("replacement insn %s: %s \t %s", + paddress (gdbarch, addr), + insn_bytes.c_str (), + insn_str.c_str ()); + addr += dislen; + } + } + return DISPLACED_STEP_PREPARE_STATUS_OK; } @@ -2711,23 +2779,6 @@ resume_1 (enum gdb_signal sig) step = false; } - if (debug_displaced - && tp->control.trap_expected - && use_displaced_stepping (tp) - && !step_over_info_valid_p ()) - { - struct regcache *resume_regcache = get_thread_regcache (tp); - struct gdbarch *resume_gdbarch = resume_regcache->arch (); - CORE_ADDR actual_pc = regcache_read_pc (resume_regcache); - gdb_byte buf[4]; - - read_memory (actual_pc, buf, sizeof (buf)); - displaced_debug_printf ("run %s: %s", - paddress (resume_gdbarch, actual_pc), - displaced_step_dump_bytes - (buf, sizeof (buf)).c_str ()); - } - if (tp->control.may_range_step) { /* If we're resuming a thread with the PC out of the step