字节堡堡(及更多)美国原住民

图片

是的,是的,它是“字节”,并且是印度文(不是印度文)。 我将按顺序开始。 最近在这里,在哈布雷(Habré)上,有关字节码的文章开始出现。 从前,我很高兴编写Fort系统。 当然是在汇编程序中。 它们是16位的。 我从未在x86-64上编程。 连32都打不了。 这样的想法来了-为什么不呢? 为什么不搅动64位的堡垒,甚至使用字节码? 是的,在Linux上,我也没有编写任何系统。

我有一台装有Linux的家庭服务器。 通常,我在Google上搜索了一下,发现Linux上的汇编程序称为GAS,而as命令则称为。 我通过SSH连接到服务器,键入为-是! 我已经安装了。 仍然需要一个链接器,输入ld-是的! 因此,尝试在汇编器中编写一些有趣的东西。 没有文明,只有一片森林,就像真正的印第安人:)没有开发环境,只有一条命令行和午夜指挥官。 编辑器将是Nano,它挂在我的MC F4上。 “零”乐队如何唱歌? 一个真正的印度人只需要一件事...一个真正的印度人还需要什么? 当然是调试器。 我们输入gdb-是! 好吧,按Shift + F4,然后开始!

建筑学


首先,让我们决定架构。 在已经确定位深度的情况下,为64位。 在经典的Fort实现中,数据段和代码段相同。 但是,我们将尽力做到正确。 我们只有代码段中的代码,而数据段中的数据。 结果,我们得到了平台的内核和完全独立于平台的字节码。

让我们尝试制造最快的堆叠字节机器(但不使用JIT)。 因此,我们将得到一个包含256个地址的表-每个字节命令一个。 少于一切-额外检查,这是1-2个处理器指令。 我们需要迅速而无妥协。

堆栈


通常,在Fort实现中,处理器返回堆栈(* SP)用作数据堆栈,而Fort系统返回堆栈是使用其他方式实现的。 确实,我们的机器将被堆叠起来,并且主要工作在数据栈上。 因此,让我们做同样的事情-RSP将是一个数据堆栈。 好吧,让返回堆栈为RBP,默认情况下,它也可以与堆栈段一起使用。 因此,我们将具有三个内存段:一个代码段,一个数据段和一个堆栈段(它将同时具有数据堆栈和返回堆栈)。

寄存器


我进入寄存器x86-64的描述,哎呀! 与32或16位模式相比,有多达8个其他通用寄存器(R8-R16)。

已经决定他们将需要RSP和RBP。 仍然需要字节码命令的指针(计数器)。 在该寄存器上的操作中,仅需要读取存储器。 主寄存器(RAX,RBX,RCX,RDX,RSI,RDI)更加灵活,通用,并具有许多特殊命令。 它们对于我们执行各种任务将很有用,对于字节码指令计数器,我们为我选择一个新的寄存器作为R8。

让我们开始吧


我没有使用汇编语言在Linux上编程的经验。 因此,对于初学者来说,我们将找到完成的“世界您好”,以了解程序如何启动并显示文字。 出乎我意料的是,我发现了具有奇怪语法的选项,其中甚至重新安排了源和接收器。 事实证明,这是AT&T语法,它主要是用GAS编写的。 但是支持另一个语法选项,称为Intel语法。 思考之后,我决定全部使用。 好吧,在.intel_syntax noprefix的开头写。

编译并执行“ Hello,world”以确保一切正常。 通过阅读帮助和实验,我开始使用以下命令进行编译:
$ as fort.asm -o fort.o -g -ahlsm >list.txt
在此,-o开关指示结果文件,-g开关指示生成调试信息,而-ahlsm开关设置列表格式。 我将输出保留在清单中,您可以在其中看到很多有用的东西。 我承认,在工作开始时我没有列出清单,甚至没有指定-g开关。 第一次使用调试器后,我开始使用-g开关,并且在代码中出现宏之后开始进行清单:)

