用天然气进口的正确方法

如何解决用天然气进口的正确方法

从我之前的两个问题(一个与导入常量有关,一个与导入函数有关)-Why does this program loop?.include "filename",我想知道以下内容是否准确总结了如何使用{{ 1}},并附带示例:

as
# constants.s
SYS_EXIT        = 60
SYS_WRITE       = 1
STDOUT_FILENO   = 1
# utils.s
.include "constants.s"

# Global function
.globl print_string
print_string:

    call get_string_length
    mov %eax,%edx
    mov %rdi,%rsi
    mov $1,%edi
    mov $SYS_WRITE,%eax
    syscall
    ret

# Local function (for now)
get_string_length:
    mov $0,%eax # string length goes in rax
  L1_get_string_length:
    cmp $0,(%rdi,%rax,)
    je L2_get_string_length
    inc %eax
    jmp L1_get_string_length
  L2_get_string_length:
    ret

如果我的理解是正确的,那么:

  1. 在链接过程中,需要使功能# file.s .include "constants.s" .data str: .string "Hellllloooo" .text .globl _start _start: mov $str,%rdi call print_string mov $0,%edi mov $SYS_EXIT,%eax syscall 可供其他目标文件访问。这两个目标文件都需要链接在一起,例如:.globl
  2. 可以使用https://www.w3schools.com/js/js_loop_for.asp导入/包含定义或宏。这实际上是将包含的文件的内容复制/粘贴到该指令所在的位置。除了该文件的ld file.o utils.o -o file语句之外,我们不需要链接或做任何其他事情。多个文件是否使用相同的include语句有关系吗?
  3. 我可能还缺少其他任何东西或有关导入,包含等的提示吗? .include是否采用标准的Unix路径,例如我可以这样做:.include.include "../constants.s"

解决方法

这里有四种“从文件导入常量”的方法。

1。使用.include=(仅使用气体)

constants.inc:

    ANSWER_TO_LIFE = 0x42

code.s:

    .include "constants.inc"
    mov $ANSWER_TO_LIFE,%eax
    add $ANSWER_TO_LIFE,%ebx   # best encoding
    mov $(ANSWER_TO_LIFE+17),%ecx
    mov $(ANSWER_TO_LIFE*ANSWER_TO_LIFE),%edx

建筑物:

as -o code.o code.s         # or gcc -c code.s
ld -o prog code.o code2.o   # or gcc -o prog code.o code2.o

这是仅使用GNU汇编器本身功能的最直接的方法。我已将包含文件.inc而不是.s命名为表明该文件应包含在其他程序集源文件中,但不能单独进行汇编(因为它将生成不包含任何内容的目标文件) 。您可以根据需要使用常数将其包含到尽可能多的不同文件中,并且支持相对或绝对路径(.include ../include/constants.inc.include /usr/share/include/constants.inc都可以使用)。

由于汇编器知道常量的值,因此可以选择最佳的指令编码。例如,the x86 add $imm,%reg32 instruction has two possible encodings:使用32位立即操作数(操作码0x81)的6字节编码,以及使用8位符号扩展立即操作数(操作码0x83)的较小的3字节编码。由于0x42可以容纳8位,因此后者在此处可用,因此add $0x42,%ebx可以在三个字节中编码为83 c3 42。该示例还表明,我们可以在汇编时对常量执行任意运算。

2。使用C预处理器(实际上是最常用的)

constants.h:

#define ANSWER_TO_LIFE 0x42

code.S:

#include "constants.h"
    mov $ANSWER_TO_LIFE,%ebx   # also gets best encoding
    mov $(ANSWER_TO_LIFE*ANSWER_TO_LIFE),%ecx

建筑物:

gcc -c code.S              # can't use as by itself here
ld -o prog code.o code2.o  # or gcc if you prefer

采用这种方法,您在将源文件运行C预处理程序cpp之前,将其交给了汇编程序。如果使用gcc命名源文件,则.S命令将为您执行此操作(注意区分大小写)。然后,扩展C样式的#include#define伪指令,因此汇编器仅看到mov $0x42,%eax,而没有任何迹象表明该常量曾经有一个名称。

这种方法的优点是文件constants.h可以很好地包含在C代码中,这在项目将C和汇编源代码混合在一起的非常常见的情况下很有用。因此,这是我在“野外”最常看到的方法。 (实际上,没有现实生活中的程序是完全用汇编语言编写的。)

在您的原始用例中,所讨论的常量是Linux系统调用号,这种方法是最好的,因为相关的包含文件已经由内核开发人员编写,您可以使用#include <asm/unistd.h>来获取它。 。该文件使用__NR_exit形式的宏名称定义了所有系统调用号码。

3。作为符号在链接时解析(有点尴尬)

constants.s:

    .global ANSWER_TO_LIFE
    ANSWER_TO_LIFE = 0x42

code.s:

    mov $ANSWER_TO_LIFE,%ebx   # not the optimal encoding
    mov $(ANSWER_TO_LIFE+17),%ecx
    #mov $(ANSWER_TO_LIFE*ANSWER_TO_LIFE),%ecx # error

建筑物:

as -o constants.o constants.s          # or gcc -c constants.s
as -o code.o code.s                    # etc
ld -o prog constants.o code.o code2.o  # or gcc

