linux平台学x86汇编(十九):C语言中调用汇编函数

【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】

除了内联汇编以外,还有一种途径可以把汇编代码整合到C/C++语言中,C/C++语言可以直接调用汇编函数,把输入值传递给函数,然后从函数获得输出值。

如果希望汇编语言函数和C/C++程序一起工作,就必须显示地遵守C样式的函数格式,也就是说所有输入变量都必须从堆栈读取,并且大多数输入值都返回到EAX嫁寄存器中。在汇编函数代码中,C样式函数对于可以修改哪些寄存器和函数必须保留哪些寄存器有着特定的规则。如果必须保留的寄存器被修改了,那么必须恢复寄存器的原始值,否则在执行返回发出调用的C程序会出现不可预料的后果。在函数中使用通用寄存器必须谨慎。下表列出寄存器在函数中的状态:


寄存器


状态


eax


用于保存输出值,但是可能在函数返回之前被修改


ebx


用于指向全局偏移表,必须保留


ecx


在函数中可用


edx


在函数中可用


ebp

C程序使用它作为堆栈基址指针;必须保留


esp

C程序使用它作为堆栈基址指针;必须保留


edi

C程序使用它作为堆栈基址指针;必须保留


esi

C程序使用它作为局部寄存器;必须保留


ST(0)

保存浮点输出值


ST(1)-ST(7)

在函数中可用

被调用的函数必须保留ebx、edi、esi、esp寄存器,这要求在执行函数代码之前把寄存器的值压入堆栈,并且在函数准备返回调用程序时把他们弹出堆栈。

1 C函数调用的汇编语言函数的基本模板如下:

.section .text
.type func, @function
func:
pushl %ebp
movl %esp, %ebp
subl $12, %esp #为函数局部变量保留堆栈空间,可以保存3个4字节的数据值。在函数中,相对于ebp寄存器引用局部变量。
pushl %edi
pushl %esi
pushl %ebx 

# here is function code 

popl %ebx
popl %esi
popl %edi
movl %ebp, %esp
popl %ebp
ret

该模板可以用于C/C++函数使用的所有汇编语言函数。

在把C文件和包含汇编函数的S文件编译可执行文件时,需要把所有文件包含在编译器命令行中,如下:

$ gcc -o app cfile.c asmfun.s

最终编译器输出单一可执行文件app,不过这样不会生成中间文件,所以我们还可以使用单独的汇编语言目标文件编译程序。如下:

$ as -o asmfunc.o asmfunc.s

$ gcc -o app cfile.c asmfun.o

对于大型程序使用许多文件时,可以编写Makefile文件来自动完成编译连接工作。 如下:

# Makefile for linux as

CFLAGS= -Wall -g
ASFLAGS= -gstabs

SRC_BIN=target_bin

SRC_C=$(wildcard *.c)
SRC_S=$(wildcard *.s)

SRC_OBJ=$(SRC_C:.c=.o)
SRC_OBJ+=$(SRC_S:.s=.o)

all: $(SRC_BIN)

$(SRC_BIN): $(SRC_OBJ)
 $(CC) -o [email protected] $(SRC_OBJ)

clean:
 $(RM) $(SRC_OBJ) $(SRC_BIN) *~
.PHONY:
 all clean

下面是汇编语言函数和使用该函数的C程序示例,函数接收二个整数输入参数,求它们的和,然后把结果返回到eax寄存器中:

# add.s
.type add, @function
.globl add
add:
    pushl %ebp        # 因为该函数不影响ebx,edi、esi寄存器,所以开头和结尾没有包含它们。
    movl %esp, %ebp
    movl 8(%ebp), %eax
    addl 12(%ebp), %eax
    movl %ebp, %esp
    popl %ebp
    ret

c文件:

include <stdio.h>
int add(int, int);
int main(int argc, const char *argv[])
{
    int ret;
    ret = add(7, 11);
    printf("The return value is %d.\n", ret);
    return 0;
}

make及执行结果输出如下:

$ make
cc -Wall -g   -c -o main.o main.c
as -gstabs  -o add.o add.s
cc -o target_bin main.o add.o
$ ./target_bin
The return value is 18.
$

在C函数调用之前,每个输入值都要存放在堆栈中,在使用超过一个输入值时,必须按照顺序把它们存放进堆栈,C函数后面的参数先进栈,前面的参数后进栈。由于堆栈是向下增长的,所以在汇编函数中位8(%ebp)引用第一个输入值,位置12(%ebp)引用第二个输入值。

时间: 2024-10-07 20:41:38

linux平台学x86汇编(十九):C语言中调用汇编函数的相关文章