在那之后,我们使用链接器,但是这里没有比这更简单的了:

$ ld forth.o -o forth
好吧,快跑!
$ ./forth
Hello, world!

可以用

这是第一个first.asm(实际上是“ Hellow,world!”,当然)
 .intel_syntax noprefix .section .data msg: .ascii "Hello, world!\n" len = . - msg #  len    .section .text .global _start #     _start: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, OFFSET FLAT:msg #     mov edx, len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit xor ebx, ebx #    0 int 0x80 #   

顺便说一句,稍后我发现在x86-64中使用syscall进行系统调用比使用int 0x80更正确。 尽管支持该架构,但0x80调用被认为已过时。

已经开始,现在...

走吧


至少会有一些细节,我们将编写一个字节命令的代码。 让它成为Fort单词“ 0”,将0放在栈顶:

 bcmd_num0: push 0 jmp _next 

在执行该命令时,R8已经指向下一个字节命令。 必须读取它,增加R8,通过字节命令的代码确定可执行地址,并将控制权转移给它。

但是...字节命令地址表将是多少位深度? 然后,我不得不为我深入研究新的x86-64命令系统。 las,我找不到允许您转到内存偏移量的命令。 因此,要么计算地址,要么地址将准备好-64位。 我们没有时间进行计算,这意味着-64位。 在这种情况下,表大小将为256 * 8 = 4096字节。 好了,最后,对_next调用进行编码:

 _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] # bcmd -   - 

从我看来,还不错。从一个字节命令切换到另一个字节命令时,只有三个处理器指令。

实际上,这些命令对我而言并不那么容易。 我不得不再次研究0x86-64命令系统,并为我找到一个新的MOVZX命令。 实际上,此命令将8、16或32位的值转换为64位寄存器。 此命令有两种变体:无符号,其中较高的数字用零填充,而有符号的为MOVSX。 在正负号变体中,正负号会展开,即对于正数,零将移至较高的数字,对于负数,将扩至零。 这个选项对我们点亮字节命令也很有用。

顺便说一句,这个选项最快吗? 也许有人会建议更快?

好了,现在我们有了一个字节机,它可以运行一系列字节命令并执行它们。 有必要在实践中进行测试,以强制执行至少一个团队。 但是哪一个呢? 零栈? 但是,如果您不看调试器下的堆栈,这里甚至都不知道结果。但是如果程序启动了,就可以完成:)

我们编写了一个再见命令来完成程序并编写程序,特别是因为我们拥有“ Hellow,world!”。

 bcmd_bye: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bye #     mov edx, msg_bye_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 0 #    0 int 0x80 #   

剩下的唯一事情就是创建一个字节命令地址表,初始化寄存器,然后启动字节机。 因此...表中有256个值,并且有两个命令。 其他单元中有什么?
其余的将具有无效的操作代码。 但是,您无法对此进行检查,这是额外的团队,我们现在有3个团队,而选中的团队将是5个。 因此,我们将发出这样一个存根命令-一个糟糕的团队。 首先,我们为其填充整个表格,然后开始使用有用的命令来占据单元格。 让一个坏团队的代码为0x00,再见团队的代码为0x01,一旦写完,“ 0”的代码为0x02。 现在,糟糕的团队将与再见相同,只是使用不同的完成代码和文字(我将其放入扰流板中,与再见几乎相同):

bcmd_bad
 bcmd_bad: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bad_byte #     mov edx, msg_bad_byte_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 1 #    1 int 0x80 #   
现在绘制一个地址表。 为了方便起见,我们将每行放置8行,总共16行,该表的大小非常大:

字节命令地址表
 bcmd: .quad bcmd_bad, bcmd_bye, bcmd_num0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad 
我们编写字节程序的主体。 为此,将命令代码分配给汇编程序变量。 我们将签订以下协议:

  • 执行字节命令的地址将从bcmd_开始
  • 命令代码本身将存储在以b_开头的变量中

因此,字节程序的主体将如下所示:

 start: .byte b_bye 

