🔎

VSCodeからNetBSD2(i386)をSSH経由でgdbアタッチデバッグする

2024/05/07に公開

VSCodeからNetBSD6(i386)をSSH経由でgdbアタッチデバッグするの続き
NetBSD 2.0.2(i386)に対応させる。

TL;DR

  • --interpreter=mi2に対応するためにgdb-7.2aをビルド・インストール
  • SSH接続時のアルゴリズム(鍵交換・暗号化)をNative Debugで指定する機能実装
    • 鍵交換:diffie-hellman-group-exchange-sha1
    • 暗号化:aes128-cbc
    • launch.json"ssh": {"kex": ["diffie-hellman-group-exchange-sha1"], "cipher": ["aes128-cbc"]}を指定する
  • launch.json"debugger_args": ["--symbols", "デバッグ情報のあるリモート上のバイナリパス"]を指定する
  • 変更差分

事前準備:NetBSD2.0.2(i386)のQEMU作成

既に作成済みあるいはホストマシンがあればgdb-7.2aのビルド・インストールまでスキップ。

流れはNetBSD6.0.1とほぼ同じのため重複部分は割愛するが、インストールウィザードが若干異なる。

OSイメージファイル取得

i386cd.isoを保存。

初回起動コマンド

command
qemu-system-i386 ^
    -m 1024 ^
    -smp sockets=1,cores=4,threads=2 ^
    -hda netbsd.qcow2 ^
    -cdrom i386cd.iso ^
    -netdev user,id=n1,ipv6=off ^
    -device e1000,netdev=n1 ^
    -monitor telnet::5432,server,nowait ^
    -rtc base=utc

インストール

インストールウィザード

1番目を選択

1番目を選択

Yesを選択

エンター

1番目を選択

1番目を選択

2番目を選択

Yesを選択

1番目を選択

2番目のスワップサイズを適宜変更

一番下を選択

エンター

エンター

Yesを選択

1番目を選択

エンター

1番目を選択

3番目を選択

一番下を選択

ここでインストールが始まるのでしばらく待つ。

エンター

エンター

Japanを選択

一番下を選択

3番目を選択

Yesを選択

rootログイン用パスワードを設定

エンター

3番目を選択

エンター

4番目を選択

ブート画面が出たらウィンドウを閉じる

2回目以降の起動

command
qemu-system-i386 ^
    -m 1024 ^
    -smp sockets=1,cores=4,threads=2 ^
    -hda netbsd.qcow2 ^
    -netdev user,id=n1,ipv6=off,hostfwd=tcp::44444-:22 ^
    -device e1000,netdev=n1 ^
    -monitor telnet::5432,server,nowait ^
    -rtc base=utc

キーボードレイアウトの修正

101キーになっているらしいので日本語レイアウトに変更する。

command
wsconsctl -k -w encoding=jp

=^で入力できる。
永続的に変更するには/etc/wscons.confを編集する。

vi /etc/wscons.conf

encoding jp

SSH接続設定

vi /etc/rc.conf

dhclient=yes
sshd=yes
vi /etc/hosts

127.0.0.1 localhost.localdomain
command
vi /etc/ssh/sshd_config
  • PermitRootLoginのコメントを削除してyesに変更
  • PubkeyAuthenticationのコメントを削除
  • PasswordEmptyAuthenticationのコメントを削除

sshd再起動

command
/etc/rc.d/sshd restart

マシン再起動

command
reboot now

参考

SSH接続でUnable to negotiate with ***: no matching key exchange method found. Their offer: diffie-hellman-group-exchange-sha1,…no matching cipher found. Their offer: aes128-cbc,…等と表示されて接続できない

おそらくクライアント側で非推奨の接続形式を除外しているためかと思われる。
サーバー側から提案された方式を指定して接続する。

command
ssh root@127.0.0.1 -p 44444 -oKexAlgorithms=+diffie-hellman-group-exchange-sha1 -c +aes256-cbc

SSH接続時に毎回tset: terminal type vt100 is unknown Terminal type?と入力を促される

詳細は未確認だが環境変数TERM=vt100を事前に設定しておくと解消される。
~/.profileを編集し、tsetのチェック処理の直前にexport TERM=vt100を追記する。

