自己学驱动1——基本命令和链接文件

1.安装linux虚拟机。

(1)在vmvare中安装ubuntu主机。

(2)安装NFS、FTP、SSH、SMABA服务等。

(3)安装交叉编译工具链。

(4)安装ncurses,make menuconfig会用到的一个动态库。


2.代码处理的四个步骤

预处理(处理宏定义等.i文件)->编译(转化为汇编)->汇编(汇编转化为机器码.o文件)->链接(生成可执行文件)。


3.编译时

arm-linux-gcc -v -o hello hello.c使用-v命令的作用是可以查看编译的细节;

amr-linux-gcc -Wall -o hello hello.c使用的-Wall选项是打开所有的警告;

gcc -g hello.c -o hel使用的-g选项是生成gdb调试文件;

gcc -O2 -o hello hello.c使用的-O2表示的是优化等级。

-c选项:预处理、编译、汇编,但是不做链接,只是生成obj文件,也就是.o文件。


4.arm-linux-ld用于将多个目标文件、库文件链接成可执行文件

-T选项:直接使用它来指定代码段、数据段、bss段的起始地址,也可以用来指定一个链接脚本,在链接脚本中进行更复杂的地址设置。例如:-Ttext startaddr、-Tdata startaddr、-Tbss startaddr。如果只指定了代码段的地址,那么数据段和bss段会依次放在代码段的后面。例如:

arm-linux-ld -Ttimer.lds -o timer_elf main.o hello.o

用-T参数指定链接脚本timer.lds来设置可执行文件timer_elf的地址信息,这种用法类似于-lpthread,-l选项表示库,pthread是库名。

arm-linux-ld -Ttext 0x00000000 -g led.o -o led_elf

直接给出地址的形式

注意:这个命令链接出来的文件格式是elf格式,并不是bin格式,如果需要bin格式的文件,还需要使用arm-linux-objcopy命令转换为bin格式文件。


5.arm-linux-objdump

常用来显示二进制文件信息,常用来查看反汇编代码。

-D选项:反汇编所有段

-b选项:指定输入文件的格式

-m选项:指定反汇编目标文件时使用的架构

例如:

arm-linux-objdump -D elf_file > dis_file

arm-linux-objdump -D -b binary -m arm bin_file > dis_file


6.一小段汇编代码

.text

.global _start

_start:

    b step1

step1:

    ldr pc, =step2

step2:

    b step2

分析:

其中的.text表示以下开始为代码段;.global _start表示定义一个全局的标号_start。

b或bl跳转指令,要跳转到的地址是这样计算的:将指令中24位带符号的补码([31:28]为条件码,[27:24]1010时表示b指令,1011时表示bl指令,[23:0]表示偏移地址)扩展为32位(扩展其符号位),然后将这个32位数左移两位,再将得到的值加到pc寄存器中,即得到跳转的目标地址。pc寄存器的值是当前指令的下两条指令的地址(arm处理器的流水线技术)。


7.位置无关码

        使用b或者bl指令跳转的位置依赖于当前pc寄存器的值,这个特性也使得使用b指令的程序不依赖于代码存储的位置——即不管这条代码放在什么位置,b指令都可以跳到正确的位置。即使在编译的时候用-Ttext选项指定不同的位置,生成的代码仍然是一样的。这就是位置无关码。-T选项指定的是程序的运行地址,如果指定了一个运行地址,那么程序代码就必须要放到这个地址去,否则如果代码中出现位置相关码的话就会出错。

        然而对于ldr pc,=step2,对应的汇编为ldr pc,[pc,#0]可以看出,这条指令从内存的某个位置读出数据,并且赋值给pc,刚看到这个的时候可能会觉得奇怪,因为这条语句是想跳转到step2的位置去运行,按理说pc里面存放的应该是下两条指令的地址,所以应该是ldr pc,[pc,#-4]才对,因为step2的位置是当前指令的下一条指令,这是因为这段代码在step2的位置就结束了,pc没有办法存放下两条的值。

怎么编写位置无关码:在汇编中使用b,bl,mov,adr,ldr(不使用=的时候是位置无关)等指令,在c语言中不要使用全局变量和静态变量。


8.链接脚本编写

链接脚本的格式:

SECTIONS {

...

secname start ALIGN(align) (NOLOAD) : AT(ldadr)

    {contents} >region :phdr =fill

...

}

SECTION(段)是链接脚本的基本单元,它表示输入文件的某部分应该怎么放置。在SECTION中,secname和contents两项是必须的,其他是可选的,secname用来命名这个段,contents用来确定代码中的什么部分放在这个段中。

start表示这个段的重定位地址,即运行地址。

ALIGH(align):用来指定对齐的要求,start指定的起始地址未必符合处理器的一些优化要求,所以这个相对于start对齐的地址才是真正的运行地址。

(NOLOAD):告诉加载器,在运行时不用加载这个段。这个段只有在有操作系统的情况下才有意义。

AT(ldadr):指定这个段在编译出来的影响文件中的地址——加载地址(load address)。在没有指定这个地址的时候,加载地址就等于运行地址,也就是说加载地址是根据用户指定的start做过偏移计算的(那不然不会等于运行地址)。通过这个选项可以控制各段分别保存输出在文件中不同的位置,便于文件的保存:A段在A处,B段在B处,运行前再把A、B段分别读出来组装成一个完整的执行程序。

>region:通过使用>region把一个节赋给前面已经定义的一个内存区域比如下面的这个例子:

MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 }