将数据堆栈的大小声明为stack_size。 使其到目前为止为1024。在初始化时,我们将执行RBP = RSP-stack_size。

实际上,我们得到了这样的程序代码(forth.asm)
 .intel_syntax noprefix stack_size = 1024 .section .data msg_bad_byte: .ascii "Bad byte code!\n" msg_bad_byte_len = . - msg_bad_byte #  len    msg_bye: .ascii "bye!\n" msg_bye_len = . - msg_bye msg_hello: .ascii "Hello, world!\n" msg_hello_len = . - msg_hello bcmd: .quad bcmd_bad, bcmd_bye, bcmd_num0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad start: .byte b_bye .section .text .global _start #     _start: mov rbp, rsp sub rbp, stack_size lea r8, start jmp _next _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] b_bad = 0x00 bcmd_bad: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bad_byte #     mov edx, msg_bad_byte_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 1 #    1 int 0x80 #   b_bye = 0x01 bcmd_bye: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bye #     mov edx, msg_bye_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 0 #    0 int 0x80 #   b_num0 = 0x02 bcmd_num0: push 0 jmp _next 


编译,运行:

$ as fort.asm -o fort.o -g -ahlsm >list.txt
$ ld forth.o -o forth
$ ./forth
bye!

有效! 我们从一个字节开始的第一个字节码程序启动了:)
当然,如果一切都正确完成,情况将会如此。 如果没有,那么结果可能是这样的:

$ ./forth


当然,其他选择也是可行的,但是我经常遇到这种情况。 我们需要一个调试器。

调试器歌词
如前所述,我使用了GDB。 这是一个非常强大的调试器,但是具有命令行界面。 运行它非常简单:

 $ gdb ./forth GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./forth...done. (gdb) 

接下来,通过输入命令,我们进行调试。 我有足够的时间来查找一些必要的命令,并学习如何使用它们进行调试。 它们是:
b <label>-设置一个断点
l <label>-查看源代码
r-启动或重新启动程序
ir-查看处理器寄存器状态
s-步骤

顺便说一句,还记得您需要使用-g开关编译程序吗? 否则,标签和源代码将不可用。 在这种情况下,仅通过反汇编代码进行调试即可使用内存中的地址。 我们当然是印第安人,但程度不一样...

但是以某种方式,该程序几乎没有作用。 我们只对她说“你好”,她立即说“再见!”。 让我们成为真正的“ Hello world!” 在字节码上。 为此,将地址和字符串长度放在堆栈上,然后执行显示字符串的命令,再执行再见命令。 为此,需要新的命令:键入以输出字符串,并点亮以放置字符串的地址和长度。 首先我们写类型,让它的代码为0x80。 同样,我们需要使用sys_write调用的那段代码:

 b_type = 0x80 bcmd_type: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout pop rdx pop rcx push r8 int 0x80 #   pop r8 jmp _next 

在这里,我们使用POP命令从数据堆栈中获取地址和字符串长度。 调用int 0x80可以更改R8的寄存器,因此我们将其保存。 我们之前没有这样做,因为程序即将结束。 这些寄存器的内容无关紧要。 现在这是一个常规的字节命令,此后字节代码将继续执行,您需要表现出自己的行为。

现在让我们来写点灯。 这将是我们带参数的第一支队伍。 在带有该命令代码的字节之后,将包含包含要放入堆栈的数字的字节。 问题立即出现-这里需要什么位深度? 要输入任何数字,您需要64位。 但是,命令每次占用9个字节时,将放置一个数字是什么? 因此我们失去了紧凑性,这是字节码的主要属性之一,而且堡的代码也...

