linux as ld gcc 更新,gcc, as, ld的一些笔记(2)

9.gas汇编工具:as(at&t风格)语法说明

使用$标识立即数

再寄存器前面加上%

源操作数在前,目标操作数在后

使用$获取变量地址

长跳转使用:ljmp $section, $offset

一个简单的汇编语言程序框架:

.section .data

……

.section .bss

……

.section .text

.globl _start

_start:

……

范例:

#cpuid2.s View the CPUID Vendor ID string using C library calls

.section .datatext

output:

.asciz "The processor Vendor ID is '%s'\n"

.section .bss

.lcomm buffer, 12

.section .text

.globl _start

_start:

movl $0, %eax

cpuid

movl $buffer, %edi

movl %ebx, (%edi)

movl %edx, 4(%edi)

movl %ecx, 8(%edi)

pushl $buffer

pushl $output

call printf

addl $8, %esp

pushl $0

call exit

伪指令说明:

data

.ascii

定义字符串,没有\0结束标记

data

.asciz

有\0结束标记

data

.byte

字节

data

.int

32位

data

.long

32位

data

.shot

16位

bss

.lcomm

对于上面的例子是声明12字节的缓冲区,l标识local,仅当前汇编程序可用

bss

.comm

通用内存区域

data/text

.equ

.equ  LINUX_SYS_CALL, 0x80

movl $ LINUX_SYS_CALL, %eax

说明:equ不是宏而是常量,会占据数据/代码段空间

指令集说明:

movb/movw/movl

cmov

根据cf, of, pf, zf等标识位判断并mov

xchg

操作时会lock内存,非常耗费cpu时间

bswap

翻转寄存器中字节序

xadd

pushx, popx

pushad, popad

jmp

call

cmp

jz/jb/jne/jge

loop

addb/addw/addl

subb/subw/subl

dec/inc

mulb/muw/mull

无符号乘法

源操作数长度

目标操作数

目标位置

8位

al

ax

16位

ax

dx:ax

32位

eax

edx:eax

imul有符合乘法

divb/divw/divl

无符合除法

(被除数在eax中,除数在指令中给出)

被除数

被除数长

余数

ax

16位

al

ah

dx:ax

32位

ax

dx

edx:eax

64位

eax

edx

idiv有符合除法

sal/shl/sar/shr

移位

rol/ror/rcl/rcr

循环移位

leal

取地址:leal  output, %eax

等同于:movl  $output, %eax

rep

rep movsb执行ecx次

lodsb/lodsw/lodsl

stosb/stosw/stosl

取存内存中的数据

gas程序范例(函数调用):

文件1:area.s定义函数area

# area.s - The areacircumference function

.section .text

.type area, @function

.globl area

area:

pushl %ebp

movl %esp, %ebp

subl $4, %esp

fldpi

filds 8(%ebp)

fmul %st(0), %st(0)

fmulp %st(0), %st(1)

fstps -4(%ebp)

movl -4(%ebp), %eax

movl %ebp, %esp

popl %ebp

ret

文件2:functest4.s调用者

# functest4.s - An example of using external functions

.section .data

precision:

.byte 0x7f, 0x00

.section .bss

.lcomm result, 4

.section .text

.globl _start

_start:

nop

finit

fldcw precision

pushl $10

call area

addl $4, %esp

movl %eax, result

pushl $2

call area

addl $4, %esp

movl %eax, result

pushl $120

call area

addl $4, %esp

movl %eax, result

movl $1, %eax

movl $0, %ebx

int $0x80

10.读连接器和加载器的一些笔记,感谢原作者colyli at gmail dot com,看了他翻译的lnl及写的一个os,受益匪浅。

如果不是很深入的研究连接器和加载器的话,了解一些原理就足够了。举个例子说明吧:

1 #include

2 #include

3 #include

4 #include

5

6 int a = 1;

7 int main()

8 {

9         printf("value: %d\n", a);

10

11         return 0;

12 }

编译指令:gcc -c hello.c -o hello.o汇编

gcc -o hello hello.o编译

objdump -d hello.o反汇编目标文件

objdump -d hello反汇编可执行文件

比较两端结果:

objdump -d hello.o

objdump -d hello

00000000 :

0:   55         push  %ebp

1:   89 e5       mov  %esp,%ebp

3:   83 ec 08    sub   $0x8,%esp

6:   83 e4 f0     and  $0xfffffff0,%esp

9:   b8 00 00 00 00  mov    $0x0,%eax

e:   83 c0 0f        add    $0xf,%eax

11:   83 c0 0f        add    $0xf,%eax

14:   c1 e8 04        shr    $0x4,%eax

17:   c1 e0 04        shl   $0x4,%eax

1a:   29 c4          sub   %eax,%esp

