c++代码,,采用g++编译器。如果内联函数内的汇编使用了ebx寄存器,则如果按照-fPIC方式编译,则有下面错误提示
错误:重新加载‘asm’时在类‘BREG’中找不到寄存器
错误:‘asm’操作数中有不可能的约束
原因 :采用-fPIC 时候,是共享库方式。可实现
position independent(位置无关)
relocate(可重定位)
位置无关代码主要是在访问全局变量和全局函数的时候采用了位置无关的重定位方法,即依赖GOT和PLT来重定位。普通的重定位方法需要修改代码段,比如偏移地址0x100处需要重定位,loader就修改代码段的0x100处的内容,通过查找重定位信息得到具体的
值。这种方法需要修改代码段的内容,对于动态连接库来说,其初衷是让多个进程共享代码段,若对其进行写操作,就回引起COW,从而失去共享。
-fPIC选项告诉编绎器使用GOT和PLT的方法重定位,GOT是数据段,因此避免了COW,真正实现了共享。
但是实现以上功能,要借助ebx寄存器。所以当使用ebx寄存器时候会包错误。
(如果不用-fPIC,动态连接库依然可以使用,但其重定位方法为一般方法,必然会引起COW,但也无所谓,除了性能在COW时稍微受些影响,其他也没啥。)
处理方法可请参考下文:
/////////////////////////////////
当用gcc指定-fPIC的时候,gcc将从C源代码中产生PIC的汇编语言代码。但有
时候,我们需要用汇编语言来产生PIC代码。
在ELF下,PIC的实现是使用基寄存器(base register)。在PIC下,所有的
标号引用都是通过基寄存器实现的,为此,要用汇编写PIC的话,必须保存
基寄存器(base register)。由于位置无关代码,控制传送指令的目的地址
必须被替换或者是在PIC情况下计算的。对X86机器来说,该基寄存器
(base register)就是ebx.这里我们将介绍在X86上安全的PIC汇编代码的
两种方法。这些技术在Linux C库中也被使用到。
gcc支持内嵌汇编的声明,可让程序员在C语言中使用汇编语言。当写LINUX系
统调用接口的时候这是很有用的,而无须使用机器相关指令。
在linux 下系统调用是通过int $0x80的。一般的,系统调用会有三个参数:
#include
extern int errno;
int read( int fd,void *buf ,size count)
{
long ret;
__asm__ __volatile__ ("int $0x80"
:"=a"(ret)
:"O"(SYS_read),"b"((long)fd),
"c"((long)buf),"d"((long)count):"bx");
if (ret>=0)
{
return (int) ret:
}
errno=-ret;
retrun -1;
}
以上汇编代码把系统调用号SYS_read放到了eax中,fd到ebx中,buf到
ecx中,count到edx中,从int $0x80中返回值ret放在eax中。在不用
-fPIC的情况下,这样定义运行良好。带-fPIC的gcc应该要检查ebx是否被
被改变,并且应该在汇编代码里保存和恢复ebx。但是不幸的是,事实上不是
这样的。我们为了支持PIC必须自己写汇编代码。
#include
extern int errno;
int read( int fd,void *buf ,size count)
{
long ret;
__asm__ __volatile__ ("pushl %%ebx\n\t"
"movl %%esi,%%ebx\n\t"
"int $0x80\n\t"
"popl %%ebx"
:"=a"(ret)
:"O"(SYS_read),"S"((long)fd),
"c"((long)buf),"d"((long)count):"bx");
if (ret>=0)
{
return (int) ret:
}
errno=-ret;
return -1;
}
这里首先把fd放到esi中,然后保存ebx,把esi移到ebx,在int $0x80后恢复
ebx。这样保证ebx不被改变(除了在int $0x80中断调用中)。同样的原则也
适用于其他内嵌的汇编。
在任何时候,当ebx可能要被改变时,千万要记得保存和恢复ebx.
阅读(3349) | 评论(0) | 转发(0) |