关于gcc 嵌入式汇编的一个问题(current在x86_64下的实现)
在ULK3.0中,作者描述了current的实现(使用esp寄存器的值),查看X86_64下的实现代码,发现有较大差别:
static inline struct task_struct *get_current(void)
{
struct task_struct *t;
asm volatile("movq %%gs:%P1,%0"
:"=r" (t)
:"i"(offsetof(struct x8664_pda, pcurrent))
:"memory");
return t;
}
不明白两个问题:
1、为什么对X86_64结构不使用esp寄存器的值去获取?
2、这段嵌入式汇编中的movq %%gs:%P1,% 中有两个不明白的地方,一是%%gs:%P1 算是什么意思?段前缀?但是貌似linux汇编没这个方式啊;而是%%gs:%P1中%p1是什么意思?1应该表示是第二个参数,offsetof(struct x8664_pda, pcurrent),P表示什么呢?我查了下GCC手册,没有查到。。。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
沙发很给力啊。。。
有时侯我们会想这么写一条汇编语句:
/* 错误写法 */
asm volatile ("movl %0(%%esp), %%eax" : /* no outputs */ : "i" (4) );
但这样子gcc会报错:
Error: junk `(%esp)' after expression
原因是,%0是立即数(用"i"约束的),于是gcc会把这条汇编指令变成:
movl $4(%esp), %eax
于是它就会报错,因为多了一个$符号 -- 这在GNU as的寄存器间接寻址中是不允许的。需要改成:
asm volatile ("movl %c0(%%esp), %%eax" : /* no outputs */ : "i" (4) );
例如下面的程序:
/* WARNING: WRONG! */
$ cat -n dollar.c
1 #include <stdio.h>
2
3 int main()
4 {
5 long var = 0;
6
7 asm volatile
8 ("pushl $0x11223344nt"
9 "pushl $0x55667788nt"
10 "movl %1(%%esp), %0nt"
11 "addl $8, %%esp " /* 消减前面两条push指令导致的esp值改变 */
12
13 : "=r" (var)
14 : "i" (4)
15 : "cc", "esp"
16 );
17
18 printf("var is 0x%xn", var);
19 return 0;
20 }
使用gcc编译:
$ gcc dollar.c -m32 -S
查看一下生成的dollar.s文件,我们看到inline asm被gcc转换成:
pushl $0x11223344
pushl $0x55667788
movl $4(%esp), %eax
addl $8, %esp
如上所说,"$4(%esp)"这种寻址方式是无法通过GNU as的,因为多了一个$符号。 GCC为我们提供了一些方法,
允许我们消除那个无用的$符号:
%c0 消除%0的$符号
%n0 消除%0的$符号,并添加负号。 (例如%0是4,那么%n0就是-4)
%l0 和%0相同,但是只用于跳转指令的目标
%a0 %0本来是一个地址,例如0x8048000,放在%eax中。 现在把它转换为寄存器间接寻址,亦即: (%eax)
%P0 %c0相同,消除%0的$符号
让我们再回头来看上面的dollar.c程序。 我们把第10行改成:
"movl %c1(%%esp), %0nt"
然后再次用gcc dollar.c -m32 -S编译,查看dollar.s中的相关代码,发现这条汇编语句变成了:
movl 4(%esp), %eax
这正是我们期待的结果。 至于%n0、%l0和%P0,留给读者自己试验一下。
看一下%a0的例子:
$ cat a.c
#include <stdio.h>
int main(void)
{
char c = 0;
asm volatile ("movb %a1, %0"
: "=r" (c)
/* GNU ld 为IA32连接程序时,默认在从0x8048001开始的地方保存'E'、'L'、'F'三个字母 */
: "r" (0x8048001)
);
printf("c is %cn", c);
return 0;
}
编译运行:
$ gcc -m32 a.c
$ ./a.out
c is E
成功的打印出了字母'E'。 倘若我们用-S生成*.s文件,再查看其中的相关汇编语句:
movb (%eax), %al
这说明gcc为0x8048001接合了eax寄存器,这就是%1;而%a1,则把它转换为间接寻址,也就是『(%%eax)』。