C++ 中如何实现非静态、非虚拟方法?

发布于 2025-01-11 03:49:30 字数 6912 浏览 1 评论 0原文

我想知道 C++ 中的方法是如何实现的。我想知道方法是如何“在幕后”实现的。 因此,我制作了一个简单的 C++ 程序,其中有一个带有 1 个非静态字段和 1 个非静态、非虚拟方法的类。

然后我在主函数中实例化该类并调用该方法。我使用了 objdump -d 选项来查看该程序的 CPU 指令。我有一个 x86-64 处理器。 代码如下:

#include<stdio.h>


class TestClass {
public:
   int x;
   int xPlus2(){
       return x + 2;
   }
};

int main(){
    TestClass tc1 = {5};
    int variable = tc1.xPlus2();
    printf("%d \n", variable);
    return 0;
}

这是方法xPlus2的说明:

  0000000000402c30 <_ZN9TestClass6xPlus2Ev>:
  402c30:   55                      push   %rbp
  402c31:   48 89 e5                mov    %rsp,%rbp
  402c34:   48 89 4d 10             mov    %rcx,0x10(%rbp)
  402c38:   48 8b 45 10             mov    0x10(%rbp),%rax
  402c3c:   8b 00                   mov    (%rax),%eax
  402c3e:   83 c0 02                add    $0x2,%eax
  402c41:   5d                      pop    %rbp
  402c42:   c3                      retq   
  402c43:   90                      nop
  402c44:   90                      nop
  402c45:   90                      nop
  402c46:   90                      nop
  402c47:   90                      nop
  402c48:   90                      nop
  402c49:   90                      nop
  402c4a:   90                      nop
  402c4b:   90                      nop
  402c4c:   90                      nop
  402c4d:   90                      nop
  402c4e:   90                      nop
  402c4f:   90                      nop

如果我理解正确的话,这些指令可以只用3条指令代替,因为我相信我不需要使用堆栈,我认为编译器冗余地使用了它:

mov (%rcx), eax
add $2, eax
retq

然后也许我仍然需要大量 nop 指令用于同步目的或其他目的。如果您查看 CPU 指令,您会发现 x 字段的值 存储在内存中 rcx 寄存器 保存的位置。稍后您将看到其余的 CPU 指令。对我来说跟踪这里发生的事情有点困难(特别是调用 _main 函数时发生了什么),我什至不知道汇编的哪些部分是重要的。编译器生成 main 函数(正如我所期望的),但随后它也生成了从 main 调用的 _main 函数,这两个函数之间也有一些奇怪的函数。 以下是我认为可能有趣的组件的其他部分:

  0000000000401550 <main>:
  401550:   55                      push   %rbp
  401551:   48 89 e5                mov    %rsp,%rbp
  401554:   48 83 ec 30             sub    $0x30,%rsp
  401558:   e8 e3 00 00 00          callq  401640 <__main>
  40155d:   c7 45 f8 05 00 00 00    movl   $0x5,-0x8(%rbp)
  401564:   48 8d 45 f8             lea    -0x8(%rbp),%rax
  401568:   48 89 c1                mov    %rax,%rcx
  40156b:   e8 c0 16 00 00          callq  402c30 <_ZN9TestClass6xPlus2Ev>
  401570:   89 45 fc                mov    %eax,-0x4(%rbp)
  401573:   8b 45 fc                mov    -0x4(%rbp),%eax
  401576:   89 c2                   mov    %eax,%edx
  401578:   48 8d 0d 81 2a 00 00    lea    0x2a81(%rip),%rcx        # 404000 <.rdata>
  40157f:   e8 ec 14 00 00          callq  402a70 <printf>
  401584:   b8 00 00 00 00          mov    $0x0,%eax
  401589:   48 83 c4 30             add    $0x30,%rsp
  40158d:   5d                      pop    %rbp
  40158e:   c3                      retq   
  40158f:   90                      nop

