Сообщение из темы "Экстраполяция."

Тема в разделе "WASM.X64", создана пользователем GRAFik, 26 апр 2026 в 12:17.

  1. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    313
    Чисто для такой демки ещё можно один раз выделить общий шадов, но в больших проектах категорически не рекомендуется так делать. Стек не понимает шуток и если внутри этого блока вы запросите внешнюю процедуру, то ситуация может выйти из под контроля, отправив адрес-возврата к чертям. Особенно если из тушки блока вызываются функи с переменным числом параметров, как в msvcrt.dll или wsprintf() из user32.dll. Поэтому общий шадов плохая практика, и лучше выделять по 4*8 байт при каждом вызове api, о чём и говорит Ахимов.
    Код (ASM):
    1.  
    2.  sub   rsp,80         ; + шадов для 10 аргументов
    3. ;.......
    4.  call  MessageBox()   ; всё норм
    5.  call  MyProc         ; выход из блока
    6. ;.......
    7.  add   rsp,80         ; - шадов
    8.  call  ExitProcess
    9. ;-----------------
    10. proc  MyProc
    11.  call  wsprintf(...)  ; 6 аргументов
    12.    @cycle:
    13.       call  wsprintf(...)    ; 6 аргументов = затёрли адрес-возврата
    14.       call  MessageBox(...)
    15.    loop  @cycle
    16.  ret
    17. endp
    18.  
    А 300 строк кода для обычного окна с двумя пимпами это конечно круто (интересно, можно накодить это-же за 600 строк). У nasm'a нет своих структур чтоли? Иначе он тупо не предназначен для х64, и инструмент нужно менять. И зачем создавать буттоны в динамике, когда можно описать их в ресурсах? Тогда всё это хозяйство не займёт и 50 строк, например на fasm'e.
    Код (ASM):
    1. format pe64 gui 6.0
    2. include 'win64ax.inc'
    3. entry start
    4. ;//-----------------
    5. section '.text' code readable executable
    6. start:  sub     rsp,8
    7.         invoke  GetModuleHandle,0
    8.         invoke  DialogBoxParam,rax,37,0,DialogProc,0
    9.         invoke  ExitProcess,0
    10.  
    11. proc  DialogProc uses rsi rdi rbx, hWnd,Msg,wParam,lParam
    12.         mov    [hWnd],  rcx
    13.         mov    [wParam],r8
    14.         cmp     edx,WM_COMMAND
    15.         je      @command
    16.         cmp     edx,WM_CLOSE
    17.         je      @close
    18.         xor     rax,rax
    19.         jmp     @ret
    20.  
    21. @command:
    22.         cmp     [wParam],BN_CLICKED shl 16 + IDCANCEL
    23.         je      @close
    24.         cmp     [wParam],BN_CLICKED shl 16 + IDOK
    25.         jne     @next
    26.         invoke  MessageBox,0,'Hello World!','Example',0
    27.         jmp     @next
    28.  
    29. @close: invoke  EndDialog,[hWnd],0
    30. @next:  mov     rax,1
    31. @ret:   ret
    32. endp
    33. ;//--------------------
    34. section '.idata' import data readable writeable
    35. library  kernel32,'kernel32.dll',user32,'user32.dll'
    36. include  'api\kernel32.inc'
    37. include  'api\user32.inc'
    38. ;//--------------------
    39. section '.rsrc' resource data readable
    40. directory RT_DIALOG,  dialogs
    41. resource  dialogs,37,LANG_ENGLISH + SUBLANG_DEFAULT,form
    42.  
    43. dialog form, 'PE64 Example',0,0,130,030, WS_CAPTION + WS_SYSMENU + DS_CENTER
    44.     dialogitem 'BUTTON','Ok',  IDOK,    010,010,50,14,WS_VISIBLE + BS_DEFPUSHBUTTON
    45.     dialogitem 'BUTTON','Exit',IDCANCEL,070,010,50,14,WS_VISIBLE + BS_PUSHBUTTON
    46. enddialog
    47.  
     
    Mikl___ нравится это.
  2. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    434
    Тимур, давайте чисто для эксперимента создадим такой пример, где будет видно что ситуация вышла из под контроля и общий шадов - это плохая практика. Чтобы это было не на словах и теории, а на уровне практики.
    Дело же не в том сколько строк у тебя в исходном коде, а в том в какой размер exe-файла компилируется этот исходнк. Можно вынести все во внешние инклуды, DLL-ки и код в исходнике будет состоять из каких-нибуть 10 или 20 строк. Тут нужно брать конкретый пример и делать выводы, что что-то на том же NASM x64 - невозможно реализовать, а на FASM64 - без проблем. И исходя из конкретного примера и реализации всего этого на практике делать выводы, что FASM64 - это круто и профессионально, а NASM x64 - годится только для каких-нибудь простеньких студенческих примеров.
     
    Marylin нравится это.
  3. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    615
    Marylin,

    > зачем создавать буттоны в динамике

    Такое было тз:

    Тоесть принято что если асм и простое => обучающий семпл => нужна оконная процедура, обработка сообщений. Диалоги/ресурсы не требовались, как и ограничение по размеру:
    Короче нужно четко техзадание формулировать.

    На счет стека, абсолютно согласен:
    Он не знает интернал апи и не может криво вызвать, все согласно специф.

    ps. Можно дать изучить интернал, он соберет используя его, только в этой версии и будет работать.
    --- Сообщение объединено, 27 апр 2026 в 12:50 ---
    ..
     

    Вложения:

    • _ntuserSPI.pdf
      Размер файла:
      2,6 МБ
      Просмотров:
      9
    Последнее редактирование: 27 апр 2026 в 12:49
    Marylin нравится это.
  4. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    313
    Я не собираюсь вам что-то доказывать, а просто констатирую факт, с которым сталкивался. Хотите выделяйте один шадов на входе, мне как-то без разницы, но такие ошибки трудно потом вылавливать. Когда у вас будет в коде 5/10 тыс строк, тогда и можете нарваться на глюк.

    Компилятор должен работать на вас, а не вы на него. Я говорю про инструмент, у которого должно быть под катом всё, включая инклуды. Возьмите теже плюсы - там вообще нет call/invoke, а сразу имя api. Видно, что синтаксис для программиста, чтобы он сосредотачивался именно на алгоритмах, а не заполнении вручную 2-метровых простыней структур.
     
    Mikl___ нравится это.
  5. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    434
    А что за факт, если не секрет? Давайте попробуем этот факт проанализировать.
    Ну, 10 тыс. строк кода я бы, наверно, и не рискнул писать на ассемблере, а взял бы, возможно, СИ. :)

    Тимур, я переслал ваш пост одному программисту, который хорошо разбирается в 64 битной архитектуре и в 64 битных ассемблерах и вот что он ответил:
     
  6. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    313
    Мда... что-то ваш надёжный источник вызывает сомнения - лучше бы он не упоминал раскрутку стека.
    Автор этих строк походу сам запутался в механизмах обработки исключений х32/64.
    Если вы вызываете api, то она лежит в dll, а dll имеет секции .pdata/.xdata, где и хранится инфа о раскрутке стека. У вызвавшей исключение функции свой пролог mov rbp,rsp, а потому значение внешнего rsp ей абсолютно не интересно. Аналогичную ситуацию наблюдаем и в случае, когда внутри одной api вызывается другая - эта вложенная так-же имеет свой пролог, и о внешнем rsp уже никто и не помнит.

    Всё-что нужно для обработки исключений х64, хранится далеко от текущего call/rsp - в отдельной секции .pdata, которая содержит массив структур RUNTIME_FUNCTION - каждая описывает одну api в данной dll (сколько fn, столько и структур). Все эти записи отсортированы по "BeginAddress", что позволяет ОС быстро находить функцию по адресу вызвавшей исключение инструкции.

    Код (Text):
    1. 0: kd> dt _runtime_function
    2. ole32!_RUNTIME_FUNCTION
    3.    +0x000 BeginAddress  : Uint4B   ; RVA начала функции
    4.    +0x004 EndAddress    : Uint4B   ; RVA её конца
    5.    +0x008 UnwindData    : Uint4B   ; RVA на секцию .xdata, где лежит подробная инфа по раскрутке именно этой функции.
    6. 0: kd>
    Секция .xdata может быть отдельной, или встроенной прямо в .pdata. Когда происходит исключение, система смотрит на текущий адрес инструкции, находит через .pdata соответствующую api, читает поле UnwindData и прыгнув по нему в .xdata, попадает в структуру UNWIND_INFO - это и есть мозг раскрутки стека глючной функции. Во вложенной UNWIND_CODE лежат байт-коды, которые и описывают фактические детали обратного трейса:

    Код (Text):
    1. 0: kd> dt _unwind_info
    2. ole32!_UNWIND_INFO
    3.    +0x000 Version       : Pos 0, 3 Bits
    4.    +0x000 Flags         : Pos 3, 5 Bits
    5.    +0x001 SizeOfProlog  : UChar
    6.    +0x002 CountOfCodes  : UChar
    7.    +0x003 FrameRegister : Pos 0, 4 Bits
    8.    +0x003 FrameOffset   : Pos 4, 4 Bits
    9.    +0x004 UnwindCode    : [1] _UNWIND_CODE
    10.  
    11. 0: kd> dt _unwind_code
    12. ole32!_UNWIND_CODE
    13.    +0x000 CodeOffset    : UChar
    14.    +0x001 UnwindOp      : Pos 0, 4 Bits
    15.    +0x001 OpInfo        : Pos 4, 4 Bits
    16.    +0x000 FrameOffset   : Uint2B
    17.  
    Таким образом .pdata служит каталогом для поиска функций, а .xdata детальной картой для каждой из них. Эта пара таблиц полностью заменила старый механизм связанного списка FS:[0], и позволяет:
    1. Восстановить значения NonVolatile регистров.
    2. Найти текущий кадр стека rsp/rbp.
    3. Перейти к вызвавшей функции.​

    unwind.png

    Отладчик WinDbg на команду .fnent отзывается инфой о раскрутки стека для любой х64 api, и повторюсь она никак не связана с инструкцией call и текущим значением rsp - это заложено в генах самой API-функции:

    Код (Text):
    1. 0:000> .fnent CreateFileA
    2. Debugger function entry 00000000`01da4a20 for:
    3. (00000000`77371720) kernel32!CreateFileA  | (00000000`77371810) kernel32!InternalFindAtom
    4. Exact matches:      kernel32!CreateFileA = <no type information>
    5.  
    6. BeginAddress      = 00000000`00021720
    7. EndAddress        = 00000000`00021810
    8. UnwindInfoAddress = 00000000`00101d2c
    9.  
    10. Unwind info at 00000000`77451d2c, 14 bytes
    11.   version 1, flags 0, prolog 14, codes 8
    12.   00: offs 14, unwind op 4, op info 6 UWOP_SAVE_NONVOL  FrameOffset: 80 reg: rsi.
    13.   02: offs 14, unwind op 4, op info 5 UWOP_SAVE_NONVOL  FrameOffset: 78 reg: rbp.
    14.   04: offs 14, unwind op 4, op info 3 UWOP_SAVE_NONVOL  FrameOffset: 70 reg: rbx.
    15.   06: offs 14, unwind op 2, op info b UWOP_ALLOC_SMALL.
    16.   07: offs 10, unwind op 0, op info 7 UWOP_PUSH_NONVOL  reg: rdi.
    17. ----------------------------
    18.  
    19. 0:000> .fnent CreateWindowEx
    20. Debugger function entry 00000000`01da4a20 for:
    21. (00000000`77480294) user32!CreateWindowEx  | (00000000`7748051c) user32!_fnINOUTNCCALCSIZE
    22. Exact matches:      user32!CreateWindowEx = <no type information>
    23.  
    24. BeginAddress      = 00000000`00010294
    25. EndAddress        = 00000000`0001051c
    26. UnwindInfoAddress = 00000000`00090b34
    27.  
    28. Unwind info at 00000000`77500b34, 1e bytes
    29.   version 1, flags 0, prolog 1f, codes d
    30.   00: offs 1f, unwind op 4, op info 6 UWOP_SAVE_NONVOL  FrameOffset: 168 reg: rsi.
    31.   02: offs 1f, unwind op 4, op info 5 UWOP_SAVE_NONVOL  FrameOffset: 160 reg: rbp.
    32.   04: offs 1f, unwind op 4, op info 3 UWOP_SAVE_NONVOL  FrameOffset: 158 reg: rbx.
    33.   06: offs 1f, unwind op 1, op info 0 UWOP_ALLOC_LARGE  FrameOffset: 120.
    34.   08: offs 18, unwind op 0, op info f UWOP_PUSH_NONVOL  reg: r15.
    35.   09: offs 16, unwind op 0, op info e UWOP_PUSH_NONVOL  reg: r14.
    36.   0a: offs 14, unwind op 0, op info d UWOP_PUSH_NONVOL  reg: r13.
    37.   0b: offs 12, unwind op 0, op info c UWOP_PUSH_NONVOL  reg: r12.
    38.   0c: offs 10, unwind op 0, op info 7 UWOP_PUSH_NONVOL  reg: rdi.
    39.  
    Что касается шадов, то макрос fasm'a invoke сам резервирует стек нужного размера по кол-ву параметров функции, и отнюдь не потому, что он такой глупый. Во первых это исключает буквально всякого рода ошибки, а во вторых строго придерживается стандарта _fastcall, который написал не я, и не вы. Когда в коде 2 функции, можно рассчитать макс кол-во параметров из общего пула и выделить общий фрейм, но когда функций 100 и более, это уже проблема.
     
    Последнее редактирование: 27 апр 2026 в 19:15
  7. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    434
    Тимур, угадайте откуда эта цитата? :)
     
  8. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    476
    Имхо, хеллворд здорового человека. Всего 2кб в современных реалиях. Да этож круто!
    Код (ASM):
    1. format pe64 gui 6.0
    2. include 'win64ax.inc'
    3. entry start
    4. ;//-----------------
    5. section '.text' code readable executable
    6. start:  sub     rsp,8
    7.         invoke  GetModuleHandle,0
    8.         invoke  DialogBoxParam,rax,37,0,DialogProc,0
    9.         invoke  ExitProcess,0
    10. proc  DialogProc uses rsi rdi rbx, hWnd,Msg,wParam,lParam
    11.         mov    [hWnd],  rcx
    12.         mov    [wParam],r8
    13.         cmp     edx,WM_COMMAND
    14.         je      @command
    15.         cmp     edx,WM_CLOSE
    16.         je      @close
    17.         xor     rax,rax
    18.         jmp     @ret
    19. @command:
    20.         cmp     [wParam],BN_CLICKED shl 16 + IDCANCEL
    21.         je      @close
    22.         cmp     [wParam],BN_CLICKED shl 16 + IDOK
    23.         jne     @next
    24.         invoke  MessageBox,0,'Hello World!','Example',0
    25.         jmp     @next
    26. @close: invoke  EndDialog,[hWnd],0
    27. @next:  mov     rax,1
    28. @ret:   ret
    29. endp
    30. ;//--------------------
    31. section '.idata' import data readable writeable
    32. library  kernel32,'kernel32.dll',user32,'user32.dll'
    33. include  'api\kernel32.inc'
    34. include  'api\user32.inc'
    35. ;//--------------------
    36. section '.rsrc' resource data readable
    37. directory RT_DIALOG,  dialogs
    38. resource  dialogs,37,LANG_ENGLISH + SUBLANG_DEFAULT,form
    39. dialog form, 'PE64 Example',0,0,130,030, WS_CAPTION + WS_SYSMENU + DS_CENTER
    40.     dialogitem 'BUTTON','Ok',  IDOK,    010,010,50,14,WS_VISIBLE + BS_DEFPUSHBUTTON
    41.     dialogitem 'BUTTON','Exit',IDCANCEL,070,010,50,14,WS_VISIBLE + BS_PUSHBUTTON
    42. enddialog
    --- --
    Прогр. как раз такая сфера где теорию можно проверить на практике и узнать кто обосрался.
    Забавно когда в этой сфере воз никают ожесточеные споры. Достаточно собрать обе идеи и сравнить.
    --- Сообщение объединено, 28 апр 2026 в 01:51 ---
    --- --
    Хочу одну фишку рассказать до которой многие не допирают.

    Обьясню для си подобных яп на примере delphi 7, другого под рукой нет.
    Очень многие люди ненавидят писать приложения на WinApi.

    В обрабтке сообщений все традиционно делают так:
    Код (Pascal):
    1.  
    2. function DialogProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LongBool; stdcall;
    3. begin
    4.   Result := False;  // по умолчанию сообщение не обработано
    5.   case uMsg of
    6.     WM_COMMAND:
    7.       begin
    8.         if (LOWORD(wParam) = IDOK) then
    9.         begin
    10.           MessageBox(hWnd, 'Hello World!', 'Example', MB_OK);
    11.           Result := True;  // сообщение обработано
    12.         end
    13.         else if (LOWORD(wParam) = IDCANCEL) then
    14.         begin
    15.           EndDialog(hWnd, 0);
    16.           Result := True;
    17.         end;
    18.       end;
    19.     WM_CLOSE:
    20.       begin
    21.         EndDialog(hWnd, 0);
    22.         Result := True;
    23.       end;
    24.   end;
    25. end;
    26.  
    Можно сделать по другому, если попробуете на практике, поймете почему это правильно:
    Делаем такую функцию:

    Код (Pascal):
    1.  
    2. function ShowMsg(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): bool;
    3. begin
    4.   Result := False;
    5.   if (LOWORD(wParam) = IDOK) then
    6.   begin
    7.     MessageBox(hWnd, 'Hello World!', 'Example', MB_OK);
    8.     Result := True;
    9.   end
    10.   else if (LOWORD(wParam) = IDCANCEL) then
    11.   begin
    12.     EndDialog(hWnd, 0);
    13.     Result := True;
    14.   end;
    15. end; //ShowMsg
    16.  
    Осн. фишка до которой большинство не допирает, это сделать ее такой, чтобы в нее передавалис все hWnd, wParam, lParam:
    И для других функций точно так же:
    Код (Pascal):
    1.  
    2. function DialogProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LongBool; stdcall;
    3. begin
    4.   Result := False;
    5.   case uMsg of
    6.     WM_COMMAND: ShowMsg(hWnd, uMsg, wParam, lParam);
    7.     WM_CLOSE: CloseApp(hWnd, uMsg, wParam, lParam);
    8.   end;
    9. end; //DialogProc
    10.  
    Тогда основная функция будет выглядеть так:
    Код (Pascal):
    1.  
    2. function DialogProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LongBool; stdcall;
    3. begin
    4.   Result := False;
    5.   case uMsg of
    6.     WM_COMMAND: ShowMsg(hWnd, uMsg, wParam, lParam);
    7.     WM_CLOSE: CloseApp(hWnd, uMsg, wParam, lParam);
    8.   end;
    9. end; //DialogProc
    10.  
    Когда типов сообщений много, это очень удобно. Нет длинной портянки в DialogProc. Становится в кайф писать WinApi приложения.
    --- Сообщение объединено, 28 апр 2026 в 02:02 ---
    --- --

    Не могу отредактировать пред. сообщение.

    Основной смысл, чтобы главная функция стала такой:
    Код (Pascal):
    1. function DialogProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LongBool; stdcall;
    2. begin
    3.   Result := False;
    4.   case uMsg of
    5.     WM_COMMAND: ShowMsg(hWnd, uMsg, wParam, lParam);
    6.     WM_CLOSE: CloseApp(hWnd, uMsg, wParam, lParam);
    7.   end;
    8. end; //DialogProc
    9.  
    Нужно в другие функции передавать все параметры:
    Код (Pascal):
    1.  
    2. function CloseApp(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): bool;
    3. function ShowMsg(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): bool;
    4.  
    Тогда весь код можно вынести в них и от DialogProc не будет тошнить.
    --- Сообщение объединено, 28 апр 2026 в 02:13 ---
    + удобнее искать ошибки кода все в отдельных функциях.
    --- Сообщение объединено, 28 апр 2026 в 02:56 ---
    --- --
    --- Сообщение объединено, 28 апр 2026 в 03:07 ---
    Ресурсы довольно забористая вещь, сделал Edit и Button на окне. DialogExample 2.
     

    Вложения:

    Последнее редактирование: 28 апр 2026 в 01:23
    Marylin нравится это.
  9. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    313
    Именно про это я говорю (да, это мой пост). Как оказалось, нельзя использовать этот макрос для всего кода программы от чердака и до подвала - нужно заворачивать в этот блок лишь отдельные участки, в которых параметры всех api известны. Если внутри блока frame/endf имеется выход за его пределы, то макрос не может уже вычислить размер необходимого фрейма, и прожка может упасть в самый неподходящий момент. Я сталкивался с таким поведением, поэтому и предупреждаю. Если-же последовательность вызовов api конечна, то профит от макроса несомненно есть.
     
  10. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    615

    Вложения:

    • _nasm_macro.pdf
      Размер файла:
      800,8 КБ
      Просмотров:
      4