Linux點點滴滴(二):在Linux上安裝GNU工具鏈並進行編譯

Linux學習的點點滴滴(二)

其實本文跟Linux關係並不是那麼大,是我在自己寫CPU的過程中總結的東西,之前只用Word寫在自己電腦裏了,想着哪天放到博客上。
本文是在寫CPU進行測試的時候需要將彙編翻譯成機器碼的過程,剛開始學的時候,也遇到了一些小問題,在此記錄。

一、安裝GNU工具鏈

gcc編譯器使用的是龍芯公司的,使用了MIPS架構。下載地址
進入Linux虛擬機的/opt文件夾(其實哪個都無所謂),在終端輸入tar -zxvf gcc-4.3-ls232.tar.gz,這裏tar -zxvf.tar.gz文件的解壓縮命令,後面會詳細介紹其他的解壓縮命令
解壓縮之後,首先配置環境變量,找到當前用戶的文件夾下的隱藏文件.bashrc,如果遇到權限不夠,用chmod修改權限之後,打開文件,在文件最後一行加上
export PATH=”$PATH:password/opt/gcc-4.3-ls232/bin”
在終端輸入echo $PATH查看已經配好的環境變量,(echo是回顯命令,是常見的腳本命令)
echo
然後輸入mipsel-linux-gcc -v,如下圖表示GNU工具鏈安裝完成:
GNU
有個很坑的地方,如果提示:bash ./ 沒有那個文件或目錄,是因爲Ubuntu是64位的,沒有32位的運行庫,安裝一個32位運行庫即可:apt-get install lib32z1
至此基本的準備工作就完成了,但是在此過程中不熟悉Linux系統會導致一些列問題,在此總結一下。

二、解壓縮命令

常見的解壓縮命令

tar
-c: 建立壓縮檔案
-x:解壓
-t:查看內容
-r:向壓縮歸檔文件末尾追加文件
-u:更新原壓縮包中的文件
這五個是獨立的命令,壓縮解壓都要用到其中一個,可以和別的命令連用但只能用其中一個。下面的參數是根據需要在壓縮或解壓檔案時可選的。

-z:有gzip屬性的
-j:有bz2屬性的
-Z:有compress屬性的
-v:顯示所有過程
-O:將文件解開到標準輸出
下面的參數-f是必須的
-f: 使用檔案名字,切記,這個參數是最後一個參數,後面只能接檔案名。
tar -cf all.tar *.jpg
這條命令是將所有.jpg的文件打成一個名爲all.tar的包。-c是表示產生新的包,-f指定包的文件名。
tar -rf all.tar *.gif
這條命令是將所有.gif的文件增加到all.tar的包裏面去。-r是表示增加文件的意思。
tar -uf all.tar logo.gif
這條命令是更新原來tar包all.tar中logo.gif文件,-u是表示更新文件的意思。
tar -tf all.tar
這條命令是列出all.tar包中所有文件,-t是列出文件的意思
tar -xf all.tar
這條命令是解出all.tar包中所有文件,-t是解開的意思

//壓縮
tar -cvf jpg.tar *.jpg //將目錄裏所有jpg文件打包成tar.jpg 
tar -czf jpg.tar.gz *.jpg   //將目錄裏所有jpg文件打包成jpg.tar後,並且將其用gzip壓縮,生成一個gzip壓縮過的包,命名爲jpg.tar.gz
tar -cjf jpg.tar.bz2 *.jpg //將目錄裏所有jpg文件打包成jpg.tar後,並且將其用bzip2壓縮,生成一個bzip2壓縮過的包,命名爲jpg.tar.bz2
tar -cZf jpg.tar.Z *.jpg   //將目錄裏所有jpg文件打包成jpg.tar後,並且將其用compress壓縮,生成一個umcompress壓縮過的包,命名爲jpg.tar.Z
rar a jpg.rar *.jpg //rar格式的壓縮,需要先下載rar for linux
zip jpg.zip *.jpg //zip格式的壓縮,需要先下載zip for linux

//解壓
tar -xvf file.tar 			//解壓 tar包
tar -xzvf file.tar.gz 		//解壓tar.gz
tar -xjvf file.tar.bz2   	//解壓 tar.bz2
tar -xZvf file.tar.Z   		//解壓tar.Z
unrar e file.rar 			//解壓rar
unzip file.zip				//解壓zip

//總結
1、*.tar 用 tar -xvf 解壓
2、*.gz 用 gzip -d 或者gunzip 解壓
3、*.tar.gz 和 *.tgz 用 tar -xzf 解壓
4、*.bz2 用 bzip2 -d或者用bunzip2 解壓
5、*.tar.bz2 用 tar -xjf 解壓
6、*.Z 用 uncompress 解壓
7、*.tar.Z 用tar -xZf 解壓
8、*.rar 用 unrar e解壓
9、*.zip 用 unzip 解壓*