解决方案很简单-我们将针对不同的位深度发出多个命令。 这些将是lit8,lit16,lit32和lit64。 对于较小的数字,我们将使用lit8和lit16,对于较大的数字-我们将使用lit32和lit64。 小数字是最常用的,对于它们,将使用最短的命令,该命令占用两个字节。 不错!..我们将使这些命令的代码为0x08-0x0B。

 b_lit8 = 0x08 bcmd_lit8: movsx rax, byte ptr [r8] inc r8 push rax jmp _next b_lit16 = 0x09 bcmd_lit16: movsx rax, word ptr [r8] add r8, 2 push rax jmp _next b_lit32 = 0x0A bcmd_lit32: movsx rax, dword ptr [r8] add r8, 4 push rax jmp _next b_lit64 = 0x0B bcmd_lit64: mov rax, [r8] add r8, 8 push rax jmp _next 

在这里,我们使用MOVSX命令-这是我们已经知道的MOVZX命令的标志性版本。 R8我们有一个字节命令计数器。 我们在其上加载所需大小的值,将其移至下一个命令,然后将转换为64位的值放入堆栈中。

不要忘记将表中新团队的地址添加到所需位置。

准备编写您的第一个程序“ Hello,world!” 在我们的字节码上。 让我们使用编译器! :)

 start: .byte b_lit64 .quad msg_hello .byte b_lit8 .byte msg_hello_len .byte b_type .byte b_bye 

我们使用了两个不同的lit命令:lit64(将字符串的地址放置在堆栈中)和lit8(将其长度放置在堆栈中)。 接下来,我们再执行两个字节命令:type和bye。
编译,运行:

 $ as fort.asm -o fort.o -g -ahlsm >list.txt $ ld forth.o -o forth $ ./forth Hello, world! bye! 

赚了我们的字节码! 如果一切正常,这应该是结果。

全文
 .intel_syntax noprefix stack_size = 1024 .section .data msg_bad_byte: .ascii "Bad byte code!\n" msg_bad_byte_len = . - msg_bad_byte #  len    msg_bye: .ascii "bye!\n" msg_bye_len = . - msg_bye msg_hello: .ascii "Hello, world!\n" msg_hello_len = . - msg_hello bcmd: .quad bcmd_bad, bcmd_bye, bcmd_num0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x00 .quad bcmd_lit8, bcmd_lit16, bcmd_lit32, bcmd_lit64, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x10 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x20 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x30 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x40 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x60 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_type, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x80 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad start: .byte b_lit64 .quad msg_hello .byte b_lit8 .byte msg_hello_len .byte b_type .byte b_bye .section .text .global _start #     _start: mov rbp, rsp sub rbp, stack_size lea r8, start jmp _next _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] b_bad = 0x00 bcmd_bad: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bad_byte #     mov edx, msg_bad_byte_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 1 #    1 int 0x80 #   b_bye = 0x01 bcmd_bye: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bye #     mov edx, msg_bye_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 0 #    0 int 0x80 #   b_num0 = 0x02 bcmd_num0: push 0 jmp _next b_lit8 = 0x08 bcmd_lit8: movsx rax, byte ptr [r8] inc r8 push rax jmp _next b_lit16 = 0x09 bcmd_lit16: movsx rax, word ptr [r8] add r8, 2 push rax jmp _next b_lit32 = 0x0A bcmd_lit32: movsx rax, dword ptr [r8] add r8, 4 push rax jmp _next b_lit64 = 0x0B bcmd_lit64: mov rax, [r8] add r8, 8 push rax jmp _next b_type = 0x80 bcmd_type: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout pop rdx pop rcx push r8 int 0x80 #   pop r8 jmp _next 


但是可能性仍然是非常原始的,您不能创造一个条件,一个循环。

怎么可能呢? 您可以,一切都在我们手中! 让我们在循环中执行此行10次。 这将需要一个条件分支命令,以及一些堆栈运算:一个命令将堆栈上的值减1(在“ 1-”堡上)和一个顶点复制命令(“ dup”)。

使用算术,一切都很简单,我什至不予评论:

 b_dup = 0x18 bcmd_dup: push [rsp] jmp _next b_wm = 0x20 bcmd_wm: decq [rsp] jmp _next 

