0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

riscv64裸機(jī)編程實(shí)踐與分析

嵌入式IoT ? 來(lái)源:嵌入式IoT ? 作者:嵌入式IoT ? 2020-12-31 10:54 ? 次閱讀

riscv64 裸機(jī)編程實(shí)踐與分析

  • 1.概述

  • 2.最小工程的構(gòu)成

  • 3. 鏈接腳本

  • 4.可執(zhí)行的程序源代碼分析

  • 5.編譯與運(yùn)行

    • 5.1 編譯

    • 5.2 運(yùn)行

    • 5.3 調(diào)試

  • 6.總結(jié)

1.概述

任何芯片在啟動(dòng)之前都需要有一段匯編代碼,從這段匯編代碼上就可以體現(xiàn)一些架構(gòu)設(shè)計(jì)的特點(diǎn)。往往做嵌入式底層開(kāi)發(fā)都需要關(guān)注這段匯編代碼的含義,這樣在使用的時(shí)候才能全面的了解啟動(dòng)時(shí)做了什么事情,在后續(xù)的程序中遇到問(wèn)題也能復(fù)盤推演。

本文就針對(duì)riscv64的最開(kāi)始的啟動(dòng)部分代碼進(jìn)行分析,從最小的一個(gè)裸機(jī)代碼開(kāi)始分析,徹底的弄清楚riscv啟動(dòng)的流程。

本次使用的環(huán)境是riscv64 qemu,而編譯器是通過(guò)下面的地址進(jìn)行下載

https://www.sifive.com/software

2.最小工程的構(gòu)成

一個(gè)最小的工程包含兩個(gè)東西:鏈接腳本以及源代碼。

源代碼就是可以讓cpu執(zhí)行的代碼,通過(guò)交叉編譯工具鏈編譯生成可執(zhí)行的二進(jìn)制程序。

鏈接腳本文件則可以告訴程序的布局,比如代碼段,函數(shù)的入口等等。有了這兩個(gè)文件將編譯出來(lái)的程序loader到板子上運(yùn)行即可。

3. 鏈接腳本

下面看一下hello.ld文件。

OUTPUT_ARCH("riscv")
OUTPUT_FORMAT("elf64-littleriscv")
ENTRY(_start)
SECTIONS
{
/*text:testcodesection*/
.=0x80000000;
.text:{*(.text)}
/*data:Initializeddatasegment*/
.gnu_build_id:{*(.note.gnu.build-id)}
.data:{*(.data)}
.rodata:{*(.rodata)}
.sdata:{*(.sdata)}
.debug:{*(.debug)}
.+=0x8000;
stack_top=.;

/*Endofuninitalizeddatasegement*/
_end=.;
}

對(duì)于鏈接腳本(linker script),往往都是規(guī)定如何把輸入的文件按照特定的地址放到內(nèi)存中。

其中就上面的腳本而言:

OUTPUT_ARCH("riscv"):表示輸入文件的架構(gòu)是riscv。

OUTPUT_FORMAT("elf64-littleriscv"):表示elf64小端。一般arm,riscv,x86都是小端,小端是比較主流的。

ENTRY( _start ):表示函數(shù)入口是_start。

然后開(kāi)始進(jìn)行代碼段的布局,起始地址開(kāi)始處為0x80000000。然后依次放代碼段、數(shù)據(jù)段、只讀數(shù)據(jù)段、全局?jǐn)?shù)據(jù)段,debug段等等。

這里需要注意:

.+=0x8000;
stack_top=.;

這里說(shuō)明,棧頂預(yù)留了0x8000個(gè)字節(jié)空間作為程序的??臻g,因?yàn)闂J窍蛏显鲩L(zhǎng)的,所以這里預(yù)留了一些??臻g。

通過(guò)反匯編來(lái)查看生成程序的布局情況

#riscv64-unknown-elf-objdump-dhello

hello:fileformatelf64-littleriscv


Disassemblyofsection.text:

