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 (синтаксическая ошибка выражения).
Update: чтобы быть немного более конкретным, мне нужно, чтобы компилятор генерировал адрес, который он перемещает в eax как константу во время компиляции/связывания, а не во время выполнения, поэтому он будет компилироваться как "mov eax, 0x00401234".
может кто-нибудь предложить, как заставить это работать?
UPDATE: чтобы ответить на вопрос Pax (см. Комментарий), Если базовый адрес изменяется во время выполнения загрузчиком Windows, образ DLL / EXE PE будет по-прежнему перемещается загрузчиком Windows, и адрес меток будет исправлен во время выполнения загрузчиком, чтобы использовать повторно основанный адрес, поэтому использование значения времени компиляции/ссылки для адреса метки не является проблемой.
заранее большое спасибо.
12 ответов
в последний раз, когда я пытался сделать некоторый сборочный код Borland-совместимым, я столкнулся с ограничением, что вы не можете пересылать ссылки. Не уверен, что это то, с чем ты столкнешься здесь.
все, что я могу найти о 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:
оператор && является нестандартным расширением, я бы не ожидал, что он будет работать в любом месте другие, чем ССЗ. Надеюсь, это может пробудить некоторые новые идеи... Удачи!
Edit: хотя он указан как Microsoft specific,здесь - еще один пример перехода к ярлыкам.
3 предложения:
1) поместите ' _ 'перед SomeLabel в сборке, чтобы он стал" mov eax, _SomeLabel ". Обычно компилятор добавляет один, когда он переводит C в сборку.
или
2) Поместите метку в раздел сборки. Это предотвратит добавление компилятором '_'.
или
3) прокомментируйте сборку, скомпилируйте и посмотрите в файле списка (*.ЛСТ), чтобы увидеть, что имя метки будет.
есть ли у среды Turbo C++ способ установить параметры для TASM (я знаю, что некоторые из Borland IDEs сделали)?
Если да, посмотрите, помогает ли изменение параметра "максимальные проходы (/m)" на 2 или более (по умолчанию это может быть 1 проход).
кроме того, если вы используете длинное имя метки, которое может создать проблему - по крайней мере, одна IDE имеет значение по умолчанию 12. Измените параметр"максимальная длина символа (/mv)".
эта информация основана на RAD Studio Borland IDE:
еще пара вещей (выстрелы в темноте), чтобы попробовать:
-
см., если использование следующей инструкции по сборке помогает:
mov eax, offset SomeLabel
-
большинство компиляторов могут создавать список сборок кода, который они генерируют (не уверен, что Turbo C++ может, так как Codegear/Embarcadero позиционирует его как свободный, непрофессиональный компилятор).
попробуйте создать список с кодом C, который использует метку (как
goto
target например), с некоторыми встроенная сборка в той же функции - но не пытайтесь получить доступ к метке из сборки. Это позволяет получить компилятор без ошибок и список сборок. Что-то вроде: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++) в своей встроенной сборке, хотя у вас могут быть метки в стиле TASM в блоке asm, на которые могут ссылаться сами инструкции по сборке. Я думаю, что я бы использовал флаг и оператор коммутатора post-assembler для обработки ветвления. Например:
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;
}
это немного хаки, но он работает в версии Turbo C++, которая у меня есть. Это почти наверняка зависит от настроек компилятора и оптимизации.
одним из вариантов было бы использовать отдельную "голую" (без пролога) процедуру SomeLabel вместо label