0000000000401590 <__do_global_dtors>:
  401590:   48 83 ec 28             sub    $0x28,%rsp
  401594:   48 8b 05 75 1a 00 00    mov    0x1a75(%rip),%rax        # 403010 <p.93846>
  40159b:   48 8b 00                mov    (%rax),%rax
  40159e:   48 85 c0                test   %rax,%rax
  4015a1:   74 1d                   je     4015c0 <__do_global_dtors+0x30>
  4015a3:   ff d0                   callq  *%rax
  4015a5:   48 8b 05 64 1a 00 00    mov    0x1a64(%rip),%rax        # 403010 <p.93846>
  4015ac:   48 8d 50 08             lea    0x8(%rax),%rdx
  4015b0:   48 8b 40 08             mov    0x8(%rax),%rax
  4015b4:   48 89 15 55 1a 00 00    mov    %rdx,0x1a55(%rip)        # 403010 <p.93846>
  4015bb:   48 85 c0                test   %rax,%rax
  4015be:   75 e3                   jne    4015a3 <__do_global_dtors+0x13>
  4015c0:   48 83 c4 28             add    $0x28,%rsp
  4015c4:   c3                      retq   
  4015c5:   90                      nop
  4015c6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4015cd:   00 00 00 

00000000004015d0 <__do_global_ctors>:
  4015d0:   56                      push   %rsi
  4015d1:   53                      push   %rbx
  4015d2:   48 83 ec 28             sub    $0x28,%rsp
  4015d6:   48 8b 0d 23 2d 00 00    mov    0x2d23(%rip),%rcx        # 404300 <.refptr.__CTOR_LIST__>
  4015dd:   48 8b 11                mov    (%rcx),%rdx
  4015e0:   83 fa ff                cmp    $0xffffffff,%edx
  4015e3:   89 d0                   mov    %edx,%eax
  4015e5:   74 39                   je     401620 <__do_global_ctors+0x50>
  4015e7:   85 c0                   test   %eax,%eax
  4015e9:   74 20                   je     40160b <__do_global_ctors+0x3b>
  4015eb:   89 c2                   mov    %eax,%edx
  4015ed:   83 e8 01                sub    $0x1,%eax
  4015f0:   48 8d 1c d1             lea    (%rcx,%rdx,8),%rbx
  4015f4:   48 29 c2                sub    %rax,%rdx
  4015f7:   48 8d 74 d1 f8          lea    -0x8(%rcx,%rdx,8),%rsi
  4015fc:   0f 1f 40 00             nopl   0x0(%rax)
  401600:   ff 13                   callq  *(%rbx)
  401602:   48 83 eb 08             sub    $0x8,%rbx
  401606:   48 39 f3                cmp    %rsi,%rbx
  401609:   75 f5                   jne    401600 <__do_global_ctors+0x30>
  40160b:   48 8d 0d 7e ff ff ff    lea    -0x82(%rip),%rcx        # 401590 <__do_global_dtors>
  401612:   48 83 c4 28             add    $0x28,%rsp
  401616:   5b                      pop    %rbx
  401617:   5e                      pop    %rsi
  401618:   e9 f3 fe ff ff          jmpq   401510 <atexit>
  40161d:   0f 1f 00                nopl   (%rax)
  401620:   31 c0                   xor    %eax,%eax
  401622:   eb 02                   jmp    401626 <__do_global_ctors+0x56>
  401624:   89 d0                   mov    %edx,%eax
  401626:   44 8d 40 01             lea    0x1(%rax),%r8d
  40162a:   4a 83 3c c1 00          cmpq   $0x0,(%rcx,%r8,8)
  40162f:   4c 89 c2                mov    %r8,%rdx
  401632:   75 f0                   jne    401624 <__do_global_ctors+0x54>
  401634:   eb b1                   jmp    4015e7 <__do_global_ctors+0x17>
  401636:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40163d:   00 00 00 

