Я использую 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, а адрес меток будет исправлен во время выполнения загрузчик должен использовать повторно основанный адрес, поэтому использование значения времени компиляции / компоновки для адреса метки не является проблемой.
Спасибо заранее.
для gcc получение адреса ярлыков - "&& label", вы пробовали? может и на борланд работает?
Как именно вы собираетесь получить значение во время компиляции? Загрузчик может изменить (во время загрузки, спустя много времени после связывания) адрес, по которому загружается этот код, что сделает ваше значение компиляции / ссылки бесполезным.
Возможно, вам нужно еще раз сформулировать вопрос: что ИМЕННО вы собираетесь делать с eax после его загрузки.
paxdiablo: загрузчик Windows PE переместит исполняемый файл и обработает раздел .reloc PE файлов, обновляя любой фиксированный адрес вновь загруженным базовым адресом, чтобы EAX всегда был правильным. Также не имеет значения, что я собираюсь делать с EAX после его загрузки, вопрос в том, как установить EAX на адрес SomeLabel (что легко возможно в MSVC, но не в C++ Builder).





3 предложения:
1) поместите "_" перед SomeLabel в сборке, чтобы он стал "mov eax, _SomeLabel ". Обычно компилятор добавляет его, когда переводит C в сборку.
Или же
2) наклеить этикетку на монтажный участок. Это предотвратит добавление компилятором символа '_'.
Или же
3) закомментируйте сборку, скомпилируйте и посмотрите в файл листинга (* .lst), чтобы увидеть, каким станет имя метки.
Спасибо, хорошие предложения, но, к сожалению, №1 и №2 не работают. Я попробую еще немного протестировать №3, но пока безуспешно.
Насколько я помню, вы не можете использовать внешнюю (C++) метку в своей встроенной сборке, хотя вы можете иметь метки в стиле TASM в блоке asm, на которые могут ссылаться сами инструкции сборки. Я думаю, что для обработки ветвления я бы использовал флаг и оператор 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 бесполезно. Спасибо хоть.
Я не знаю конкретно о вашем компиляторе / ассемблере, но уловка, которую я использовал довольно часто, - это вызвать следующее местоположение, а затем вставить стек в ваш регистр. Будьте уверены, что при звонке используется только обратный адрес.
хорошее предложение. Я использовал этот прием для многих других вещей в области безопасности. Для этого мне, в частности, нужно, чтобы компилятор собирал адрес как константу во время компиляции, а не во время выполнения. спасибо за ваше предложение.
В прошлый раз, когда я пытался сделать какой-то ассемблерный код совместимым с Borland, я столкнулся с ограничением, заключающимся в том, что вы не можете ссылаться на метки вперед. Не уверен, что это то, с чем вы здесь столкнулись.
Я думаю, что проблема, с которой вы столкнулись, заключается в том, что метка внутри блока __asm и метка в коде C++ - это две совершенно разные вещи. Я бы не ожидал, что вы можете ссылаться на метку C++ таким образом из встроенной сборки, но я должен сказать, что прошло очень много времени с тех пор, как я использовал Turbo C++.
Вы пробовали инструкцию lea вместо mov?
Вот возможный метод:
// 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, следующий за вызовом, не выполняется.
Просто догадываюсь, поскольку я не использовал встроенный ассемблер ни с одним компилятором C / ++ ...
void foo::bar( void )
{
__asm
{
mov eax, SomeLabel
// ...
}
// ...
__asm
{
SomeLabel:
// ...
}
// ...
}
Я не знаю точного синтаксиса TASM.
Все, что я могу найти о 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:
Оператор && - нестандартное расширение, я бы не ожидал, что он будет работать где-либо, кроме gcc. Надеюсь, это могло породить новые идеи ... Удачи!
Обновлено: хотя он указан как специфичный для Microsoft, здесь - еще один пример перехода к ярлыкам.
Спасибо за ответ feonixrift, к сожалению, && не работает с Turbo C++.
Есть ли в среде Turbo C++ способ установки параметров для TASM (я знаю, что это было в некоторых интегрированных средах разработки Borland)?
Если это так, посмотрите, поможет ли изменение параметра «Максимальное количество проходов (/ m)» на 2 или более (по умолчанию может быть 1 проход).
Кроме того, если вы используете длинное имя метки, которое может создать проблему - по крайней мере, одна IDE имеет значение по умолчанию, равное 12. Измените параметр «Максимальная длина символа (/ mv)».
Эта информация основана на среде разработки Borland RAD Studio:
Привет, Майкл, хорошая идея о настройке максимального количества передач, просто попробовал, и без радости, но я попробую еще немного подправить
Также я знаю, что некоторые ассемблеры используют подсказку, чтобы указать, что метка является прямой ссылкой. Я не думаю, что TASM был одним из них, но попробовать стоит: используйте "mov eax,> SomeLabel" - символ '>' является подсказкой ассемблеру.
отрицательный для меток, которые находятся перед блоком asm. Мне удалось вывести компилятор из строя, добавив к метке символ @! :)
Еще пара вещей (снимки в темноте), которые стоит попробовать:
посмотрите, помогает ли следующая инструкция по сборке:
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;
}
Посмотрите на список сборок и посмотрите, украшает ли сгенерированная сборка имя метки таким образом, чтобы вы могли реплицировать ее во встроенной сборке.
Это вариант предложения Ивана, но попробуйте:
void foo::bar( void )
{
__asm
{
mov eax, offset SomeLabel
// ...
}
// ...
__asm SomeLabel:
// ...
}
один из вариантов - использовать отдельную "голую" (без пролога) процедуру SomeLabel вместо метки
Работает ли это, если вы определяете SomeLabel внутри блока asm?