Borland x86内嵌汇编:如何获取标签的地址?

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加载程序重新定位,并且标签的地址将由加载程序在运行时修补以使用重新基于的地址,因此在标签地址上使用编译/链接时间值不是问题。
非常感谢您的帮助。

如果你在 asm 块内定义 SomeLabel,这个代码还能正常工作吗? - user57368
对于gcc而言,获取标签的地址是"&&label",你试过了吗?也许它对于borland也能起作用? - Johannes Schaub - litb
你打算如何在编译时获取值?加载器可能会更改(在加载时间长时间之后)加载此代码的地址,使得你的编译/链接值无用。 - paxdiablo
也许你需要重新阐述问题:eax一旦加载,你究竟要做什么。 - paxdiablo
paxdiablo:Windows PE加载器将重新定位exe并处理PE文件的.reloc部分,使用新加载的基地址更新任何固定地址,因此EAX始终正确。而且,一旦加载了EAX,我要做什么并不重要,问题是如何将EAX设置为SomeLabel的地址(在MSVC中很容易实现,但在C++ Builder中不行)。 - QAZ
12个回答

5

上次我尝试编写一些与Borland兼容的汇编代码时,遇到了无法前向引用标签的限制。不确定这是否是你在这里遇到的问题。


4

我能找到的所有关于Borland的信息都表明这应该可以工作。其他网站上类似的问题(这里这里)表明Borland可以处理标签的前向引用,但是坚持要求标签在asm块外部。然而,由于您的标签已经在asm块外部...

我很好奇你的编译器是否允许你在jmp指令中使用这个标签。当我在另一个编译器上尝试时,我发现编译器会抱怨操作数类型。

语法非常不同,这是我很久以来第一次尝试内联汇编,但我相信我已经足够修改它以在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之外的任何地方都能工作。希望这能激发一些新想法...祝你好运!

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


感谢您的回答,feonixrift。不幸的是,“&&”在Turbo C++中似乎无法使用。 - QAZ

1

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

如果可以,尝试更改“最大通过次数 (/m)”选项为2或更多(默认可能为1次)。

另外,如果您正在使用较长的标签名称,则可能会出现问题 - 至少一个 IDE 的默认设置为12。更改“最大符号长度 (/mv) 选项”。

此信息基于Borland的RAD Studio IDE:


嗨,迈克尔,设定最大尝试次数的想法不错,我刚试了一下,但没有成功,我会再调整一下的。 - QAZ
同样我知道有些汇编器使用提示来指示标签是前向引用。我不认为TASM是其中之一,但值得一试:使用“mov eax,> SomeLabel”-‘>’字符是给汇编器的提示。 - Michael Burr
在汇编块之前的标签上加上@符号,可以使编译器崩溃!:) - QAZ

1

我认为你遇到的问题是,在 __asm 块内部的标签和 C++ 代码中的标签是两个完全不同的东西。我不认为你可以从内联汇编引用 C++ 标签,但我必须说,我已经很久没有使用 Turbo C++ 了。

你尝试过使用 lea 指令而不是 mov 吗?


完美的解决方案!完美运作。 - Epic Speedy

1

3个建议:

1)在汇编中在SomeLabel前面加上'_',这样它就变成了"mov eax, _SomeLabel"。通常编译器在将C翻译成汇编时会添加一个下划线。

或者

2)将标签放在汇编部分中。这将防止编译器添加'_'。

或者

3)注释掉汇编,编译后查看列表文件(*.lst),以查看标签名称的变化。


谢谢,提供了很好的建议,但不幸的是第一和第二个都不起作用。我将再测试第三个,但至今没有成功。 - QAZ

1

还有几件事情(试图尝试的无准备之举):

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

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

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

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

    查看汇编列表,并查看生成的汇编是否以一种您可以在内联汇编中复制的方式装饰标签名称。


0

其中一个选项是使用单独的“裸露”(无Prolog)过程SomeLabel,而不是label。


0

我不知道你的编译器/汇编器具体是什么,但我经常使用的一个技巧是调用下一个位置,然后将堆栈弹出到寄存器中。确保你所做的调用只推送返回地址。


好建议。我已经在安全领域中用了许多其他技巧。不过,对于这个问题,我特别需要编译器在编译时将地址组装为常量,而不是运行时。感谢您的建议。 - QAZ

0

这是Ivan建议的一种变体,但你可以试试这个:

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

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;
}

这种方法可能有点取巧,但是在我使用的 Turbo C++ 版本中可以正常工作。它几乎肯定取决于编译器和优化设置。


get_label_address函数修改其返回地址,因此不会执行跟随调用的jmp。 - Skizz

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