小庭画 發表於 2020-12-8 22:54:00

汇编语言之基础(三 嵌入汇编)

<h2 id="嵌入汇编">嵌入汇编</h2>
<ol>
<li>
<p>说明:</p>
<p>用来在c语言中写汇编程序</p>
<p>汇编语言无法实现内存到内存操作‘</p>
<p>但是可以实现寄存器到内存的操作</p>
</li>
<li>
<p>通用写法:</p>
</li>
</ol>
<pre><code>__asm__ volatile("汇编指令"         必须存在字段
               : =限制符(输出参数)   这是可选的可以不需要写
             : 限制符(输入参数)    这是可选的可以不需要写
               : 保留列表
            );
</code></pre>
<p>​                其中volatile代表是否优化,存在代表不允许编译器优化</p>
<ol start="3">
<li>
<p>汇编搭配宏</p>
<p>使用汇编语言最常用的方式就是把汇编语言写在宏里,加上({})这种GNU独有的语法,在{}像函数一样进行定义,把最后一个语句作为返回值返回</p>
</li>
</ol>
<pre><code>#define get_value(input, output)({ \
    __asm__ volatile("mov %0, %1"      \
                     : "=a"(output)   \
                     : "a"(input)   \
                  );                \
    output;                           \
})
a是限制符,是对参数的说明,对于输入参数来说就是把输入参数放到eax寄存器中
输出参数代表汇编结束把该寄存器内容给到输出参数
通过通用寄存器间接操作变量,汇编语言是操作 寄存器的,不支持直接操作内存
=就是用来区分输入参数
</code></pre>
<ol start="4">
<li>汇编多指令两种写法:</li>
</ol>
<pre><code> __asm__ volatile("mov %0, %1\n"      \
                                  "mov %0, %1\n"      \
                     : "=a"(output)   \
                     : "a"(input)   \
                  );   
每行用换行符分割,作为一行的结束

__asm__ volatile("mov %0, %1;" \
                     "mov %0, %1;"      \
                     : "=a"(output)   \
                     : "a"(input)   \
                  );                \
每行用;分割,作为一行的结束
__asm__ volatile("mov %0, %1; \
                     mov %0, %1;"      \
                     : "=a"(output)   \
                     : "a"(input)   \
                );                \
相当于直接使用一条指令,用\进行连接                           

</code></pre>
<p>值得注意的是,在gcc编译嵌入汇编,汇编指令中寄存器访问需要加两个%%</p>
<ol start="5">
<li>
<p>定义寄存器变量:</p>
<p>register char ___res; 编译器会为我们分配寄存器</p>
<p>如果想指定寄存器,那么register char ___resasm(“‘eax’”);</p>
</li>
<li>
<p>当使用标号“0”代表使用同上个位置相同的寄存器</p>
</li>
<li>
<p>编译器规定把输出输入寄存器统一编号,顺序从输出寄存器序列从左到右从上到下以%0开始,分别记为 %0 %1</p>
</li>
<li>
<p>看一下常用的限定符</p>
<p><img src="https://img2020.cnblogs.com/blog/1989901/202012/1989901-20201208225116217-272406512.png"></p>
</li>
<li>
<p>关于volatile也可以放在没有返回值的函数前面,volatile void print();这样的好处是明确告诉编译器该函数不会返回,这样就可以让gcc产生更好的代码</p>
</li>
</ol>
<pre><code>        asm volatile ( "movl %%eax, %%ecx\n"
                                "movl %%ebx, %%eax\n"
                                "mov %%ecx, %%ebx\n"
                               : "=a"(output), "=b"(input)
                               : "a"(output), "b"(input)
        );
        printf("input=%d, output = %d\n", input, output);
</code></pre>
<p>​        汇编交换两个值,注意的是,input output 既可以在输入的地方,也可以在输出的地方</p>
<ol start="10">
<li>
<p>使用汇编可以做什么?</p>
<p>在Linux中,应用层想要调用内核层的函数,不能直接调用,需要使用系统调用函数,而调用系统调用的方式就是通过汇编,使用软中断实现,INT 80H, INT是使用Linux内核指令,80H是中断向量号,用于执行系统调用</p>
<p>fd文件描述符,0代表标准输入,1代表标准输出,2代表异常输出</p>
<p>代码:</p>
</li>
</ol>
<pre><code>        asm volatile (
                                "movl $4, %%eax\n"
                                "movl $1, %%ebx\n"
                                "movl %0, %%ecx\n"
                                "movl %1, %%edx\n"
                                "int $0x80 \n"
                               :
                               : "r"(s), "r"(len)
                               : "eax", "ebx", "ecx", "edx"
        );
</code></pre>
<p>​        对于 sys_write这个系统调用,需要%eax寄存器写入4,代表选择sys_write, ebx寄存器代表fd的值,ecx代表字符串指针,edx代表写的个数</p>
<p>​        注意的是,嵌入式汇编除了汇编模板外,其他参数都可以省略</p>
<p>​        当省略的参数在中间时,对应分隔符 “:” 不可省略</p>
<p>​        当省略保留列表时,对应分隔符可以省略</p>
<p>​        当可选参数被省略,寄存器前使用单个%作为参数</p>
<p>​        当可选参数不被省略,寄存器前使用两个%%作为参数</p>
<h2 id="更多的系统调用函数请看">更多的系统调用函数请看</h2>
<h2 id="参考linux内核注释">参考Linux内核注释</h2>
<h2 id="参考狄泰软件门徒计划">参考狄泰软件门徒计划</h2>


</div>
<div id="MySignature" role="contentinfo">
    一个圆,圆内是你会的,圆外是你不知道的。而当圆越大,你知道的越多,不知道的也越多了<br><br>
来源:https://www.cnblogs.com/chaohacker/p/14106162.html
頁: [1]
查看完整版本: 汇编语言之基础(三 嵌入汇编)