三、關於環境變量

類似於Windows下的環境變量,本質含義就是讓系統運行一個程序時,不僅僅要在當前目錄下面尋找,還要到PATH指定的路徑下尋找,此操作實際簡化了程序執行的複雜度,提高了效率。
以上面的GCC路徑爲例,我們寫入的PATH路徑爲:export PATH=”$PATH:/opt/gcc-4.3-ls232/bin”
什麼意思?可以把PATH看成一個字符串,用“/”、“:”、“\$” 等符號進行劃分,系統根據劃分的字符串尋找目標地址。因爲PATH是一個字符串,用“$PATH”表示引用這個字符串,就是表示PATH所指的內容;是Linux中分隔符,相當於Windows中的,因此該語句表示在原有的PATH路徑下再加上/opt/gcc-4.3-ls232/bin路徑,我們在終端中檢驗一下:
在這裏插入圖片描述
實際上,若要僅僅配置GCC,不需要加上之前的PATH路徑,因此只要這樣寫即可:
export PATH=”/opt/gcc-4.3-ls232/bin".我們在終端中再次輸入echo $PATH,結果證明是正確的,gcc安裝無誤:
在這裏插入圖片描述

四、編譯彙編指令

我們先編寫一組彙編指令,命名爲inst_rom.S
在這裏插入圖片描述
然後我們在終端中輸入指令:
mipsel-linux-as -mips32 inst_rom.S -o inst_rom.o,表示用as工具將inst_rom.S文件編譯成inst_rom.o文件再輸入:
mipsel-linux-ld -T ram.ld inst_rom.o -o inst_ram.om,表示將inst_rom.o文件鏈接成inst_rom.om文件。再輸入:
mipsel-linux-objcopy -O binary inst_rom.om inst_ram.bin,根據.om文件得到了bin文件,再輸入:
mipsel-linux-objdump -D inst_rom.om > inst_ram.asm,對彙編指令進行反彙編,得到與機器指令對應的二進制字。
最後將Bin2MEM.exe拷貝到與inst_rom系列文件相同的目錄下,執行
./Bin2Mem.exe -f inst_rom.bin -o inst_rom.data
第一次執行應該需要修改Bin2Mem.exe文件的權限,輸入:
Chmod 777 Bin2Mem.exe,最終轉化爲與Vivado程序中讀入的文件類型(.data),執行結果如圖:
沒有描述~~~
生成的文件:
在這裏插入圖片描述
我們打開inst_rom.data文件查看:
在這裏插入圖片描述
這便是我們一開始那幾條彙編指令的機器指令。

五、大小端地址

大端(存儲)模式:是指一個數據的低位字節序的內容放在高地址處,高位字節序存的內容放在低地址處。
小端(存儲)模式:是指一個數據的低位字節序內容存放在低地址處,高位字節序的內容存放在高地址處。(可以總結爲“小小小”即低位、低地址、小端)
在上述的例子中,我們發現原來的機器指令34011100變成了00110134,高位數據放在了地址的高位,是小端存儲,這樣看上去不是很方便,我們可以修改編譯指令改變最終編譯的結果,輸入mipsel-linux-as -help查看幫助:
在這裏插入圖片描述
在這裏插入圖片描述
我們修改指令:
在這裏插入圖片描述
查看inst_rom.data文件:
在這裏插入圖片描述
已經修改爲大端模式了。我們需要修改爲大端模式,因爲該OpenMIPS就是大端模式。

六、Make工具

每次都輸入四個(加上.asm一共五個)指令顯得很麻煩,我們可以編寫一個腳本來自動執行命令。
make便是Linux上的一個腳本,當我們只輸入make命令的工作流程是:

  1. make會在當前目錄下找名字叫“Makefile”或“makefile”的文件;
  2. 如果找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到“inst_rom.data”這個文件,並把這個文件作爲最終的目標文件;
  3. 如果inst_rom.data文件不存在,或是.data所依賴的後面的 .om 文件的文件修改時間要比.data這個文件新,那麼make會執行下面定義的命令來生成.data文件;
  4. 如果.data所依賴的.om文件也存在,那麼make會在當前文件中找目標爲.om文件的依賴性,如果找到再根據命令生成.om文件(這是一個遞歸的過程);

如果在找尋的過程中,出現了被依賴的文件找不到的錯誤,那麼make就會直接退出,並報錯。
如果在一條依賴鏈中,比如:A依賴B,B依賴C,C依賴D。那麼當D更新後,make發現D比C新則會重新構建C,以此類推,最終A也會被更新。
簡單的說,makefile帶來的好處就是——自動化編譯,只要一個make命令,所有工程和文件自動編譯,類似於Shell的.sh和cmd的.bat

