汇编语言基础:寄存器和系统调用
<blockquote><p>本文转载自汇编语言基础:寄存器和系统调用</p>
</blockquote>
<h1 id="寄存器">寄存器</h1>
<p>寄存器是处理器临时保存数据指令的的一部分。在<code>x86_64</code>架构中,寄存器能处理高达64位的数据。这意味着每个寄存器都可以保存该值<br>
没符号整数:<code>0〜18,446,744,073,709,551,616</code><br>
有符号整数:<code>-9,223,372,036,854,775,808-9,223,372,036,854,775,807</code></p>
<p>这是<code>x86_64</code>架构下的所有寄存器的逻辑图表。</p>
<p><img src="https:////upload-images.jianshu.io/upload_images/16148197-aeea827a0ac3f9f3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/452/format/webp" alt="img" loading="lazy"></p>
<p>这是我们的寄存器列表,表中有有四列:</p>
<ul>
<li>64位寄存器的列;</li>
<li>32位寄存器</li>
<li>16位寄存器</li>
<li>8位寄存器:</li>
</ul>
<h1 id="各个寄存器的关系">各个寄存器的关系</h1>
<p>这里是<code>rax</code>寄存器的图,你可以看到我们的一个64位寄存器叫做<code>rax</code>,还有<code>eax 32</code>位寄存器,那实际上不是它自己的寄存器,它是<code>rax</code>寄存器的一半,那么<code>ax</code>是16位寄存器是<code>eax</code>寄存器的一半。同理<code>al</code>寄存器是<code>ax</code>寄存器的一半。使用<code>al</code>寄存器就好像是一个8位寄存器,但它是 实际上修改了<code>ax</code>寄存器的低8位。以上说的并不需要刻意记忆,因为本文的立足点是让你能够读懂别人或<code>C/C++</code>编译器转译的汇编代码,你不记得的话,可以收藏本文已被你翻查。</p>
<p><img src="https:////upload-images.jianshu.io/upload_images/16148197-3e050d40aa5e6b01.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/537/format/webp" alt="img" loading="lazy"></p>
<p>不同数据长度的寄存器之间的关系</p>
<h1 id="系统调用syscall">系统调用(Syscall)</h1>
<p>系统调用是指程序向系统内核请求服务,系统调用因操作系统的不同而不同,因为不同的操作系统使用不同的内核。</p>
<ul>
<li>
<p>所有系统调用都有一个与之关联的ID(一个数字)</p>
</li>
<li>
<p>系统调用的输入接受一个参数列表。</p>
<p><img src="https:////upload-images.jianshu.io/upload_images/16148197-1a3c5c9a6deccbff.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/393/format/webp" alt="img" loading="lazy"></p>
<p>寄存器参数顺序表</p>
</li>
</ul>
<p>这是一个非常重要的表,当你使用系统调用时,它有许多输入是基于存储在寄存器中的值,并且是讲求顺序的,如果你想使用系统调用,你可以将对应系统调用的ID加载到<code>rax</code>寄存器中 并按上表指定的顺序加载参数在你的<code>RAX</code>寄存器中,<strong>如果你在这里还没有概念的话,可以跳过,往下看完一个示例,回头看就明白了</strong>。</p>
<ul>
<li>第一个参数是<code>rdi</code>寄存器,</li>
<li>第二个参数是<code>rsi</code>寄存器,</li>
<li>第三个参数是<code>rdx</code>寄存器</li>
<li>第四个参数是<code>r10</code>寄存器</li>
<li>第五个参数是<code>r8</code>寄存器</li>
<li>第六个参数是<code>r9</code>寄存器.</li>
</ul>
<h1 id="系统调用列表">系统调用列表</h1>
<p>我们这里只列出常用系统调用的ID,完整的系统调用ID列表,可以在网上都可以找得到。</p>
<p><img src="https:////upload-images.jianshu.io/upload_images/16148197-b7f88b183a09c66e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/580/format/webp" alt="img" loading="lazy"></p>
<p>系统调用ID示例表</p>
<ol>
<li>正如你所见,文件操作的系统调用是和<code>Unix/Linux</code>交互的常用接口.ID列对应的数字和对应<code>syscall</code>列对应系统调用名称相关联。</li>
<li>与系统调用相关的还有参数;我这里用颜色标记了,用'#'符号和红色标记的表明是直接来自寄存器的一个数字,用'$'符号和绿色标记的也是来自寄存器,但它不是真实的值,而是寄存器里面缓存的内存地址所指向的位置保存的值(如果你有C指针的基础,这就很容易理解了)。</li>
</ol>
<h1 id="sys_write系统调用示例">sys_write系统调用示例</h1>
<p>前文写到<code>Hello World</code>程序将"Hello World"字符串打印到屏幕,已经用到了<code>sys_write</code>系统调用,我们作为示例来讲解一下寄存器和系统调用之间是如何交互的。</p>
<p><img src="https:////upload-images.jianshu.io/upload_images/16148197-eacaa0f000a352df.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/341/format/webp" alt="img" loading="lazy"></p>
<p><code>sys_write</code>参数类型</p>
<ul>
<li>第一个参数是文件描述符0代表文件输入,1代表文件输出,2代表文件错误。如果你只是将文本写到屏幕,那么就使用标准输出即可</li>
<li>第二个文件描述符是缓存,即字符串输出到屏幕之前,首先写入的是高速缓存的位置</li>
<li>第三个参数是字符串的长度</li>
</ul>
<p>看完上面参数表,再回顾一下上面的寄存器ID参数表,看如何使用寄存器ID参数和下表的参数相关联,其实很简单,只需要将那些寄存器的值替换下表的参数列名即可。</p>
<p><img src="https:////upload-images.jianshu.io/upload_images/16148197-8cd0ba646d9d9e60.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/603/format/webp" alt="img" loading="lazy"></p>
<p>所以,正如你所见</p>
<ul>
<li><code>rax</code>寄存器应该处理值1(也即是<code>sys_write</code>系统调用的ID),</li>
<li><code>rdi</code>寄存器应该处理我们的文件描述符,</li>
<li><code>rsi</code>寄存器应该处理字符串的缓存对象,</li>
<li><code>rdx</code>寄存器应该处理字符串的长度.</li>
</ul>
<p>最后,按照《参数类型表》的描述替换为各列寄存器对应的值,具体分析逻辑如下,这样就生成我们最后关于Hello World程序关于系统调用的相关指令。</p>
<p><img src="https:////upload-images.jianshu.io/upload_images/16148197-163876c773504feb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/497/format/webp" alt="img" loading="lazy"></p>
<h1 id="汇编语言常用关键字">汇编语言常用关键字</h1>
<ul>
<li><code>mov</code>:表示<code>move</code>的意思就是移动数据,所以诸如如下语句
<ul>
<li><code>move rax,1</code>的意思就是将ID为1的<code>sys_write</code>系统调用移动寄存器<code>rax</code>,那么你应该记住,<code>以后遇到类似move rax ID这样格式的命令就应该记住这是通过寄存器执行某项系统调用</code>。</li>
<li>"move rsi text":的意思就是从text变量指定的内存位置加载字符串到rsi寄存器</li>
</ul>
</li>
<li><code>syscall</code>:每调用<code>move</code>指令后以<code>syscall</code>结尾,表示执行一次系统调用,也就是说你执行一个不同的系统调用都需要以<code>syscall</code>关键字结束。</li>
<li>从上面的两点理解,应该不难看出<code>mov rax 60</code>这条指令是关于系统调用ID等于60(从网上查到是sys_exit),关于告知操作系统,你的程序已经执行完毕,<code>同理,move rdi 0是执行与描述符有关的操作,即告知程序退出的状态 ,事实上这个状态码你可以任意定义的,按照程序设计的一般约定,0代表程序执行正常退出,其他为异常状态</code>。</li>
<li><code>section</code>:所有<code>x86_64</code>的汇编文件有三种<code>section</code>
<ul>
<li>.<code>data</code>:该section下定义的所有变量,在汇编之前,编译器会完成所有相关变量指向对应内存位置的值的替换。你可以理解为C/C++的变量相关的操作。</li>
<li><code>.bss</code> 该section,为某些功能的使用而分配内存,和<code>.data</code> section有些类似</li>
<li><code>.text</code> 该section,主要定义程序运行的逻辑和各种的系统调用。</li>
</ul>
</li>
<li><code>label</code>:标签用于标记代码的一部分。在编译时,编译将计算标签在内存中的位置。 每次使用标签的名称后,该名称将被编译器替换为内存中的位置。如果你看看helloword定义text的位置,它基本上是相同的,除了我们可以在位置插入这些标签。在我们的代码中将会看到为什么它在将来会有用武之地。
<ul>
<li>_<code>start</code>:标签对所有程序都至关重要。当您编写程序并稍后执行时,它首先在“_start”的位置执行。如果链接器找不到“_start”标签,则会抛出错误。</li>
<li><code>global</code>:当您希望链接器能够知道某个标签的地址时,使用global关键字。 生成的目标文件将包含指向“global”的每个标签的链接。在helloworld这个示例中,我们必须将“_start”声明为全局,因为代码必须正确链接。</li>
</ul>
</li>
</ul>
</div>
<div id="MySignature" role="contentinfo">
自律者自由<br><br>
来源:https://www.cnblogs.com/yungyu16/p/13024485.html
頁:
[1]