0000000080000000<_start>:
80000000:f14022f3csrrt0,mhartid
80000004:00029c63bnezt0,8000001c
80000008:00008117auipcsp,0x8
8000000c:04410113addisp,sp,68#8000804c<_end>
80000010:00000517auipca0,0x0
80000014:03450513addia0,a0,52#80000044
80000018:008000efjalra,80000020

000000008000001c:
8000001c:0000006fj8000001c

0000000080000020:
80000020:100102b7luit0,0x10010
80000024:00054303lbut1,0(a0)
80000028:00030c63beqzt1,80000040
8000002c:0002a383lwt2,0(t0)#10010000
80000030:fe03cee3bltzt2,8000002c
80000034:0062a023swt1,0(t0)
80000038:00150513addia0,a0,1
8000003c:fe9ff06fj80000024
80000040:00008067ret

對(duì)于qemu來(lái)說(shuō),sifive_u的起始地址為0x80000000,將代碼段的入口放在此處。

4.可執(zhí)行的程序源代碼分析

前面已經(jīng)描述了鏈接腳本的布局,也就是給程序指定了執(zhí)行的地址,每個(gè)函數(shù)以及函數(shù)入口在什么地址都已經(jīng)規(guī)劃好了,那么具體的入口函數(shù)該如何寫呢?

看看hello.s編程代碼:

.align 2
.equ UART_BASE,         0x10010000
.equ UART_REG_TXFIFO,   0

.section .text
.globl _start

_start:
        csrr  t0, mhartid             # read hardware thread id (`hart` stands for `hardware thread`)
        bnez  t0, halt                   # run only on the first hardware thread (hartid == 0), halt all the other threads

        la    sp, stack_top           # setup stack pointer

        la    a0, msg                 # load address of `msg` to a0 argument register
        jal   puts                    # jump to `puts` subroutine, return address is stored in ra regster

halt:   j     halt                    # enter the infinite loop

puts:                                 # `puts` subroutine writes null-terminated string to UART (serial communication port)
                                      # input: a0 register specifies the starting address of a null-terminated string
                                      # clobbers: t0, t1, t2 temporary registers

        li    t0, UART_BASE           # t0 = UART_BASE
1:      lbu   t1, (a0)                # t1 = load unsigned byte from memory address specified by a0 register
        beqz  t1, 3f                  # break the loop, if loaded byte was null

                                      # wait until UART is ready
2:      lw    t2, UART_REG_TXFIFO(t0) # t2 = uart[UART_REG_TXFIFO]
        bltz  t2, 2b                  # t2 becomes positive once UART is ready for transmission
        sw    t1, UART_REG_TXFIFO(t0) # send byte, uart[UART_REG_TXFIFO] = t1

        addi  a0, a0, 1               # increment a0 address by 1 byte
        j     1b

3:      ret

.section .rodata
msg:
     .string "Hello.
"

根據(jù)匯編語(yǔ)言的規(guī)則

.align2

表示入口程序以2^2也就是4字節(jié)對(duì)齊。

.equUART_BASE,0x10010000
.equUART_REG_TXFIFO,0

定義了UART的寄存器的基地址。

接著主要從_start:開(kāi)始分析。

csrrt0,mhartid#readhardwarethreadid(`hart`standsfor`hardwarethread`)
bnezt0,halt#runonlyonthefirsthardwarethread(hartid==0),haltalltheotherthreads

根據(jù)riscv的設(shè)計(jì),如果一個(gè)部件包含一個(gè)獨(dú)立的取指單元,那么該部件被稱為核心(core)。

一個(gè)RiscV兼容的核心能夠通過(guò)多線程技術(shù)(或者說(shuō)超線程技術(shù))支持多個(gè)RiscV兼容硬件線程(harts),harts這兒就是指硬件線程, hardware thread的意思。

ba4f8054-4ad0-11eb-8b86-12bb97331649.png


上面的就包含一個(gè)E51的核和4個(gè)U54的核。

而這段匯編就是將其他的核掛起,只運(yùn)行hartid == 0的核。

緊接著

lasp,stack_top#setupstackpointer

這里將棧指針sp賦值,sp此時(shí)指向棧頂。

laa0,msg#loadaddressof`msg`toa0argumentregister
jalputs#jumpto`puts`subroutine,returnaddressisstoredinraregster