我們編寫如下代碼:

ifndef CROSS_COMPILE
	CROSS_COMPILE = mipsel-linux-
endif
CC = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld

OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump

OBJECTS = inst_rom.o
export CROSS_COMPILE
all:inst_rom.data inst_rom.om inst_rom.o inst_rom.bin inst_rom.asm

%.o:%.S
	$(CC) -mips32 -EB $< -o $@

inst_rom.om:ram.ld $(OBJECTS) 
	$(LD) -EB -T ram.ld $(OBJECTS) -o $@

inst_rom.bin:inst_rom.om
	$(OBJCOPY) -O binary $< $@

inst_rom.asm:inst_rom.om
	$(OBJDUMP) -D $< >$@

inst_rom.data:inst_rom.bin
	./Bin2Mem.exe -f $< -o $@

clean:
	rm -f *.o *.om *.bin *.data

在這裏插入圖片描述
其中 $< 表示第一個依賴文件的名稱,$@ 表示目標的完整名稱

至此,我們用gcc編譯彙編指令生成機器碼的過程就做完了。

七、ram.ld鏈接代碼

MEMORY
        {      
        ram(RW)    : ORIGIN = 0x00000000, LENGTH = 0x00001000
        }
SECTIONS
{
	  /*
	  For some reason the linker script can't see the _reset_vector symbol 
	  (even if we declare it global), so we explicitly set it. */
	.text :
        {
        *(.text)
        } > ram

        .data :
        {
        *(.data)
        } > ram
        .bss :
        {
        *(.bss)
        } > ram
        .stack  ALIGN(0x10) (NOLOAD):
        {
        *(.stack)
        _ram_end = .;
        } > ram
}
ENTRY (_start)

八、Bin2Mem.c代碼

#include <stdlib.h>
#include <stdio.h>

char *option_invalid  = NULL;
char *option_file_in  = NULL;
char *option_file_out = NULL;

FILE *file_in_descriptor  = NULL;
FILE *file_out_descriptor = NULL;

void exception_handler(int code) {
    switch (code) {
        case 0:
            break;
        case 10001:
            printf("Error (10001): No option recognized.\n");
            printf("Please specify at least one valid option.\n");
            break;
        case 10002:
            printf("Error (10002): Invalid option: %s\n", option_invalid);
            break;
        case 10003:
            printf("Error (10003): No input Binary file specified.\n");
            break;
        case 10004:
            printf("Error (10004): Cannot open file: %s\n", option_file_in);
            break;
        case 10005:
            printf("Error (10005): Cannot create file: %s\n", option_file_out);
            break;
        default:
            break;
    }

    if (file_in_descriptor  != NULL) {
        fclose(file_in_descriptor);
    }
    if (file_out_descriptor != NULL) {
        fclose(file_out_descriptor);
    }
    exit(0);
}

int main(int argc, char **argv) {
 
    int i=0,j=0;
    unsigned char temp1,temp2,temp3,temp4;
    unsigned int option_flag = 0;

    while (argc > 0) {
        if (**argv == '-') {
            (*argv) ++;
            switch (**argv) {
                case 'f':
                    option_flag |= 0x4;
                    argv ++;
                    option_file_in = *argv;
                    argc --;
                    break;
                case 'o':
                    option_flag |= 0x8;
                    argv ++;
                    option_file_out = *argv;
                    argc --;
                    break;
                default:
                    option_flag |= 0x1;
                    (*argv) --;
                    option_invalid = *argv;
                    break;
            }
        }
        argv ++;
        argc --;
    }

    file_in_descriptor = fopen(option_file_in, "rb");
    if (file_in_descriptor == NULL) {
        exception_handler(10004);
    }

    file_out_descriptor = fopen(option_file_out, "w");
    if (file_out_descriptor == NULL) {
        exception_handler(10005);
    }
    
    while (!feof(file_in_descriptor)) {
         
            fscanf(file_in_descriptor, "%c", &temp1);
            fscanf(file_in_descriptor, "%c", &temp2);
            fscanf(file_in_descriptor, "%c", &temp3);
            fscanf(file_in_descriptor, "%c", &temp4);

            if(!feof(file_in_descriptor)) {
             fprintf(file_out_descriptor, "%02x", temp1);
             fprintf(file_out_descriptor, "%02x", temp2);
             fprintf(file_out_descriptor, "%02x", temp3);
             fprintf(file_out_descriptor, "%02x", temp4);
             fprintf(file_out_descriptor, "\n");
            } 
    }

    exception_handler(0);
    return 0;
}

發佈了54 篇原創文章 · 獲贊 19 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章