Встроенный ассемблер Borland x86;получить адрес лейбла?

StackOverflow https://stackoverflow.com/questions/209198

  •  03-07-2019
  •  | 
  •  

Вопрос

Я использую Borland Turbo C++ с некоторым встроенным ассемблерным кодом, то есть, предположительно, ассемблерным кодом в стиле Turbo Assembler (TASM).Я хочу сделать следующее:

void foo::bar( void )
{
    __asm
    {
      mov eax, SomeLabel
      // ...
    }
    // ...
SomeLabel:
    // ...
}

Таким образом, адрес SomeLabel помещается в EAX.Это не работает, и компилятор жалуется на:Неопределенный символ SomeLabel.

В Microsoft Assembler (MASM) символ доллара ($) служит счетчиком текущего местоположения, что было бы полезно для моей цели.Но опять же, похоже, это не работает в Borlands Assembre (синтаксическая ошибка выражения).

Обновлять:Чтобы быть немного более конкретным, мне нужно, чтобы компилятор генерировал адрес, который он перемещает в eax, как константу во время компиляции/связывания, а не во время выполнения, поэтому он будет компилироваться как «mov eax, 0x00401234».

Может ли кто-нибудь подсказать, как заставить это работать?

ОБНОВЛЯТЬ:Отвечая на вопрос Пакса (см. комментарий): если базовый адрес будет изменен во время выполнения загрузчиком Windows, PE-образ DLL/EXE все равно будет перемещен загрузчиком Windows, а адрес меток будет исправлен загрузчиком во время выполнения. использовать перебазированный адрес, поэтому использование значения времени компиляции/ссылки для адреса метки не является проблемой.

Спасибо заранее.

Это было полезно?

Решение

В прошлый раз, когда я пытался сделать некоторый ассемблерный код совместимым с Borland, я столкнулся с ограничением, заключающимся в невозможности прямой ссылки на метки.Не уверен, что вы здесь с этим столкнулись.

Другие советы

Все, что я могу найти о Borland, говорит о том, что это должно работать.Похожие вопросы на других сайтах (здесь и здесь) предполагают, что Borland может обрабатывать прямые ссылки для меток, но настаивает на том, чтобы метки находились вне блоков 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 в сборку, чтобы она стала «Move eax, _somelabel».Обычно компилятор добавляет его при трансляции C в ассемблер.

Или

2) поместить метку в раздел сборки.Это не позволит компилятору добавить '_'.

Или

3) закомментируйте сборку, скомпилируйте и просмотрите файл листинга (*.lst), чтобы увидеть, каким станет имя метки.

Есть ли в среде Turbo C++ возможность установки параметров для TASM (я знаю, что в некоторых IDE Borland это было)?

Если да, то посмотрите, поможет ли изменение параметра «Максимальное количество проходов (/m)» на 2 или более (по умолчанию это может быть 1 проход).

Кроме того, если вы используете длинное имя метки, это может создать проблему: по крайней мере в одной IDE по умолчанию установлено значение 12.Измените параметр «Максимальная длина символа (/mv)».

Эта информация основана на интегрированной среде разработки RAD Studio от Borland:

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

  • посмотрите, поможет ли использование следующей инструкции по сборке:

    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, на которые могут ссылаться сами инструкции сборки.Я думаю, что для обработки ветвления я бы использовал флаг и оператор переключения постассемблера.Например:

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 вместо метки

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top