對(duì)于riscv 架構(gòu)來(lái)說(shuō),a0寄存器表示第一個(gè)參數(shù)賦值,接著跳轉(zhuǎn)到puts函數(shù)中。

此時(shí)傳遞過(guò)去的參數(shù)為a0,也就是

.section.rodata
msg:
.string"Hello.
"

指向一個(gè)只讀的字符串結(jié)構(gòu)的數(shù)據(jù)。

puts的實(shí)現(xiàn)

通過(guò)匯編來(lái)描述一個(gè)串口驅(qū)動(dòng)程序的編寫是比較重要的。

puts:#`puts`subroutinewritesnull-terminatedstringtoUART(serialcommunicationport)
#input:a0registerspecifiesthestartingaddressofanull-terminatedstring
#clobbers:t0,t1,t2temporaryregisters

lit0,UART_BASE#t0=UART_BASE
1:lbut1,(a0)#t1=loadunsignedbytefrommemoryaddressspecifiedbya0register
beqzt1,3f#breaktheloop,ifloadedbytewasnull

#waituntilUARTisready
2:lwt2,UART_REG_TXFIFO(t0)#t2=uart[UART_REG_TXFIFO]
bltzt2,2b#t2becomespositiveonceUARTisreadyfortransmission
swt1,UART_REG_TXFIFO(t0)#sendbyte,uart[UART_REG_TXFIFO]=t1

addia0,a0,1#incrementa0addressby1byte
j1b

3:ret

首先剛才通過(guò)a0寄存器將參數(shù)傳遞過(guò)來(lái),然后從1:開(kāi)始,讀取字符串,beqz t1, 3f表示當(dāng)t1 == 0時(shí),跳轉(zhuǎn)到3:之前。此時(shí)會(huì)跳出2:循環(huán)。

2:則是向串口FIFO送數(shù)的過(guò)程。

到這里一個(gè)字符串輸出就可以正常的執(zhí)行了。

5.編譯與運(yùn)行

5.1 編譯

上述程序分析完成會(huì),可以將其進(jìn)行編譯。

riscv64-unknown-elf-gcc-march=rv64g-mabi=lp64-static-mcmodel=medany-fvisibility=hidden-nostdlib-nostartfiles-Thello.ld-Isifive_uhello.s-ohello

上述編譯過(guò)程可以生成hello程序。

#readelf-hhello
ELFHeader:
Magic:7f454c46020101000000000000000000
Class:ELF64
Data:2'scomplement,littleendian
Version:1(current)
OS/ABI:UNIX-SystemV
ABIVersion:0
Type:EXEC(Executablefile)
Machine:RISC-V
Version:0x1
Entrypointaddress:0x80000000
Startofprogramheaders:64(bytesintofile)
Startofsectionheaders:4680(bytesintofile)
Flags:0x0
Sizeofthisheader:64(bytes)
Sizeofprogramheaders:56(bytes)
Numberofprogramheaders:1
Sizeofsectionheaders:64(bytes)
Numberofsectionheaders:7
Sectionheaderstringtableindex:6

可以分析一下gcc攜帶的參數(shù)。

-march:可以指定編譯出來(lái)的架構(gòu),比如rv32或者rv64等等。

-static:表示靜態(tài)編譯。

-mabi=lp64:數(shù)據(jù)模型和浮點(diǎn)參數(shù)傳遞規(guī)則

數(shù)據(jù)模型:

- int字長(zhǎng) long字長(zhǎng) 指針字長(zhǎng)
ilp32/ilp32f/ilp32d 32bits 32bits 32bits
lp64/lp64f/lp64d 32bits 64bits 64bits

浮點(diǎn)傳遞規(guī)則

- 需要浮點(diǎn)擴(kuò)展指令? float參數(shù) double參數(shù)
ilp32/lp64 不需要 通過(guò)整數(shù)寄存器(a0-a1)傳遞 通過(guò)整數(shù)寄存器(a0-a3)傳遞
ilp32f/lp64f 需要F擴(kuò)展 通過(guò)浮點(diǎn)寄存器(fa0-fa1)傳遞 通過(guò)整數(shù)寄存器(a0-a3)傳遞
ilp32d/lp64d 需要F擴(kuò)展和D擴(kuò)展 通過(guò)浮點(diǎn)寄存器(fa0-fa1)傳遞 通過(guò)浮點(diǎn)寄存器(fa0-fa1)傳遞