SECTIONS {

ROM : { *(.text) } >rom

}

:phdr:通过使用`:PHDR'把一个节赋给前面已定义的一个程序段。如果一个节被赋给一个或多个段,那后来分配的节都会被赋给这些段,除非它们显式使用了':PHDR'修饰符。你可以使用':NONE'来告诉连接器不要把节放到任何一个段中。看下面的例子:

PHDRS { text PT_LOAD ; }

SECTIONS {

.text : { *(.text) } :text

}

fill:你可以通过使用'=FILLEXP'为整个节设置填充样式。FILLEXP是一个表达式。任何没有指定的输出段内的内存区域(比如,因为输入段的对齐要求而产生的裂缝)会被填入这个值。如果填充表达式是一个简单的十六进制值,比如,一个以'0x'开始的十六进制数字组成的字符串,并且尾部不是'k'或'M',那一个任意的十六进制数字长序列可以被用来指定填充样式;前导零也变为样式的一部分。对于所有其他的情况,包含一个附加的括号或一元操作符'+',那填充样式是表达式的最低四字节的值。在所有的情况下,数值是big-endian.你还可以通过在输出节命令中使用'FILL'命令来改变填充值。例子:

SECTIONS {

.text : { *(.text) } =0x90909090

}


一个简单的lds文件例子

SECTIONS {

. = 0x30000000; //冒号两边需要填充空格 1

.text            : {*(.text)} //2

.rodata ALIGH(4) : {*(.rodata)} //3

.data ALIGH(4)   : {*(.data)} //4

.bss ALIGH(4)    : {*(.bss) *(COMMON)} //5

}

1:设置当前运行地址为0x30000000;

2:定义了一个名字为.text的段,它的内容为* (.text),表示所有文件的代码段,这些代码段被集合在一起,起始运行地址为0x30000000;

3、4:类似于2,只是有了4字节对齐的约束。

5:这个例子中将所有输入文件的所有通用符号放入输出.bss section内。

注意:一般bss段是紧跟着data段之后的,段的顺序不要改变。

更详细的了解链接文件,请于网上搜索:GNU linker script (GNU官方给出的说明)


9.在编译bootloader、内核时,常用arm-linux-objcopy命令将ELF格式的生成结果转换为二进制文件,例如:

arm-linux-objcopy -O binary -S elf_file bin_file

-O选项:使用指定的格式来输出文件。

-S选项:不从源文件中复制重定位信息和符号信息到目标文件中去。


10.反汇编代码的查看

4bc:    e3a0244e    mov r2, #1308622848; 0x4e000000

4bc:表示这个代码的运行地址。

ea30244e:机器码。

ARM的数据处理格式为:

[31:28]:0b1110,表示这条指令无条件执行。

[25]:0b1,表示Operand2是一个立即数。

[24:21]:0b1101,表示这是mov指令。

[20]:0b0,表示这条指令执行时不影响状态位。

[15:12]:0b0010,表示Rd就是r2。

[11:0]:0x44e,这是一个立即数。

立即数占据机器码中的12位bits表示,最低8位为immed_8,高4位称为rotate_imm。最终的立即数计算方法为:immed_8>>(2*rotate_imm)。这里是循环右移。

你可能感兴趣的:(编译命令,链接文件)