Borland x86 assembler inlined; Obter o endereço de uma gravadora?
Pergunta
Estou usando o Borland Turbo C ++ com algum código de montagem de montagem de assembler Turbo (TAMM), portanto, presumivelmente TAMS (TASM). Eu gostaria de fazer o seguinte:
void foo::bar( void )
{
__asm
{
mov eax, SomeLabel
// ...
}
// ...
SomeLabel:
// ...
}
Portanto, o endereço de Somelabel é colocado no EAX. Isso não funciona e o compilador reclama: símbolo indefinido 'Somelabel'.
No Microsoft Assembler (MASM), o símbolo do dólar ($) serve como o contador de localização atual, o que seria útil para o meu objetivo. Mas, novamente, isso não parece funcionar em Borlands Assember (erro de sintaxe da expressão).
ATUALIZAÇÃO: Para ser um pouco mais específico, preciso do compilador para gerar o endereço que ele se move para o EAX como uma constante durante a compilação/vinculação e não no tempo de execução, para que ele seja compilado como "MOV EAX, 0x00401234".
Alguém pode sugerir como fazer isso funcionar?
ATUALIZAÇÃO: Para responder à pergunta de Pax (consulte o comentário), se o endereço base for alterado no tempo de execução pelo carregador do Windows, a imagem DLL/EXE PE ainda será realocada pelo carregador do Windows e o endereço do rótulo será corrigido no tempo de execução pelo tempo O carregador para usar o endereço baseado em RE, portanto, usar um valor de tempo de compilação/link para o endereço do rótulo não é um problema.
Muito obrigado antecipadamente.
Solução
Na última vez em que tentei fazer com que algum código de montagem compatível com a Borland, encontrei a limitação de que você não pode fazer rótulos de referência para a frente. Não tenho certeza se é isso que você está encontrando aqui.
Outras dicas
Everything I can find about Borland suggests this ought to work. Similar questions on other sites (here and here) suggest that Borland can handle forward-references for labels, but insists on labels being outside asm blocks. However, as your label was already outside the asm block...
I am curious whether your compiler would allow you to use this label within, for instance, a jmp instruction. When toying around with it (admittedly, on a completely different compiler), I found a pesky tendency for the compiler to complain about operand types.
The syntax is quite different, and it's my first attempt at inline asm in a long time, but I believe I've munged this enough to work under gcc. Perhaps, despite the differences, this might be of some use to you:
#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;
}
This generates:
...
movl $.L2, %eax
...
.L2:
The && operator is a non-standard extension, I wouldn't expect it to work anywhere other than gcc. Hopefully this may have stirred up some new ideas... Good luck!
Edit: Though it's listed as Microsoft specific, here is another instance of jumping to labels.
3 suggestions:
1) put a '_' in front of the SomeLabel in the assembly so it becomes "mov eax, _SomeLabel ". Usually the compiler will add one when it translates C into assembly.
Or
2) put the label in an assembly section. This will prevent the compiler from adding the '_'.
Or
3) comment out the assembly, compile, and look in the listing file (*.lst) to see what the label name becomes.
Does the Turbo C++ environment have a way to set options for TASM (I know that some of the Borland IDEs did)?
If so, see if changing the option for "Maximum passes (/m)" to 2 or more helps (it might default to 1 pass).
Also, if you're using a long label name that might pose a problem - at least one IDE had the default set to 12. Change the "Maximum symbol length (/mv) option".
This information is based on Borland's RAD Studio IDE:
A couple more things (shots in the dark) to try:
see if using the following assembly instruction helps:
mov eax, offset SomeLabel
most compilers can produce an assembly listing of the code they generate (not sure if Turbo C++ can, since Codegear/Embarcadero position it as a free, non-professional compiler).
Try producing a listing with C code that has an uses a label (as a
goto
target for example), with some inline assembly in the same function - but don't try to access the label from the assembly. This is so you can get a compiler with no errors and an assembly listing. Something like:int foo() { int x = 3; printf( "x =%d\n", x); goto SomeLabel; // __asm { mov eax, 0x01 } // SomeLabel: printf( "x =%d\n", x); // return x; }
Look at the assembly listing and see if the generated assembly decorates the label name in a way that you might be able to replicate in the inline assembly.
From what I recall, you can't use an external (C++) label in your inline assembly, although you can have TASM-style labels in the asm block that can be referenced by the assembly instructions itself. I think I would use a flag and a post-assembler switch statement to handle branching. For example:
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;
}
I don't know about your compiler / assembler specifically, but a trick I've used quite a bit is to call the next location and then pop the stack into your register. Be certain the call you make only pushes the return address.
I think the problem you're running into is that a label inside the __asm
block and the label in the C++ code are two completely different things. I wouldn't expect that you could reference a C++ label in that way from inline assembly, but I must say it's been a very long time since I've used Turbo C++.
Have you tried the lea
instruction instead of mov
?
Just guessing since I haven't used inline assembler with any C/++ compiler...
void foo::bar( void )
{
__asm
{
mov eax, SomeLabel
// ...
}
// ...
__asm
{
SomeLabel:
// ...
}
// ...
}
I don't know the exact syntax of TASM.
This is a variant of Ivan's suggestion but give this a try:
void foo::bar( void )
{
__asm
{
mov eax, offset SomeLabel
// ...
}
// ...
__asm SomeLabel:
// ...
}
Here's a possible method:
// 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;
}
It's a bit hacky but it works in the version of Turbo C++ that I have. It almost certainly is dependant on the compiler and optimisation settings.
one of the options would be to use separate "naked" (prolog-less) procedure SomeLabel instead of label