首页 > 其他分享 >计组学习08——CALL

计组学习08——CALL

时间:2022-12-24 19:55:06浏览次数:39  
标签:文件 汇编器 计组 标签 08 地址 指令 CALL 链接

计组学习 —— CALL

翻译VS解释 Translation vs Interpretation

  • 如何跑一个用原始语言写的程序?

    • Interpreter:直接在源语言中执行程序
    • Translator:将一个源语言中的程序转化为一个别的语言的程序
    • Translate会非常快,Interperter的代码量会很小
  • C的特点:C的一个很特别的点就是,可以分别编译文件,再之后把他们组合起来形成一个可执行的程序

    • 文件都可以访问什么?
      • 程序
      • 全局变量
  • CALL的结构(C的编译过程)

image

那么接下来就一个一个解析一下这几个东西!

Compiler编译器

  • 输入:高级语言代码(比如C或者Java)

  • 输出:汇编语言代码(比如RISC-V架构的foo.s)

  • 输出中可能包含伪指令(pseudo-instructions)

  • 事实上,还有一个预处理指令,它将处理所有的宏定义指令

编译器实际上很复杂,需要讲的内容有很多,在计组里不过多阐述

CS164是完整的编译原理,可以到那里继续进修

Assembler汇编器

  • 输入:汇编语言代码(比如foo.s)
  • 输出:目标代码/文件(object code/file) 比如(foo.o)

汇编器都会:

  1. 读取并且使用指令
  2. 把伪指令替换掉
  3. 产生机器语言

Assembler Directives

  • 他们向汇编语言提供方向但是不产生机器指令

    .text:在后面加上一些用户文本部分或者代码部分中的项目

    .data:类似于text,除此之外把内容放在静态字段或者内存的数据段

    .globl sym:声明 sym这个全局变量,并且可以被其他文件使用

    .asciiz str: 把字符串str存储在内存里,也是以null终止

    .word w1 w2 wn:接受由空格分割的整数或者单词,然后存储着n个32位量在连续的内存里

替换伪指令:在之前已经举过例子了,会把一些伪指令替换成真正的指令

产生机器语言

  • 简单的情况
    • 算术或者逻辑指令,位运算都算比较简单的,交给计算机就好
    • 因为所有的信息都在指令里
  • 分支和跳跃呢?
    • 分支和跳跃需要相对地址
    • 当我们把伪指令转化为真正指令的时候,我们会计算每条指令,并且弄清他们的去向这意味着我们会知道地址
  • 如果跳跃到一些标签呢?
    • 分支控制语句可能跳转到一些以后的标签,我们还没有计算过地址的标签,甚至于汇编器都不知道这个标签的意义是什么,该怎么办呢?
      • 解决方案:让汇编器扫描两次,这样就都知道了

汇编器的两次扫描

  • 第一次
    • 把伪指令替换成真正的指令
    • 将标签的地址记住
    • 去掉注释,空白行
    • 检查错误
  • 第二次
    • 使用标签的地址区生成相对地址(相对地址用于分支跳转和标签跳转等)
    • 输出目标文件(object file),目标文件只是二进制代码中指令的集合

仍然不能被创建的东西

  • 如果跳转到外部标签
    • 需要知道最终地址
    • 不知道是不是向前或者向后,不能生成机器指令,因为不知道内存里指令的位置
  • 关于数据
    • la这种伪指令会被拆分程lui 和 addi
    • 这回需要32位地址的数据
  • 这些不能被决定的东西,我们的想法是创建两个表,这两个表可以位其他文件储存这些信息。当我们使用连接器把两个程序连接到一起是,使用这个表。

符号表(symbol table)

  • 符号表是一个用来储存其他文件可能使用的”东西‘的列表

  • 这些“东西”是指:

    • 标签:函数调用

    • 数据:所有在.data部分

      变量可被文件访问

  • 保持对标签的追踪解决了“前向关联问题”