0000000000401640 <__main>:
  401640:   8b 05 ea 59 00 00       mov    0x59ea(%rip),%eax        # 407030 <initialized>
  401646:   85 c0                   test   %eax,%eax
  401648:   74 06                   je     401650 <__main+0x10>
  40164a:   c3                      retq   
  40164b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  401650:   c7 05 d6 59 00 00 01    movl   $0x1,0x59d6(%rip)        # 407030 <initialized>
  401657:   00 00 00 
  40165a:   e9 71 ff ff ff          jmpq   4015d0 <__do_global_ctors>
  40165f:   90                      nop

I wanted to know how methods are implemented in C++. I wanted to know how methods are implemented "under the hood".
So, I have made a simple C++ program which has a class with 1 non static field and 1 non static, non virtual method.

Then I instantiated the class in the main function and called the method. I have used objdump -d option in order to see the CPU instructions of this program. I have a x86-64 processor.
Here's the code:

#include<stdio.h>


class TestClass {
public:
   int x;
   int xPlus2(){
       return x + 2;
   }
};

int main(){
    TestClass tc1 = {5};
    int variable = tc1.xPlus2();
    printf("%d \n", variable);
    return 0;
}

Here are instructions for the method xPlus2:

  0000000000402c30 <_ZN9TestClass6xPlus2Ev>:
  402c30:   55                      push   %rbp
  402c31:   48 89 e5                mov    %rsp,%rbp
  402c34:   48 89 4d 10             mov    %rcx,0x10(%rbp)
  402c38:   48 8b 45 10             mov    0x10(%rbp),%rax
  402c3c:   8b 00                   mov    (%rax),%eax
  402c3e:   83 c0 02                add    $0x2,%eax
  402c41:   5d                      pop    %rbp
  402c42:   c3                      retq   
  402c43:   90                      nop
  402c44:   90                      nop
  402c45:   90                      nop
  402c46:   90                      nop
  402c47:   90                      nop
  402c48:   90                      nop
  402c49:   90                      nop
  402c4a:   90                      nop
  402c4b:   90                      nop
  402c4c:   90                      nop
  402c4d:   90                      nop
  402c4e:   90                      nop
  402c4f:   90                      nop

If I understand it correctly, these instructions can be replaced by just 3 instructions, because I believe that I don't need to use the stack, I think the compiler used it redundantly:

mov (%rcx), eax
add $2, eax
retq

and then maybe I still need lots of nop instructions for synchronization purposes or whatnot. If you look at the CPU instructions, it looks like the value that x field has is stored at the location in memory which rcx register holds. You will see the rest of the CPU instructions in a moment. It is a little bit hard for me to track what has happened here (especially what is going on with the call of _main function), I don't even know what parts of assembly are important to look at. Compiler produces main function (as I expected), but then it also produced _main function which is called from the main, there are some weird functions in between those two as well.
Here are other parts of the assembly that I think may be interesting:

  0000000000401550 <main>:
  401550:   55                      push   %rbp
  401551:   48 89 e5                mov    %rsp,%rbp
  401554:   48 83 ec 30             sub    $0x30,%rsp
  401558:   e8 e3 00 00 00          callq  401640 <__main>
  40155d:   c7 45 f8 05 00 00 00    movl   $0x5,-0x8(%rbp)
  401564:   48 8d 45 f8             lea    -0x8(%rbp),%rax
  401568:   48 89 c1                mov    %rax,%rcx
  40156b:   e8 c0 16 00 00          callq  402c30 <_ZN9TestClass6xPlus2Ev>
  401570:   89 45 fc                mov    %eax,-0x4(%rbp)
  401573:   8b 45 fc                mov    -0x4(%rbp),%eax
  401576:   89 c2                   mov    %eax,%edx
  401578:   48 8d 0d 81 2a 00 00    lea    0x2a81(%rip),%rcx        # 404000 <.rdata>
  40157f:   e8 ec 14 00 00          callq  402a70 <printf>
  401584:   b8 00 00 00 00          mov    $0x0,%eax
  401589:   48 83 c4 30             add    $0x30,%rsp
  40158d:   5d                      pop    %rbp
  40158e:   c3                      retq   
  40158f:   90                      nop