vi ~/.profile

export TERM=vt100
if [ -x /usr/bin/tset ]; then
        eval `tset -sQrm 'unknown:?unknown'`
fi

次回以降のSSH接続で入力が要求されなくなる。

ssh root@127.0.0.1 -p 44444 -oKexAlgorithms=+diffie-hellman-group-exchange-sha1 -c +aes256-cbc

root@127.0.0.1's password:
Last login: *** *** ** **:**:** ****
NetBSD 2.0.2 (GENERIC) #0: Wed Mar 23 08:53:42 UTC 2005

Welcome to NetBSD!

Terminal type is vt100.
We recommend creating a non-root account and using su(1) for root access.
#

gdb-7.2aのビルド・インストール

gdbのバージョン確認

gdb --version

GNU gdb 5.3nb1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386--netbsdelf".

Native DebugはMI2に準拠しているがMIが正式に2へ対応したのがおそらくgdb-6.0からのようなのでプリインストール版では拡張機能が使えない。

  • Default MI syntax changed to "mi2".

The default MI (machine interface) syntax, enabled by the command line
option "-i=mi", has been changed to "mi2". The previous MI syntax,
"mi1", can be enabled by specifying the option "-i=mi1"

ただ対応されているはずのバージョンgdb-7.0.1aで確認したところ、デバッグモードに入れるもののローカル変数値がVSCodeから一覧で表示されないバグがあった/gdb-7.3.1ではビルドエラーになったので、ビルドも通りかつローカル変数値一覧が表示できるgdb-7.2aを選択する。

command
mkdir -p ~/build_ws
cd ~/build_ws
ftp ftp://gcc.gnu.org/pub/gdb/releases/
get gdb-7.2a.tar.gz
quit
tar zxvf gdb-7.2a.tar.gz
mkdir -p gdb-7.2/build
cd gdb-7.2/build
LDFLAGS=-static \
    ../configure \
    --build=i486--netbsdelf
make -j8
make install
/usr/local/bin/gdb --version

GNU gdb (GDB) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486--netbsdelf".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.

Native DebugにSSH接続方式機能を実装

NetBSD2の制約によりSSH接続方式を指定できないと以下のようなエラーが出てNative DebugからSSH接続できない。

Error running over ssh!
Error: error occurred on connecting the ssh.
Error: Handshake failed: no matching key exchange algorithm

Native DebugでのSSH接続にはnpmモジュールのssh2を用いており、モジュール自体では対応できているがNative Debug側が未実装のためlaunch.jsonから指定できないので、これらを指定できるように修正する。

SSH接続方式機能の変更差分

割り込み実装の変更差分を適用してからの差分。

変更差分
algorithm.diff
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -325,6 +325,53 @@
 										"type": "string",
 										"description": "Only connect via resolved IP address family for host",
 										"default": "IPv4"
+									},
+									"forceIPv6": {
+										"type": "boolean",
+										"description": "Only connect via resolved IPv6 address for host",
+										"default": false
+									},
+									"algorithms": {
+										"type": "object",
+										"description": "Specify algorithms",
+										"properties": {
+											"kex": {
+												"type": "array",
+												"description": "Key exchanges",
+												"default": [
+													"diffie-hellman-group-exchange-sha1",
+													"diffie-hellman-group1-sha1"
+												]
+											},
+											"cipher": {
+												"type": "array",
+												"description": "Ciphers",
+												"default": [
+													"aes128-cbc",
+													"3des-cbc",
+													"blowfish-cbc",
+													"cast128-cbc",
+													"arcfour",
+													"aes192-cbc",
+													"aes256-cbc"
+												]
+											},
+											"serverHostKey": {
+												"type": "array",
+												"description": "Server host keys",
+												"default": []
+											},
+											"hmac": {
+												"type": "array",
+												"description": "Hash based message authentication codes",
+												"default": []
+											},
+											"compress": {
+												"type": "array",
+												"description": "Compresses",
+												"default": []
+											}
+										}
 									}
 								}
 							}
