栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

汇编 —— 混合编程与内联汇编

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

汇编 —— 混合编程与内联汇编

文章目录
  • 混合编程
  • 内联汇编
    • 扩展内联汇编
      • 寄存器约束
      • 内存约束
  • 参考文献

写在前面:
之前都是讲的汇编理论,今天将汇编应用在实战之中。主要有两种方式:混合编程和内联汇编。

混合编程

混合编程就是单独的汇编文件和单独的C语言文件分别编译成目标文件之后,一起链接成可执行文件。

假设我们要写一个打印函数,既然是分别编译那么肯定是分两部分了。C语言部门相信大家都有基础,就不做多解释了,直接贴代码:

extern void asm_print(char *, int);

void c_print(char* str)
{
    int len = 0;
    while(str[len++])
        ;
    asm_print(str, len);
}

值得注意的是,这里我们从外部引用了一个asm_print函数了,这个函数就在汇编文件中定义。其主要原理是触发0x80中断调用 4 号子功能号write将输出定位到屏幕stdout。

注:如何进行系统调用
Linux要触发0x80中断需要用到int 0x80这个指令,那么怎么去指定我要调用哪个子功能号呢?这个时候需要利用寄存器eax传入子功能号了,当然这个是在触发中断之前了。有些子功能号需要参数,记得在触发中断之前,通过寄存器传入参数。

mov eax,4
int 0x80

下表附上Linux调用号大全,图源来自虫虫搜奇

我们可以看到asm_print这个函数有两个参数,一个是打印字符串的地址,一个额是字符串的长度。这两个参数都是我们需要压入栈的:

    mov eax,4               ;调用write
    mov ebx,1               ;输出指向stdout
    mov ecx,[ebp+4]			;获取地址
    mov edx,[ebp+8]			;获取字符串长度
    int 0x80

基本有技术的我们都讲完了,下面正式上代码,文件名叫C_with_S_S.S:

section .data
str: db "hello Gos!",0xa,0		;结尾添加''
str_len equ $-str

section .text
extern c_print
global _start
_start:
    ;c调用约定
    push str            ;传参
    call c_print        ;调用哈数
    add esp,4           ;回收栈空间

    ;退出程序
    mov eax,1               ;1子功能号是exit
    int 0x80

global asm_print
;模拟C库函数write
asm_print:
    push ebp
    mov ebp,esp
    mov eax,4               ;调用write
    mov ebx,1               ;输出指向stdout
    mov ecx,[ebp+8]
    mov edx,[ebp+12]
    int 0x80
    pop ebp
    ret

这里我们可以看到程序入口_start在汇编文件中定义。之后将参数压栈,指向C语言中函数c_print,而C语言中又反过来调用汇编文件中真正打印的函数asm_print。最后执行exit系统调用退出程序。

代码已经写完了,我们现在来编译一下:

[ik@bogon kernel]$ gcc -m32 -c -o C.o C_with_S_c.c 
[ik@bogon kernel]$ nasm -f elf -o S.o C_with_S_S.S 
[ik@bogon kernel]$ ld -m elf_i386 -o test.bin C.o S.o
[ik@bogon kernel]$ chmod +x test.bin 
[ik@bogon kernel]$ ./test.bin

最终执行结果也正如我们所料,打印出了hello,Gos!

[ik@localhost C_with_S]$ ./test.bin 
hello Gos!
内联汇编

内联汇编即在C语言中嵌入汇编代码,直接编译成可执行文件。而这正是利用了gcc强大的asm机制在代码中直接嵌入汇编代码,所以称为gcc inline assembly。

内联汇编最基本的内联形式格式为:

asm [volatile]("汇编代码")

注:
asm 和 __asm__是一样的,是gcc定义的宏;同理的是volatile。

#define __asm__ asm
#define __volatile__ volatile

这里要注意几个规则:

  • 无论有多少条指令,指令必须在双引号之中
  • 如果指令过多,可以用接续符
  • 指令之间用分号或者换行符进行分割

所以,刚刚的asm_print函数等同于如下代码:

int count = 1;
char *str = "hello world!n";

void main()
{
    asm("push %rax;
        push %rbx;
        push %rcx;
        push %rdx;
        movl $4,%eax;
        movl $1,%ebx;
        movl str,%ecx;
        movl $13,%edx;
        int $0x80;
        mov %eax,count;
        pop %rax;
        pop %rbx;
        pop %rcx;
        pop %rdx;
        ");
}

这个直接编译就可以了运行了:

[ik@localhost C_with_S]$ ./inline.bin 
hello world!
扩展内联汇编

可以看到,为了不破坏其他函数正在寄存器中保存的数据,我们必须手动备份寄存器,太累了。这就有了扩展内联汇编,其格式如下:

asm [volatile]("汇编代码":output:input:clobber/memory)
寄存器约束

我们先不管最后的clobber/memory,前面多加了一个输入input和输出output。

输入的就是我们要压入的参数,其需要遵守寄存器约束:

a:表示寄存器eax
b:表示寄存器ebx
c:表示寄存器ecx
d:表示寄存器edx
D:表示寄存器edi
S:表示寄存器esi
q:表示以下任意四个寄存器之一:eax/ebx/ecx/edx
r:表示任意六个通用寄存器之一:eax/ebx/ecx/edx/edi/esi
g:表示可以存放到任意地点
A:把eax和edx组合成64位数
f:表示浮点寄存器
t:表示第一个浮点寄存器
u:表示第二个浮点寄存器

那么我们先不管输出,但看输入,以上代码就可以改写成如下:

    asm("int $0x80"
        : output
        : "a"(4), "b"(1), "c"(str), "d"(13));

这里相当于把 4 传给eax,1 传给 ebx,str 传给 str,d 传给 edx;之后调用int $0x80。gcc会帮我们备份好这四个寄存器,不会干扰到其他函数的运行。

注:
基础内联汇编中用单个%前缀修饰寄存器,而在扩展编程需要用两个%,这个原因与后面的内存约束有关系

刚刚讲了输入,我们现在来讲一下输出,输出与输入相比会多一个=,比如说下面便表示count = %eax

"=a"(count)

所以上面的代码我们可以写成如下形式:

    asm("int $0x80"
        : "=a"(count)
        : "a"(4), "b"(1), "c"(str), "d"(13));

而除了=之外,output还有三种:

  • =:表示赋值
  • +:表示可读写,其修饰的寄存器作为输入,也作为输出
  • &:表示此寄存器独占,其他的不许用
内存约束

刚刚我们也提到内存约束。内存约束时要求gcc直接将位于input和output中的C变量的内存地址作为内联汇编代码的操作数,不需要寄存器做中转。其约束如下:

m:表示操作数可以使用任意一种内存形式
o:操作数作为内存变量,但访问时通过偏移量的形式访问

下面的例子很好的展示了内存约束的作用:

#include 
using namespace std;

int main()
{
    int a = 1;
    int b = 2;
    asm("movl %%eax,%1" ::"a"(a), "m"(b));
    cout << b << endl;
}
[ik@localhost C_with_S]$ ./mem.bin 
1

注:
这里的%1表示第一个参数是b,相当于代码 a = b 的效果。

参考文献
[1] 操作系统真相还原
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/316968.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号