assembly 为什么内联程序集不需要像.data或.text这样的节指令

ua4mk5z4  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(143)

作为一个新手,我正在遵循教程。一个是大写字符串中的所有字符,在VS 2022的内联汇编中:

int main()
{
    char mystr[] = "Hello World:";

    _asm
    {
        mov ecx, length mystr
    my: cmp [mystr + ecx], 'a';
        jl nocap;
        cmp [mystr + ecx], 'z';
        ja nocap;
        sub [mystr + ecx], 32;

    nocap:
    loop my
    }

    std::cout << mystr;

我的问题是:为什么此程序集不需要.data、.text或_start等节:示例中可能混合使用了x86 asm和Linux asm。

tvokkenx

tvokkenx1#

因为它是内联的asm!它就在C函数体的内部,编译器会选择将函数的机器码放入哪一部分。(.text)。
你不需要段指令,就像C
程序不需要手动段指令或GNU C __attribute__((section(".text")))一样-- C++编译器有工作默认值来放置东西。
事实上,MSVC甚至不允许切换节或使用dbasm{}块中发出任意字节;它不是一个完整的汇编程序,因为它必须解析和 * 理解 * 您的asm,以了解它可能修改哪些寄存器,因此它知道要保存哪些内容。
GNU C inline asm leave由你来告诉编译器你的inline asm的输出/输入/乱码是什么,并允许你发出汇编程序将汇编的任意文本。
GCC的工作原理是生成一个.s文本文件,然后运行as对其进行汇编。GNUC内联asm ("add %1, %0" : "=r"(dst) : "r"(src))的工作原理类似于编译时printf将自定义文本格式化到该汇编文件中。您 * 可以 * 做一些会破坏以下编译器生成的代码的事情,例如,在不使用.pushsection/.popsection的情况下切换节以返回到编译器所在的节。或者将asm语法切换为编译器正在使用的语法之外的语法。
或者做一些有用的事情,比如使用.pushsection .data并在.data中发出一些字节,然后返回。Linux内核在某种程度上利用了这一点。例如,在arch/x86/asm/alternative. h中,它们.pushsection .smp_locks记录了原子RMW指令的lock前缀的地址(使用.long发出4个字节),因此如果内核在只有一个CPU的机器上引导,则它可以将那些lock前缀修补为nop或伪前缀,因为相关指令都是原子写中断,而不是同时运行的其他CPU。

您也不需要编写自己的_start(Linux中进程入口点的标准名称),因为C++编译器将程序与CRT启动代码链接,CRT启动代码提供一个调用main的启动代码,如果main返回,则退出时返回main的返回值。如果您想编写自己的程序,您可以在GNU C inline asm for Linux中使用gcc -nostdlib(这意味着-nostartfiles):How Get arguments value using inline assembly in C without Glibc?

对于MSVC,您需要使用一个单独的.asm文件; MSVC不允许在全局范围内内联asm,或者您可以使用_declspec(naked)函数来定义WinMain或任何可执行文件的实际入口点,并使用手写的asm指令进行必要的设置,如调用标准库init函数,然后是call main/push eax/call ExitProcess(或者实际上是call exit,以确保进行清除,如刷新stdio缓冲区)。

相关问题