BitVisor
BitVisorDay 13

macでdbgsh / dbgshに機能を追加

More than 1 year has passed since last update.

2018-1-4 追記

最近気づきましたが昨年ここに書いてあるdbgshの問題が修正されたようです (当該コミット).

従って今のbitvisorでは下記のような修正はいらないはずです.

またこれは別件ですがUEFIブート時に必要であればファームウェアのドライバをアンロードする修正も入りました(当該コミット).これによりmacでbnx + virtioドライバを利用する際わざわざefi shellからfirmwareのドライバをアンロードしなくてよくなりました(便利).


macでdbgsh

(本当は先週の投稿でここまで書くはずだったのが,力尽きて倒れてしまったのでここに書きますm(_ _)m)

macでdbgshは動くのでしょうか.早速試してみましょう.

(OS: macOS Sierra 10.12.1, コンパイラ: Apple LLVM version 8.0.0 (clang-800.0.42.1))

普通にdbgshをmakeしようとするとmingwがないと怒られてしまうので,直接コンパイルコマンドを打ち込んでみます.

% cd bitvisor/tools/dbgsh

% cc -o dbgsh dbgsh.c ../common/call_vmm.c
fatal error: error in backend: 32-bit absolute addressing is not supported in 64-bit mode
fatal error: error in backend: 32-bit absolute addressing is not supported in 64-bit mode

...何やら怒られてしまいました.homebrewで入れたgccでも試してみます.

% gcc-6  -s -o dbgsh dbgsh.c ../common/call_vmm.c

/var/folders/wq/nk3z_f6s6kb5chkssxy5n8sc0000gn/T//ccZ9UxUo.s:42:4: error: 32-bit absolute addressing is not supported in 64-bit mode
2: mov $0b,%rdx; mov $1b,%rsi
^
/var/folders/wq/nk3z_f6s6kb5chkssxy5n8sc0000gn/T//ccZ9UxUo.s:42:18: error: 32-bit absolute addressing is not supported in 64-bit mode
2: mov $0b,%rdx; mov $1b,%rsi
^
/var/folders/wq/nk3z_f6s6kb5chkssxy5n8sc0000gn/T//ccZ9UxUo.s:96:2: error: 32-bit absolute addressing is not supported in 64-bit mode
mov $1f,%rsi
^

同じエラーですがこちらでは親切にどこが問題かを示してくれています.

この "32-bit absolute addressing is not supported in 64-bit mode" というエラーですが,どうやら64bitのMach-Oでは32bit absoluteの再配置ができないという仕様が原因のようです.(たぶんELFのR_X86_64_32相当のものがない).

アセンブラyasmのソースには以下のような記述があります.

https://github.com/yasm/yasm/blob/master/modules/objfmts/macho/macho-objfmt.c#L50

  2.2) data referencing in 64 bit mode

While ELF allows 32 bit absolute relocations in 64 bit mode, Mach-O
does not. Therefore code like
lea rbx,[_foo] ;48 8d 1c 25 00 00 00 00
mov rcx,[_bar] ;48 8b 0c 25 00 00 00 00
with a 32 bit address field cannot be relocated into an address >= 0x100000000 (OSX actually
uses that).

Actually, the only register where a 64 bit displacement is allowed in x86-64, is rax
as in the example 1).
A plausible workaround is either classic PIC (like in C), which is in turn
not implemented in this object format. The recommended was is PC relative
code (called RIP-relative in x86-64). So instead of the lines above, just write:
lea rbx,[_foo wrt rip]
mov rcx,[_bar wrt rip]

そこでrip相対を使うようにコードを書き換えてみます.

diff -r 76db0ae4260b tools/common/call_vmm.c

--- a/tools/common/call_vmm.c Mon Sep 12 20:01:54 2016 +0900
+++ b/tools/common/call_vmm.c Sat Dec 10 02:41:50 2016 +0900
@@ -78,7 +78,7 @@
intptr_t tmp;