0000000000401590 <__do_global_dtors>:
  401590:   48 83 ec 28             sub    $0x28,%rsp
  401594:   48 8b 05 75 1a 00 00    mov    0x1a75(%rip),%rax        # 403010 <p.93846>
  40159b:   48 8b 00                mov    (%rax),%rax
  40159e:   48 85 c0                test   %rax,%rax
  4015a1:   74 1d                   je     4015c0 <__do_global_dtors+0x30>
  4015a3:   ff d0                   callq  *%rax
  4015a5:   48 8b 05 64 1a 00 00    mov    0x1a64(%rip),%rax        # 403010 <p.93846>
  4015ac:   48 8d 50 08             lea    0x8(%rax),%rdx
  4015b0:   48 8b 40 08             mov    0x8(%rax),%rax
  4015b4:   48 89 15 55 1a 00 00    mov    %rdx,0x1a55(%rip)        # 403010 <p.93846>
  4015bb:   48 85 c0                test   %rax,%rax
  4015be:   75 e3                   jne    4015a3 <__do_global_dtors+0x13>
  4015c0:   48 83 c4 28             add    $0x28,%rsp
  4015c4:   c3                      retq   
  4015c5:   90                      nop
  4015c6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4015cd:   00 00 00 

00000000004015d0 <__do_global_ctors>:
  4015d0:   56                      push   %rsi
  4015d1:   53                      push   %rbx
  4015d2:   48 83 ec 28             sub    $0x28,%rsp
  4015d6:   48 8b 0d 23 2d 00 00    mov    0x2d23(%rip),%rcx        # 404300 <.refptr.__CTOR_LIST__>
  4015dd:   48 8b 11                mov    (%rcx),%rdx
  4015e0:   83 fa ff                cmp    $0xffffffff,%edx
  4015e3:   89 d0                   mov    %edx,%eax
  4015e5:   74 39                   je     401620 <__do_global_ctors+0x50>
  4015e7:   85 c0                   test   %eax,%eax
  4015e9:   74 20                   je     40160b <__do_global_ctors+0x3b>
  4015eb:   89 c2                   mov    %eax,%edx
  4015ed:   83 e8 01                sub    $0x1,%eax
  4015f0:   48 8d 1c d1             lea    (%rcx,%rdx,8),%rbx
  4015f4:   48 29 c2                sub    %rax,%rdx
  4015f7:   48 8d 74 d1 f8          lea    -0x8(%rcx,%rdx,8),%rsi
  4015fc:   0f 1f 40 00             nopl   0x0(%rax)
  401600:   ff 13                   callq  *(%rbx)
  401602:   48 83 eb 08             sub    $0x8,%rbx
  401606:   48 39 f3                cmp    %rsi,%rbx
  401609:   75 f5                   jne    401600 <__do_global_ctors+0x30>
  40160b:   48 8d 0d 7e ff ff ff    lea    -0x82(%rip),%rcx        # 401590 <__do_global_dtors>
  401612:   48 83 c4 28             add    $0x28,%rsp
  401616:   5b                      pop    %rbx
  401617:   5e                      pop    %rsi
  401618:   e9 f3 fe ff ff          jmpq   401510 <atexit>
  40161d:   0f 1f 00                nopl   (%rax)
  401620:   31 c0                   xor    %eax,%eax
  401622:   eb 02                   jmp    401626 <__do_global_ctors+0x56>
  401624:   89 d0                   mov    %edx,%eax
  401626:   44 8d 40 01             lea    0x1(%rax),%r8d
  40162a:   4a 83 3c c1 00          cmpq   $0x0,(%rcx,%r8,8)
  40162f:   4c 89 c2                mov    %r8,%rdx
  401632:   75 f0                   jne    401624 <__do_global_ctors+0x54>
  401634:   eb b1                   jmp    4015e7 <__do_global_ctors+0x17>
  401636:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40163d:   00 00 00 