重定位表(relocation table)

  • 重定位表是一个用来存储文件里一会可能要用到地址的“东西”
  • 这些“东西”是指:
    • 任何拓展标签(jal,jar)
      • 内部标签 internal
      • 拓展标签 external (包括库文件)
    • 认可数据段
      • 比如任何在data部分提到的

标准的Object File格式

  1. 目标文件头:大小、目标文件的其他部分的位置
  1. text段:机器代码
  2. data段:source文件里的数据(二进制)
  3. 重定位表:指明需要被链接器“处理”代码的行
  4. 符号表:这个文件的可被引用的标签和可以被其他文件引用的数据的表格
  5. 调试信息:(比如-g的调试信息)标准格式是ELF

Linker

  • 输入:目标文件(object file)
  • 输出:可执行代码(Executable Code)
  • 把多个目标文件组合成为一个可执行文件("linking")
  • 可以进行单独的编译文件(非常有用!)
    • 如果改变单个文件不需要重新编译整个项目

image

比如object file 1是我们的程序文件,object file 2是我们的库文件链接器可以把我们两个部分链接到一起

  1. 链接器将从每个.o文件中提取文本段然后把他们放在一起

  2. 把每个.o文件的数据段放在一起,之后把数据连接到文本段之后

  3. 解决所有引用

    • 看重定位表并且处理其中每个条目
    • 填写所有最终的绝对地址

三种地址类型

  • 相对PC的地址(beq, bne, jal)
    • 不需要重定位,只要代码之间的相对位置不变,就不需要改变
  • 外部函数的引用(往往都是jal)
    • 往往需要重定位
  • 静态数据的引用(往往都是auipc和addi)
    • 往往需要重定位
    • RISCV经常用auipc

RISC-V中的绝对寻址

哪些指令需要重新定位编辑?

  • J格式的指令: jump / jump and link
  • 加载或者储存变量 到 静态区域里,和全局指针相关
  • 条件分支,也是PC相关的,但是不用担心搬移,因为代码之间的相对位置没有改变

image

解决引用问题
  • 链接器假设文本段的第一个是0x1000

    • 在之后涉及到虚拟内存可以解释
  • 链接器知道:

    • 每一个文本段和数据段的长度
    • 文本段和数据段的顺序
  • 链接器计算:

    • 每一个被跳转的标签(内部的或者拓展的)的绝对地址
    • 被引用的所有数据
  • 解决引用问题:

    • 在所有"调用者"的符号表里搜索引用(数据或者标签)
    • 如果没找到,那么就在库文件里搜索(比如printf)
    • 一旦绝对地址被决定了,立刻填写进机器代码里
  • 链接器的输出:包含文本段和数据段的可执行文件

Loader 装载程序

  • 输入:可执行代码

  • 输出: <程序跑起来了>

  • 可执行文件储存在磁盘里

  • 当一个程序要运行的时候,装载器的作用就是把他加载进入内存里然后启动它

  • 事实上,装载器就是操作系统的一部分

    • loading是操作系统的工作的一部分
  1. 读取可执行文件的头,以确定文本和数据段的大小
  2. 为程序创建一个空间,以容纳文本段和数据段还有栈空间和堆空间
  3. 把指令和数据复制进入新的地址空间
  4. 复制参数传给在栈里的程序
  5. 初始化寄存器
    • 大多数寄存器都被清空,stack pointer指向栈的第一个空间
  6. 跳转到启动例程(start-up routine),从栈复制程序的参数,粘贴给寄存器,设置PC指针

一个CALL的例子

我们有如下的C程序

#include<stdio.h>
int main(){
    printf("Hello,%s\n","world");
    return 0;
}
  • 第一步,把Hello.c编译为Hello.s

    image

    可以看到,有一些汇编指令告诉我们接下来的部分是在内存的.text部分,接下来我们把main这个标签声明为全局的,这意味着其他文件可能访问它

    接下来在下面可以看到有一个.section告诉我们下一个部分变成数据的只读部分,接下来声明了两个string,这两个部分将进入内存然后接下来被引用

  • 接下来,汇编器需要讲文件变成目标文件

    image

    可以看到我们这里有许多的占位符,这些用红色标注的0都是我们目前并不知道位置,所以只能暂时用0来填充。可以看到我们为1c的jalr也进行了填充,因为我们并不知道printf在哪里

  • 接下来,链接器工作

    image

    链接器接受整个目标文件并且和标准库链接在一起,这样我们就知道了printf的地址,和数据部分的地址

  • 接下来,加载器加载程序到内存并且启动,计算机执行程序