1c:   83 ec 08        sub  $0x8,%esp

1f:   ff 35 00 00 00 00  pushl  0x0

25:   6800 00 00 00  push   $0x0

2a:   e8fc ff ff ff call  2b

2f:   83 c4 10        add $0x10,%esp

32:   b8 00 00 00 00  mov  $0x0,%eax

37:   c9            leave

38:   c3            ret

08048368 :

8048368: 55      push   %ebp

8048369: 89 e5    mov    %esp,%ebp

804836b: 83 ec 08  sub    $0x8,%esp

804836e: 83 e4 f0  and    $0xfffffff0,%esp

8048371: b8 00 00 00 00  mov  $0x0,%eax

8048376: 83 c0 0f        add  $0xf,%eax

8048379: 83 c0 0f       add   $0xf,%eax

804837c: c1 e8 04       shr   $0x4,%eax

804837f:  c1 e0 04      shl   $0x4,%eax

8048382: 29 c4          sub  %eax,%esp

8048384: 83 ec 08       sub   $0x8,%esp

8048387: ff 35 94 95 04 08 pushl 0x8049594

804838d: 68 84 84 04 08  push $0x8048484

8048392:   e8 19 ff ff ff  call  80482b0

8048397: 83 c4 10       add  $0x10,%esp

804839a:  b8 00 00 00 00 mov $0x0,%eax

804839f: c9            leave

80483a0: c3            ret

80483a1: 90           nop

80483a2: 90           nop

80483a3: 90           nop

简单说明:由于程序运行时访问内存,执行跳转都需要确切的地址。所以汇编处理的目标文件里面没有包含,而是把这个工作放到连接器中:即定位地址。

当程序需要动态链接到某个库上时,使用该库的got表动态定位跳转即可。

具体可以看colyli大侠的《链接器和加载器Beta 2》,及《从程序员角度看ELF》

11.连接器脚本ld—script(相关内容来自《GLD中文手册》)

ld --verbose查看默认链接脚本

ld把一定量的目标文件跟档案文件连接起来,并重定位它们的数据,连接符号引用.一般在编译一个程序时,最后一步就是运行ld。

实例1:

SECTIONS

{

. = 0x10000;

.text : { *(.text) }

. = 0x8000000;

.data : { *(.data) }

.bss : { *(.bss) }

}

注释:“.”是定位计数器,设置当前节的地址。

实例2:

floating_point = 0;

SECTIONS

{

. = ALIGN(4);

.text :

{

*(.text)

_etext = .;

PROVIDE(etext = .);

}

. = ALIGN(4);

_bdata = (. + 3) & ~ 3;

.data : { *(.data) }

}

注释:定义一个符合_etext,地址为.text结束的地方,注意源程序中不能在此定义该符合,否则链接器会提示重定义,而是应该象下面这样使用:

extern char _etext;

但是可以在源程序中使用etext符合,连接器不导出它到目标文件。

实例3:

SECTIONS {

outputa 0x10000 :

{

all.o

foo.o (.input1)

}

outputb :

{

foo.o (.input2)

foo1.o (.input1)

}

outputc :

{

*(.input1)

*(.input2)

}

}

这个例子是一个完整的连接脚本。它告诉连接器去读取文件all.o中的所有节,并把它们放到输出节outputa的开始位置处, 该输出节是从位置0x10000处开始的。从文件foo.o中来的所有节.input1在同一个输出节中紧密排列。 从文件foo.o中来的所有节.input2全部放入到输出节outputb中,后面跟上从foo1.o中来的节.input1。来自所有文件的所有余下的.input1和.input2节被写入到输出节outputc中。

示例4:连接器填充法则:

SECTIONS { .text : { *(.text) } LONG(1) .data : { *(.data) } }错误

SECTIONS { .text : { *(.text) ; LONG(1) } .data : { *(.data) } }正确

示例5:VMA和LMA不同的情况

SECTIONS

{

.text 0x1000 : { *(.text) _etext = . ; }

.mdata 0x2000 :

AT ( ADDR (.text) + SIZEOF (.text) )

{ _data = . ; *(.data); _edata = . ;  }

.bss 0x3000 :

{ _bstart = . ;  *(.bss) *(COMMON) ; _bend = . ;}

}

程序:

extern char _etext, _data, _edata, _bstart, _bend;

char *src = &_etext;

char *dst = &_data;

/* ROM has data at end of text; copy it. */

while (dst < &_edata) {

*dst++ = *src++;

}

/* Zero bss */

for (dst = &_bstart; dst< &_bend; dst++)

*dst = 0;

示例6:linux-2.6.14/arch/i386/kernel $ vi vmlinux.lds.S

linux内核的链接脚本,自行分析吧,有点复杂哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值