abi linux
2018-06-07 13:30:46 xuejianbest 阅读数 397

本篇文章是基于C和汇编的

首先采用调用0x80中断的方式进行系统调用:

void prints(char *str){
    int i = 0;
    while(str[i] != '\0'){
        i++;
    }

    asm("movl $4, %%eax \n\t"
        "movl $1, %%ebx \n\t"
        "movl %0, %%ecx \n\t"
        "movl %1, %%edx \n\t"
        "int $0x80 \n\t"
        ::"m"(str), "m"(i)
      );

    return;
}

int main(){
    char *str1  = "ab"; //字符串内容存储在堆上
    char str2[] = "ab"; //字符串内容存储在栈上
    prints(str1);  //正常显示
    prints(str2);  //不显示
}

这里的关键在于Linux系统0x80号中断是32位系统调用。其接收4个参数,寄存器ecx中的参数为要显示的内容的内存首地址,而在64位系统下用gcc编译,生成的程序默认是64位的,64位程序的内存地址(也就是C语言中的指针)是64位的,我们把内存地址传给ecx,ecx只有32位,所以地址的高32位会丢失,系统按照错误的地址去寻址,肯定找不到内容。

那么为何堆上的数据可以显示,段上的不行呢,因为Linux系统在给程序分配内存空间的时候,堆的内存位置位于低内存地址,地址虽然是64位的,但是高32位全为0;而堆栈却位于高内存地址,高32位不为零,因此只能正确寻找到堆上的数据。

0x80中断传入不正确的地址会怎样:并不会报错,没有任何提示,但是会返回一个错误码(负数),存在eax中,具体错误码可以在头文件中查看(头文件中是正数):

/usr/include/asm-generic/errno-base.h 
/usr/include/asm-generic/errno.h

通过echo $?可以看到程序返回242(0xF2),也就是-14(负数代表调用出错,绝对值是错误码;若正确调用会返回正确输出的字符数)。查看errno-base.h中定义:

#define    EFAULT        14    /* Bad address */

和上面分析的一样:传入了错误的内存地址。

实际上printf正确调用也会返回写入(标准输出)的字符数(返回值用eax寄存器传递),错误调用返回负数,这点和系统调用的返回值类似。


我们怎么修改我们的程序呢?64位程序不再用int $0x80进行系统调用,可以考虑用syscall指令,这是64位系统的ABI调用方式,系统调用号和0x80不同,参数传递的寄存器约定也不一样。
详情可以查看:http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

修改后的程序:

void prints(char *str){
    long i = 0;  //这里也可以用int,因为用int后面会编译为`movl ..., %edx`,这个语句会把edx高32位清零。
    while(str[i] != '\0'){
        i++;
    }
    asm("syscall \n\t"::"a"(1), "D"(1), "S"(str), "d"(i));
}

int main(){
    char *str = "ab\n";
    char str1[] = "cd\n";

    prints(str);
    prints(str1);
}

syscall参数寄存器约定:

系统调用功能号:rax
参数列表按顺序分别是:rdi、rsi、rdx、r10、r8、r9

普通函数参数寄存器约定:

参数列表按顺序分别是:rdi、rsi、rdx、rcx、r8、r9

2018-12-04 17:14:39 xuejianbest 阅读数 38

首先采用调用0x80中断的方式进行系统调用:

void prints(char *str){
    int i = 0;
    while(str[i] != '\0'){
        i++;
    }

    asm("movl $4, %%eax \n\t"
        "movl $1, %%ebx \n\t"
        "movl %0, %%ecx \n\t"
        "movl %1, %%edx \n\t"
        "int $0x80 \n\t"
        ::"m"(str), "m"(i)
      );
    
    return;
}

int main(){
    char *str1  = "ab"; //字符串内容存储在堆上
    char str2[] = "ab"; //字符串内容存储在栈上
    prints(str1);  //正常显示
    prints(str2);  //不显示
}

这里的关键在于linux系统0x80号中断是32位系统调用,接收4个参数,寄存器ecx中的参数为要显示的内容的内存地址,而在64位系统用gcc编译,生成的程序默认是64位的,64位程序的内存地址也是64位的,我们把内存地址传给ecx,ecx只有32位,所以64位的地址高32位会丢失,系统按照错误的地址取寻址,肯定找不到内容。

那么为何堆上的数据可以显示,段上的不行呢,因为linux系统在给程序分配内存空间的时候,堆的内存位置就位于低内存,地址虽然是64位的,但是高32位全为0;而堆栈却位于内存高位地址,高32位不位零,因此只能正确寻找到堆上的数据。

0x80中断传入不正确的地址会怎样:并不会报错,没有任何提示,但是会返回一个错误码(负数),存在eax中,具体错误码可以在头文件中查看:

/usr/include/asm-generic/errno-base.h 
/usr/include/asm-generic/errno.h

通过echo $?可以看到程序返回242(0xF2),也就是-14(负数代表调用出错,绝对值是错误码;若正确调用会返回正确输出的字符数)。实际上printf正确调用也会返回写入的字符数(返回值用eax寄存器传递),错误调用返回负数,这点和系统调用的返回值类似。
查看errno-base.h中定义:

#define    EFAULT        14    /* Bad address */

和上面分析的一样:传入了错误的内存地址。


我们怎么修改我们的程序呢?64位程序不再用int $0x80进行系统调用,可以考虑syscall,这个是64位系统的ABI入口,系统调用号和0x80不同,参数传递的寄存器约定也不一样。详情可以查看:http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

修改后的程序:

