【问题标题】:Borland x86 inlined assembler; get a label's address?Borland x86 内联汇编器;获取标签的地址?
【发布时间】:2010-09-17 13:48:38
【问题描述】:

我正在使用带有一些内联汇编代码的 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 是否有效?
  • 对于gcc,获取labels地址是“&&label”,你试过吗?也许它也适用于 borland?
  • 您打算如何在编译时获取值?加载器可能会更改(在加载时,链接后很长时间)加载此代码的地址,使您的编译/链接值无用。
  • 也许你需要重申一下这个问题:一旦 eax 被加载,你到底要做什么。
  • paxdiablo:Windows PE 加载器将重新定位一个 exe 并处理 PE 文件的 .reloc 部分,使用新加载的基地址更新任何固定地址,因此 EAX 将始终正确。此外,加载 EAX 后我将如何处理它也不重要,问题是如何将 EAX 设置为 SomeLabel 的地址(这在 MSVC 中很容易实现,而不是 C++ Builder)。

标签: c++ assembly x86 turbo-c++


【解决方案1】:

3 条建议:

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

或者

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

或者

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

【讨论】:

  • 谢谢,很好的建议,但不幸的是 #1 和 #2 不起作用。我将尝试再测试#3,但到目前为止还没有运气。
【解决方案2】:

据我回忆,您不能在内联汇编中使用外部 (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;
}

【讨论】:

  • 我尝试了 asm 块内的标签,但没有帮助。不幸的是,我需要它按照示例工作,因此附加 switch 语句是没有用的。不过还是谢谢。
【解决方案3】:

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

【讨论】:

  • 好建议。我在安全领域的许多其他事情上都使用了这个技巧。为此,尽管我特别需要编译器在编译时而不是运行时将地址汇编为常量。不过谢谢你的建议。
【解决方案4】:

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

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

    • 完美解决方案!完美运行。
    【解决方案6】:

    这是一种可能的方法:

    // 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++ 版本中使用。几乎可以肯定,这取决于编译器和优化设置。

    【讨论】:

    • get_label_address 函数修改了它的返回地址,因此调用之后的 jmp 不会被执行。
    【解决方案7】:

    只是猜测,因为我没有将内联汇编器与任何 C/++ 编译器一起使用...

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

    我不知道 TASM 的确切语法。

    【讨论】:

      【解决方案8】:

      我能找到的关于 Borland 的一切都表明这应该可行。其他网站上的类似问题(herehere)表明 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 特定的,但 here 是跳转到标签的另一个实例。

      【讨论】:

      • 感谢您的回答 feonixrift,不幸的是 && 似乎不适用于 Turbo C++。
      【解决方案9】:

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

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

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

      此信息基于 Borland 的 RAD Studio IDE:

      【讨论】:

      • 嗨迈克尔,关于设置最大通行证的好主意,刚刚尝试过,没有任何乐趣,但我试着再调整一下
      • 我也知道一些汇编程序使用提示来指示标签是前向引用。我不认为 TASM 是其中之一,但值得一试:使用 "mov eax, >SomeLabel" - '>' 字符是对汇编程序的提示。
      • 对于出现在 asm 块之前的标签是否定的。我设法通过在标签上添加@符号来使编译器崩溃! :)
      【解决方案10】:

      尝试更多的东西(在黑暗中拍摄):

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

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

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

      【讨论】:

        【解决方案11】:

        这是 Ivan 建议的变体,但请尝试一下:

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

        【讨论】:

          【解决方案12】:

          其中一个选项是使用单独的“裸”(无序言)过程 SomeLabel 而不是标签

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-07
            相关资源
            最近更新 更多