日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UEFI 文件类型.efi

發布時間:2023/12/10 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UEFI 文件类型.efi 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們首先先看一些UEFI 的框架圖:

無論是從功能劃分還是各個文件劃分,我們都可以看到這種模塊話思想。模塊話是現在軟件的主流,這樣能讓不同的人獨立開發自己的模塊,通過統一的接口添加整合到一個firmware file里。

我們uefi firmware 模塊,是通過幾個core 聯系到一起的,這幾個core 包括(SEC core, PEI core, Dxe core, SMM core)。如果單單從軟件角度講想學習UEFI 架構,那么這幾個core 是很值得學習的,因為他們說架構核心。當然這是指軟件方面的,因為還有各種外圍設備,總線協議,等等要去學習或者了解。

在uefi 世界里,efi 文件我們是不會陌生的,這些文件就是圍繞各種core 身邊然后組成一個完整uefi firmware 。所以也有必要了解一下efi。efi 文件就是一個小的module ,是一種可執行文件類型。比如Microsoft PE/COFF , Linux EFL 等等。這些可執行文件類型都有相似之處,應為都是同一種文件類型發展演化出來的。
efi 文件里面包的文件類型可能是PE32, TE(精簡版PE), PE32+中的一種。efi 文件作為可以被動態載入然后被執行,那么其中的文件類型可以選擇linux 下.so 和 windows .dll。 相比so 文件,dll 文件重定向更簡單,效率可能會高點,但是dll 會比.so 文件占用更多的空間。intel 選擇了dll 文件類型,可能有他們的考量吧。

介紹EFI 這種能被動態load 然后還能被執行的image 來講,我們首先先來熱身一下,通過一個例子,我們可能會比列出各種spec ,貼代碼強多了。通過例子來思考可能會有帶入感,再去看spec,就會很有感覺。

  • 我們首先先實現一個c 程序a.c 和一個經過編譯過的可執行文件b.bin。在a.c 里去動態加載到內存里,然后把控制權交給b.bin
  • 這個問題首先我們是要練習如果在一個程序里去加載另外的程序并執行它,所以我們不考慮增加線程或者進程去實現它。

    下面是我的實現思路(linux )

  • 這個linux 下比較好實現,其實windows 下思路一樣,只不過函數名字不一樣而已。linux 有一個mmap函數它會把其他文件映射到當前進程的虛擬空間里。并且映射的地址空間可以設置成可讀,可寫,可執行。所以知道這個mmap function 其他就迎刃而解了。下面是偽代碼:
  • //a.c handle = open(b.bin) // address 是映射文件到內存返回的虛擬地址 address = mmap(handle,可讀,可寫,可執行) //把控制權交給b.bin jmp/call address -------------------------------- b.c // b.bin 就是一個打印hello world 的可執行文件 char str[] = "hello world\n" write (4, 1, str, strlen(str));
  • 思路就是這樣,看起來a.c 不難,b.c 更是簡單,但是把這兩個合在一塊能在屏幕上打印一個hello world 還是有點細節要注意的。(趕緊去寫一個吧,看看能不能打印出來)。

  • 如果自己寫的執行有點問題可以接著往下看看,我直接就貼代碼了。

  • .section .data output:.asciz "Hello World\n" length:.int .-output.section .text .global _start _start:movl $4, %eaxmovl $1, %ebxmovl $output, %ecxmovl length, %edxint $0x80movl $1, %eaxmovl $0, %ebxint $0x80 ; 這個就是那個只打印hello world 的源文件 hello.s, ; 這里是用的匯編調用int 0x80 的function 4 也就是 write

    編譯hello.s :
    as hello.s -o hello.o
    ld hello.o -T test.ld -o hello.elf
    objcoby -j .text -O binary hello.elf hello.bin
    第一條,把hello.s 編譯成目標文件hello.o
    第二條,把hello.o 鏈接成一個可執行的文件hello.elf 其中test.ld 是我寫的一個鏈接腳本主要是把鏈接的的Section 從0 地址開始,并且把數據段和代碼段合并成一個Segment。
    第三條, 主要是避免還得多了解elf 文件格式而浪費時間,我這邊只輸出一個沒有文件格式的可執行文件.bin (當然在linux 下也可以用nasm 去編譯,會省去第二條和第三條)

    hello.s 還是很簡單的,但是有一個細節地方

    movl $output, %ecx

    這句話意思是把output 的地址給到ecx寄存器,那么問題來了,當我們編譯的時候這個地址編譯器會用0x00000000先代替,鏈接的時候鏈接器會根據代碼實際地址進行修正。鏈接器是如何知道哪個地方要修正?修正成多少?
    第一個問題,鏈接器會根據目標文件里的可重定向表,去修正要重定向的值。
    第二個問題,修正成多少(這里有很多規則,不詳細講解,具體請參考elf 文件相關文檔),簡單理解是由鏈接器決定的,配置的話可以下參數,也可以寫一個鏈接器腳本(相當于我們.fdf 文件)

    先來看一下hello.s 匯編之后的目標文件

    我們可以看到代碼段a 行,f 行,那兩段給寄存器填的值都是0,也就是說這兩個值是要重定向的。我們也看一下重定向表長啥樣:

    這張圖,我們可以看到編譯器要告訴它人,.text 需要重定向,兩個地方,分別是.text 段偏移0x0b 位置,和 0x12 的位置,填什么值,后面VALUE 字段有講。

    于是乎,鏈接器知道哪里需要修改了,好了它去執行鏈接(這篇文章例子是用的靜態鏈接,linux 動態鏈接因為和主題無關,略去),看看靜態鏈接之后可執行文件長什么樣。

    看到了a,f 行,分別給寄存器的值修正成0x24,和0x31, 因為我們編譯的時候把base address 設成0 了。仔細一想,我們如果把hello.bin 在a.c 里load 到虛擬地址0開始的位置,那么這段代碼就不用修正(這叫固定加載,之后看efi 加載的時候會看到,固定下載相關的代碼)。但是這樣只能load 在固定位置顯示不太合理。所以我們還得手動修改a,f 行的修正值,舉例,如果我們的hello.bin被load在0x10000處,那么output地址0x10024,
    length 地址就是0x10031,我們的任務就是讓代碼段偏移0x0b,0x12地方的里值 + 0x10000。(這個例子就和uefi 代碼里load efi image 的時候很像了,如果這個例子弄明白,關于efi 文件加載,執行,估計只要貼兩個圖,就明白怎么回事了,根本不用看代碼翻spec,信不?)

    主要分析過程結束。看看代碼的執行結果

    hello.bin 被load 在虛擬地址0x50000000 位置,我們看看0x0B 和 0x12 位置被修改成了0x50000000 + 0x24和0x50000000 + 0x31, 最后打印出hello world。

    代碼鏈接:
    app

    總結

    以上是生活随笔為你收集整理的UEFI 文件类型.efi的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。