Borland x86 内联汇编器;获取标签的地址?
题
我使用 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 加载程序重新定位,并且标签地址将由加载程序在运行时修补使用重新基地址,因此使用标签地址的编译/链接时间值不是问题。
提前谢谢了。
解决方案
上次我尝试使一些汇编代码与 Borland 兼容时,我遇到了不能前向引用标签的限制。不确定这是否是您在这里遇到的情况。
其他提示
我能找到的关于 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 之外的任何地方工作。希望这可能会激起一些新想法......祝你好运!
编辑:尽管它被列为 Microsoft 特定的, 这里 是跳到标签的另一个例子。
3条建议:
1) 在程序集中的 SomeLabel 前面放一个“_”,这样它就变成了“mov eax, _SomeLabel ".通常编译器在将 C 语言翻译成汇编语言时会加一。
或者
2) 将标签放在装配部分。这将阻止编译器添加“_”。
或者
3) 注释掉程序集,编译,然后查看列表文件(*.lst)以查看标签名称变成了什么。
Turbo C++ 环境是否有办法为 TASM 设置选项(我知道一些 Borland IDE 是这样做的)?
如果是这样,请查看将“最大通过次数 (/m)”选项更改为 2 次或更多是否有帮助(可能默认为 1 次通过)。
另外,如果您使用的长标签名称可能会造成问题 - 至少有一个 IDE 将默认值设置为 12。更改“最大符号长度 (/mv) 选项”。
此信息基于 Borland 的 RAD Studio IDE:
还有一些事情可以尝试(在黑暗中拍摄):
看看使用以下汇编指令是否有帮助:
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; }
查看程序集列表,看看生成的程序集是否以您可以在内联程序集中复制的方式修饰标签名称。
据我所知,您不能在内联汇编中使用外部(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
C++代码中的块和标签是完全不同的两个东西。我不希望您可以通过内联汇编以这种方式引用 C++ 标签,但我必须说,自从我使用 Turbo C++ 以来已经很长时间了。
你有没有尝试过 lea
指令而不是 mov
?
只是猜测,因为我没有在任何 C/++ 编译器中使用内联汇编器......
void foo::bar( void )
{
__asm
{
mov eax, SomeLabel
// ...
}
// ...
__asm
{
SomeLabel:
// ...
}
// ...
}
我不知道 TASM 的确切语法。
这是伊万建议的一个变体,但请尝试一下:
void foo::bar( void )
{
__asm
{
mov eax, offset SomeLabel
// ...
}
// ...
__asm SomeLabel:
// ...
}
这是一个可能的方法:
// 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++ 版本中工作。几乎可以肯定,它取决于编译器和优化设置。
选择之一是使用单独的“裸”(无序言)过程 SomeLabel 而不是标签