8

我正在使用带有一些内联汇编代码的 Borland Turbo C++,所以大概是 Turbo Assembler (TASM) 风格的汇编代码。我希望执行以下操作:

void foo::bar( void )
{
    __asm
    {
      mov eax, SomeLabel
      // ...
    }
    // ...
SomeLabel:
    // ...
}

所以 SomeLabel 的地址被放入 EAX 中。这不起作用,编译器抱怨:未定义的符号'SomeLabel'。

在 Microsoft Assembler (MASM) 中,美元符号 ($) 用作当前位置计数器,这对我的目的很有用。但这似乎在 Borlands Assember 中不起作用(表达式语法错误)。

更新:更具体地说,我需要编译器在编译/链接期间而不是在运行时生成它作为常量移动到 eax 中的地址,因此它会像“mov eax,0x00401234”一样编译。

任何人都可以建议如何让这个工作?

更新:为了回答 Pax 的问题(请参阅评论),如果 Windows 加载程序在运行时更改了基地址,则 DLL/EXE PE 映像仍将由 Windows 加载程序重新定位,并且标签地址将在运行时由加载程序使用基于重的地址,因此使用标签地址的编译/链接时间值不是问题。

提前谢谢了。

4

12 回答 12

4

上次我尝试制作一些与 Borland 兼容的汇编代码时遇到了不能前向引用标签的限制。不知道这是否是你在这里遇到的。

于 2008-10-16T23:55:39.457 回答
4

我能找到的关于 Borland 的一切都表明这应该可行。其他站点(此处此处)上的类似问题表明 Borland 可以处理标签的前向引用,但坚持标签位于 asm 块之外。但是,由于您的标签已经在 asm 块之外...

我很好奇您的编译器是否允许您在例如 jmp 指令中使用此标签。在玩弄它时(诚然,在完全不同的编译器上),我发现编译器有一种讨厌的倾向,即抱怨操作数类型。

语法完全不同,这是我很长时间以来第一次尝试内联 asm,但我相信我已经掌握了足够的能力,可以在 gcc 下工作。也许,尽管存在差异,但这可能对您有用:

#include <stdio.h>
int main()
{
    void *too = &&SomeLabel;
    unsigned int out;
    asm
    (
      "movl %0, %%eax;"
      :"=a"(out)
      :"r"(&&SomeLabel)
    );
SomeLabel:
    printf("Result: %p %x\n", too, out);

    return 0;
}

这会产生:

...
        movl    $.L2, %eax
...
.L2:

&& 运算符是一个非标准扩展,我不希望它可以在 gcc 以外的任何地方工作。希望这可能激起了一些新的想法......祝你好运!

编辑:虽然它被列为 Microsoft 特定的,但这是跳转到标签另一个实例。

于 2009-01-28T12:49:01.847 回答
1

3条建议:

1) 在程序集中的 SomeLabel 前面放一个 '_',使其变为“mov eax, _SomeLabel”。通常编译器在将 C 转换为汇编时会加一个。

或者

2) 将标签放在组装部分。这将阻止编译器添加“_”。

或者

3) 注释掉程序集,编译并查看列表文件 (*.lst) 以查看标签名称变为什么。

于 2008-10-16T16:34:18.380 回答
1

我认为您遇到的问题是__asm块内的标签和 C++ 代码中的标签是两个完全不同的东西。我不希望您可以从内联汇编中以这种方式引用 C++ 标签,但我必须说自从我使用 Turbo C++ 以来已经很长时间了。

您是否尝试过该lea指令而不是mov

于 2009-01-27T09:44:01.783 回答
1

Turbo C++ 环境是否有办法为 TASM 设置选项(我知道一些 Borland IDE 有)?

如果是这样,请查看是否将“最大通过次数 (/m)”选项更改为 2 次或更多帮助(它可能默认为 1 次通过)。

此外,如果您使用可能会造成问题的长标签名称 - 至少有一个 IDE 将默认设置为 12。更改“最大符号长度 (/mv) 选项”。

此信息基于 Borland 的 RAD Studio IDE:

于 2009-02-03T09:14:11.460 回答
1