现在是有条件的跳跃。 首先,让我们简化任务-无条件过渡。 显然,您只需要更改寄存器R8的值即可。 首先想到的是字节命令,后跟一个参数-转换地址为64位。 还是九个字节。 我们需要这9个字节吗? 过渡通常发生在短距离内,通常在几百个字节之内。 因此,我们将不使用地址,而是使用偏移量!

位深度? 在许多情况下,仅8位(向前/向后127位)就足够了,但是有时这还不够。 因此,我们将执行与lit命令相同的操作,我们将选择两个选项-8位和16位数字,命令代码将为0x10和0x11:

 b_branch8 = 0x10 bcmd_branch8: movsx rax, byte ptr [r8] add r8, rax jmp _next b_branch16 = 0x11 bcmd_branch16: movsx rax, word ptr [r8] add r8, rax jmp _next 
现在,条件转换很容易实现。 如果堆栈为0,请转到_next,否则,请转到branch命令!
 b_qbranch8 = 0x12 bcmd_qbranch8: pop rax or rax, rax jnz bcmd_branch8 inc r8 jmp _next b_qbranch16 = 0x13 bcmd_qbranch16: pop rax or rax, rax jnz bcmd_branch16 add r8, 2 jmp _next 
现在,我们可以进行循环:
 start: .byte b_lit8 .byte 10 #  #  m0: .byte b_lit64 .quad msg_hello .byte b_lit8 .byte msg_hello_len .byte b_type .byte b_wm .byte b_dup .byte b_qbranch8 .byte m0 - . .byte b_bye 

前两个命令-我们将循环计数器放在堆栈上。接下来,打印字符串Hello。然后,我们从计数器中减去1,将其复制并执行(或不执行)转换。需要复制命令,因为条件分支命令从堆栈顶部获取值。因为距离只有几个字节,所以这里的转换是八位的。

我们将新命令的地址放在表中,进行编译和执行。

我将其放入扰流器中,否则我们的程序将变得冗长)
 $ as fort.asm -o fort.o -g -ahlsm >list.txt $ ld forth.o -o forth $ ./forth Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! bye! 


好了,我们已经可以做条件和循环了!

全文
 .intel_syntax noprefix stack_size = 1024 .section .data msg_bad_byte: .ascii "Bad byte code!\n" msg_bad_byte_len = . - msg_bad_byte #  len    msg_bye: .ascii "bye!\n" msg_bye_len = . - msg_bye msg_hello: .ascii "Hello, world!\n" msg_hello_len = . - msg_hello bcmd: .quad bcmd_bad, bcmd_bye, bcmd_num0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x00 .quad bcmd_lit8, bcmd_lit16, bcmd_lit32, bcmd_lit64, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_branch8, bcmd_branch16, bcmd_qbranch8, bcmd_qbranch16, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x10 .quad bcmd_dup, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_wm, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x20 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x30 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x40 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x60 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_type, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x80 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad start: .byte b_lit8 .byte 10 #  #  m0: .byte b_lit64 .quad msg_hello .byte b_lit8 .byte msg_hello_len .byte b_type .byte b_wm .byte b_dup .byte b_qbranch8 .byte m0 - . .byte b_bye .section .text .global _start #     _start: mov rbp, rsp sub rbp, stack_size lea r8, start jmp _next _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] b_bad = 0x00 bcmd_bad: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bad_byte #     mov edx, msg_bad_byte_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 1 #    1 int 0x80 #   b_bye = 0x01 bcmd_bye: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bye #     mov edx, msg_bye_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 0 #    0 int 0x80 #   b_num0 = 0x02 bcmd_num0: push 0 jmp _next b_lit8 = 0x08 bcmd_lit8: movsx rax, byte ptr [r8] inc r8 push rax jmp _next b_lit16 = 0x09 bcmd_lit16: movsx rax, word ptr [r8] add r8, 2 push rax jmp _next b_lit32 = 0x0A bcmd_lit32: movsx rax, dword ptr [r8] add r8, 4 push rax jmp _next b_lit64 = 0x0B bcmd_lit64: mov rax, [r8] add r8, 8 push rax jmp _next b_type = 0x80 bcmd_type: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout pop rdx pop rcx push r8 int 0x80 #   pop r8 jmp _next b_dup = 0x18 bcmd_dup: push [rsp] jmp _next b_wm = 0x20 bcmd_wm: decq [rsp] jmp _next b_branch8 = 0x10 bcmd_branch8: movsx rax, byte ptr [r8] add r8, rax jmp _next b_branch16 = 0x11 bcmd_branch16: movsx rax, word ptr [r8] add r8, rax jmp _next b_qbranch8 = 0x12 bcmd_qbranch8: pop rax or rax, rax jnz bcmd_branch8 inc r8 jmp _next b_qbranch16 = 0x13 bcmd_qbranch16: pop rax or rax, rax jnz bcmd_branch16 add r8, 2 jmp _next 