-mcmodel=medany:對(duì)于-mcmodel=medlow-mcmodel=medany。

-mcmodel=medlow

使用 LUI 指令取符號(hào)地址的高20位。LUI 配合其它包含低12位立即數(shù)的指令后,可以訪問(wèn)的地址空間是 -2GiB ~ 2GiB。

對(duì)于 RV64 而言,能訪問(wèn)的就是 0x0000000000000000 ~ 0x000000007FFFFFFF,以及 0xFFFFFFFF800000000 ~ 0xFFFFFFFFFFFFFFFF 這兩個(gè)區(qū)域,前一個(gè)區(qū)域即 +2GiB 的地址空間,后一個(gè)區(qū)域即 -2GiB 的地址空間。其它地址空間就訪問(wèn)不到了。

-mcmodel=medany

使用 AUIPC 指令取符號(hào)地址的高20位。AUIPC 配合其它包含低12位立即數(shù)的指令后,可以訪問(wèn)當(dāng)前 PC 的前后2GiB (PC - 2GiB ~ PC + 2GiB)的地址空間。

對(duì)于RV64,取決于當(dāng)前 PC 值,能訪問(wèn)到是 PC - 2GiB 到 PC + 2GiB 這個(gè)地址空間。假設(shè)當(dāng)前 PC 是 0x1000000000000000,那么能訪問(wèn)的地址范圍是 0x0000000080000000 ~ 0x100000007FFFFFFF。假設(shè)當(dāng)前 PC 是 0xA000000000000000,那么能訪問(wèn)的地址范圍是0x9000000080000000~0xA00000007FFFFFFF。

-fvisibility=hidden:動(dòng)態(tài)庫(kù)部分需要對(duì)外顯示的函數(shù)接口顯示出來(lái)。

-nostdlib:不連接系統(tǒng)標(biāo)準(zhǔn)啟動(dòng)文件和標(biāo)準(zhǔn)庫(kù)文件,只把指定的文件傳遞給連接器

-nostartfiles:不帶main函數(shù)的入口程序。

-Thello.ld:加載鏈接地址。

5.2 運(yùn)行

輸入下面的命令即可看到Hello.字符串輸出。

#qemu-system-riscv64-nographic-machinesifive_u-biosnone-kernelhello
Hello.

5.3 調(diào)試

調(diào)試過(guò)程比較只需在運(yùn)行的后面加-s -S,即

qemu-system-riscv64-nographic-machinesifive_u-biosnone-kernelhello-s-S

另外再開(kāi)一個(gè)終端輸入

riscv64-unknown-elf-gdbhello

接著輸入target remote localhost:1234即可。

通過(guò)b _start打斷點(diǎn),并且通過(guò)si進(jìn)行單步跳轉(zhuǎn)可實(shí)現(xiàn)程序的單步運(yùn)行。

6.總結(jié)

riscv64最小裸機(jī)程序的運(yùn)行很好理解,主要梳理清楚其啟動(dòng)地址與鏈接文件即可。還有就是注意gcc的編譯參數(shù),這些對(duì)于riscv的啟動(dòng)來(lái)說(shuō)也是非常關(guān)鍵的部分。

責(zé)任編輯:xj

原文標(biāo)題:riscv64 裸機(jī)編程實(shí)踐與分析

文章出處:【微信公眾號(hào):嵌入式IoT】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3546

    瀏覽量

    93502
  • RISC
    +關(guān)注

    關(guān)注

    6

    文章

    461

    瀏覽量

    83621

原文標(biāo)題:riscv64 裸機(jī)編程實(shí)踐與分析

