Rax

X86-64寄存器和栈帧

十年热恋 提交于 2020-03-17 10:40:35
某厂面试归来,发现自己落伍了!>>> X86-64寄存器和栈帧 概要 说到x86-64,总不免要说说AMD的牛逼,x86-64是x86系列中集大成者,继承了向后兼容的优良传统,最早由AMD公司提出,代号AMD64;正是由于能向后兼容,AMD公司打了一场漂亮翻身战。导致Intel不得不转而生产兼容AMD64的CPU。这是IT行业以弱胜强的经典战役。不过,大家为了名称延续性,更习惯称这种系统结构为x86-64。 X86-64在向后兼容的同时,更主要的是注入了全新的特性,特别的:x86-64有两种工作模式,32位OS既可以跑在传统模式中,把CPU当成i386来用;又可以跑在64位的兼容模式中,更加神奇的是,可以在32位的OS上跑64位的应用程序。有这种好事,用户肯定买账啦。 值得一提的是,X86-64开创了编译器的新纪元,在之前的时代里,Intel CPU的晶体管数量一直以摩尔定律在指数发展,各种新奇功能层出不穷,比如:条件数据传送指令cmovg,SSE指令等。但是GCC只能保守地假设目标机器的CPU是1985年的i386,额。。。这样编译出来的代码效率可想而知,虽然GCC额外提供了大量优化选项,但是这对应用程序开发者提出了很高的要求,会者寥寥。X86-64的出现,给GCC提供了一个绝好的机会,在新的x86-64机器上,放弃保守的假设,进而充分利用x86-64的各种特性,比如

我可以在Visual Studio中进行调试时在返回之前找出返回值吗?

帅比萌擦擦* 提交于 2020-02-28 00:54:54
具有以下功能: DataTable go() { return someTableAdapter.getSomeData(); } 当我在此函数中设置断点时,是否有可能检查返回的值? go() 直接与 .aspx 页中的数据网格耦合。 检查返回的数据表的唯一方法是使用临时变量。 但是,这有点不方便。 有没有其他办法? #1楼 根据Microsoft的说法,无法使用托管代码可靠地实现此目的。 这是他们意识到并正在解决的问题: 对于那些有调试本机C ++或VB6代码经验的人,您可能使用了在“自动”窗口中为您提供函数返回值的功能。 不幸的是,托管代码不存在此功能。 尽管您可以通过将返回值分配给局部变量来解决此问题,但这并不方便,因为它需要修改您的代码。 在托管代码中,确定您跨过的函数的返回值要麻烦得多。 我们意识到在这里无法始终如一地做正确的事情,因此我们删除了该功能,而不是在调试器中给您错误的结果。 但是,我们希望为您带来帮助,我们的CLR和Debugger团队正在寻找解决此问题的许多潜在解决方案。 不幸的是,这不是Visual Studio 11的一部分。 https://connect.microsoft.com/VisualStudio/feedback/details/597933/add-a-return-pseudo-variable-to-the-visual

Dev 日志 | 一次 Segmentation Fault 和 GCC Illegal Instruction 编译问题排查

吃可爱长大的小学妹 提交于 2019-12-05 01:50:55
摘要 笔者最近在重新整理和编译 Nebula Graph 的第三方依赖,选出两个比较有意思的问题给大家分享一下。 Flex Segmentation Fault——Segmentation fault (core dumped) 在编译 Flex 过程中,遇到了 Segmentation fault: make[2]: Entering directory '/home/dutor/flex-2.6.4/src' ./stage1flex -o stage1scan.c ./scan.l make[2]: *** [Makefile:1696: stage1scan.c] Segmentation fault (core dumped) 使用 gdb 查看 coredump: Core was generated by `./stage1flex -o stage1scan.c ./scan.l'. Program terminated with signal SIGSEGV, Segmentation fault. #0 flexinit (argc=4, argv=0x7ffd25bea718) at main.c:976 976 action_array[0] = '\0'; (gdb) disas Dump of assembler code for function

调用约定

旧巷老猫 提交于 2019-12-04 13:56:11
对于常见的指令集,在指令层面没有所谓的“函数”概念,只有“子程序”概念。子程序是存储在“主程序”之外的一段指令。子程序通过call指令调用,通过ret指令返回。子程序可以使用内存、堆栈和寄存器。通常主程序会传递参数给子程序,子程序将执行结果返回给主程序。这些参数和返回值如何传递,可以由开发者决定。不过如果程序中同时使用了高级语言和汇编语言,为了让编译器生成的汇编代码可以正确的汇编和连接,必须采用一个双方都遵守的传递参数和返回值的方法。这就是调用约定。换言之,调用约定是为了保证了不同函数可以正确汇编和链接而设计的,主程序和子程序之间传递数据的方式。 从上面的说明可以看到,调用约定涉及参数和返回值两部分。早期的高级语言(比如C)只有一个返回值,因此返回值的传递也较为简单。下表总结了几个平台上返回值的传递方法: | 平台 | 整型 | 结构体 | 浮点型 | | x86 | eax | eax | st(0) | | x86-64 | rax | rax | xmm0 | | ARM | R0 | R0 | R0 | | ARM64 | R0 | R0 | R0 | 调用约定中比较复杂的是参数传递方法,其中又以x86平台的调用约定种类繁多。 x86参数传递 在32位x86系统上,由于寄存器数量较少,参数主要通过栈传递,也产生了比较多的调用方式。MSVC和GCC支持的32位调用约定有: |

C语言结构体里的成员数组和指针

坚强是说给别人听的谎言 提交于 2019-11-26 12:39:11
单看这文章的标题,你可能会觉得好像没什么意思。你先别下这个结论,相信这篇文章会对你理解C语言有帮助。这篇文章产生的背景是在微博上,看到 @Laruence 同学出了一个关于C语言的题, 微博链接 。微博截图如下。我觉得好多人对这段代码的理解还不够深入,所以写下了这篇文章。 为了方便你把代码copy过去编译和调试,我把代码列在下面: #include <stdio.h> struct str{ int len; char s[0]; }; struct foo { struct str *a; }; int main(int argc, char** argv) { struct foo f={0}; if (f.a->s) { printf( f.a->s); } return 0; } 你编译一下上面的代码,在VC++和GCC下都会在14行的printf处crash掉你的程序。 @Laruence 说这个是个经典的坑,我觉得这怎么会是经典的坑呢?上面这代码,你一定会问,为什么if语句判断的不是f.a?而是f.a里面的数组?写这样代码的人脑子里在想什么?还是用这样的代码来玩票?不管怎么样,看过原微博的回复,我个人觉得大家主要还是对C语言理解不深,如果这算坑的话,那么全都是坑。 接下来,你调试一下,或是你把14行的printf语句改成: printf("%x\n", f.a->s);