1、指針函數 指針函數,從名字上看它本質上是一個函數。指針函數:返回值類型是指針的函數。函數聲明如下: int *plusfunction(int a,int b); 當然也可以寫成如下格式: int* plusfunction(int a,int b); 讓指針標誌 * 與int緊貼在一起,而與函 ...
1. 常用二進位相關工具
strip: 可以實現剔除可執行文件的符號表 (減少二進位文件的空間體積)
objcopy : 將ELF文件的相關段拷貝成一個文件 (-d 反編譯 -R 顯示重定向的入口)
readelf : 讀取ELF格式的內容 (-h 顯示ELF信息 -S 查看ELF結構)
objdump :將ELF文件格式進行反彙編 (生成純二進位數據文件)
size :列出目標文件每一段的大小以及總體的大小
nm:查看符號表
2. Makefile內置的變數
預定義變數
LDFLAGS: 鏈接選項
CFLAGS : 編譯選項 (C編譯器的選項,無預設值)
AR:庫文件維護程式的名稱,預設值為ar
AS 彙編程式的名稱,預設值為as
CC C編譯器的名稱,預設值為cc
CPP C預編譯器的名稱,預設值為$(CC) –E
CXX C++編譯器的名稱,預設值為g++
FC FORTRAN編譯器的名稱,預設值為f77
RM 文件刪除程式的名稱,預設值為rm -f
自動變數
$@ 目標文件的完整名稱
$^ 所有不重覆的目標依賴文件,以空格分開
$< 第一個依賴文件的名稱
$* 不包含擴展名的目標文件名稱
$+ 所有的依賴文件,以空格分開,並以出現的先後為序,可能 包含重覆的依賴文件
$? 所有時間戳比目標文件晚的依賴文件,並以空格分開
$% 如果目標是歸檔成員,則該變數表示目標的歸檔成員名稱
變數定義方式:遞歸展開方式、簡單方式
遞歸展開eg.
bar=$(ugh)
查看變數:
echo $(foo)
優點:它可以向後引用變數
缺點:不能對該變數進行任何擴展
eg.以下代碼會造成死迴圈
CFLAGS=$(CFLAGS) -O
3. 配置linux內核時的變數
ARCH : 選擇CPU的體繫結構
*在Linux內核編譯時,可以通過設置Makefile中ARCH變數的值來選擇ARM體系還是MIPS體系進行內核編譯
CROSS_COMPILE : 指定交叉編譯器的首碼
4. Linux內核編譯的預設文件
ARM體系:zImage
(image是內核鏡像文件,zimage是壓縮之後的內核鏡像文件,uimage是在zimage基礎之上加上頭部一些信息,其實也是壓縮之後的內核鏡像,uImage鏡像文件適合使用bootm命令啟動)
X86體系:bzImage
Linux內核鏡像文件可以是壓縮格式也可以不壓縮
5. Linux內核源碼目錄
net: 存放網路協議源碼
drivers/net : 存放網路驅動源碼
6. Kconfig的常用關鍵字
bool:配置源碼編譯進內核或不編譯進內核
tristate: 配置源碼編譯進內核、不編譯進內核或以模塊方式編譯進內核
*tristate意思是三態(3種狀態,對應Y、N、M三種選擇方式),bool是要麼真要麼假(對應Y和N)。所以tristate的意思就是這個配置項可以被三種選擇,bool的意思是這個配置項只能被2種選擇
source表示包含下一級Kconfig
depends on表示該配置選項依賴於其他配置選項的關係
7. busybox創建文件系統時,會創建的內容:init、ls、cd、ifconfig等命令 (常用的shell命令)
8. Linux中可以自動創建設備節點的命令是:mdev (該工具由busybox自帶)
9. 在嵌入式開發中,函數代碼鏈接到.text段 (.text 就是指代碼段)
10. 配置內核
配置內核後,預設生成的文件名是.config
make menuconfig表示以菜單形式配置Linux內核的命令
11. inittab中常見動作
inittab決定init進程如何工作,有以下2個動作:
sysinit : 系統第一個shell腳本執行的路徑指定
askfirst: 實現用戶摁任意鍵進入控制台
12. 常見的Makefile,生成裸機程式的方法 ★
(1).Makefile 概述:
本質上是一個“自動編譯管理器”
“自動”指它能夠根據文件時間戳自動發現更新過的文件而減少編譯的工作量
通過讀入Makefile文件的內容來執行大量的編譯工作
(2).Makefile基本結構:
由make工具創建的目標體(target),通常是目標文件或可執行文件
要創建的目標體所依賴的文件(dependency_file)
創建每個目標體時需要運行的命令 (command)
*命令行前面必須是一個“TAB鍵”,否則編譯錯誤為:*** missing separator. Stop.
格式如下:
target : dependency_files
<TAB> command
eg.
hello.o : hello.c hello.h gcc -c hello.c -o hello.o
(3).make使用
直接運行即可
make
-C dir 讀入指定目錄下的Makefile
-f file 讀入當前目錄下的file文件作為Makefile
-i 忽略所有的命令執行錯誤
-I dir指定被包含的Makefile所在目錄
-n 只列印要執行的命令,但不執行這些命令
-p 顯示make變數資料庫和隱含規則
-s 在執行命令時不顯示命令
-w 如果make在執行過程中改變目錄,列印當前目錄名
Makefile 生成裸機程式的模板:
#前面是一堆變數的定義
OBJCOPY = arm-linux-objcopy CC = arm-linux-gcc #gcc是肯定要有的
#後面寫Makefile規則
#生成裸機,最終目的一定是生成bin文件
xxx.bin:xxx #ELF文件與bin文件之間的關係 $(OBJCOPY) -O binary $^ $@ #objcopy是先有輸入,再寫輸出,小寫的o第一個一定是目標 xxx:xxx.o main.o #鏈接:由.o文件打包生成ELF文件 $(CC) $(LDFLAGS) -o $@ $^ #需要加編譯選項,LDFLAGS為預設值
#所有的linux項目都有的公共規則,不能寫反~ %.o:%.c $(CC) $(CFLAGS) -c -o $@ $^ %.o:%.S $(CC) $(CFLAGS) -c -o $@ $^
實例: 編寫Makefile,將start.S生成為二進位文件
思路:首先,我們可以使用”arm-none-linux-gnueabbihf-gcc -c -o start.o start.S” 命令來生成.o文件。
之後,我們可以使用“arm-none-linux-gnueabihf-gcc -nostolib -o abc start.o”命令來生成可執行文件,其中nostlib表示不需要鏈接庫文件。
最後,使用“arm-linux-gnueabihf-objcopy -O binary abc abc.bin”生成最後的二進位文件。
將這3個步驟依次按照Makefile的語法格式進行編譯即可生成二進位文件。
CROSS_COMPILE = /opt/Sourcery_CodeBench_Lite_for_Xilinx_GNU_Linux/bin/arm-linux-CC = gcc OBJS += start.o CFLAGS += -nostdlib LDFLAGS += -Tmap.lds TARGET := build.bin all:$(TARGET) $(TARGET):build $(CROSS_COMPILE)objcopy -S --gap-fill 0xff -O binary $^ $@ build:$(OBJS) $(CROSS_COMPILE)ld $(LDFLAGS) -o $@ $^ %.o:%.c $(CROSS_COMPILE)$(CC) -c $(CFLAGS) -o $@ $< %.o:%.S $(CROSS_COMPILE)$(CC) -c $(CFLAGS) -o $@ $< .PYHON:clean clean: rm -f *.o build build.bin
13. bootloader的工作流程
①. bootloader一般分為boot階段和loader階段
②. boot階段採用體繫結構相關的彙編語言編寫,主要是初始化CPU和記憶體設備
③. boot階段為第二階段初始化C語言運行前環境,設置SP寄存器
④. loader階段根據系統鏡像存儲位置,初始化對應設備的驅動,拷貝系統鏡像文件內容到記憶體載入地址
⑤. loader階段根據內核啟動要求,初始化R0,R1,R2寄存器的值,將PC指針指向內核鏡像載入地址處
14. ext4格式鏡像文件的製作過程
1.分配空間
(1).製作64M的鏡像文件,命名為:a9rootfs.ext3
sudo dd if=/dev/zero of=a9rootfs.ext3 bs=1M count=64
(2).用ext3格式化上一步的鏡像文件
sudo mkfs.ext3 a9rootfs.ext3
2.填充空間
(1).掛載鏡像文件到一個目錄,建議放在同級目錄下,假設目錄名為mnt_fs
sudo mount -t ext3 -o loop a9rootfs.ext3 mnt_fs/
(2).建立根文件系統目錄
(3).利用busybox製作可執行文件
(4).拷貝文件到mnt_fs目錄下
(5).拷貝動態庫到根文件系統里
(6).編寫inittab、etc/rc.d/rcS、etc/fstab文件
3.卸載空間
sudo umount mnt_fs
15. GNU格式的鏈接腳本
鏈接腳本:怎麼把目標文件組合在一起
#SECTIONS: SECTION 表示段,S表示很多段
#4個段有先後關係,最好不要打亂它的順序
#.text代碼段、.rodata只讀數據段、.data數據段、.bss全局初始化位清零段。語法為:段名:{*(同名段)}
#.text代碼段是第一個,需要指定地址:start.o(.text)
#ALIGN(4) 4位元組對齊
SECTIONS { . = 0x20000000; #要什麼地址給什麼地址 . = ALIGN(4); .text : { start.o(.text) *(.text) } . = ALIGN(4); .rodata : { *(.rodata) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); .bss : { *(.bss) } }
16. make執行
make執行時讀取當前目錄下的Makefile文件或makefile進行執行
Makefile和makefile文件在make執行時的優先順序是不一樣的
make中如果目標文件比依賴文件新,那麼make將不執行對應的命令
17. uboot編譯時,不僅僅會產生對應體系的文件,還可能產生x86體系的可執行文件
uboot可以支持ARM、MIPS、PowerPC等眾多CPU體繫結構(uboot即是bootloader的一種,引導啟動內核的)
18. 使用QEMU載入文件時,必須是ELF格式的文件
19.NFS
網路啟動的根文件系統稱之為NFS
使用NFS作為根文件系統時,linux內核需要載入網卡驅動
20. linux進程
linux的proc目錄下可以查看到進程信息
linux的1號進程叫init進程
21. Linux內核編譯時,objs-y的目標才被編譯進內核鏡像中
22. busybox源碼是沒有體繫結構相關的配置的
==================分============割============線==================
23.燒寫到系統的bootloader鏡像文件是binary格式的文件
24.ramdisk根文件系統不可以燒寫到flash上
25.Linux系統提供VFS(虛擬文件系統)層,方便訪問其內核掛載的根文件系統
26.在嵌入式開發中,使用交叉編譯器在主機上編譯,在目標機上運行程式
27.bootloader在載入內核啟動前,需要將內核鏡像和內核啟動參數搬移到系統記憶體中
28.函數指針
函數:一堆代碼的集合,空間的首地址
一個數據變數的記憶體地址可以儲存在相應的指針變數中,同理,函數的首地址也可以儲存在某個函數指針變數中,通過函數指針,我們就可以調用函數指針所指向的函數了。
函數指針定義的方式:
void(*abc)(int) //(*abc)先定義的地址
函數指針調用示例:
#include<stdio.h> void (*ABC)(int); void abc(int x); int main() { int a1=100; int a2=200; ABC=&abc; (*ABC)(a1); //通過函數指針變數調用函數 (*ABC)(a2); return 0; } void abc(int x) { printf("abc=%d\n",x); }
運行結果:
eg1.給一個絕對地址,如何訪問:
1.如果該地址是指向變數的地址:unsigned char *p = xxx;直接定義指針p指向該地址即可通過指針的方式訪問到該地址。
2.如果該地址指向的是一段函數空間:定義指向函數的指針p,即可通過指針p訪問到對應的函數空間。
void (*p)(void); //定義了一個可以指向一類函數地址的指針 p = (void (*)(void))xxx; //將給的地址進行強制類型轉換成我們定義的指針,它能指向的函數類型的地址
eg2:彙編語言轉為C語言表示:
彙編語言內容:
MOV R0,#1 MOV R1,#2 MOV R2,#3 MOV PC,#0x2000_8000
C語言代碼:
void (*fun_t)(int,int,int)=(void(*)(int ,int,int))0x2000_8000; fun_t(int a1,int a2,int a3); //a1=#1,a2=#2,a3=#3