@@ -518,6 +565,48 @@
 										"type": "string",
 										"description": "Only connect via resolved IP address family for host",
 										"default": "IPv4"
+									},
+									"algorithms": {
+										"type": "object",
+										"description": "Specify algorithms",
+										"properties": {
+											"kex": {
+												"type": "array",
+												"description": "Key exchanges",
+												"default": [
+													"diffie-hellman-group-exchange-sha1",
+													"diffie-hellman-group1-sha1"
+												]
+											},
+											"cipher": {
+												"type": "array",
+												"description": "Ciphers",
+												"default": [
+													"aes128-cbc",
+													"3des-cbc",
+													"blowfish-cbc",
+													"cast128-cbc",
+													"arcfour",
+													"aes192-cbc",
+													"aes256-cbc"
+												]
+											},
+											"serverHostKey": {
+												"type": "array",
+												"description": "Server host keys",
+												"default": []
+											},
+											"hmac": {
+												"type": "array",
+												"description": "Hash based message authentication codes",
+												"default": []
+											},
+											"compress": {
+												"type": "array",
+												"description": "Compresses",
+												"default": []
+											}
+										}
 									}
 								}
 							}
@@ -866,6 +955,53 @@
 										"type": "string",
 										"description": "Only connect via resolved IP address family for host",
 										"default": "IPv4"
+									},
+									"forceIPv6": {
+										"type": "boolean",
+										"description": "Only connect via resolved IPv6 address for host",
+										"default": false
+									},
+									"algorithms": {
+										"type": "object",
+										"description": "Specify algorithms",
+										"properties": {
+											"kex": {
+												"type": "array",
+												"description": "Key exchanges",
+												"default": [
+													"diffie-hellman-group-exchange-sha1",
+													"diffie-hellman-group1-sha1"
+												]
+											},
+											"cipher": {
+												"type": "array",
+												"description": "Ciphers",
+												"default": [
+													"aes128-cbc",
+													"3des-cbc",
+													"blowfish-cbc",
+													"cast128-cbc",
+													"arcfour",
+													"aes192-cbc",
+													"aes256-cbc"
+												]
+											},
+											"serverHostKey": {
+												"type": "array",
+												"description": "Server host keys",
+												"default": []
+											},
+											"hmac": {
+												"type": "array",
+												"description": "Hash based message authentication codes",
+												"default": []
+											},
+											"compress": {
+												"type": "array",
+												"description": "Compresses",
+												"default": []
+											}
+										}
 									}
 								}
 							}
diff --git a/src/backend/backend.ts b/src/backend/backend.ts
--- a/src/backend/backend.ts
+++ b/src/backend/backend.ts
@@ -38,6 +38,14 @@ export interface RegisterValue {
 	value: string;
 }
 