但是直到完成的字节机缺少另一个非常重要的功能。我们不能从字节码中调用另一个。我们没有所谓的例程,过程等。而且在要塞中,没有这个,我们就不能在某些单词中使用内核单词以外的单词。

我们把工作结束了。这是我们第一次需要一堆收益。需要两个命令-调用命令和返回命令(调用和退出)。

调用命令在原则上与分支执行相同的操作-将控制权转移到另一字节代码。但是,与分支不同,您仍然需要将返回地址保存在返回堆栈中,以便您可以返回并继续执行。还有另一个区别-这样的通话可能会发生更远的距离。因此,我们以类似于分支的方式执行call命令,但使用三种版本-8位,16位和32位。

 b_call8 = 0x0C bcmd_call8: movsx rax, byte ptr [r8] sub rbp, 8 inc r8 mov [rbp], r8 add r8, rax jmp _next b_call16 = 0x0D bcmd_call16: movsx rax, word ptr [r8] sub rbp, 8 add r8, 2 mov [rbp], r8 add r8, rax jmp _next b_call32 = 0x0E bcmd_call32: movsx rax, dword ptr [r8] sub rbp, 8 add r8, 4 mov [rbp], r8 add r8, rax jmp _next 

如您所见,这里与过渡不同,添加了3个团队。其中一个将R8重新排列为下一个字节命令,其余两个将接收到的值存储在返回堆栈中。顺便说一句,在这里,我尝试不使处理器指令相互依存,以便处理器传送器可以并行执行命令。但我不知道这能起到多大的作用。如果需要,则可以检查测试。

应该牢记的是,调用命令的自变量形式与分支的自变量形式略有不同。对于分支,偏移量计算为分支地址与字节命令后的字节地址之间的差。对于call命令,这是跳转地址和下一个命令的地址之间的差。为什么需要这个?这导致更少的处理器指令。

现在返回命令。实际上,她的工作只是从返回堆栈中还原R8并将控制权进一步转移到字节机:

 b_exit = 0x1F bcmd_exit: mov r8, [rbp] add rbp, 8 jmp _next 

这些命令将经常使用,需要对其进行最大程度的优化。退出字节命令占用三个机器指令。可以在这里减少一些东西吗?事实证明你可以!您可以简单地删除过渡命令:)

为此,请将其放置在_next字节机的入口点上方:

 b_exit = 0x1F bcmd_exit: mov r8, [rbp] add rbp, 8 _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] 

顺便说一句,最重要和最常用的命令(例如,调用)需要放在靠近字节机的位置,以便编译器可以形成一个短跳转命令。这在清单中清晰可见。这是一个例子。

  262 0084 490FBE00 bcmd_lit8: movsx rax, byte ptr [r8] 263 0088 49FFC0 inc r8 264 008b 50 push rax 265 008c EB90 jmp _next 266 267 b_lit16 = 0x09 268 008e 490FBF00 bcmd_lit16: movsx rax, word ptr [r8] 269 0092 4983C002 add r8, 2 270 0096 50 push rax 271 0097 EB85 jmp _next 272 273 b_lit32 = 0x0A 274 0099 496300 bcmd_lit32: movsx rax, dword ptr [r8] 275 009c 4983C004 add r8, 4 276 00a0 50 push rax 277 00a1 E978FFFF jmp _next 277 FF 278 

