c - 如何将变量传递给使用 gcc 编译的 intel 格式内联 asm 代码

标签 c gcc assembly

我想向您寻求一些帮助!我有一个包含大量 C 源代码的项目。大多数是用gcc编译的,但也有一些是用Intel编译器编译的。后面的代码有很多 Microsoft 的 MASM 格式的内联 asm 代码。我想用 gcc 编译整个项目并修改尽可能少的代码。所以我编写了一个 perl 脚本,它将 intel 格式内联 asm 转换为 GAS 格式。 (顺便说一句:我在 64 位 Linux 机器上编译为 32 位)。

我的问题是,我必须在内联 asm("...") 中为 gcc 指定哪些 C 变量传递给添加 ::[var1] "的代码m"var1, [var2] "m"var2, ... 行位于末尾。

这是避免这种明确规范的方法吗?

我的尝试:

虚拟测试 C 代码只是用源 char 数组的元素替换 objective-c har 数组的 4 个字符(我知道这不是最好的方法。这只是一个愚蠢的例子)。

在原始函数中没有明确的规范,但它可以用Intel编译器编译(我很羞愧,但我没有测试这个,但它应该与Intel编译器一起工作,因为我是根据真实代码制作的) 。 LOOP 标签被多次使用,甚至在同一个 C 源文件中也是如此。

#include <stdio.h>

void cp(char *pSrc, char *pDst) {
    __asm
    { 
        mov esi, pSrc
        mov edi, pDst
        mov edx, 4
LOOP:
        mov al, [esi]
        mov [edi], al
        inc esi
        inc edi
        dec edx
        jnz LOOP
    };
}   

int main() {
    char src[] = "abcd";
    char dst[] = "ABCD";

    cp(src, dst);
    printf("SRC: '%s', DST: '%s'\n", src, dst);
    return 0;
}

结果是:SRC:'abcd',DST:'abcd'

工作转换后的cp代码是(用gcc编译的)。

GAS (AT&T) 格式(编译:gcc -ggdb3 -std=gnu99 -m32 -o asm asm.c)

void cp(char *pSrc, char *pDst) {
    asm(
        "mov %[pSrc], %%esi\n\t"
        "mov %[pDst], %%edi\n\t"
        "mov $4, %%edx\n\t"
"LOOP%=:\n\t"
        "mov (%%esi), %%al\n\t"
        "mov %%al, (%%edi)\n\t"
        "inc %%esi\n\t"
        "inc %%edi\n\t"
        "dec %%edx\n\t"
        "jnz LOOP%=\n\t"
        : [pDst] "=m" (pDst)
        : [pSrc] "m" (pSrc)
        : "esi", "edi", "edx", "al"
    );
}

Intel 格式(编译:gcc -ggdb3 -std=gnu99 -m32 -masm=intel -o asm asm.c)

void cp(char *pSrc, char *pDst) {
    asm(".intel_syntax noprefix\n\t");
    asm(
        "mov esi, %[pSrc]\n\t"
        "mov edi, %[pDst]\n\t"
        "mov edx, 4\n\t"
"LOOP%=:\n\t"
        "mov al, [esi]\n\t"
        "mov [edi], al\n\t"
        "inc esi\n\t"
        "inc edi\n\t"
        "dec edx\n\t"
        "jnz LOOP%=\n\t"
        : [pDst] "=m" (pDst)
        : [pSrc] "m" (pSrc)
        : "esi", "edi", "edx", "al"
    );
    asm(".intel_syntax prefix");
}

两个代码都可以工作,但需要一些代码修改(插入“%”字符、收集变量、修改跳转标签和jump函数)。

我也尝试过这个版本:

void cp(char *pSrc, char *pDst) {
    asm(".intel_syntax noprefix\n\t");
    asm(
        "mov esi, pSrc\n\t"
        "mov edi, pDst\n\t"
        "mov edx, 4\n\t"
    "LOOP:\n\t"
        "mov al, [esi]\n\t"
        "mov [edi], al\n\t"
        "inc esi\n\t"
        "inc edi\n\t"
        "dec edx\n\t"
        "jnz LOOP\n\t"
    );
    asm(".intel_syntax prefix");
}

但它下降了