总结

image

  • 编译器 把高级语言程序(HLL)转化成简单的汇编语言
  • 汇编器 移除伪指令,转化成机器语言,为链接器创建数据表(重定位表和符号表)
    • 符号表是标签的地址列表,这样使得链接在一起的文件知道其他数据的地址
    • 在汇编器中,两遍扫描来解决内部的”前向引用“的问题
  • 链接器 合并多个目标文件,解析绝对地址。因此它将接受一个或多个目标文件或o文件然后将输出一个.out文件
    • 如果一个文件改变,只需要重编译他自己,再和其他文件链接到一起

标签:文件,汇编器,计组,标签,08,地址,指令,CALL,链接
From: https://www.cnblogs.com/ZzTzZ/p/17003297.html

相关文章

  • 实验八-web部署-20221308刘志伟
    实验步骤在华为云openEuler安装后,没有配置yum源,我们通过重新配置。输入命令以切换到rpos目录下cd/etc/yum.repos.d输入命令更换yum源viopenEuler_x86_64.repo增加......
  • 省选08. 组合计数
    P4091[HEOI2016/TJOI2016]求和有一个重要的通项公式\[\begin{Bmatrix}n\\m\end{Bmatrix}=\sum_{i=0}^{m}\frac{i^n(-1)^{m-i}}{i!(m-i)!}\]\[ans=\sum_{i=0}^{n}\sum......
  • POJ 1080 Human Gene Functions
    POJ1080HumanGeneFunctions题意:给出两组\(DNA\)序列求它们的最大相似度每个字母与其他字母或自身和空格对应都有一个打分,求在这两个字符串中插入空格,让这两个字......
  • ts08_使用webpack打包ts文件
    1.新建项目使用npminit-y在根目录生成packge.json文件,管理包2.使用npm安装webpack相关工具npmi-Dwebpackwebpack-clitypescriptts-loader,ts-loader起到一个整合......
  • 代码随想录算法训练营Day23|669. 修剪二叉搜索树、108. 将有序数组转换为二叉搜索树、
    代码随想录算法训练营Day23|669.修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树669.修剪二叉搜索树与删除节点类似,但不需要讨论左/......
  • day08-功能实现07
    家居网购项目实现07以下皆为部分代码,详见https://github.com/liyuelian/furniture_mall.git16.功能15-会员显示登录名16.1需求分析/图解会员登录成功login_ok.......
  • T1408 矩阵嵌套(DAG 记忆化搜索)
    T1408矩阵嵌套​ 有n个矩阵,每个矩阵有长x和宽y。我们定义矩阵A可以嵌套在矩阵B中:A.x>B.x且A.y>B.y或者A.x>B.y且A.y>B.x。我们现在要找一个最长......
  • Day17_08_SpringCloud教程之Eureka开启健康检查功能
    08_SpringCloud教程之Eureka开启健康检查功能一.Eureka健康监测概述默认情况下,Eureka的健康检测并不是通过actuator的health端点来实现的,而是依靠客户端心跳的方式来保持......
  • Xmind 2022 for Mac(思维导图软件) 22.11.2508中文版
    Xmind2022forMac是一款全功能的思维导图软件,拥有专业实用的功能,包括思维管理、商务演示、与办公软件协同工作等功能,让您的办公更有效,还加入了演讲模式,在演说模式可自动生......
  • Day08_03_分布式教程之Linux下搭建Zookeeper
    Linux下搭建Zookeeper一.Zookeeper配置说明1.Zookeeper的三种工作模式单机模式: 可能存在单点故障;集群模式: 在多台机器上部署 ​​Zookeeper​​ 集群,适合线上环......