此处,在第265和271行,jmp命令每个占用2个字节,而在第277行,由于跳转距离超过了short命令的长度,因此同一命令已被编译为5个字节。

因此,诸如坏,再见,类型之类的字节命令被进一步重新布置,而诸如呼叫,分支,点亮之类的字节命令则更近。不幸的是,在127个字节的过渡中没有足够的空间。
我们根据它们的代码将新命令添加到命令地址表中。

因此,我们现在面临挑战和回报,我们将对其进行测试!为此,请在单独的过程中选择行打印,我们将在循环中两次调用它。并且循环的重复次数减少到三个。

 start: .byte b_lit8 .byte 3 #  #  m0: .byte b_call16 .word sub_hello - . - 2 .byte b_call16 .word sub_hello - . - 2 .byte b_wm .byte b_dup .byte b_qbranch8 .byte m0 - . .byte b_bye sub_hello: .byte b_lit64 .quad msg_hello .byte b_lit8 .byte msg_hello_len .byte b_type .byte b_exit 

可以在这里使用Call8,但我决定将call16用作最可能使用的那个。由于为我编写的调用字节命令计算地址的特殊性,因此减去了值2。对于call8,将在此处分别减去1,对于call32,将减去4。我们
编译并调用:

 $ as forth.asm -o forth.o -g -ahlsm>list.txt $ ld forth.o -o forth $ ./forth Hello, world! Bad byte code! 

糟糕,如他们所说,出了点问题:)好吧,我们启动了GDB,看看那里发生了什么。我立即在bcmd_exit上设置了一个断点,因为很明显sub_hello调用正在传递,并且过程主体正在执行...启动...并且程序没有达到断点。马上有一个字节命令代码的怀疑。而且,确实是因为他。b_exit我分配了值0x1f,并且地址本身位于表单元号0x17中。好吧,那么我将把b_exit的值更正为0x17并重试:

 $ as forth.asm -o forth.o -g -ahlsm>list.txt $ ld forth.o -o forth $ ./forth Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! bye! 

正好六次打招呼,再见一次。应该是:)

全文
 .intel_syntax noprefix stack_size = 1024 .section .data msg_bad_byte: .ascii "Bad byte code!\n" msg_bad_byte_len = . - msg_bad_byte #  len    msg_bye: .ascii "bye!\n" msg_bye_len = . - msg_bye msg_hello: .ascii "Hello, world!\n" msg_hello_len = . - msg_hello bcmd: .quad bcmd_bad, bcmd_bye, bcmd_num0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x00 .quad bcmd_lit8, bcmd_lit16, bcmd_lit32, bcmd_lit64, bcmd_call8, bcmd_call16, bcmd_call32, bcmd_bad .quad bcmd_branch8, bcmd_branch16, bcmd_qbranch8, bcmd_qbranch16, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_exit # 0x10 .quad bcmd_dup, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_wm, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x20 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x30 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x40 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x60 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_type, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x80 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad start: .byte b_lit8 .byte 3 #  #  m0: .byte b_call16 .word sub_hello - . - 2 .byte b_call16 .word sub_hello - . - 2 .byte b_wm .byte b_dup .byte b_qbranch8 .byte m0 - . .byte b_bye sub_hello: .byte b_lit64 .quad msg_hello .byte b_lit8 .byte msg_hello_len .byte b_type .byte b_exit .section .text .global _start #     _start: mov rbp, rsp sub rbp, stack_size lea r8, start jmp _next b_exit = 0x17 bcmd_exit: mov r8, [rbp] add rbp, 8 _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] b_num0 = 0x02 bcmd_num0: push 0 jmp _next b_lit8 = 0x08 bcmd_lit8: movsx rax, byte ptr [r8] inc r8 push rax jmp _next b_lit16 = 0x09 bcmd_lit16: movsx rax, word ptr [r8] add r8, 2 push rax jmp _next b_call8 = 0x0C bcmd_call8: movsx rax, byte ptr [r8] sub rbp, 8 inc r8 mov [rbp], r8 add r8, rax jmp _next b_call16 = 0x0D bcmd_call16: movsx rax, word ptr [r8] sub rbp, 8 add r8, 2 mov [rbp], r8 add r8, rax jmp _next b_call32 = 0x0E bcmd_call32: movsx rax, dword ptr [r8] sub rbp, 8 add r8, 4 mov [rbp], r8 add r8, rax jmp _next b_lit32 = 0x0A bcmd_lit32: movsx rax, dword ptr [r8] add r8, 4 push rax jmp _next b_lit64 = 0x0B bcmd_lit64: mov rax, [r8] add r8, 8 push rax jmp _next b_dup = 0x18 bcmd_dup: push [rsp] jmp _next b_wm = 0x20 bcmd_wm: decq [rsp] jmp _next b_branch8 = 0x10 bcmd_branch8: movsx rax, byte ptr [r8] add r8, rax jmp _next b_branch16 = 0x11 bcmd_branch16: movsx rax, word ptr [r8] add r8, rax jmp _next b_qbranch8 = 0x12 bcmd_qbranch8: pop rax or rax, rax jnz bcmd_branch8 inc r8 jmp _next b_qbranch16 = 0x13 bcmd_qbranch16: pop rax or rax, rax jnz bcmd_branch16 add r8, 2 jmp _next b_bad = 0x00 bcmd_bad: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bad_byte #     mov edx, msg_bad_byte_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 1 #    1 int 0x80 #   b_bye = 0x01 bcmd_bye: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout mov ecx, offset msg_bye #     mov edx, msg_bye_len #   int 0x80 #   mov eax, 1 #   № 1 — sys_exit mov ebx, 0 #    0 int 0x80 #   b_type = 0x80 bcmd_type: mov eax, 4 #   № 4 — sys_write mov ebx, 1 #  № 1stdout pop rdx pop rcx push r8 int 0x80 #   pop r8 jmp _next 


结果如何


我们做了并测试了完整且相当快的64位堆栈字节机器。在速度上,也许这个字节机器将是同类产品中最快的(没有JIT的堆栈字节机器)。她知道如何顺序执行命令,进行有条件和无条件的跳转,调用过程以及从中返回。同时,所使用的字节码相当紧凑。基本上,字节命令占用1-3个字节,很少占用更多字节(只有大数字和很远的过程调用)。还绘制了一小组字节命令,这很容易扩展。假设所有用于堆栈的基本命令(丢弃,交换,覆盖,根等均可在20分钟内编写,相同的数量将用于算术整数命令)。

另一个重点。该字节码与经典的直接缝制堡码不同,它不包含机器指令,因此无需重新编译即可将其传输到另一个平台。一次将内核重写到新处理器的指令系统就足够了,并且可以很快完成。

字节机的当前版本不特定于任何特定语言。但是我想在其上实现Fort语言,因为我有使用它的经验,并且它的编译器可以很快完成。

如果对此有兴趣,在下一篇文章的基础上,我将在此机器上进行字符串和数字的输入输出,堡垒字典和解释器。您可以用手“触摸”团队。好吧,在第三篇文章中,我们将制作一个编译器,并且我们得到了一个几乎完整的堡垒系统。这样就可以编写和编译一些标准算法,并将性能与其他语言和系统进行比较。您可以使用例如Eratosthenes的筛子等。

. , 16- , , . _next , - ( _next). , _next, _next ( 14 ). , . , . , . .

, (, A = 5 + (B + C * 4) ).

, ! :)

继续:美洲印第安人堡垒的字节机(不仅是)(第2部分)

Source: https://habr.com/ru/post/zh-CN431932/


All Articles