+export interface SSHAlgorithms {
+    kex?: Array<any>;
+    cipher?: Array<any>;
+    serverHostKey?: Array<any>;
+    hmac?: Array<any>;
+    compress?: Array<any>;
+}
+
 export interface SSHArguments {
 	forwardX11: boolean;
 	host: string;
@@ -53,6 +61,7 @@ export interface SSHArguments {
 	bootstrap: string;
 	sourceFileMap: { [index: string]: string };
 	forceIPAddressFamily?: string;
+	algorithms?: SSHAlgorithms;
 }
 
 export interface IBackend {
diff --git a/src/backend/mi2/mi2.ts b/src/backend/mi2/mi2.ts
--- a/src/backend/mi2/mi2.ts
+++ b/src/backend/mi2/mi2.ts
@@ -148,6 +148,9 @@ export class MI2 extends EventEmitter implements IBackend {
 					this.sshArgs.forceIPv6 = true;
 				}
 			}
+			if (args.algorithms) {
+				this.sshArgs.algorithms = args.algorithms;
+			}
 			this.sshClient = new SSHClient(this.sshArgs);
 
 			this.sshConn.on("ready", () => {
diff --git a/src/frontend/extension.ts b/src/frontend/extension.ts
--- a/src/frontend/extension.ts
+++ b/src/frontend/extension.ts
@@ -291,6 +291,9 @@ function selectProcess(settings?: any): Promise<String> {
 					sshArgs.forceIPv6 = true;
 				}
 			}
+			if (sshSettings.algorithms) {
+				sshArgs.algorithms = sshSettings.algorithms;
+			}
 
 			const client: SSHClient = new SSHClient(sshArgs);
 			let succeededCheckOS = false;
diff --git a/src/utils/ssh.ts b/src/utils/ssh.ts
--- a/src/utils/ssh.ts
+++ b/src/utils/ssh.ts
@@ -1,6 +1,14 @@
 import { Client, ClientChannel } from "ssh2";
 import { ExecCommandResult } from "./process";
 
+export interface SSHConnectionAlgorithms {
+	kex?: Array<any>;
+	cipher?: Array<any>;
+	serverHostKey?: Array<any>;
+	hmac?: Array<any>;
+	compress?: Array<any>;
+}
+
 export interface SSHConnectionArgs {
 	host: string;
 	port: number;
@@ -10,6 +18,7 @@ export interface SSHConnectionArgs {
 	agent?: string;
 	forceIPv4?: boolean;
 	forceIPv6?: boolean;
+	algorithms?: SSHConnectionAlgorithms;
 }
 
 export class SSHClient {

launch.jsonでの設定方法

SSH接続方式についてはssh: algorithmsで指定する。
またNetBSD2.0固有の問題なのか不明だが、デバッグ対象のプロセスにデバッグ情報が含まれていてもアタッチ時に自動でそれを読み込んでくれない。
代わりに、デバッガの引数に--symbolsでファイルパスを指定すると読み込めるようになるので、これをlaunch.jsonで指定する(SSHの場合はリモート上のファイルパス)。

launch.json
        {
            "type": "gdb",
            "request": "attach",
            "name": "Attach Program netbsd2 (SSH)",
            "target": "${command:selectAttachProcess}",
            "cwd": "${workspaceRoot}",
            "showDevDebugOutput": false,
            "printCalls": true,
            "trace": false,
            "skipCheckDebugger": true,
            "suppressAttachFailure": false,
            "withoutAsync": true,
            "ssh": {
                "host": "localhost",
                "port": 44444,
                "cwd": "/root/sample",
                "user": "root",
                "password": "root",
                "forwardX11": false,
                "sourceFileMap": {
                    "/root/sample": "C:\\ws\\sample"
                },
                "forceIPAddressFamily": "IPv4",
                "algorithms": {
                    "kex": [
                        "diffie-hellman-group-exchange-sha1",
                        "diffie-hellman-group1-sha1"
                    ],
                    "cipher": [
                        "aes128-cbc",
                        "3des-cbc",
                        "blowfish-cbc",
                        "cast128-cbc",
                        "arcfour",
                        "aes192-cbc",
                        "aes256-cbc"
                    ]
                }
            },
            "valuesFormatting": "parseText",
            "gdbpath": "/usr/local/bin/gdb",
            "debugger_args": ["--symbols", "/root/sample/sample"]
        },

アタッチの実行例

Running /usr/local/bin/gdb over ssh...
1-list-features
2-environment-directory "/root/sample"
3-environment-cd "/root/sample"
4-target-attach 105
Reading symbols from /root/sample/sample...
done.
5-thread-info
6-stack-info-depth --thread 1
7-stack-list-frames --thread 1 0 1
8-thread-info
9-exec-continue
10-thread-info
11-stack-list-variables --thread 1 --frame 1 --simple-values
kill -INT 105
12-exec-continue
13-thread-info
kill -INT 105
14-break-insert -f "/root/sample/sample.c:9"
15-thread-info
16-stack-info-depth --thread 1
17-exec-continue
18-stack-info-depth --thread 1
19-stack-list-frames --thread 1 0 1
20-stack-list-frames --thread 1 0 0
21-stack-info-depth --thread 1
22-thread-info
23-stack-list-frames --thread 1 0 0
24-stack-info-depth --thread 1
25-stack-list-frames --thread 1 0 0
26-stack-list-variables --thread 1 --frame 0 --simple-values
27-exec-continue
kill -INT 105
28-target-detach
29-thread-info

参考

NetBSD6.0.1対応版(前回記事)

Discussion