p = data;
- asm volatile ("mov $1f,%1\n"
+ asm volatile ("lea 1f(%%rip),%1\n"
"jmp *%4\n"
"1:"
: "=a" (p->ret), "=&S" (tmp)

diff -r 76db0ae4260b tools/common/call_vmm.h
--- a/tools/common/call_vmm.h Mon Sep 12 20:01:54 2016 +0900
+++ b/tools/common/call_vmm.h Sat Dec 10 02:42:14 2016 +0900
@@ -7,7 +7,7 @@
" .org 0b+10; .string \"" name "\"\n" \
"1: vmcall; jmp *%1; .org 1b+5; vmmcall; jmp *%1;" \
" .org 1b+10; .string \"" name "\"\n" \
- "2: mov $0b,%0; mov $1b,%1" \
+ "2: lea 0b(%%rip),%0; lea 1b(%%rip),%1" \
: "=r" (call_vmm__addr0), "=S" (call_vmm__addr1)); \
call_vmm_get_function (call_vmm__addr0, call_vmm__addr1, \
5, 10, (ret)); \

% cc -o dbgsh dbgsh.c ../common/call_vmm.c

問題なくコンパイルが完了しました.早速試してみましょう.

% ./dbgsh

> log
Starting BitVisor...
Copyright (c) 2007, 2008 University of Tsukuba
All rights reserved.
ACPI DMAR found.
FACS address 0x8AD1C000
Module not found.
Processor 0 (BSP)
.................................................. oooooooooooooooooooooooooooooooooooooooooooooooooo
Using VMX.
Processor 0 2400020992 Hz (Invariant TSC)
Loading drivers.
AES/AES-XTS Encryption Engine initialized (AES=openssl)
Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
Generic ATA/ATAPI para pass-through driver 0.4 registered
Generic AHCI para pass-through driver registered
Generic RAID para pass-through driver registered
Generic IEEE1394 para pass-through driver 0.1 registered
Broadcom NetXtreme Gigabit Ethernet Driver registered
VPN for Intel PRO/100 registered
Intel PRO/1000 driver registered
PCI device concealer registered
PCI device monitor registered
Generic EHCI para pass-through driver 0.9 registered
Generic EHCI para pass-through driver 0.9 registered
Generic UHCI para pass-through driver 1.0 registered
Intel Corporation Ethernet Controller 10 Gigabit X540 Driver registered
PCI: finding devices...
PCI: 23 devices found
MCFG [0] 0000:00-9B (E0000000,9C00000)
Starting a virtual machine.
Processor 1 (AP)
Processor 2 (AP)
Processor 3 (AP)
Processor 1 2400035856 Hz (Invariant TSC)
Processor 3 2400032256 Hz (Invariant TSC)
Processor 2 2400033120 Hz (Invariant TSC)

ひとまず無事動いているようです\(^^)/

ちなみに,CALL_VMM_GET_FUNCTIONが何をしているかは,以下に解説があります.

http://qiita.com/hdk_2/items/c8c47ac91ab59b75549c#%E9%AB%98%E3%83%AC%E3%83%99%E3%83%ABapi%E5%AE%9F%E8%A3%85


dbgshに機能を追加

これだけだと味気がないので,dbgshに簡単な機能を追加してみましょう.

VMMコールを追加するよりも,場合によっては有用なことがあるかもしれません.

dbgshの各種コマンド(とdbgsh本体)は全てBitVisorのプロセスとして実現されています.ソースはprocess/以下にあります.

簡単なコマンドを追加してみます.process/myprocess.c として以下のファイルを作成します.

#include <lib_lineinput.h>

#include <lib_printf.h>
#include <lib_string.h>
#include <lib_syscalls.h>

int
_start (int a1, int a2)
{
printf("myprocess\n");
exitprocess (0);
return 0;
}

process/Makefileも修正します.

diff -r cf65f2b45794 process/Makefile

--- a/process/Makefile Wed Apr 06 14:06:39 2016 +0900
+++ b/process/Makefile Sun Dec 11 15:43:30 2016 +0900
@@ -8,6 +8,7 @@
asubdirs-1 += lib
bins-$(CONFIG_IP) += echoctl
bins-$(CONFIG_PCI_MONITOR) += monitor
+bins-1 += myprocess

debug-objs = debug.o
help-objs = help.o
@@ -27,3 +28,5 @@
vpn-libs = vpn/lib/$(outa) crypto/$(outa)
echoctl-objs = echoctl.o
monitor-objs = monitor.o
+myprocess-objs = myprocess.o

これでコンパイルしてBitVisorを起動すれば,dbgshからmyprocessコマンドが使えるようになります.(dbgshでhelpしたときに作成したコマンドの情報を表示させたい場合は,process/help.cを編集します)

% ./dbgsh

> myprocess
myprocess
>

(ただ"myprocess"とprintして終了します).

プロセスはBitVisorの保護ドメイン上で動作します.

プロセスのエントリポイントは_start()関数になります._start()は引数を2つ受け取りますが,これらは通常使用しません(内部的にプロセスの起動が後述するmsgsendint()ハンドラの呼び出しとして実現されているため引数があります).

dbgshで実行したプロセス内でprintした場合,結果はlogには出力されず,dbgshの方に出力されます.

プロセス内で利用できるライブラリ関数がprocess/lib以下に存在します.

さて,このようにしてプロセスが作成できますが,これだけだとBitVisor本体とやりとりできないのであまり嬉しくありません.BitVisor本体や他のプロセスとのデータのやりとりはメッセージでおこないます.

基本的なメッセージの使い方は,


  • 受信側: msgregister(name, func) でメッセージ名と対応するコールバック関数を追加

  • 送信側: msgopen(name) した後 msgsendint()msgsendbuf()でデータを送信 (コールバック関数が呼ばれる)

となります,

以下にmsgsendint()を使った簡単な例を示します.

BitVisorでtty出力をONにすると,logflagという変数でputchar()の結果をttyの他にlogに出力するかどうか判断しているので,これをdbgshから変更できるようにします.

core/tty.c を編集します.

diff -r cf65f2b45794 core/tty.c

--- a/core/tty.c Wed Apr 06 14:06:39 2016 +0900
+++ b/core/tty.c Sun Dec 11 15:43:30 2016 +0900
@@ -109,6 +109,39 @@
logflag = false;
}