gcc -ggdb3 -std=gnu99 -masm=intel -m32 -o ./asm.exe ./asm.c
/tmp/cc2F9i0u.o: In function `cp':
/home/TAG_VR_20130311/vr/vr/slicecodec/src/./asm.c:41: undefined reference to `pSrc'
/home/TAG_VR_20130311/vr/vr/slicecodec/src/./asm.c:41: undefined reference to `pDst'
collect2: ld returned 1 exit status

有没有办法避免输入参数的定义并避免局部标签的修改?

添加

我尝试使用全局变量。对于此 g 约束,必须使用而不是 m

char pGlob[] = "qwer";
void cp(char *pDst) {
    asm(".intel_syntax noprefix\n\t"
            "mov esi, %[pGlob]\n\t"
            "mov edi, %[pDst]\n\t"
            "mov edx, 4\n\t"
    "LOOP%=:\n\t"
            "mov al, [esi]\n\t"
            "mov [edi], al\n\t"
            "inc esi\n\t"
            "inc edi\n\t"
            "dec edx\n\t"
            "jnz LOOP%=\n\t"
            ".intel_syntax prefix" : [pDst] "=m" (pDst) : [pGlob] "g" (pGlob) 
            : "esi", "edi", "edx", "al");
}

添加#2

我试过了

            "lea esi, pGlob\n\t"  // OK
            "lea esi, %[_pGlob]\n\t"  // BAD
            //"lea esi, pGlob_not_defined\n\t"  // BAD
            //gcc failed with: undefined reference to `pGlob_not_defined'

编译为

            lea esi, pGlob
            lea esi, OFFSET FLAT:pGlob // BAD
            //compilation fails with: Error: suffix or operands invalid for `lea'

似乎只需要定义函数局部变量。全局变量可以添加到预告片中,但并不是真正必要的。两者都在工作:

            "mov esi, pGlob\n\t" // OK
            "mov esi, %[_pGlob]\n\t" // OK

编译为

             mov esi, OFFSET FLAT:pGlob
             mov esi, OFFSET FLAT:pGlob

我定义了一个函数局部变量。它必须在约束部分定义:

void cp(char *pDst) {
    char pLoc[] = "yxcv";
    asm(".intel_syntax noprefix\n\t"
...
        //"mov esi, pLoc\n\t" // BAD
        "mov esi, %[_pLoc]\n\t" // OK, 'm' BAD
...
       ".intel_syntax prefix" : [_pDst] "=m" (pDst) : [_pLoc] "g" (pLoc) 
       : "esi", "edi", "edx", "al");

不幸的是,应该确定什么是全局变量,什么是局部变量。这并不容易,因为asm代码可以在C宏中定义,甚至周围的函数也不确定。我认为只有预编译器有足够的信息。也许代码必须使用 gcc -E ... 进行预编译。

我意识到,如果不在约束部分定义输出,优化器可以消除一些代码。

TIA!

最佳答案

是的,您确实需要明确指定寄存器。 GCC 不会为你做这件事。而且(通常)您不能将 C 变量名称放入 ASM 字符串中。

对我来说,你的最终代码块看起来非常好,但在 GCC 中你不需要选择自己使用哪些寄存器。您还应该使用 volatile 关键字来防止编译器认为代码没有执行任何操作,因为它没有输出。

试试这个:

char pGlob[] = "qwer";
void cp(char *pDst) {
    asm volatile (".intel_syntax noprefix\n\t"
            "mov edx, 4\n\t"
    "LOOP%=:\n\t"
            "mov al, [%[pGlob]]\n\t"
            "mov [%[pDst]], al\n\t"
            "inc %[pGlob]\n\t"
            "inc %[pDst]\n\t"
            "dec edx\n\t"
            "jnz LOOP%=\n\t"
            ".intel_syntax prefix" :: [pGlob] "g" (pGlob), [pDst] "g" (pDst) : "edx");
}

通过这种方式,编译器会处理加载变量并为您选择寄存器(从而消除从一个寄存器到另一个寄存器的无意义的复制)。理想情况下,您还应该消除显式使用 edx,但这并不是真正必要的。

当然,在这个愚蠢的例子中,我只是用 C 语言重新编码整个事情,然后让编译器完成它的工作。

关于c - 如何将变量传递给使用 gcc 编译的 intel 格式内联 asm 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16354348/

相关文章:

c - sleep 系统调用,默认 sleep 时间是多少?

c++ - 前缀/后缀增量

c++ - 如何防止静态库中的重复符号?

c - 是什么阻止了将函数参数用作隐藏指针?

c++ - 在 C++ 中 Hook 调用函数?

c - 使用 bool 全局变量进行线程同步

c - 在基于 C 的编程难题中调试 switch 语句

c++ - 调用 std::map::clear 的段错误

assembly - 如何在 i386 中正确设置 ss 和 sp 寄存器

assembly - 用汇编语言定义 "variables"