Встроенный ассемблер 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, а адрес меток будет исправлен во время выполнения загрузчик должен использовать повторно основанный адрес, поэтому использование значения времени компиляции / компоновки для адреса метки не является проблемой.

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

Работает ли это, если вы определяете SomeLabel внутри блока asm?

user57368 27.01.2009 20:39

для gcc получение адреса ярлыков - "&& label", вы пробовали? может и на борланд работает?

Johannes Schaub - litb 28.01.2009 12:28

Как именно вы собираетесь получить значение во время компиляции? Загрузчик может изменить (во время загрузки, спустя много времени после связывания) адрес, по которому загружается этот код, что сделает ваше значение компиляции / ссылки бесполезным.

paxdiablo 30.01.2009 08:16

Возможно, вам нужно еще раз сформулировать вопрос: что ИМЕННО вы собираетесь делать с eax после его загрузки.

paxdiablo 30.01.2009 08:17

paxdiablo: загрузчик Windows PE переместит исполняемый файл и обработает раздел .reloc PE файлов, обновляя любой фиксированный адрес вновь загруженным базовым адресом, чтобы EAX всегда был правильным. Также не имеет значения, что я собираюсь делать с EAX после его загрузки, вопрос в том, как установить EAX на адрес SomeLabel (что легко возможно в MSVC, но не в C++ Builder).

QAZ 24.01.2010 14:20
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
5
6 650
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

3 предложения:

1) поместите "_" перед SomeLabel в сборке, чтобы он стал "mov eax, _SomeLabel ". Обычно компилятор добавляет его, когда переводит C в сборку.

Или же

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

Или же

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

Спасибо, хорошие предложения, но, к сожалению, №1 и №2 не работают. Я попробую еще немного протестировать №3, но пока безуспешно.

QAZ 16.10.2008 20:49

Насколько я помню, вы не можете использовать внешнюю (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 бесполезно. Спасибо хоть.

QAZ 16.10.2008 20:51

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

хорошее предложение. Я использовал этот прием для многих других вещей в области безопасности. Для этого мне, в частности, нужно, чтобы компилятор собирал адрес как константу во время компиляции, а не во время выполнения. спасибо за ваше предложение.

QAZ 16.10.2008 22:28
Ответ принят как подходящий

В прошлый раз, когда я пытался сделать какой-то ассемблерный код совместимым с 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, следующий за вызовом, не выполняется.

Skizz 27.01.2009 18:04

Просто догадываюсь, поскольку я не использовал встроенный ассемблер ни с одним компилятором 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++.

QAZ 29.01.2009 14:05

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

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

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

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

Привет, Майкл, хорошая идея о настройке максимального количества передач, просто попробовал, и без радости, но я попробую еще немного подправить

QAZ 03.02.2009 12:24

Также я знаю, что некоторые ассемблеры используют подсказку, чтобы указать, что метка является прямой ссылкой. Я не думаю, что TASM был одним из них, но попробовать стоит: используйте "mov eax,> SomeLabel" - символ '>' является подсказкой ассемблеру.

Michael Burr 03.02.2009 12:32

отрицательный для меток, которые находятся перед блоком asm. Мне удалось вывести компилятор из строя, добавив к метке символ @! :)

QAZ 03.02.2009 16:50

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

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

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

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