+void
+ttylog_start (void)
+{
+ logflag = true;
+}
+
+enum ttyctl_command {
+ TTY_CMD_LOG_START = 0,
+ TTY_CMD_LOG_STOP = 1,
+};
+
+static int
+ttyctl_msghandler (int m, int c)
+{
+ printf("ttyctl_msghandler: %d, %d\n",m,c);
+ if (m == MSG_INT){
+ switch (c) {
+ case TTY_CMD_LOG_START:
+ printf("tty log start\n");
+ ttylog_start();
+ break;
+ case TTY_CMD_LOG_STOP:
+ printf("tty log stop\n");
+ ttylog_stop();
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+
static void
wshort (char *off, unsigned short x)
{
@@ -393,6 +426,7 @@
msgregister ("ttyin", ttyin_msghandler);
msgregister ("ttyout", ttyout_msghandler);
msgregister ("ttylog", ttylog_msghandler);
+ msgregister ("ttyctl", ttyctl_msghandler);
}

process/ttyctl.cとして以下を作成します.(Makefileも適宜修正)

#include <lib_lineinput.h>

#include <lib_printf.h>
#include <lib_stdlib.h>
#include <lib_string.h>
#include <lib_syscalls.h>

int
_start (int a1, int a2)
{
char buf[100];
int d;

d = msgopen ("ttyctl");
if (d < 0) {
printf ("ttyctl not found.\n");
exitprocess (1);
}
for (;;) {
printf ("ttyctl> ");
lineinput (buf, 100);
if (!strcmp (buf, "exit"))
break;
if (!strcmp (buf, "log_start")){
msgsendint(d, 0);
continue;
}
if (!strcmp (buf, "log_stop")){
msgsendint(d, 1);
continue;
}
printf("command not found.\n");
}
msgclose (d);
exitprocess (0);
return 0;
}

実行例

% ./dbgsh

> ttyctl
ttyctl> log_stop // msgsendint()が実行される
ttyctl> exit

このような感じでmsgsendint()で数値を送信することができます.

実をいうと,dbgshには特定のメッセージに対してmsgsendintするコマンドsendintがあるので,わざわざttyctlを作成しなくてもなんとかなります.

% ./dbgsh

> sendint
sendint> ttyctl 1 // d = msgopen("ttyctl"); msgsendint(d, 1); と同じ

これを使えば簡単に登録したBitVisor内の関数を呼び出すことができます.

バッファを送信したい場合はmsgsendbuf()が使えます.

process/{recvexample,sendexample}.c にmsgsendbufを使った例があります.

% ./dbgsh

> recvexample
recvex registered -1
> sendexample
sendexample> h
s data : send
q : quit
sendexample> s hoge
sending buf 0x3fffef8c len 0x6 recvbuf 0x3fffef40 len 0x20
***recvexample start***
recvbuf 0x2fffff8c recvlen 0x6
sendbuf 0x2fffef40 sendlen 0x20
recvbuf: 0x73 0x20 0x68 0x6f 0x67 0x65
sendbuf: 0x74 0x21 0x69 0x70 0x68 0x66 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
***recvexample end***
received: 0x74 0x21 0x69 0x70 0x68 0x66 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
sendexample>