C 语言学习(5) ---- 汇编语法基础

ops/2024/12/22 13:18:53/

目录

        • 汇编语言基础
        • x86 体系的寄存器说明
        • Intel 和 AT&T 语法
        • "helloworld" 汇编程序分析
        • 指令和伪指令
        • TBD

汇编语言基础

汇编程序基本由 4 种类型的组件组成:指令(instruction)伪指令(directive)、标号(label)以及注释(comment)

类型示例含义
指令mov eax 0给 EAX 赋值为0
伪指令.section.text以下代码放入 text 节
伪指令.string "foobar"定义一个 ASCII 字符串 foobar
伪指令.long 0x12345678定义一个双字 0x12345678
标号foo:.string "foobar"使用符号定义的 foobar 字符串
注释#注释表示注释信息
x86 体系的寄存器说明

在 x86 架构的 CPU 中,存在多种可编程的寄存器,它们用于不同的目的,以下是一些主要的可编程寄存器类型:

  1. 通用寄存器
    • AX (累加器寄存器):用于算术运算和 I/O 指令。
    • BX (基址寄存器):用于存储数据指针。
    • CX (计数寄存器):通常用于循环计数。
    • DX (数据寄存器):用于 I/O 操作和其他数据传输。
    • SI (源索引寄存器)、DI (目的索引寄存器):用于字符串操作。
    • BP (基址指针寄存器):用于访问栈中的参数和局部变量。
    • SP (堆栈指针寄存器):用于管理堆栈。
    • AXBXCXDX 寄存器还可以分为高字节和低字节寄存器,例如 AHAL 分别是 AX 的高字节和低字节。

EAX 是一个32位的寄存器,用于处理 32 位的数据,而 AX 是一个 16 位的寄存器,用于处理 16 位的数据。
在32位模式下,AXEAX 的一个低位部分,可以直接用于 16 位数据的操作。

x86 基于原始的 8086 指令集,寄存器是16位宽,而32bit 的 x86 ISA 将这些寄存区扩展为32位,然后 x86-64 将它们进一步扩展为64位。
为了保证向后兼容性,新指令集中使用的寄存器是旧的寄存器的超集。

汇编中指定寄存器的操作数,需要是以寄存器的名称,比如 mov rax,64 就是将 64 赋值给 rax 寄存器,
下图显示了如何将 x86-64 rax 寄存器分为传统的 32 位和 64 位寄存器,rax 的低32位形成了一个名位 eax 的寄存器,
其低16位形成了一个原始的 8086 寄存器 AX,读者可以通过寄存器名称 AL 访问 AX 的低字节,通过 AH 访问 AX 的高字节
AX寄存器分析
同样的,其他的通用寄存器,也会与类似的用法:

描述64bit低32bit0~16 bit8~16 bit0~8 bit
累加器rxaeaxaxahal
基址寄存器rbxebxbxblbh
程序计数器rcxecxcxclch
数据寄存器rdxedxdxdldh
  1. 段寄存器

    • CS (代码段寄存器):包含当前执行指令的段地址。
    • DS (数据段寄存器):包含数据段的段地址。
    • ES (附加段寄存器):用于额外的数据段。
    • FSGS:提供额外的段寄存器,由操作系统或应用程序定义。
    • SS (堆栈段寄存器):包含堆栈段的段地址。
  2. 指令指针寄存器

    • IP (指令指针寄存器):也称为 EIP(在 32 位模式下)或 RIP(在 64 位模式下),它指向下一条要执行的指令。
      本质上就是指针指针寄存器
  3. 标志寄存器

    • FLAGS (标志寄存器):包含各种状态标志,如进位、零、符号、溢出等。
  4. 控制寄存器

    • CR0 - CR4:用于控制处理器的操作模式和特性,如分页、保护模式等。
  5. 系统寄存器

    • MSR (模型特定寄存器):提供对处理器特定功能的访问,如性能监控计数器。
  6. 浮点寄存器

    • ST0 - ST7 (浮点堆栈寄存器):用于浮点运算。
    • Control WordStatus WordTag WordInstruction PointerLast Instruction Opcode:用于控制浮点单元的操作和报告状态。
  7. SIMD 寄存器

    • MMX 寄存器:用于多媒体扩展运算。
    • XMM0 - XMM15 (SSE 寄存器):用于 SIMD 扩展运算。
    • YMM0 - YMM15 (AVX 寄存器):在 AVX 指令集中使用,提供更大的 SIMD 数据宽度。
      这些寄存器在不同的 x86 处理器模式下(实模式、保护模式、长模式等)可能会有不同的名称和大小。
      例如,在 32 位模式下,通用寄存器通常有 32 位宽,而在 64 位模式下,它们会扩展到 64 位宽。
Intel 和 AT&T 语法

正如前面所提到的,不同的汇编器对汇编程序有不同的语法,表示 x86 机器指令的语法格式主要有两种:Intel 语法 和 AT&T 语法,
AT&T 语法显式的再每个寄存器名称前面加上 % 符号,每个常量前面加上$ 符号, Intel 语法没有这种格式:

AT&TIntel之间最重要的区别是使用完全相反的指令操作数顺序:
AT&T语法中源操作数在目的操作数前面,所以是从左向右读
Intel语法中目的操作数位于源操作数前面,所以是从右向左读
如果汇编语言中出现 % 和 $ 前缀,就是使用AT&T语法,应该从左向右读

“helloworld” 汇编程序分析

helloworld汇编代码:

  1         .file   "main.c"2         .text3         .section        .rodata4 .LC0:5         .string "hello world"6         .text7         .globl  main8         .type   main, @function9 main:10 .LFB0:11         .cfi_startproc12         endbr6413         pushq   %rbp14         .cfi_def_cfa_offset 1615         .cfi_offset 6, -1616         movq    %rsp, %rbp17         .cfi_def_cfa_register 618         leaq    .LC0(%rip), %rdi19         call    puts@PLT20         movl    $0, %eax21         popq    %rbp22         .cfi_def_cfa 7, 823         ret24         .cfi_endproc25 .LFE0:26         .size   main, .-main27         .ident  "GCC: (Ubuntu 9.5.0-1ubuntu1~22.04) 9.5.0"28         .section        .note.GNU-stack,"",@progbits29         .section        .note.gnu.property,"a"30         .align 831         .long    1f - 0f32         .long    4f - 1f33         .long    534 0:35         .string  "GNU"36 1:37         .align 838         .long    0xc000000239         .long    3f - 2f40 2:41         .long    0x342 3:43         .align 844 4:

从中可以看出:

  • printf 函数最终是调用到 puts 执行打印 helloworld 的操作
  • main 函数的返回值是通过 EAX 传递的
  • 函数执行时会将当前 rbp 寄存器的值压入到堆栈中,然后将当前开辟的 rsp的值赋给它
  • 函数执行结束会弹出 rbp 寄存器的值
指令和伪指令

指令是 CPU 执行的实际操作,伪指令意在告诉汇编工具生成特定的数据,并将指令和数据放在指定的节,
标号汇编工具中引用指令或数据的符号名称,注释是可读注释。在程序被汇编链接成二进制程序后,所有的符号名称都被地址所取代

伪指令:
.section 告诉汇编工具将在那个节放置后面的内容,
.ascii 表示定义 ascii 字符串的伪指令,当前还有一些伪指令用于定义其他数据类型:
.byte 一个字节
.word 两个字节
.long 四个字节
.quad 八个字节

TBD

http://www.ppmy.cn/ops/91986.html

相关文章

更换低版本jdk8后的idea页面怎么换回来

一、问题阐述 一开始我的idea是下面的界面: 这个页面美观,简洁。后来因为工作需要,从jdk17切换到jdk8的时候,页面变了 这个没有前面的好看,怎么回事? 二、解决方案 1、file——setting 2、搜索ui——New UI…

【C++语法糖】范围for

文章目录 【C语法糖】:范围for范围for1. 范围for的模板2. &的使用3. 范围for的底层 【C语法糖】:范围for C11标准后引入了范围for,这个范围for是一种语法糖,来简化代码书写。 下面是简单遍历数组的一段简单代码 int arr[10] { 0,1,2,3,4,5,6,7,8…

【C++】C++11的新特性 — 线程库 ,原子操作 , 条件变量

勇敢就是接受发生在你身上的事,并把它尽力做到最好。 -- 约翰・欧文 -- C11的新特性 1 线程1.1 线程概念1.2 C中的线程1.3 线程并行1.4 锁 2 原子操作3 条件变量Thanks♪(・ω・)ノ谢谢阅读!!!下…

Linux查看系统线程数

Linux查看系统线程数 查看线程数查看进程内的线程统计线程数 查看线程数 想要查看Linux操作系统允许的最大线程数,可以通过命令 ulimit -a返回配置项的详细说明: # core文件的最大值为100blocks core file size (blocks, -c) 0# 进程的数…

[激光原理与应用-122]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 23 - BSB环形光斑激光焊接的特殊检测手段

目录 前言: 一、双光束(环形光斑)激光焊接的方案 1.1 概述 1.2 应用案例 1.3激光器技术参数: 1.4 应用数据 二、双光束(环形光斑) 2.1 优点:可以解决激光焊接如下常见问题 1、焊接飞溅…

AWS DMS遇到 Error : no handler found for uri

问题描述: 当我按照文档[1]配置AWS DMS 目标端为OpenSearch, 并进行数据迁移的时候,我遇到了如下报错: 00015696: 2024-07-31T03:26:57 [TARGET_LOAD ]E: Elasticsearch:FAILED SourceTable:test TargetIndex:test Operation:INSERT_E…

学习笔记第二十二天

1. time 函数 time_t time(time_t *t); 功能:获取当前时间(自1970年1月1日00:00:00 UTC以来的秒数)。 参数:t 是一个指向 time_t 类型的指针,用于存放返回的时间值。如果传递 NULL,函数将不 会使用此参数…

MongoDB学习记录

1、初识Mongo 概述:与关系型数据库不同,MongoDB 的数据以类似于 JSON 格式的二进制文档存储,通常称这种格式为Bson,Bson不仅支持JSON中已有的数据类型,还增加了一些额外的数据类型,例如日期和二进制数据&a…