有没有一种方法可以将汇编代码插入到 C 语言中?

85

我还记得早期使用旧版的Borland DOS编译器时,可以做出这样的操作:

asm {
 mov ax,ex
 etc etc...
}

现在有没有一种半平台独立的方法来做这件事?我需要调用BIOS,所以如果有一种不需要汇编代码就能实现的方法,对我来说同样有用。


请参阅inline-assembly tag wiki中的链接,了解如何正确使用内联汇编。这很困难,最好的建议是https://gcc.gnu.org/wiki/DontUseInlineAsm。为了提高性能,尽可能地调整源代码以帮助编译器生成更好的代码,而不是使用内联汇编。(例如,通过查看编译器的汇编输出,看看它未能优化或做错了什么。) - Peter Cordes
5个回答

87
使用GCC进行内联汇编。
__asm__("movl %edx, %eax\n\t"
        "addl $2, %eax\n\t");

使用VC++技术。

__asm {
  mov eax, edx
  add eax, 2
}

1
C++Builder,来自CodeGear,也支持__asm关键字。 - stukelly
1
VS编译器不支持内联汇编!几年前我曾尝试过,结果把手给烫伤了! - Jay D
3
@JayD:根据目标平台的不同,Visual Studio编译器支持内联汇编。例如,支持32位x86,但不支持64位x86-64。 - Michael Burr
3
是的,这是合法的gcc语法,但是除非你将其放在一个带有__ attribute__((naked))的函数中,该函数仅包含内联汇编,否则会出现错误。 如果您修改寄存器,则必须使用GNU C扩展汇编约束/破坏来告诉编译器;踩到编译器的寄存器可能会导致非常奇怪的结果。(仅禁用优化测试一种情况并不能证明它是安全的)。请参阅https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html / https://stackoverflow.com/tags/inline-assembly/info / http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html。 - Peter Cordes

21

在GCC中,事情不仅仅是这样的。在指令中,您必须告诉编译器发生了什么变化,以便其优化器不会出错。我不是专家,但有时看起来像这样:

    asm ("lock; xaddl %0,%2" : "=r" (result) : "0" (1), "m" (*atom) : "memory");

在编写C代码后,建议通过GCC生成汇编程序清单,然后对代码进行修改。


10

一个好的起点是阅读本文,该文章讨论了C/C++中的内联汇编:

http://www.codeproject.com/KB/cpp/edujini_inline_asm.aspx

文章中的示例:

#include <stdio.h>


int main() {
    /* Add 10 and 20 and store result into register %eax */
    __asm__ ( "movl $10, %eax;"
                "movl $20, %ebx;"
                "addl %ebx, %eax;"
    );

    /* Subtract 20 from 10 and store result into register %eax */
    __asm__ ( "movl $10, %eax;"
                    "movl $20, %ebx;"
                    "subl %ebx, %eax;"
    );

    /* Multiply 10 and 20 and store result into register %eax */
    __asm__ ( "movl $10, %eax;"
                    "movl $20, %ebx;"
                    "imull %ebx, %eax;"
    );

    return 0 ;
}

1
这些都是不安全的:你在没有告诉编译器的情况下修改了EAX和EBX。永远不要在函数内使用GNU C基本汇编语言(除非作为__attribute__((naked))函数的主体)。只使用GNU C扩展汇编,例如asm("sub %1, %0" : "+r"(dst) : "ri"(20));。请参阅https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html和https://stackoverflow.com/tags/inline-assembly/info。你链接的文章确实讨论了扩展汇编,但在其更大的`asm volatile`示例中缺少一些临时寄存器的clobbers。 - Peter Cordes
该文章也遵循了反模式,即以 mov %1, %%eax 开始 asm 模板,而不是在首次要求输入时使用寄存器。 - Peter Cordes

3

对于微软编译器,只支持x86的内联汇编。对于其他目标,您需要在单独的汇编源文件中定义整个函数,将其传递给汇编器并链接生成的对象模块。

在受保护模式操作系统下,您极有可能无法调用BIOS,并且应使用该系统上可用的任何设施。即使在内核模式下,这样做也可能不安全 - 如果这样做,BIOS可能无法正确地与操作系统状态同步。


Mike,你说的“非x86微软编译器不支持内联汇编”是什么意思?当然可以使用例如ARM(非x86)内联汇编,只需搜索即可。 - user5248982
2
"ARM和x64处理器不支持内联汇编。" - https://learn.microsoft.com/en-gb/cpp/assembler/inline/inline-assembler 。内联汇编指的是使用__asm块,我特别指的是微软的编译器。您仍然可以创建一个单独的.asm文件,使用Microsoft汇编器(ML / ML64 / armasm)汇编该文件,并使用CL将生成的.obj与C / C ++程序链接起来。其他答案表明您可以使用其他编译器进行内联汇编。 - Mike Dimmick

1

使用asm__asm__函数(在编译器中有区别)

同时,您也可以使用fortran函数编写Fortran代码

asm("syscall");
fortran("Print *,"J");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接