android通过c调用shmat函数,编译可在Android上运行的qemu user mode
編譯可在Android上運行的qemu user mode
@(Android研究)[android|qemu]
[TOC]
前言
本文在Ubuntu 64位系統上對qemu項目進行交叉編譯,并且只編譯與qemu user mode有關的代碼。
下文中的"NDK"若無特殊說明均指"Android NDK"。
下文中"$NDK"表示的是NDK的根目錄。
步驟
1. 下載并安裝Android NDK
下載并安裝Android NDK的過程在這里不做介紹。
2. 下載qemu
3. 設置NDK工具的環境變量
4. 編譯依賴庫
glib
libpng12
5. 創建pkg-config的軟鏈接
ln -s /usr/bin/pkg-config $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-pkg-config
ln命令中的源路徑是pkg-config工具的源路徑。
如果不創建這個軟鏈接,當執行configure腳本時會報下面的錯誤:
ERROR: pkg-config binary 'arm-linux-androideabi-pkg-config' not found
6. 修改configure
添加arm的PIE支持
找到下面的代碼:
if test "$pie" = ""; then
case "$cpu-$targetos" in
i386-Linux|x86_64-Linux|x32-Linux|i386-OpenBSD|x86_64-OpenBSD)
;;
*)
pie="no"
;;
esac
fi
將"i386-Linux|x86_64-Linux|x32-Linux|i386-OpenBSD|x86_64-OpenBSD"更改為"i386-Linux|x86_64-Linux|x32-Linux|i386-OpenBSD|x86_64-OpenBSD|arm-Linux"。
如果不這么做的后果 使用"readelf -S qemu-arm"查看編譯出來的qemu-arm可執行文件的段,可以發現所有在運行時可加載段的地址均以0x60000000為基址。
在configure中有這么一段代碼:
# Probe for the need for relocating the user-only binary.
if test "$pie" = "no" ; then
textseg_addr=
case "$cpu" in
arm | i386 | ppc* | s390* | sparc* | x86_64 | x32)
# ??? Rationale for choosing this address
textseg_addr=0x60000000
;;
mips)
# A 256M aligned address, high in the address space, with enough
# room for the code_gen_buffer above it before the stack.
textseg_addr=0x60000000
;;
esac
if [ -n "$textseg_addr" ]; then
cat > $TMPC <
int main(void) { return 0; }
EOF
textseg_ldflags="-Wl,-Ttext-segment=$textseg_addr"
......
fi
如果$pie等于"no",textseg_addr的值將為0x60000000,textseg_ldflags將會設置"-Wl,-Ttext-segment=$textseg_addr"這個命令行選項,這個命令行選項指定text段的基址。在腳本的后面textseg_ldflags會被添加到ldflags中。
如果qemu-arm可加載段的基址為0x60000000,當qemu-arm在Android設備上運行時將會發生"Segmentation fault",詳情請參考Android上可執行ELF文件中的段不能有基址。
7. 運行configure
PKG_CONFIG_PATH="$SYSROOT/usr/lib/pkgconfig" ./configure --prefix="$SYSROOT/usr" --target-list=arm-linux-user --disable-system --disable-bsd-user --disable-tools --disable-zlib-test --cross-prefix="arm-linux-androideabi-" --cc="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc" --host-cc="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc" --cpu="arm" --cxx="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++" --extra-ldflags="-fPIE -pie --sysroot $SYSROOT -L$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9/ -L$NDK/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi/ -L$NDK/platforms/android-21/arch-arm/usr/lib/" --extra-cflags="-fPIE -pie --sysroot $SYSROOT -I$NDK/sources/cxx-stl/gnu-libstdc++/4.9/include -I$NDK/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi/include -L$NDK/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi/" --disable-guest-agent
命令行解析
configure腳本會在終端輸出一些關鍵的信息,如:用什么編譯器,flags等。
PKG_CONFIG_PATH
上面命令中的PKG_CONFIG_PATH="$SYSROOT/usr/lib/pkgconfig"是必要的,如果不設置這個宏,configure腳本輸出"CFLAGS"的內容見下:
CFLAGS -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -pthread -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -g
關注"-I"后的路徑,首先說一下這個路徑是怎么來的,configure腳本中有下面的代碼:
glib_req_ver=2.22
glib_modules=gthread-2.0
if test "$modules" = yes; then
glib_modules="$glib_modules gmodule-2.0"
fi
for i in $glib_modules; do
if $pkg_config --atleast-version=$glib_req_ver $i; then
glib_cflags=`$pkg_config --cflags $i`
glib_libs=`$pkg_config --libs $i`
CFLAGS="$glib_cflags $CFLAGS"
LIBS="$glib_libs $LIBS"
libs_qga="$glib_libs $libs_qga"
else
error_exit "glib-$glib_req_ver $i is required to compile QEMU"
fi
done
"glib_cflags=$pkg_config --cflags $i"語句會獲得glib的包含目錄,看這篇文章的人如果電腦上安裝有glib2.0可以通過這個命令進行查看輸出內容:pkg-config --cflags glib-2.0。
然而這個路徑并不是我想要的,因為我現在是交叉編譯,目標是ARM,所以我在這里將一個新的pkgconfig目錄路徑設置到PKG_CONFIG_PATH宏,輸入下面的命令查看輸出內容:
PKG_CONFIG_PATH="$SYSROOT/usr/lib/pkgconfig" pkg-config --cflags glib-2.0
輸出內容:
-I/home/sowuy/Tools/android-ndk-r11b/platforms/android-21/arch-arm/usr/include/glib-2.0 -I/home/sowuy/Tools/android-ndk-r11b/platforms/android-21/arch-arm/usr/lib/glib-2.0/include -I/home/sowuy/Tools/android-ndk-r11b/platforms/android-21/arch-arm/usr/include
會發現此時"-I"后的路徑有了改變。
注意:pkgconfig是一個目錄,在這個目錄中包含了步驟5中安裝的依賴庫的信息。
--target-list --cpu
--target-list arm-linux-user 意味著編譯出來的qemu程序用于user mode,可以執行arm指令,并且這個arm指令的可執行程序的執行環境基于linux系統。 --cpu=arm 意味著編譯出的qemu程序只能在arm機器上執行。
--disable-system --disable-bsd-user
--disable-system:不編譯system mode的代碼。 --disable-bsd-user:不編譯bsd user mode的代碼。
--cross-prefix
交叉編譯工具的前綴,在當前命令行中它的值為"arm-linux-androideabi-",那么configure腳本會去查找名為arm-linux-androideabi-gcc、arm-linux-androideabi-g++等工具。
--disable-tools
當命令行中有--disable-tools選項時,腳本中的禁用want_tools宏將被設置為"no",這個宏默認為"yes"。當want_tools宏為"yes"時,會對tools宏進行設置,下面是與want_tools有關的設置tools宏的代碼:
tools=""
if test "$want_tools" = "yes" ; then
tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
tools="qemu-nbd\$(EXESUF) $tools"
tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools"
fi
fi
configure腳本會將tools宏的內容寫入config-host.mak文件。
--disable-guest-agent
當沒有這個選項時,編譯會報下面的錯誤:
qga/main.c:327: error: undefined reference to 'lockf'
collect2: error: ld returned 1 exit status
make: *** [qemu-ga] Error 1
為PC編譯qemu項目沒有這個命令選項時不會報這個錯誤,然而lockf函數在Android上并不存在,所以為Android編譯qemu項目時會報這個錯誤。
編譯錯誤排除
ld: error: cannot find -lutil
將根目錄下的Makefile文件中下面的內容注釋:
ifneq ($(wildcard config-host.mak),)
include $(SRC_PATH)/tests/Makefile
endif
ifaddrs.h: No such file or directory
錯誤信息
qga/commands-posix.c:45:21: fatal error: ifaddrs.h: No such file or directory
#include
^
compilation terminated.
make: *** [qga/commands-posix.o] Error 1
修復辦法
將這個鏈接中的源文件都下載下來:android-ifaddrs,將下載下來的文件拷貝到qga/目錄下。然后找到qga/Makefile.objs文件,將"ifaddrs.o"插入"qga-obj-$(CONFIG_POSIX)"宏中。
mqueue.h: No such file or directory
錯誤信息
qemu-2.5.0/linux-user/syscall.c:964:20: fatal error: mqueue.h: No such file or directory
#include
^
compilation terminated.
make[1]: *** [linux-user/syscall.o] Error 1
make: *** [subdir-arm-linux-user] Error 2
修復辦法
將"#include "更改為"#include "。
char __unused[128 - sizeof(target_sigset_t)];
錯誤信息
qemu-2.5.0/linux-user/signal.c:1452:18: error: expected identifier or '(' before '[' token
char __unused[128 - sizeof(target_sigset_t)];
^
make[1]: *** [linux-user/signal.o] Error 1
make: *** [subdir-arm-linux-user] Error 2
修復辦法
將__unused更改為_unused。
syscall.c:4108:9: error: dereferencing pointer to incomplete type
錯誤信息
qemu-2.5.0/linux-user/syscall.c:4108:9: error: dereferencing pointer to incomplete type
host->c_iflag =
^
修復辦法
將
#define termios host_termios
改為
#ifdef __ANDROID__
#define host_termios termios
#else
#define termios host_termios
#endif
disas/arm-a64.cc:67: error: undefined reference to '__cxa_end_cleanup'
錯誤信息
disas/arm-a64.cc:67: error: undefined reference to '__cxa_end_cleanup'
../disas/arm-a64.o(.ARM.extab+0x0): error: undefined reference to '__gxx_personality_v0'
../disas/arm-a64.o:arm-a64.cc:typeinfo for QEMUDisassembler: error: undefined reference to 'vtable for __cxxabiv1::__si_class_type_info'
/home/sowuy/Tools/android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: the vtable symbol may be undefined because the class is missing its key function (see go/missingkeymethod)
/home/sowuy/Tools/android-ndk-r11b/sources/cxx-stl/gnu-libstdc++/4.9/include/bits/list.tcc:106: error: undefined reference to 'std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*)'
/home/sowuy/Tools/android-ndk-r11b/sources/cxx-stl/gnu-libstdc++/4.9/include/bits/stl_list.h:1681: error: undefined reference to 'std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*)'
/home/sowuy/Tools/android-ndk-r11b/sources/cxx-stl/gnu-libstdc++/4.9/include/bits/stl_list.h:1681: error: undefined reference to 'std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*)'
/home/sowuy/Tools/android-ndk-r11b/sources/cxx-stl/gnu-libstdc++/4.9/include/bits/stl_list.h:1681: error: undefined reference to 'std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*)'
/home/sowuy/Tools/android-ndk-r11b/sources/cxx-stl/gnu-libstdc++/4.9/include/bits/stl_list.h:1697: error: undefined reference to 'std::__detail::_List_node_base::_M_unhook()'
/home/sowuy/Tools/android-ndk-r11b/sources/cxx-stl/gnu-libstdc++/4.9/include/bits/stl_list.h:1697: error: undefined reference to 'std::__detail::_List_node_base::_M_unhook()'
../disas/libvixl/a64/disasm-a64.o:disasm-a64.cc:typeinfo for vixl::DecoderVisitor: error: undefined reference to 'vtable for __cxxabiv1::__class_type_info'
/home/sowuy/Tools/android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: the vtable symbol may be undefined because the class is missing its key function (see go/missingkeymethod)
../disas/libvixl/a64/disasm-a64.o:disasm-a64.cc:typeinfo for vixl::Disassembler: error: undefined reference to 'vtable for __cxxabiv1::__si_class_type_info'
/home/sowuy/Tools/android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: the vtable symbol may be undefined because the class is missing its key function (see go/missingkeymethod)
../disas/libvixl/a64/disasm-a64.o:disasm-a64.cc:typeinfo for vixl::PrintDisassembler: error: undefined reference to 'vtable for __cxxabiv1::__si_class_type_info'
/home/sowuy/Tools/android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: the vtable symbol may be undefined because the class is missing its key function (see go/missingkeymethod)
collect2: error: ld returned 1 exit status
make[1]: *** [qemu-arm] Error 1
make: *** [subdir-arm-linux-user] Error 2
解決辦法 在configure中找到下面的代碼:
arm)
disas_config "ARM"
if test -n "${cxx}"; then
disas_config "ARM_A64"
fi
;;
將這些代碼注釋掉:
if test -n "${cxx}"; then
disas_config "ARM_A64"
fi
原因分析 目前在Android NDK中沒有64位版本的object。
syscall.c中找不到符號
錯誤信息
LINK arm-linux-user/qemu-arm
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'setdomainname'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmget'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmdt'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmat'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'mq_timedsend'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'mq_unlink'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'mq_open'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'mq_setattr'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'mq_timedreceive'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgsnd'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'semget'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'semop'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgget'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgrcv'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'stime'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'sigorset'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'vhangup'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'sigtimedwait'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'sethostname'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'semctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'semctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'semctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'semctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmget'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmdt'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgget'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'semget'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgrcv'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'semop'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgsnd'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'futimesat'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'mq_send'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'mq_receive'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'mq_getattr'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgctl'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmat'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'msgrcv'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmat'
linux-user/syscall.o:syscall.c:function do_syscall: error: undefined reference to 'shmat'
collect2: error: ld returned 1 exit status
make[1]: *** [qemu-arm] Error 1
make: *** [subdir-arm-linux-user] Error 2
解決辦法 在syscall.c文件中寫下面的內容:
#ifdef __ANDROID__
int setdomainname(const char *name, size_t len) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int shmget(key_t key, size_t size, int shmflg) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int shmdt(const void *shmaddr) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int shmctl(int shmid, int cmd, struct shmid_ds *buf) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
void *shmat(int shmid, const void *shmaddr, int shmflg) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int msgctl(int msqid, int cmd, struct msqid_ds *buf) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int mq_timedsend(unsigned long mqdes, const char *msg_ptr,
size_t msg_len, unsigned msg_prio,
const struct timespec *abs_timeout) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int mq_unlink(const char *name) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
unsigned long mq_open(const char *name, int oflag, mode_t mode,
struct mq_attr *attr) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int mq_setattr(unsigned long mqdes, const struct mq_attr *newattr,
struct mq_attr *oldattr) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
ssize_t mq_timedreceive(unsigned long mqdes, char *msg_ptr,
size_t msg_len, unsigned *msg_prio,
const struct timespec *abs_timeout) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int semget(key_t key, int nsems, int semflg) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int semop(int semid, struct sembuf *sops, size_t nsops) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int msgget(key_t key, int msgflg) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int stime(const time_t *t) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int sigorset(sigset_t * set, const sigset_t * left, const sigset_t * right) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int vhangup(void) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int sigtimedwait(const sigset_t *set, siginfo_t *info,
const struct timespec *timeout) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int sethostname(const char *name, size_t len) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int semctl(int semid, int semnum, int cmd, ...) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int futimesat(int dirfd, const char *pathname,
const struct timeval times[2]) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int mq_send(unsigned long mqdes, const char *msg_ptr,
size_t msg_len, unsigned int msg_prio) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
ssize_t mq_receive(unsigned long mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
int mq_getattr(unsigned long mqdes, struct mq_attr *attr) {
printf("[-] %s(%d)-%s\n",__FILE__,__LINE__,__FUNCTION__);
assert(0);
}
#endif
編譯清理命令
執行下面兩個命令:
make clean
make distclean
編譯debug版
調用configure腳本的命令行中添加"--enable-debug"命令選項。
總結
以上是生活随笔為你收集整理的android通过c调用shmat函数,编译可在Android上运行的qemu user mode的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微证券开户有什么风险
- 下一篇: 个人如何购买黄金,有以下两种方法