关于gcc 嵌入式汇编的一个问题(current在x86_64下的实现)

发布于 2022-09-09 01:39:03 字数 949 浏览 6 评论 2

在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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

鹤舞 2022-09-20 09:48:57

沙发很给力啊。。。

澜川若宁 2022-09-19 19:18:33

有时侯我们会想这么写一条汇编语句:
                  
                /* 错误写法 */
                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)』。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文