0000000000401640 <__main>:
  401640:   8b 05 ea 59 00 00       mov    0x59ea(%rip),%eax        # 407030 <initialized>
  401646:   85 c0                   test   %eax,%eax
  401648:   74 06                   je     401650 <__main+0x10>
  40164a:   c3                      retq   
  40164b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  401650:   c7 05 d6 59 00 00 01    movl   $0x1,0x59d6(%rip)        # 407030 <initialized>
  401657:   00 00 00 
  40165a:   e9 71 ff ff ff          jmpq   4015d0 <__do_global_ctors>
  40165f:   90                      nop

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(2

楠木可依 2025-01-18 03:49:30

我认为您正在寻找的是这些指令:

 40155d:   c7 45 f8 05 00 00 00    movl   $0x5,-0x8(%rbp)
 401564:   48 8d 45 f8             lea    -0x8(%rbp),%rax
 401568:   48 89 c1                mov    %rax,%rcx
 40156b:   e8 c0 16 00 00          callq  402c30 <_ZN9TestClass6xPlus2Ev>
 401570:   89 45 fc                mov    %eax,-0x4(%rbp)

指令与 main 中的代码匹配:

TestClass tc1 = {5};
int variable = tc1.xPlus2();
  • 在地址 40155d 处,字段 tc1.x 初始化为值 5。
  • 这些 401564 指向 tc1 的指针被加载到寄存器 %rax
  • 在地址 401568 处,指向 tc1 的指针是复制到寄存器 %rcx
  • 在地址 40156b 处调用方法 tc1.xPlus2()
  • 在地址 401570 处结果存储在 <代码>变量

I think what you are looking for are these instructions:

 40155d:   c7 45 f8 05 00 00 00    movl   $0x5,-0x8(%rbp)
 401564:   48 8d 45 f8             lea    -0x8(%rbp),%rax
 401568:   48 89 c1                mov    %rax,%rcx
 40156b:   e8 c0 16 00 00          callq  402c30 <_ZN9TestClass6xPlus2Ev>
 401570:   89 45 fc                mov    %eax,-0x4(%rbp)

These match with the code from main:

TestClass tc1 = {5};
int variable = tc1.xPlus2();
  • At address 40155d the field tc1.x is initialized with the value 5.
  • At address 401564 the pointer to tc1 is loaded into the register %rax
  • At address 401568 the pointer to tc1 is copied into the register %rcx
  • At address 40156b is the call of the method tc1.xPlus2()
  • At address 401570 the result is store in variable
小嗲 2025-01-18 03:49:30

您的观察大多是正确的。 rcx 保存指向调用该方法的对象的 this 指针。 x 存储在 this 指针指向的第一个内存区域中,因此这就是取消引用 rcx 并将结果添加到其中的原因。调用者有责任在调用函数之前确保 rcx 是对象的地址。我们可以看到 main 通过将 rcx 设置为堆栈帧中的地址来准备 rcx。您是正确的,编译器在这里生成了低效的代码并且不需要使用堆栈。使用更高的优化级别 -O1-O2-O3 进行编译可能会解决该问题。这些更高的优化也可能会消除 nop,因为它们用于函数对齐。您基本上可以忽略__main。它用于 libc 初始化。

Your observations are mostly correct. rcx holds the this pointer to the object on which the method was called. x is stored in the first area of memory that the this pointer points to, so that is why rcx was dereferenced and the result added to. It is the responsibility of the caller to make sure that rcx is the address of the object before invoking the function. We can see main prepare rcx by setting it to an address in its stack frame. You are correct that the compiler produced inefficient code here and did not need to use the stack. Compiling with higher optimization levels -O1, -O2, or -O3 will likely fix that. These higher optimizations will probably get rid of the nops too, since they are used for function alignment. You can mostly ignore __main. It's used for libc initialization.

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