Ассемблер 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. Когда он играл с ним (правда, на совершенно другом компиляторе), я обнаружил, что компилятор жалуется на типы операндов.

Синтаксис совсем другой, и это моя первая попытка встроенного asm в течение длительного времени, но я полагаю, что я сделал это достаточно для работы под gcc. Возможно, несмотря на различия, это может принести вам пользу:

 #include  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)».

Эта информация основана на среде RAD Studio от Borland:

Еще пару вещей (выстрелы в темноте), чтобы попробовать:

  • посмотрите, помогает ли следующая инструкция по сборке:

     mov eax, offset SomeLabel 
  • большинство компиляторов могут создать список сборок кода, который они генерируют (не уверен, что Turbo C ++ может, поскольку Codegear / Embarcadero позиционирует его как бесплатный, непрофессиональный компилятор).

    Попробуйте создать листинг с кодом 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; } 

    Посмотрите на листинг сборки и посмотрите, будет ли сгенерированная assembly украшать имя метки таким образом, чтобы вы могли реплицироваться в встроенной сборке.

Из того, что я помню, вы не можете использовать внешнюю метку (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 и метка кода 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 // } //  // 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 ++, которая у меня есть. Это почти наверняка зависит от настроек компилятора и оптимизации.

одним из вариантов было бы использовать отдельную «голую» (prologовую) процедуру SomeLabel вместо метки