文章出處:【微信號(hào):Embeded_IoT,微信公眾號(hào):嵌入式IoT】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    freertos和裸機(jī)有什么區(qū)別

    FreeRTOS 和裸機(jī)編程是兩種不同的嵌入式系統(tǒng)開(kāi)發(fā)方法,它們?cè)谠O(shè)計(jì)理念、資源使用、功能實(shí)現(xiàn)等方面有著顯著的差異。 1. 基本概念 1.1 FreeRTOS FreeRTOS 是一個(gè)小型的、可裁剪
    的頭像 發(fā)表于 09-02 14:13 ?713次閱讀

    美國(guó)硅谷raksmart站群裸機(jī)服務(wù)器租用費(fèi)用分析

    RAKsmart是一家提供數(shù)據(jù)中心服務(wù)的公司,其在美國(guó)硅谷擁有數(shù)據(jù)中心,并提供包括站群裸機(jī)服務(wù)器在內(nèi)的多種服務(wù)器租賃服務(wù)。站群服務(wù)器通常用于托管大量網(wǎng)站或應(yīng)用程序,因此對(duì)硬件性能和網(wǎng)絡(luò)穩(wěn)定性有著較高的要求。接下來(lái)我們將對(duì)RAKsmart硅谷站群裸機(jī)服務(wù)器的租用費(fèi)用進(jìn)行
    的頭像 發(fā)表于 08-29 10:05 ?140次閱讀

    東京裸機(jī)云多IP服務(wù)器全面分析

    東京裸機(jī)云多IP服務(wù)器是一種提供多IP地址分配和高性能網(wǎng)絡(luò)服務(wù)的云計(jì)算解決方案,廣泛應(yīng)用于需要多IP管理和高穩(wěn)定性的網(wǎng)絡(luò)應(yīng)用。下面將從幾個(gè)方面具體介紹東京裸機(jī)云多IP服務(wù)器,rak部落為您整理發(fā)布東京裸機(jī)云多IP服務(wù)器的全面
    的頭像 發(fā)表于 07-22 09:49 ?252次閱讀

    在ubuntu 24.04下嘗試使用riscv64-linux-musleabi_for_x86_64-pc-linux-gnu工具鏈編譯cv1800大核出現(xiàn)報(bào)錯(cuò)的原因?

    在ubuntu 24.04下嘗試使用riscv64-linux-musleabi_for_x86_64-pc-linux-gnu工具鏈編譯cv1800大核,結(jié)果出現(xiàn)如下報(bào)錯(cuò): /home
    發(fā)表于 07-16 08:20

    洛杉磯裸機(jī)云大寬帶服務(wù)器的特性和優(yōu)勢(shì)

    洛杉磯裸機(jī)云大寬帶服務(wù)器是結(jié)合了物理服務(wù)器性能和云服務(wù)靈活性的高性能計(jì)算服務(wù),為用戶提供高效、安全的計(jì)算和存儲(chǔ)能力。在了解如何使用洛杉磯裸機(jī)云大寬帶服務(wù)器之前,需要了解其基本特性和優(yōu)勢(shì)。以下是對(duì)洛杉磯裸機(jī)云大寬帶服務(wù)器的具體
    的頭像 發(fā)表于 07-08 10:11 ?185次閱讀

    振弦采集儀的工程安全監(jiān)測(cè)實(shí)踐與案例分析

    振弦采集儀的工程安全監(jiān)測(cè)實(shí)踐與案例分析 振弦采集儀是一種常用的工程安全監(jiān)測(cè)儀器,通過(guò)測(cè)量被監(jiān)測(cè)結(jié)構(gòu)的振動(dòng)頻率與振型,可以實(shí)時(shí)監(jiān)測(cè)結(jié)構(gòu)的安全狀況。本文將結(jié)合實(shí)踐經(jīng)驗(yàn)和案例分析,探討振弦采
    的頭像 發(fā)表于 07-01 11:01 ?181次閱讀
    振弦采集儀的工程安全監(jiān)測(cè)<b class='flag-5'>實(shí)踐</b>與案例<b class='flag-5'>分析</b>

    使用msys2 mingw64編譯nuclei openocd源碼出錯(cuò)的原因?

    :msys64homeAdministratorbuildnuclei-riscv-openocdbuild/../src/jtag/drivers/mpsse.c:358:(.text+0xc71): undefined reference
    發(fā)表于 05-29 07:52

    谷歌安卓系統(tǒng)即將取消對(duì)RISC-V架構(gòu)的支持

    負(fù)責(zé)安卓Linux核心分支開(kāi)發(fā)的谷歌高級(jí)工程師向AOSP提交了一系列補(bǔ)丁,其中顯示“已去除ACK對(duì)riscv64的支持”。這些補(bǔ)丁詳細(xì)描述指出“對(duì)risc64 GKI內(nèi)核的支持已停止”。
    的頭像 發(fā)表于 04-30 15:40 ?1405次閱讀

    國(guó)產(chǎn)riscv芯片大匯總?

    請(qǐng)問(wèn)有統(tǒng)計(jì)國(guó)產(chǎn)的riscv芯片的嗎?能匯總一下嗎?
    發(fā)表于 04-27 11:53

    RISCV soft JTAG調(diào)試_v1.2

    因?yàn)槟壳败浖南拗疲?b class='flag-5'>RISCV的邏輯不能同時(shí)共用JTAG,所以如果想要同時(shí)去調(diào)試邏輯和RISCV的話,可以通過(guò)RISCV的soft Jtag來(lái)實(shí)現(xiàn)。soft Jtag就是通過(guò)GPIO來(lái)實(shí)現(xiàn)的軟件
    的頭像 發(fā)表于 04-23 08:38 ?890次閱讀

    全志D1s開(kāi)發(fā)板裸機(jī)開(kāi)發(fā)之壞境搭建

    環(huán)境搭建 開(kāi)發(fā)板介紹 張?zhí)祜w老師編寫的《RISC-V體系結(jié)構(gòu)編程實(shí)踐》,里面的源碼是基于 QEMU 模擬器的,可以認(rèn)為它是一款虛擬的開(kāi)發(fā)板。如果需要在真實(shí)開(kāi)發(fā)板上學(xué)習(xí),可以使用百問(wèn)網(wǎng)
    發(fā)表于 03-06 13:54

    RISCV soft JTAG調(diào)試_v1.1

    因?yàn)槟壳败浖南拗疲?b class='flag-5'>RISCV的邏輯不能同時(shí)共用JTAG,所以如果想要同時(shí)去調(diào)試邏輯和RISCV的話,可以通過(guò)RISCV的soft Jtag來(lái)實(shí)現(xiàn)。soft Jtag就是通過(guò)GPIO來(lái)實(shí)現(xiàn)的軟件
    的頭像 發(fā)表于 02-23 16:16 ?554次閱讀
    <b class='flag-5'>RISCV</b> soft JTAG調(diào)試_v1.1

    【昉·星光 2 高性能RISC-V單板計(jì)算機(jī)體驗(yàn)】為 Ubuntu 安裝 Docker 及常用軟件

    : 獲取鏡像 通常來(lái)說(shuō),RISC-V 架構(gòu)的開(kāi)發(fā)板不能使用基于其他架構(gòu)開(kāi)發(fā)的鏡像,下面是一些基于 RISC-V 鏡像的合集:https://hub.docker.com/u/riscv64/ 安裝其他常用軟件 sudo apt install vim htop net-tools 根據(jù)所需即可。
    發(fā)表于 02-21 17:54

    醫(yī)院配電與能耗監(jiān)管系統(tǒng)建設(shè)實(shí)踐分析

    電子發(fā)燒友網(wǎng)站提供《醫(yī)院配電與能耗監(jiān)管系統(tǒng)建設(shè)實(shí)踐分析.docx》資料免費(fèi)下載
    發(fā)表于 01-31 09:11 ?0次下載

    如何在裸機(jī)系統(tǒng)中集成SystemView

    /OS-II、μC/OS-III、FreeRTOS、embOS外,SystemView也支持無(wú)OS的裸機(jī)系統(tǒng)分析,深入地了解應(yīng)用。
    的頭像 發(fā)表于 12-29 11:07 ?1799次閱讀
    如何在<b class='flag-5'>裸機(jī)</b>系統(tǒng)中集成SystemView