还有几件事(在黑暗中拍摄)可以尝试:

  • 看看使用以下汇编指令是否有帮助:

    mov eax, offset SomeLabel
    
  • 大多数编译器可以生成它们生成的代码的汇编列表(不确定 Turbo C++ 是否可以,因为 Codegear/Embarcadero 将其定位为免费的非专业编译器)。

    尝试使用具有使用标签(例如作为目标)的 C 代码生成列表,并goto在同一函数中使用一些内联程序集 - 但不要尝试从程序集中访问标签。这样您就可以获得没有错误的编译器和程序集列表。就像是:

    int foo()
    {
        int x = 3;
        printf( "x =%d\n", x);
        goto SomeLabel;
                               //
        __asm {
            mov eax, 0x01
        }
                               //
    SomeLabel:
        printf( "x =%d\n", x);
                               //
        return x;
    }
    

    查看程序集列表,看看生成的程序集是否以您可以在内联程序集中复制的方式装饰标签名称。

于 2009-02-03T19:22:16.100 回答
0

据我回忆,您不能在内联汇编中使用外部(C++)标签,尽管您可以在汇编指令本身可以引用的 asm 块中拥有 TASM 样式的标签。我想我会使用一个标志和一个后汇编器 switch 语句来处理分支。例如:

int result=0;

__asm__ {
    mov result, 1
}

switch (result){
    case 1:  printf("You wanted case 1 to happen in your assembler\n"); break;
    case 0:  printf("Nothing changed with the result variable.. defaulting to:\n");
    default: printf("Default case!\n"); break;
}
于 2008-10-16T16:37:38.013 回答
0

我不具体了解您的编译器/汇编器,但我经常使用的一个技巧是调用下一个位置,然后将堆栈弹出到您的寄存器中。确保您拨打的电话只会推送返回地址。

于 2008-10-16T17:56:26.810 回答
0

这是一种可能的方法:

// get_address
// gets the address of the instruction following the call
// to this function, for example
//     int addr = get_address (); // effectively returns the address of 'label'
//   label:
int get_address ()
{
    int address;
    asm
    {
        mov eax,[esp+8]
        mov address,eax
    }
    return address;
}
// get_label_address
// a bit like get_address but returns the address of the instruction pointed
// to by the jmp instruction after the call to this function, for example:
//     int addr;
//     asm
//     {
//       call get_label_address // gets the address of 'label'
//       jmp label
//       mov addr,eax
//     }
//     <some code>
//   label:
// note that the function should only be called from within an asm block.
int get_label_address()
{
    int address = 0;
    asm
    {
        mov esi,[esp+12]
        mov al,[esi]
        cmp al,0ebh
        jne not_short
        movsx eax,byte ptr [esi+1]
        lea eax,[eax+esi-1]
        mov address,eax
        add esi,2
        mov [esp+12],esi
        jmp done
    not_short:
        cmp al,0e9h
        jne not_long
        mov eax,dword ptr [esi+1]
        lea eax,[eax+esi+2]
        mov address,eax
        add esi,5
        mov [esp+12],esi
        jmp done
    not_long:
        // handle other jmp forms or generate an error
    done:
    }
    return address;
}
int main(int argc, char* argv[])
{
    int addr1,addr2;
    asm
    {
        call get_label_address
        jmp Label1
        mov addr1,eax
    }

    addr2 = get_address ();
Label1:
    return 0;
}

这有点 hacky,但它适用于我拥有的 Turbo C++ 版本。它几乎可以肯定取决于编译器和优化设置。

于 2009-01-27T15:03:25.100 回答
0

只是猜测,因为我没有使用任何 C/++ 编译器的内联汇编程序......

void foo::bar( void )
{
    __asm
    {
      mov eax, SomeLabel
      // ...
    }
    // ...
    __asm
    {
      SomeLabel:
      // ...
    }
    // ...
}

我不知道 TASM 的确切语法。

于 2009-01-28T10:22:05.277 回答
0

这是 Ivan 建议的变体,但试试看:

void foo::bar( void )
{
    __asm
    {
      mov eax, offset SomeLabel
      // ...
    }
    // ...
    __asm SomeLabel:
    // ...
}
于 2010-11-13T12:42:28.103 回答
0

一种选择是使用单独的“裸”(无序言)过程 SomeLabel 而不是标签

于 2013-10-10T05:14:38.587 回答