void prints(char *str){
    long i = 0;  //这里也可以用int,因为用int后面会编译为`movl ..., %edx`,这个语句会把edx高32位清零。
    while(str[i] != '\0'){
        i++;
    }
    asm("syscall \n\t"::"a"(1), "D"(1), "S"(str), "d"(i));
}

int main(){
    char *str = "ab\n";
    char str1[] = "cd\n";

    prints(str);
    prints(str1);
}

syscall寄存器参数约定:

系统调用功能号:rax
参数列表按顺序分别是:rdi、rsi、rdx、r10、r8、r9


查看c函数可以用man 2 write

2013-01-25 09:46:41 CHENYU123123 阅读数 1150

我在编译linux2.6.30的时候,关于是否在内核特性主页中选择ABI或EABI出现了疑问,现在网上引用了下面的内容,在此向作者表示感谢:


1。什么是ABI

ABI,application binary interface (ABI),应用程序二进制接口。
既然是 接口,那就是某两种东西之间的沟通桥梁,此处有这些种情况:
A。应用程序 <-> 操作系统;
B。应用程序 <-> (应用程序所用到的)库
C 。应用程序各个组件之间

类似于API的作用是使得程序的代码间的兼容,ABI目的是使得程序的二进制(级别)的兼容。

2。什么是OABI 和 EABI
OABI中的O,表示“Old”,“Lagacy”,旧的,过时的,OABI就是旧的/老的ABI。
EABI中的E,表示“Embedded”,是一种新的ABI。
EABI有时候也叫做GNU EABI。
OABI和EABI都是专门针对ARM的CPU来说的。

3。EABI的好处 / 为何要用EABI
A。支持软件浮点和硬件实现浮点功能混用
B。系统调用的效率更高
C。后今后的工具更兼容
D。软件浮点的情况下,EABI的软件浮点的效率要比OABI高很多。

4。OABI和EABI的区别
两种ABI在如下方面有区别:
A。调用规则(包括参数如何传递及如何获得返回值)
B。系统调用的数目以及应用程序应该如何去做系统调用
C。目标文件的二进制格式,程序库等

D。结构体中的 填充(padding/packing)和对齐。


Embedded application binary interface, 即嵌入式应用二进制接口,是描述可连接目标代码,库目标代码,可执行文件影像,如何连接,执行和调试,以及目标代码生成过程,和c, c++语言接口的规范,是编译连接工具的基础规范,也是研究它们工作原理的基础.




2012-08-13 15:42:53 force_eagle 阅读数 3008

New X32 ABI: 64-bit mode with 32-bit pointers

The 64 bits mode of x86 CPUs enlarges the CPU registers to 64 bit, allowing to address larger (>4GB) amounts of memory. This widening, however, has a drawback. Because memory addresses are 64-bit wide, pointers occupy 64 bits of space, the double of space used in 32 bits mode, so binaries compiled for the 64-bit mode are bigger, and when these programs run they use more RAM. And since they are bigger they can cause a performance loss, because with bigger memory addresses, less CPU instructions will fit in the CPU caches.

Some programs have workloads CPU and pointer intensive enough to care about this performance, but with memory requirements not big enough to care about 64-bit memory addressing. They can avoid the 64-bit pointer overhead by just using the 32 bits mode: Processors still allow to run 32-bit operative systems, or run 32-bit programs on top of 64-bit kernels. But this choice also has problems. When a program runs in 32-bit mode, it loses all the other features of the 64-bit mode: larger number of CPU registers, better floating-point performance, faster PIC (position-independent code) shared libraries, function parameters passed via registers, faster syscall instruction...

So a new X32 kernel ABI has been created. A program compiled for this new ABI can run in the 64-bit mode, with all its features, but it uses 32 bits pointers and 32-bit long C type. So applications who need it can enjoy the performance of the 64-bit mode, but with the memory requirements of a 32 bits ABI. Code: (commit)

Recommended LWN article: The x32 system call ABI

Slides from the developers: link

Official X32 coordination site: http://sites.google.com/site/x32abi

2012-08-13 15:42:00 iteye_4476 阅读数 43

New X32 ABI: 64-bit mode with 32-bit pointers

The 64 bits mode of x86 CPUs enlarges the CPU registers to 64 bit, allowing to address larger (>4GB) amounts of memory. This widening, however, has a drawback. Because memory addresses are 64-bit wide, pointers occupy 64 bits of space, the double of space used in 32 bits mode, so binaries compiled for the 64-bit mode are bigger, and when these programs run they use more RAM. And since they are bigger they can cause a performance loss, because with bigger memory addresses, less CPU instructions will fit in the CPU caches.

Some programs have workloads CPU and pointer intensive enough to care about this performance, but with memory requirements not big enough to care about 64-bit memory addressing. They can avoid the 64-bit pointer overhead by just using the 32 bits mode: Processors still allow to run 32-bit operative systems, or run 32-bit programs on top of 64-bit kernels. But this choice also has problems. When a program runs in 32-bit mode, it loses all the other features of the 64-bit mode: larger number of CPU registers, better floating-point performance, faster PIC (position-independent code) shared libraries, function parameters passed via registers, faster syscall instruction...

So a new X32 kernel ABI has been created. A program compiled for this new ABI can run in the 64-bit mode, with all its features, but it uses 32 bits pointers and 32-bit long C type. So applications who need it can enjoy the performance of the 64-bit mode, but with the memory requirements of a 32 bits ABI. Code:(commit)

Recommended LWN article:The x32 system call ABI

Slides from the developers:link

Official X32 coordination site:http://sites.google.com/site/x32abi

没有更多推荐了,返回首页