C asm内联以及x86-64调用c函数


C内联汇编

之前对C的内联汇编了解较少,最近做毕设的时候想搞个rpc协议,想着可以动态注册C函数以及动态调用C函数,其主要的难点在于动态你调用注册的函数时如何处理参数的问题,顺便把内联汇编学习下。

基于gcc的官方文档Extended Asm (Using the GNU Compiler Collection (GCC))分析, 使用的是x64的 AT&T 汇编格式

基本格式

asm asm-qualifiers ( AssemblerTemplate 
                      : OutputOperands
                      : InputOperands
                      : Clobbers
                      : GotoLabels)

asm-qualifiers

这是一个可选项,有两个选择

  • volatile 不优化你写的汇编代码, 不指定asm-qualifiers则默认是volatile
  • inline 编译器可以优化你写的代码

AssemblerTemplate

汇编语句模板,直接写汇编,叫模板代表里面的部分关键值可以被替换。

$值 将是即时值

%开头 代表被替换

%%寄存器 代表一个寄存器

OutputOperands

单个输出基本格式

[ [asmSymbolicName] ] constraint (cvariablename)
  • asmSymbolicName 用于给这个输出变量一个别名,在汇编模板里使用**%[别名]引用,正常是使用%序列号**引用
  • constraint 用于指定对这个变量操作的约束的字符串,在输出列表里,这个必须是以**’=’或者’+’**开头,=代表覆盖,+代表读写
  • cvariablename 用于指定C语言中对应的变量

constraint常见约束,所有约束见Constraints (Using the GNU Compiler Collection (GCC))

约束符号 作用
r 操作数将被放到通用寄存器中,先把操作数放到寄存器中再进行操作
m 替换后是操作数在内存中的地址
o 同m,但要求内存地址范围在同一段内
V 同m,但要求内存地址范围不在同一段内

多个输出以 ‘,‘分开

InputOperands

单个输入基本格式

[ [asmSymbolicName] ] constraint (cvariablename)
  • asmSymbolicName 用于给这个输出变量一个别名,在汇编模板里使用**%[别名]引用,正常是使用%序列号**引用

  • constraint 用于指定对这个变量操作的约束的字符串,在输入列表里,这个必须不以**’=’或者’+’**开头,约束同输出的constraint表

  • cvariablename 用于指定C语言中对应的变量

多个输出以 ‘,‘分开

输入列表里的任何变量在执行内联汇编语句前后值会保持不变,即使在汇编代码里修改了也一样。

Clobbers

告诉编译器哪些寄存器在你写的汇编代码里被修改了

GotoLabels

用于goto label语句,可以在这里指定要汇编语句模板中的一个地址,label应该使用%l+序号使用

几个例子

使用符号名(寄存器)

#include<stdio.h>
int main(int argc, char const *argv[])
{
    int v = 0,new_value = 1;
    fprintf(stdout,"before asm: v=%d\n",v);
    asm (
        "mov  %[a],%[v];" //a、v被分别替换
        :[v] "=g"(v)//放到寄存器里
        :[a] "g"(new_value)//放到寄存器里
        :
    );
    fprintf(stdout,"after asm: v=%d\n",v);
    return 0;
}
/*
before asm: v=0
after asm: v=1
*/

使用符号名(内存)

#include<stdio.h>
int main(int argc, char const *argv[])
{
    int v = 0,new_value = 1;
    fprintf(stdout,"before asm: v=%d\n",v);
    asm (
        "movl  $2, %[v];" //即时值2,  AT&T通过movx(x={b,w,l})来给内存赋值
        :[v] "=m"(v) //之际放内存地址
        :
        :
    );
    fprintf(stdout,"after asm: v=%d\n",v);
    return 0;
}
/*
before asm: v=0
after asm: v=2
*/

使用asm goto

#include<stdio.h>
int main(int argc, char const *argv[])
{
    asm goto (
        "jmp %l0" //用%l+序列号(是小写的L)
        :
        :
        :
        :error //指定label
    );
    fprintf(stdout,"normal\n");
    return 0;
error:
    fprintf(stdout,"error label\n");
    return 1;
}
/*
error label
*/

动态调用函数

目前只是一个demo,毕设里的想法是建一个hashtable,把注册的调用名map到函数的地址,并且在注册时可以指定参数,这样可以很方便地实现rpc协议

x64的函数调用abi

RDFM
文档链接

Passing Once arguments are classified, the registers get assigned (in left-to-right order) for passing as follows:

  1. If the class is MEMORY, pass the argument on the stack.
  2. If the class is INTEGER or POINTER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used.
  3. If the class is SSE, the next available vector register is used, the registers are taken in the order from %xmm0 to %xmm7

如果是INTEGER或者POINTER,且参数数量<=6,则可以把参数依次直接放到rdi、rsi、rdx、rcx、r8、r9。

上代码

按照ABI把参数放入对应的寄存器并call函数的地址即可

#include<stdio.h>

#define MAX_ARGC 6
typedef struct{
    int len;
    void* args[MAX_ARGC];
} rpc_args;

int register_function(const char *fun_name,void* fun);
int call_function(const char *fun_name,rpc_args *args);


void* fun_ptr;

void hello(int a,int b,int c){
    fprintf(stdout,"hello a=%d,b=%d,c=%d\n",a,b,c);
}



int main(int argc,const char *argv[]){
    register_function("hello",hello);
    call_function("hello",NULL);
}

int register_function(const char *fun_name,void* fun){
    fun_ptr = fun;
}



int call_function(const char *fun_name,rpc_args *args){
    int a = 2,b=1,c=2;
    // rdi, rsi, rdx, rcx, r8, r9
    asm volatile(
        "mov %0,%%rdi;"//arg0
        "mov %1,%%rsi;"//arg1
        "mov %2,%%rdx;"//arg2
        "call *%3;"//call
        : 
        :"g"(a),"g"(b),"g"(c),"m"(fun_ptr)
        :"%rdi","%rsi","%rdx"
    );
}


/*
hello a=2,b=1,c=2
*/

文章作者: f19
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 f19 !
  目录