Fasm проблемы с адресами локальных переменных

Тема в разделе "WASM.ASSEMBLER", создана пользователем Miller Rabin, 16 окт 2006.

  1. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    есть к примеру макрос
    invoke MessageBox, 0, MessageText, 0,0

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

    push 0
    push 0
    push ebp+xx - Здесь ошибка компиляции
    push 0
    call [MessageBox]

    С одним макросом invoke это непроблема заменил на
    lea eax, [MessageText]
    push eax и все.
    Но когда макросов подобных invoke много, то такие махинации надоедают

    Так вот как бы так перекрыть макросом команду push чтобы, в случае если аргументом бы выступало
    значение вида <регистр+что-то> оно бы заменялось на
    lea eax, [<регистр+что-то>]
    push eax

    по моему представлению, макрос будет следующим

    Для простоты использовал пока только регистр esi
    macro push arg
    {
    param equ arg
    if ~arg eqtype [] - если аргумент вида [var] -то пропускаем блок, все в порядке
    match esi+n, arg - сравниваем соответствует ли arg паттерну esi+n
    \{
    lea eax, [arg] - если соответствует, то делаем махинацию
    param equ eax
    \}
    end if

    push param
    }

    однако при выполнении кода

    dlg equ [esi+6]
    push dlg

    в отладчике получаю

    push eax

    в общем вопроса два
    откуда взялось eax если по условию вместо eax должно быть [esi+6]?
    И если идея неверна, то как подобный макрос написать правильно?
     
  2. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    invoke MessageBox, 0, addr MessageText, 0,0
    Но использует edx (в масме еах).

    да, нужен win32ax.inc. Или скопируй его макросы в win32a.inc
     
  3. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    fasmpre тебе в помощь. Он выдаст листинг после разворачивания макросов.
    А вообще, ты же сам написал, param equ eax
     
  4. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Ну да писал. только присваивание же выполняется внутри блока match
    если match выполнился то в отладчике после компиляции

    dlg equ [esi+6]
    push dlg

    должно получиться
    lea eax, [[esi+6]] - вообще будет error invalid expresion
    push eax

    А если match не выполнился
    то param должен быть остаться равен [esi+6]

    А тут получается вообще что-то среднее.
    присваивание param equ eax выполнилось, а команды lea не появилось


    Я исследовал макрос pushd в win32ax.inc
    он позволяет использовать конструкции вида pushd addr value
    Это решает проблему.


    Но можно ли как-нибудь здесь правильно использовать match, чтобы макрос заработал правильно?
     
  5. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Miller Rabin
    Ок, если хочешь разобраться.

    Различай стадии компиляции: есть preprocessing, есть assembly. Это хорошо отражено в сорце фасма :)
    Код (Text):
    1.     call    preprocessor ; обработка макроинструкций и др. директив препроцессора
    2.     call    parser       ; парсер выражений
    3.     call    assembler    ; собственно ассемблер
    4.     call    formatter    ; форматтер, оформляет полученный бинарный код в выбранный формат
    Соответственно, есть control directives (управляющие директивы), влияющие на assembly-level, и есть preprocessor directives (директивы препроцессора), которые обрабатываются первыми. И те, и те описаны в мануале фасма.

    Так вот. match, equ - это директивы препроцессора и обрабатываются первыми. if - это директива ассемблера. Кстати, match - это препроцессорный эквивалент (весьма расширенный ;) ) директивы if.

    Поэтому в твоём макросе сначала обрабатываются макроинструкции, а потом if. Вывод: при написании макросов нужно учитывать стадии обработки директив.

    Код (Text):
    1. macro mpush arg
    2. {
    3.     local push_val
    4.     push_val equ arg
    5.     match esi+n, arg
    6.     \{
    7.         lea     eax,arg
    8.         push_val equ eax
    9.     \}
    10.     push push_val
    11. }
    12.  
    13. dlg equ [esi+6]
    14. mpush dlg
    15.  
    16. dlg equ var
    17. mpush dlg
    18.  
    19. dlg equ  [dvar]
    20. mpush dlg
    21.  
    22. var:
    23.         rb 1
    24. dvar    dd 0
    Вообще, советую проштудировать мануал фасма, плюс его доки ("Understanding fasm" & "Design Principles"), плюс FAQ по макросам. Потом будет легче понимать макросы и самому писать их.
     
  6. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    Добавлю что мануал и дока по макросам есть и на русском =)
     
  7. pas

    pas New Member

    Публикаций:
    0
    Регистрация:
    18 апр 2003
    Сообщения:
    330
    Адрес:
    Russia
    1)А зачем вообще ebp+? Должно быть push .MessageText т.к. локальные переменные объявляются с точкой.
     
  8. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    pas
    Ну, можно и без точки обьявить:
    Код (Text):
    1. dummy_sub:
    2. local_var1 equ [ebp-4]
    3. push ebp
    4. mov ebp,esp
    5. push eax
     
  9. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    pas
    Я так думаю, это было для примера. Локальные переменные адресуются с отрицательным смещением. И они уже давно не с точкой объявляются :)
     
  10. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Понятно :) так и думал что в конце отправят маны читать.

    ICEStudent огромное спасибо за макрос и мудрые советы. Так, что теперь у меня будет плод для размышления.
    Я надеюсь, что я все таки разберусь в том что мне до конца мешает понять правильную разработку макросов

    Только вот беда. Макрос то все равно нерабочий

    macro mpush arg
    {
    local push_val
    push_val equ arg
    match esi+n, arg
    \{
    lea eax,arg - здесь точно не lea eax, [arg] ????
    push_val equ eax
    \}
    push push_val
    }

    Просто получается следующее

    mpush esi+6

    в отладчике становится как
    lea eax, [esi+6]
    push eax
    Все правильно

    mpush [esi+6]
    получаем
    lea eax, [[esi+6]] - то есть ошибку компиляции
    а должно быть
    push [esi+6]

    Выходит что match одинаково срабатывает
    как при arg = esi+6
    так и при arg = [esi+6]
    А этого не должно быть
    if ~arg eqtype [] - поидее подходило бы идеально
    но if здесь использовать неполучится так как в разных слоях обрабатывается.

    И как же тогда обойти проблему?
    вот блин опять вернулся к тому откуда начал.
     
  11. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Да. Да. Да
    Я написал его. Правильный макрос. Теперь наконец можно работать с данными внутри сложных макросов одинаково одинаково независимо от того где они расположены в стеке, или глобально в сегменте или вообще адресуются регистром общего назначения.
    Вот правильный макрос.

    macro push arg
    {
    local push_val
    push_val equ arg
    match =esi+n, arg
    \{
    lea eax,[arg]
    push_val equ eax
    \}
    push push_val
    }

    Теперь все тесты выполняются верно
    В Ассемблере В отладчике
    dlg DD 0
    push dlg push 00403000
    push [dlg] push DS:[00403000]
    push DWORD [esi+6] push DWORD DS:[esi+6]
    push esi+6 lea eax, [esi+6]
    push eax

    Я добился цели :)

    Теперь надо изменить так чтобы все регистры понимал но это уже не проблема. Я понял как match работает
     
  12. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    скорее написал его IceStudent, а ты исправил косяк :P
     
  13. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Miller Rabin
    А еще лучше было бы не использовать eax, а прицепить скобки к arg - типа push_val equ [arg]
     
  14. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    leo
    Ему нужно положить в стек адрес локальной переменной.

    Miller Rabin
    С текущим макросом proc будут проблемы.
     
  15. P_F

    P_F New Member

    Публикаций:
    0
    Регистрация:
    27 мар 2006
    Сообщения:
    116
    Адрес:
    Russia
    Может я чё не понял, но ели это fasm и:
    переходит в:
    а MessageText локальная переменная или переменная-параметр ( esp+xx ),
    то разве не написать так:
    invoke MessageBox, 0, [MessageText], 0,0
    в случае с глобальной переменной конечно же пишется просто MessageText но там и подставляется то адрес, а здесь адрес находится по адресу...
     
  16. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    IceStudent
    Упс, точно ;) Ну тогда ИМХО лучше забить на эту идею, т.к.
    А помнить об ограничении на eax и вылавливать возможные ошибки из-за невнимательности не надоест ? Разве что какой-нить изврат типа push ebp + add [esp],offs замутить или add ebp,offs + push ebp + sub ebp,offs ;))
     
  17. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Так масм предупреждает ведь, если пробуешь положить еах перед addr. И фасм можно научить предупреждать об этом.
     
  18. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    IceStudent ты как обычно прав с текущим макросом proc действительно проблемы. Я не знаю как это лечить.

    перекрытие макроса push мне нужно было для работы со следующими структурами.
    Этот топик я привел чтобы как можно быстрее и без лишних дискуссий получить ответ. А теперь все работает.
    Я хочу привести простенький пример. Правда боюсь что это вызовет еще больше вопросов.

    См. код в аттаче

    Так вот имеется класс TThread наследник от TObject
    его поля можно сохранить в памяти так
    TThread MyThread
    MyThread.Store

    И потом создать сам поток
    mov [MyThread.StartAddress], Myproc
    MyThread.Create

    А можно эту структуру адресовать регистром так

    MyThread.At esi
    invoke GlobalAlloc, GMEM_FIXED+GMEM_ZEROINIT, 4096 ; один фиг меньше не выделит
    mov [Mythread.StartAddress], MyProc
    MyThread.Create

    Обратите внимание на изврат в макросе TThread.Create
    Это сделано для того чтобы он одинаково работал как с глобальными структурами так и с адресованными регистром. В противном случае при адресации регистром и
    вызове Create будет команда push MyThread.ThreadID которая раскроется как push esi+24 что вызывет ошибку
    Но теперь есть макрос push который это дело заменит на
    lea eax, [esi+24]
    push eax
    вот так.

    Подобный классы у меня созданы для работы с сокетами, окнами, базами данных.
    Я знаю что в этом форуме есть много противников такого подхода. Но тем не менее с помощью данного стиля я уже реализовал много больших и серьезных проектов пользуясь лишь компилятором FASM и отладчиком OllyDbg.
     
  19. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Еще такое подход хорош тем что можно например сделать так
    macro TMyThread name
    {
    TThread name

    macro name#.Store
    \{
    TThread.Store
    name#.MyField1 DD 0
    name#.SetSize
    \}

    }

    И работать с собственным производным классом со своими полями. Вот так вот. :)
     
  20. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Решил я проблему с локальными переменными. И написал наконец-то макрос, который не конфликтует с какими бы то ни было переменными FASM идея была взята из макроса pushd в котором предусматривалось конструкция вроде
    pushd addr Value
    Единственное только что он не подходил для моих задач хотя бы тем, что
    pushd addr edx компилировался в
    lea edx, [edx] - Изврат
    push edx

    а pushd addr [edx] - вообще приводил к ошибке

    Я думаю подобный макрос будет интересен многим в том числе и IceStudent'у

    Код (Text):
    1. macro pushd Value
    2. {
    3. local ..address, ..address2, ..opcode, ..index, ..reg
    4. if ~Value eqtype []
    5.     virtual at 0
    6.         ..address:
    7.         mov eax, DWORD [..address]
    8.         load ..opcode from 0
    9.         load ..reg      from 1
    10.         if $-..address>2
    11.             load ..index    from $-1
    12.         else
    13.             ..index=0
    14.         end if
    15.     end virtual
    16.    
    17.     if ..reg<8h
    18.         ..index=0
    19.     end if
    20.  
    21.     if ..opcode=8Bh&..index = 0
    22.         ..address2:
    23.         push eax
    24.         ..reg=..reg mod 10h
    25.         ..reg=..reg+50h
    26.         store ..reg at ..address2
    27.     else if ..opcode = 0A1h
    28.         push Value
    29.     else
    30.         lea edx,[Value]
    31.         push edx
    32.     end if
    33. else
    34.     push Value
    35. end if
    36. }
    Ну как? :)

    с такой штукой можно делать конструкции вроде
    Global DD 0

    proc Test
    local str[100]: BYTE
    pushd str -> lea edx, [str] push edx
    pushd edx -> push edx
    pushd Global -> push Global

    команду push перекрывать макросом глупо
    В Fasm макрос pushd отведен для всякого подобного рода извращений и вызывается макросами stdcall и Invoke
    Это значить что после перекрыти я макроса pushd такая конструкция будет работать
    invoke MessageBox, 0, Str, 0,MB_OK
    ret
    endp