帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

歡迎關注”技術簡說“,持續分享linux內核和驅動開發乾貨。


帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

本文內容包括:

  • 編譯器和交叉編譯器的介紹
  • 交叉編譯器的命名規則
  • 如何交叉編譯C代碼
  • 大端、小端的通俗講解
  • 如何判斷你的系統是大端系統還是小端系統

要學習linux內核開發,編譯器或者交叉編譯器是繞不過去的坎。

帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

編譯器的作用是把人類容易標識的程序代碼轉換為機器所理解的機器代碼,那交叉編譯器呢?

交叉編譯器也是實現類似的功能,只不過,咳咳,此處劃重點:

帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

編譯器生下的孩子(編譯出來的二進制文件)是自己的(能在編譯器所在硬件平臺上運行),

而交叉編譯器生下的孩子(編譯出來的二進制文件)是別人的(不能在交叉編譯器所在的硬件平臺上運行)。

為什麼會有交叉編譯器一說呢?

因為早期的嵌入式硬件(比如)arm的性能較低,不適合在上面進行程序編譯這種耗費cpu的任務,所以,交叉編譯器應用而生了:我們在pc機上(pc機是x86架構)安裝arm的交叉編譯器,並(在pc機上)編譯出能在arm體系上運行的二進制代碼,這就是交叉編譯器所做的事情。

瞭解這個歷史原因之後,可能會有腦子靈光的童鞋提問:

現在arm的性能已經有了大幅提升,還需要交叉編譯器嗎?

答案是:需要。

有些arm的linux系統中,已經可以使用gcc,可以編譯一些比較上層的應用,但是這是有一個前提的:那就是linux系統已經run起來了。那如果arm的板子連linux內核都沒有,你怎麼使用gcc?另外linux內核是需要提前做好的,如果arm系統都沒有起來,怎麼編譯linux內核? 你只能通過在PC機上進行交叉編譯。

以上就是關於交叉編譯器的作用的介紹,接下來我們看一下交叉編譯器的命名規則

帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

交叉編譯工具鏈的命名規則一般為:

$arch [-$vendor] -$os [-[gnu][eabi][hf]]-gcc

  • arch - 體系架構,如arm,mips等,不可省略
  • vendor - 工具鏈提供商,可省略
  • os - 目標操作系統,不可省略
  • eabi - 嵌入式應用二進制接口(Embedded Application Binary Interface),可選的參數包括:

abi: 二進制應用接口。

eabi: 嵌入式二進制應用接口,主要針對嵌入式平臺。

gnu: 加gnu表示編譯器使用的是gnu glibc的庫

el:表示使用軟浮點處理單元(softfp)。其實在armel中,關於浮點數計算的約定有三種。以gcc為例,對應的-mfloat-abi參數值有三個:soft,softfp,hard。soft是指所有浮點運算全部在軟件層實現,效率當然不高,會存在不必要的浮點到整數、整數到浮點的轉換,只適合於早期沒有浮點計算單元的ARM處理器;softfp是目前armel的默認設置,它將浮點計算交給FPU處理,但函數參數的傳遞使用通用的整型寄存器而不是FPU寄存器;hard則使用FPU浮點寄存器將函數參數傳遞給FPU處理。需要注意的是,在兼容性上,soft與後兩者是兼容的,但softfp和hard兩種模式不兼容,armel使用softfp,因此將hard模式的armel單獨作為一個abi,稱之為armhf

hf: 表示使用硬件浮點處理單元(hard)

以上就是交叉編譯器的命名規則了,我們來看看幾個例子吧:

arm-none-eabi-gcc:針對arm的裸機程序的gcc編譯器,如果你在arm板子上直接運行c程序,可能需要這個。

arm-uclinux-gcc:針對arm的uclinux的交叉編譯器

arm-linux-gcc : 針對arm的linux的交叉編譯器

arm-linux-gnueabi-gcc:針對arm的linux的交叉編譯器,此編譯器使用gnu glibc的庫

arm-linux-gnueabihf-gcc:針對arm的linux的交叉編譯器,此編譯器使用硬件浮點處理浮運算。

arm-linux-gnueabiel-gcc:針對arm的linux的交叉編譯器,此編譯器使用軟浮點處理浮點運算。

瞭解了交叉編譯器之後,我們用它來編譯一個最簡單的程序。

帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

代碼如下:

#include

void main()

{

printf("hello arm");

}

在pc上編譯:

# arm-linux-gnueabi-gcc -o main main.c

在pc上運行:

root@ubuntu:/home/jinxin/app# ./main

bash: ./main: cannot execute binary file: Exec format error

運行出錯了,為什麼呢?

因為當前pc是x86架構,而剛剛編譯出來的二進制文件是arm格式的:

root@ubuntu:/home/jinxin/app# file main

main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=ec9b455a1c1df94060c7364d0efc42975207fca9, not stripped

如果想要正常運行,需要把編譯出來的二進制代碼拷貝到arm的板子上去運行。

以上的輸出,有一個需要強調的地方:LSB

這裡的LSB是幹什麼的呢?接下來我們就來看看大小端。

帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

大端和小端主要描述了數據在內存裡的存儲的順序。

大端模式,是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中;

小端模式,是指數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中;

比如:

帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

x86是小端模式,而網絡字節序是大端模式。所以,專門出了網絡字節序和主機字節序之間的轉換接口(ntohs、htons、ntohl、htonl)。以上的LSB,全稱Least Significant Bi,表示可執行文件是小端格式的。

而arm則支持大小端模式,但是同一時刻只能支持一種,一般情況下,arm配置為大端模式,這也是跟x86分庭抗禮的意思。

針對arm,我再強調一下:如果arm硬件配置為大端,那麼編譯器要使用大端配置,內核、文件系統、應用程序也要編譯為大端的格式。

那如何知道你的系統是大端還是小端呢?

帶你閱讀linux內核源碼:通俗講解編譯器、交叉編譯器和大小端

一個最簡單的測試你係統是大端還是小端的程序:

#include

void main()

{

int a = 0x1234;

char b = *(char*)&a;

if(0x34 == b){

printf("your system is little endian\n");

} else {

printf("your system is big endian\n");

}

}

在我的pc上執行,輸出:

root@ubuntu:/home/jinxin/app# ./endian

your system is little endian

以上基本內容,你學懂了嗎?

關注”技術簡說“,持續分享linux內核和驅動開發乾貨。


分享到:


相關文章: