<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Assembly on pemako</title>
    <link>http://pemako.cn/categories/assembly/</link>
    <description>Recent content in Assembly on pemako</description>
    <image>
      <title>pemako</title>
      <url>http://pemako.cn/images/papermod-cover.png</url>
      <link>http://pemako.cn/images/papermod-cover.png</link>
    </image>
    <generator>Hugo -- 0.146.6</generator>
    <language>en</language>
    <lastBuildDate>Thu, 09 Apr 2026 20:00:00 +0800</lastBuildDate>
    <atom:link href="http://pemako.cn/categories/assembly/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>macOS 汇编</title>
      <link>http://pemako.cn/posts/macos-assembly/</link>
      <pubDate>Thu, 09 Apr 2026 20:00:00 +0800</pubDate>
      <guid>http://pemako.cn/posts/macos-assembly/</guid>
      <description>&lt;h3 id=&#34;十进制与二进制的相互转换&#34;&gt;十进制与二进制的相互转换&lt;/h3&gt;
&lt;h4 id=&#34;十进制转为二进制分为整数部分和小数部分&#34;&gt;十进制转为二进制，分为整数部分和小数部分&lt;/h4&gt;
&lt;p&gt;整数部分采用除 2 倒取余法: 用2去除十进制整数，可以得到一个商和余数；在用2去除商，又会得到一个商和余数，如此进行，知道商为0时为止，然后把先的到的余数作为二进制的低位有效位，后得到的余数作为二进制数的高位有效位，依次排列起来。&lt;/p&gt;
&lt;p&gt;小数部分采用乘2取整法，具体做法：用2乘十进制小数，可以得到积，将积中的整数部分取出，在用2乘余下的小数部分，又得到一个积，在将积中的整数部分取出，如此进行，直到积中的小数部分为0，此时0或1为二进制的最后一位，或者达到所要求的精度为止，然后把取出的整数部分按顺序排列起来，先取得整数作为二进制小数的最高位有效位，后取的整数作为低位有效位。&lt;/p&gt;
&lt;h4 id=&#34;二进制转为十进制&#34;&gt;二进制转为十进制&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;按权相加法&lt;/strong&gt;，即将二进制每位上的数乘以权，然后相加之和即是十进制数&lt;/p&gt;
&lt;h3 id=&#34;预备知识&#34;&gt;预备知识&lt;/h3&gt;
&lt;p&gt;由于计算机的硬件决定，&lt;strong&gt;任何存储于计算机中的数据，其本质都是以二进制码存储&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;根据冯·诺依曼提出的经典计算机体系结构框架，一台计算机由运算器、控制器、存储器、输入和输出设备组成。其中运算器&lt;strong&gt;只有加法运算器&lt;/strong&gt;，没有减法运算器（据说一开始是有的，后来由于减法运算器硬件开销太大，被废了）。&lt;/p&gt;
&lt;p&gt;所以计算机中没办法直接做减法的，它的减法是通过加法实现的。现实世界中所有的减法也可以当成加法的，减去一个数可以看作加上这个数的相反数，但前提是要先有负数的概念，这就是为什么不得不引入一个符号位。&lt;strong&gt;符号位在内存中存放的最左边一位，如果该位为0，则说明该数为正；若为1，则说明该数为负。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而且从硬件的角度上看，只有正数加负数才算减法，正数与正数相加，负数与负数相加，其实都可以通过加法器直接相加。&lt;/p&gt;
&lt;p&gt;原码、反码、补码的产生过程就是为了解决计算机做减法和引入符号位的问题。&lt;/p&gt;
&lt;h3 id=&#34;原码&#34;&gt;原码&lt;/h3&gt;
&lt;p&gt;原码：&lt;strong&gt;是最简单的机器数表示法，用最高位表示符号位，其他位存放该数的二进制的绝对值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;以带符号位的四位二进制数为例：1010，最高位为1表示这是一个负数，其它三位010，即0&lt;em&gt;2^2+1&lt;/em&gt;2^1+0*2^0=2，所以1010表示十进制数-2。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;正数&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;负数&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0000&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;-0&lt;/td&gt;
          &lt;td&gt;1000&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0001&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;-1&lt;/td&gt;
          &lt;td&gt;1001&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2&lt;/td&gt;
          &lt;td&gt;0010&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;-2&lt;/td&gt;
          &lt;td&gt;1010&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3&lt;/td&gt;
          &lt;td&gt;0011&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;-3&lt;/td&gt;
          &lt;td&gt;1011&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;4&lt;/td&gt;
          &lt;td&gt;0100&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;-4&lt;/td&gt;
          &lt;td&gt;1100&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;5&lt;/td&gt;
          &lt;td&gt;0101&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;-5&lt;/td&gt;
          &lt;td&gt;1101&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;6&lt;/td&gt;
          &lt;td&gt;0110&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;-6&lt;/td&gt;
          &lt;td&gt;1110&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;7&lt;/td&gt;
          &lt;td&gt;0111&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;-7&lt;/td&gt;
          &lt;td&gt;1111&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;原码的表示法很简单，虽然出现了+0和-0，但是直观易懂。于是开始运算 &amp;mdash;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;0001 + 0010 = 0011,  1+2=3;
0000 + 1000= 1000, +0+(-0)=-0;
0001 + 1001=1010, 1+(-1)=-2&lt;/p&gt;</description>
    </item>
    <item>
      <title>汇编入门</title>
      <link>http://pemako.cn/posts/assembly-intro/</link>
      <pubDate>Thu, 09 Apr 2026 20:00:00 +0800</pubDate>
      <guid>http://pemako.cn/posts/assembly-intro/</guid>
      <description>&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# exit.s
 .section __TEXT, __text
 .globl _main
_main:
 mov1 $0, %rax
 retq
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;注释&#34;&gt;注释&lt;/h2&gt;
&lt;p&gt;程序的第一行是注释。在macOS的&lt;code&gt;as&lt;/code&gt;汇编器语法下，注释由&lt;code&gt;#&lt;/code&gt;开头，在进行汇编的时候会自动将其处理为空白字符。&lt;/p&gt;
&lt;p&gt;我们习惯上将注释写在语句的上方（如例程）或后方，如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;movq    $0, %rax    # mov 0 to register rax
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;缩进&#34;&gt;缩进&lt;/h2&gt;
&lt;p&gt;在最古老的机器上，汇编代码的文本包含四列：标签、助记符、操作数与注释。汇编器通过识别一个文本在哪个列来判断该文本有什么作用。现代的汇编器已经抛弃了这种方法，采用先进的词法分析技术来判断。但是，我们最好仍然按照这种格式来缩进。&lt;/p&gt;
&lt;h2 id=&#34;汇编器指令directive&#34;&gt;汇编器指令(Directive)&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Directive&amp;quot;是汇编语言中一个重要的组成部分，然而它的中文译名似乎还不固定，这里暂且叫它汇编器指令。在汇编语言中，以&lt;code&gt;.&lt;/code&gt;开头的都是汇编器指令，如例程中的&lt;code&gt;.section&lt;/code&gt;, &lt;code&gt;.globl&lt;/code&gt;等。由汇编器指令开头的语句，一般不会被直接翻译成机器码。汇编器指令并不是告诉汇编器&lt;strong&gt;做什么&lt;/strong&gt;, 而是告诉汇编器&lt;strong&gt;如何做&lt;/strong&gt;。就比如说例程中，&lt;code&gt;movq $0, %rax&lt;/code&gt; 会被汇编器直接翻译为机器码，最终会由CPU直接执行，而&lt;code&gt;.section __TEXT,__text&lt;/code&gt;, 则不会被翻译成机器码，在最终的可执行文件中也不会找到这句话的踪影。它的作用是告诉汇编器如何汇编。下面，就介绍一下&lt;code&gt;.section&lt;/code&gt;的作用&lt;/p&gt;
&lt;h3 id=&#34;section&#34;&gt;.section&lt;/h3&gt;
&lt;p&gt;我们之前在&lt;a href=&#34;https://zhuanlan.zhihu.com/p/73544165&#34;&gt;操作系统基础&lt;/a&gt;中提到，mach-o可执行文件的Data部分拥有许多段(Segment), 每个段又有许多节(section). 同一个段的作用往往是类似的，同时在执行的时候一个段会被分配到一个页之中。而&lt;code&gt;.section&lt;/code&gt;最常用的格式，就是&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.section    segname, sectname
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;segment&lt;/code&gt;是段名，&lt;code&gt;sectname&lt;/code&gt;是节名。我们目前编写的第一个汇编语言程序，只包含纯代码。在macho中，纯代码被放在了&lt;code&gt;__TEXT&lt;/code&gt;段的&lt;code&gt;__text&lt;/code&gt;节中，因此，我们在文件的第二行写了&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.section    __TEXT, __text
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;代表之后的语句都是&lt;code&gt;__TEXT&lt;/code&gt;段的&lt;code&gt;__text&lt;/code&gt;节中。&lt;/p&gt;
&lt;p&gt;此外，由于这个节过于常用，因此，汇编器给予了我们一个简单的记号：&lt;code&gt;.text&lt;/code&gt;. 我们可以直接用&lt;code&gt;.text&lt;/code&gt;代替&lt;code&gt;.section __TEXT, __text&lt;/code&gt;. 在以后的程序中，我也都会用这种记号。&lt;/p&gt;
&lt;p&gt;除了&lt;code&gt;__TEXT&lt;/code&gt;段&lt;code&gt;__text&lt;/code&gt;节后，还有许多段和节。常用的段和节的名称和作用可参见&lt;a href=&#34;https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/Assembler/040-Assembler_Directives/asm_directives.html&#34;&gt;Assembler Directives&lt;/a&gt;. 我们之后更复杂的程序中也会用到更多的段和节。&lt;/p&gt;
&lt;h3 id=&#34;globl&#34;&gt;.globl&lt;/h3&gt;
&lt;p&gt;我们在由汇编语言翻译机器码的时候，得到的文件并不仅仅包含操作的指令，还需要包含一些名字和记号。比如说，C语言中，程序执行的起点是&lt;code&gt;main&lt;/code&gt;函数。那么，这个函数的名字&lt;code&gt;main&lt;/code&gt;就要包含在文件中，使得程序执行的时候知道执行哪个函数。&lt;/p&gt;
&lt;h3 id=&#34;_main&#34;&gt;_main&lt;/h3&gt;
&lt;p&gt;macOS中，汇编语言程序执行的起点是&lt;code&gt;_main&lt;/code&gt;函数。关于函数与下一行的&lt;code&gt;_main:&lt;/code&gt;标签，我会在之后的文章中提到。是谁决定它叫这个名字的呢，是链接器。如果我们写的程序想把它主函数叫做&lt;code&gt;_start&lt;/code&gt;, 那么只需要在链接的时候写上即可。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ld -e _start exit.o -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lSystem -o &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;movq&#34;&gt;movq&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;movq&lt;/code&gt;是我们遇到的第一个真正的指令。在汇编语言中，这种能直接翻译成机器码的指令被称作助记符(mnemonic). 之前我们也提到过，在GAS语法下，一条指令是助记符+源+目的，也就是说，它后面紧跟的是源操作数，然后是目的操作数。在x86-64架构下所有的可以被识别的助记符可以参考&lt;a href=&#34;https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf&#34;&gt;64-ia-32-architectures-software-developer-instruction-set-reference-manual&lt;/a&gt;, 但值得注意的是，这份官方的参考文档是用的Intel语法，我们只需要把源和目的颠倒过来看就行。&lt;/p&gt;</description>
    </item>
    <item>
      <title>汇编语言</title>
      <link>http://pemako.cn/posts/assembly/</link>
      <pubDate>Thu, 09 Apr 2026 20:00:00 +0800</pubDate>
      <guid>http://pemako.cn/posts/assembly/</guid>
      <description>&lt;h1 id=&#34;汇编语言&#34;&gt;汇编语言&lt;/h1&gt;
&lt;h2 id=&#34;第一章-基础知识&#34;&gt;第一章: 基础知识&lt;/h2&gt;
&lt;h3 id=&#34;13-汇编语言的组成&#34;&gt;1.3 汇编语言的组成&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;汇编指令：机器码的助记符，有对应的机器码。&lt;/li&gt;
&lt;li&gt;伪指令：没有对应的机器码，由编译器执行，计算机并不执行。&lt;/li&gt;
&lt;li&gt;其它符号：如 &lt;code&gt;+&lt;/code&gt; &lt;code&gt;-&lt;/code&gt; &lt;code&gt;*&lt;/code&gt; &lt;code&gt;/&lt;/code&gt;等，由编译器识别，没有对应的机器码。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;汇编语言的核心是汇编指令，它决定了汇编语言的特性。&lt;/p&gt;&lt;/blockquote&gt;
&lt;h4 id=&#34;小结11110&#34;&gt;小结（1.1~1.10）&lt;/h4&gt;
&lt;p&gt;1）. 汇编指令是机器指令的助记符，同机器指令一一对应。
2）. 每一种 CPU 都有自己的汇编指令集。
3）. CPU 可以直接使用的信息在存储器中存放。
4）. 在存储器中指令和数据没有任何区别，都是二进制信息。
5）. 存储单元从零开始顺序编号。
6）. 一个存储单元可以存储 8 个 &lt;code&gt;bit&lt;/code&gt;,即 8 位二进制数。
7）. 1Byte=8bit  1KB=1024B 1MB=1024KB 1GB=1024MB
8）. 每一个 CPU 芯片都有许多管脚，这些管脚和总线相连。也可以说，这些管脚引出总线。一个 CPU 可以引出 3 种总线的宽度标志了这个 CPU 的不同方面的性能&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地址总线的宽度决定了 CPU 的寻址能力&lt;/li&gt;
&lt;li&gt;数据总线的宽度决定了 CPU 与其它器件进行数据传送时的一次数据传送量&lt;/li&gt;
&lt;li&gt;控制总线的宽度决定了 CPU 对系统中其它器件的控制能力&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;检测点-11&#34;&gt;检测点 1.1&lt;/h4&gt;
&lt;p&gt;（1） 1个CPU的寻址能力为8KB，那么他的地址总线的宽度为【 &lt;strong&gt;13&lt;/strong&gt; 】。
（2） 1KB的存储器有 【 &lt;strong&gt;1024&lt;/strong&gt; 】个存储单元。存储单元编号从【 &lt;strong&gt;0000&lt;/strong&gt; 】到【 &lt;strong&gt;1023&lt;/strong&gt; 】。
（3） 1KB的存储器可以存储【 &lt;strong&gt;1024 x 8&lt;/strong&gt; 】个bit，【 &lt;strong&gt;1024&lt;/strong&gt; 】个Byte。
（4） 1GB、1MB、1KB分别是【&lt;strong&gt;1024x1024x1024、1024x1024、1024&lt;/strong&gt; 】Byte。
（5） 8080、8088、8026、80386的地址总线宽度分别为16根、20根、24根、32根，则他们的寻址能力分别为：【 &lt;strong&gt;64&lt;/strong&gt; 】(KB)、【 &lt;strong&gt;1&lt;/strong&gt; 】(MB)、【 &lt;strong&gt;16&lt;/strong&gt; 】(MB)、【 &lt;strong&gt;4&lt;/strong&gt; 】(GB)。
（6） 8080、8088、8086、8026、80386的数据总线宽度分别为8根、8根、16根、16根、32根。则它们一次可以传送的数据为：【 &lt;strong&gt;1&lt;/strong&gt; 】(B)、【 &lt;strong&gt;1&lt;/strong&gt; 】(B)、【 &lt;strong&gt;2&lt;/strong&gt; 】(B)、【 &lt;strong&gt;2&lt;/strong&gt; 】(B)、【 &lt;strong&gt;4&lt;/strong&gt; 】(B)。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