这是@fuz在评论中提到的方法。它将符号ANSWER_TO_LIFE视为恰好位于绝对地址0x42的标签。汇编程序会像对待其他任何标签一样对待它。它在汇编时不知道其地址,因此将其保留为目标文件code.o中的未解析引用,链接器将最终对其进行解析。

这种方法的唯一真正好处是,如果我们要更改常量的值(例如0x43),则不必在所有源文件上重新运行汇编器{{1 }};我们只需要重新组装code.s code2.s ...并重新链接。因此,我们节省了一些构建时间,但是并没有太多,因为反正汇编代码通常都非常快。 (如果我们从C或C ++代码中引用符号,可能会有所不同,因为C或C ++代码的编译速度较慢,但​​请参见下文。)

但是有一些明显的缺点:

  • 由于汇编器不知道该常量的值,因此必须假定该常量的大小对于使用该常量的每个指令均有效。特别是在constant.s中,不能假定8位0x83编码将可用,因此必须选择较大的32位编码。因此,指令add $ANSWER_TO_LIFE,%ebx必须被汇编为add $ANSWER_TO_LIFE,%ebx,其中81 c3 00 00 00 00被链接器替换为正确的值00 00 00 00。但是我们最终在一条指令上使用了6个字节,理想情况下,本来可以使用3个字节进行编码。

  • 与此相反,立即mov到64位寄存器中也有两种编码:一种采用带符号扩展的32位立即42 00 00 00(带有REX的操作码c7) .W前缀),它是7个字节,另一个则是完整的64位立即数mov $imm32,%reg64(带有REX.W的操作码b8-b4),它是10个字节。默认情况下,汇编器将选择32位格式,因为64位格式确实很长,几乎不需要。但是,如果事实证明您的符号的值不适合32位,则在链接时会收到错误消息(“重定位被截断以适合”),您必须返回并强制输入64使用助记符mov $imm64,%reg64进行位编码。如果您使用的是方法1或2,则汇编程序将知道常数的值,并且首先将选择适当的编码。

  • 如果要对常量执行构建时算术,则仅限于可以在目标文件中表示为重定位的任何算术。常数偏移量起作用,所以movabs可以;目标文件告诉链接器用符号mov $(ANSWER_TO_LIFE+17),%ecx的值加常量17填充相关的字节。(对于实际标签,您希望这样做是为了从静态{{1 }}。)但不支持更通用的运算,例如乘法,因为人们通常不希望在地址上执行这些运算,因此ANSWER_TO_LIFE导致汇编程序出错。如果我们需要求生答案的平方,就必须编写一条struct指令来在运行时对其进行计算,如果这是经常调用且需要快速执行的代码,那将毫无乐趣。>

也可以从链接到我们项目中的C代码访问该常量,但必须将其视为标签(变量的地址),这使其看起来很奇怪。我们必须写类似

mov $(ANSWER_TO_LIFE*ANSWER_TO_LIFE),%edx

如果我们尝试写一些看起来更自然的东西

mul

程序将尝试从内存地址0x42中获取值,这将导致崩溃。

(此外,即使在第一个示例中,编译器的汇编输出也使用extern void *ANSWER_TO_LIFE; printf("The answer is %lu\n",(unsigned long)&ANSWER_TO_LIFE); 助记符,这再次导致汇编器选择32位移动。如果extern unsigned long ANSWER_TO_LIFE; printf("The answer is %lu\n",ANSWER_TO_LIFE); 大于{{ 1}},然后链接将失败,这一次不那么容易解决。AFAIK您需要给gcc一个适当的选项,告诉它更改其code model,这会导致地址加载以使用效率较低的64位格式,并且您必须在整个程序中都这样做。)

4。作为存储在内存中并在运行时获取的值(效率低)

constants.s:

mov

code.s:

ANSWER_TO_LIFE

建筑物:

2^32

此方法等效于在C程序中使用 .section .rodata .global answer_to_life answer_to_life: .int 0x42 (尽管C ++不同)。值42存储在程序的内存中,每当需要访问它时,就需要一条从内存中读取的指令。我们不能再将其编码为每个指令中的立即数。这通常执行起来较慢。如果需要对其进行任何算术运算,则必须编写代码以将其加载到寄存器中,并在运行时执行适当的指令,这会占用周期和代码空间。

我将此处的名称更改为小写,以匹配内存中变量的约定,而不是不再使用的“编译时”常量。还要注意说明中的不同语法。没有符号 mov answer_to_life,%eax add answer_to_life,%ebx # mov answer_to_life+17,%ecx # not valid,no such instruction exists mov answer_to_life,%ecx add $17,%ecx # needs two instructions # mov answer_to_life*answer_to_life,%edx # not valid mov answer_to_life,%eax mul %eax # clobbers %edx 的{​​{1}}是内存中的负载,而不是立即移动。在此示例中,as -o constants.o constants.s as -o code.o code.s ld -o prog constants.o code.o code2.o 为您提供了变量的地址(在我的测试程序中,巧合的是const int answer_to_life = 42;)。如果您希望能够构建位置无关的可执行文件(这是现代Linux程序的标准),则需要编写mov answer_to_life,%eax

由于上述原因,这种方法对于在编译时真正已知的数值常量并不理想,但出于完整性考虑,我将其包括在内,因为您在注释中提出了要求。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res