AT&T 汇编总结篇

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/for_cxc/article/details/89048046

1、汇编器和链接器

GNU binutils 里的 as、ld

2、通常一个汇编程序包含三个部分

The data section        //初始化的数据部分
The bss section         //未初始化的数据部分以0填充
The text section         //程序部分

汇编器声明一个部分 使用 .section 指令,后面跟一个参数,下图是一个基本布局:

bss 部分应该总是在 text 部分之前,而 data 部分可以在 text 部分之后,尽管这不是一个标准。除了功能,您的汇编语言程序还应该易于阅读。在源代码开始时将所有的数据定义放在一起,使其他程序员更容易理解您的工作。

3、定义程序的起点或入口

当汇编程序转换为可执行程序,链接器必须知道起点在哪。在多个分散的源代码文件、多个函数的复杂程序中,查找程序的起始位置可能是一个问题。为了解决这个问题,GNU汇编器声明了一个默认的标签或标识符,该标签或标识符应该用于应用程序的入口点。_start 标签用于指示程序应该从哪条指令开始运行。如果链接器找不到这个标签,它会产生一个错误信息:

ld: warning: cannot find entry symbol _start; defaulting to 08048074

您可以使用 _start 之外的另一个标签作为起点。您可以使用链接器的 -e 参数来定义新起点的名称。

除了在应用程序中声明起始标签外,还需要使入口点对外部应用程序可用。这是通过 .globl 指令完成的。

.globl 指令声明可以从外部程序访问的程序标签。

如果您正在编写一组由外部程序集或C语言程序使用的实用程序,则每个函数节标签都应该使用 .globl 指令声明。

有了这些信息,您可以为所有汇编语言程序创建一个基本模板。模板应该是这样的:

扫描二维码关注公众号,回复: 6054212 查看本文章
.section.data
    < initialized data here>
.section .bss
    < uninitialized data here>
.section .text
.globl _start
_start:
    <instruction code goes here>

4、一个简单的汇编程序

#cpuid.s Sample program to extract the processor Vendor ID
.section .data
output:
   .ascii "The processor Vendor ID is 'xxxxxxxxxxxx'\n"
.section .text
.globl _start
_start:
   movl $0, %eax
   cpuid
   movl $output, %edi
   movl %ebx, 28(%edi)
   movl %edx, 32(%edi)
   movl %ecx, 36(%edi)
   movl $4, %eax
   movl $1, %ebx
   movl $output, %ecx
   movl $42, %edx
   int $0x80
   movl $1, %eax
   movl $0, %ebx
   int $0x80

编译和链接命令:

as -o cpuid.o cpuid.s
ld -o cpuid cpuid.o

5、使用gdb调试上面程序

要调试汇编程序,您必须在汇编源代码时加上 -g 选项,如下:

as -g -o cpuid.o cpuid.s
ld -o cpuid cpuid.o

然后开始调试,如下:

gdb cpuid

根据 Label 设置断点:

br *_start

运行:

r

逐行调试:

n (next) 或者 s (step)

继续运行:

c

6、调试时查看数据

查看所有寄存器的值:

i r (info registers)

查看程序中特定寄存器或变量的值:

p (print)

❑ print /d to display the value in decimal
❑ print /t  to display the value in binary
❑ print /x to display the value in hexadecimal

(gdb) print /x $ebx
$9 = 0x756e6547

查看特定内存位置的内容:

x

x 命令跟 p 命令类似, 它的输出可以用修饰符修改。 格式如下:
x /nyz
n 是要显示的字段数,y 是输出格式, 可以是以下几种情况
❑ c for character
❑ d for decimal
❑ x for hexadecimal
z 是要显示的字段的大小:
❑ b for byte
❑ h for 16-bit word (half-word)
❑ w for 32-bit word

x 命令的使用示例如下:

x /42cb &output

这条命令显示了  output 变量的前42字节,&符号指示它是一个内存位置,c代表字符模式。

这种特性在跟踪操作内存位置的指令时非常有用。

7、在汇编中使用 C 库函数

cpuid.s 程序使用Linux系统调用在控制台显示供应商ID字符串信息。还有其他方法可以在不使用系统调用的情况下执行此函数。

一种方法是使用C程序员熟悉的标准C库函数。很容易利用这个资源来使用许多常见的C函数。

下面是示例程序:

#cpuid2.s View the CPUID Vendor ID string using C library calls
.section .data
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

printf 函数需要一个以 null 结尾的字符串作为输出字符串。.asciz 指令将 null 添加到定义的字符串的末尾。

要将参数传递给 printf C函数,必须将它们推入堆栈。这是使用 pushl 指令完成的。

参数以与 printf 函数检索参数的方式相反的顺序放置在堆栈上,因此首先放置 buffer 值,然后是 output 值。

然后使用 call 调用。addl 指令用于清除为 printf 函数放置在堆栈上的参数。

使用相同的方式在堆栈上放置一个 0 返回值,以便C exit函数使用。

8、链接在汇编中使用 C 库函数的程序

要链接 C 库函数有两种方式。静态链接和动态链接,静态链接产生比较大的可执行文件和浪费更多的内存,每个可执行文件都有同一份数据。而动态链接库是程序运行时调用的,多个程序可以共享动态链接库。

在 Linux 系统,标准 C 动态链接库是在  libc.so.x 文件里面,x表示该库的版本号。以下是链接示例:

ld -dynamic-linker /lib/ld-linux.so.2 -o cpuid2 -lc cpuid2.o

其实也可以使用 gcc 来编译链接 汇编与 c组装的程序。并且它使用更简单,不需要复杂的参数,但是一定要把 _start 改为 main,然后 只需要 :

gcc -o cpuid2 cpuid2.s

结束!

猜你喜欢

转载自blog.csdn.net/for_cxc/article/details/89048046