linux平台学x86汇编(二十):汇编库的使用

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 汇编语言和C一样,可以通过使用库来简化阻止大量函数的目标文件的问题.GNU C编译器可以不在命令行中独立地包含每个独立地函数目标文件,它允许吧所有目标文件组合在单一存档文件中.在编译C程序时,要做的工作就是包含单一的目标库文件,在编译时,编译器可以从库文件中挑出所需的正确目标文件.在库文件中,经常按照应用程序类型或者函数类型把函数分组在一起,单一应用程序项目可以使用多个库文件

linux平台学x86汇编(十八):内联汇编

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 使用汇编语言笔编程最常见的方式是在高级语言(C和C++)程序内编写汇编函数,这种吧汇编语言直接写到C和C++语言程序内的技术称为内联汇编. GNU的C编译器使用asm关键字指出使用汇编语言编写的源代码段落.asm段的基本格式如下: asm("as code"): 括号中的汇编指令必须在括号,指令超过一条的话必须使用新的行分隔汇编语言代码每一行,因为编译器逐字地取得a

linux平台学x86汇编(十四):函数的使用

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 和高级语言一样,汇编语言在多个项目之间可能会编写相同的过程和处理,如果使用函数的话就可以不必每次需要时都重复编写实用程序代码,从而在需要它的时候调用它. 函数包含完成特定功能所需的代码,数据从主程序传递给函数,然后结果返回给主程序.调用函数时,程序执行路径被改变,切换到函数代码的第一条指令.处理器从这个位置开始执行指令,直到函数表明它可以把控制返回到主程序中的原始位置. 在汇

linux平台学x86汇编(十六):在汇编语言中调用C库函数

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 其实在汇编语言中也是可以使用C库函数的,这一节我们来看一下如何在汇编语言中调用C库函数以使得我们的程序看上去很方便地和用户交互. C库包括C程序通用的喝多函数,如printf和exit等,下面我们紧接着上一节的知识来实现一个两整数想加的计算并输出计算结果的程序. # libc.s .section .data output: .asciz "The result is %d.

linux平台学x86汇编(八):条件跳转

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 在此之前我们使用的汇编代码示例都是从第一条指令开始,直到最后最后一条指令程序退出.但实际上和高级语言类似,汇编代码也提供指令来改变程序处理数据方式. 正常情况下,程序要执行要执行的下一条指令是在指令指针寄存器中,指令指针确定程序中哪条指令是应该执行的下一条指令. 当指令指针在程序指令中移动时,EIP寄存器会递增.指令长度可能是多个字节,所以指向下一条指令不仅仅是每次是指令指针

linux平台学x86汇编(十五):使用命令行参数

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 在高级语言中,程序在命令行上启动时常常带一个或多个参数,在汇编语言中也可以实现这一特性.在实现这一特性之前,我们先了解一下linux如何从命令行执行程序. 每一个应用程序开始执行时,系统都会为该程序分配一块内存区域,并且每个程序都分配相同的虚拟内存地址.虚拟内存地址由操作系统映射到物理内存地址.在Linux中,程序的虚拟内存地址是从0x80480000开始,到地址0xbfff

linux平台学x86汇编(十):整数运算

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 这一节介绍如何在编语言中上使用整数运算,包括加法.减法.乘法和除法. 加法指令 add指令用于把两个整数想加.格式如下: add src, dest 其中src可以是立即数值.内存地址.寄存器.dest可以是寄存器或内存中的值,不能同时使用内存地址作为源和目标.结果存放在dest中.和其他GNU汇编指令一样,需要在add结尾添加b.w.l来指定操作数长度.如果没有使用整个寄存

linux平台学x86汇编(九):循环指令

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 循环也是改变指令执行顺序的一种方式,循环操作重复的执行,直到满足条件.我们可以使用条件跳转指令来创建循环,但事实上汇编语言中有更简单的循环指令系列. 循环指令使用ECX寄存器作为计数器,随着循环指令的执行自动递减它的值,并且不会影响EFLAGS寄存器的标志位,当ecx寄存器值到达0时,0标志不会被设置.循环指令有如下:loop(循环直到ecx寄存器为0).loope/loop

linux平台学x86汇编(十二):字符串的存储与加载

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 字符串的存储与加载是指,将字符串的值加载到寄存器和将其传回内存位置中.其使用指令lods指令和stos指令. lods指令用于把内存中的字符串值传送到eax寄存器中,该指令有三种不同格式:lodsb(1字节).lodsw(2字节).lodsl(4字节).lods指令使用esi寄存器作为隐含的源操作数.esi寄存器必须包含要加载的字符串所在的内存地址. 在使用lods指令把字符