связка Visual C++ 2017 с MASM

Тема в разделе "WASM.BEGINNERS", создана пользователем kol1978, 18 фев 2025 в 07:20.

  1. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    144
    Ну вот вам точно стоило бы почитать мою ссылку и тогда бы не писали такое...
    Какое отношение разрядность шины адреса имеет к размерам регистров? Например, сейчас у вашего ЦПУ шина адреса 48-бит, а у серверных моделей встречается шина адреса в 57-бит, хотя регистры общего назначения 64-битные.

    А ограничение в 2 Гб не связано с возможностью адресоваться по абсолютному адресу... Просто прочитайте то что я вам дал.
     
  2. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    510
    Ты неисправим. :)
    upload_2025-2-21_13-5-8.png

    Вот скажи мне зачем тебе что-то пытаться объяснять, если ты не читаешь? Это касается просто всего что ты в ответ пишешь - все ответы были ранее по тексту.
    Ты последние две страницы каждый раз когда что-то спрашиваешь - это был ранее по тексту.
    Соберись уже и перечитай тему, может несколько раз, бывает что пока чего то одно в голове не отложится, информация новая не зайдёт. Тогда просто надо перечитать.
     
  3. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    144
    Все в кучу. Какое отношение PAE имеет к смещению в командах. Вы технологию расширения физического адресного пространства смешали с адресацией на уровне команд. А вашу текущую пользовательскую программу едва допускают кувыркаться в виртуальных адресах памяти. Т.е. вы даже с сегментами не работаете, а просто создаете оффсеты.

    И так вообще то PAE уже давно не актуален. Для x86_64 PAE не используется. Он для расширения 32-битных процов был сделаг, где 64-битных регистров и в помине не было от слова совсем.
     
  4. Intro

    Intro Active Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    611
    Я из-за таких сложностей стараюсь использовать х86-32, всё таки проще, ну и что что меньше регистров. Так же неработает некоторый из х86-32. Вот так пришлось делать:
    Код (ASM):
    1. foreach MACRO it_reg:req, vec:req
    2.     .for (it_reg = vec._Myfirst: it_reg < vec._Mylast: it_reg += vec._Mysizeclass)
    3.     EXITM <>
    4. ENDM
    5. static_vector struct ;(sizeof=24, align=8)
    6.     _Myfirst            qword ?     ; 0     pointer to beginning of array       указатель на начало массива
    7.     _Mylast             qword ?     ; 8     pointer to current end of sequence  указатель на текущий конец последовательности
    8.     _Mysizeclass        qword ?     ; 16    размер класса в байтах
    9. static_vector ends
    10. static_vector@init      = 0
    11. static_vector@@push MACRO name_vec:req, name_class:req, data:vararg
    12. LOCAL lbl_begin,lbl_end
    13.     lbl_begin   TEXTEQU <>
    14.     lbl_end     TEXTEQU <>
    15.     IF static_vector@init EQ 0      ;;создание и загрузка данных в вектор
    16.         lbl_begin   CATSTR <autogenerate??>,<name_vec>,<?begin>
    17.         align_data
    18.         lbl_begin   name_class  data
    19.         static_vector@init  = 1
    20.     ELSEIFNB <data>                 ;;загрузка данных в вектор
    21.                     name_class  data
    22.     ELSE                            ;;завершения загрузки данных в вектор
    23.         lbl_begin   CATSTR <autogenerate??>,<name_vec>,<?begin>
    24.         lbl_end     CATSTR <autogenerate??>,<name_vec>,<?end>
    25.         lbl_end     label name_class
    26.         align_data
    27.         name_vec    static_vector { offset lbl_begin, offset lbl_end, sizeof name_class }
    28.         static_vector@init  = 0
    29.         ;% echo @CatStr(%.type ptr )
    30.     ENDIF
    31.     EXITM <>
    32. ENDM
    Это небольшая часть библиотеки для перебора коллекций на ассемблере, это именно const static, если делать массивами как раньше, то на х86-64 появляются проблемы из-за указанного выше. Есть ещё с динамическими данными, но там недоделано...
     
    Последнее редактирование: 21 фев 2025 в 09:46
  5. kol1978

    kol1978 Member

    Публикаций:
    0
    Регистрация:
    16 фев 2025
    Сообщения:
    32
    Парни! вы слишком много вопросов на вопрос задаёте юзеру из секции новичков..."смещению в командах" - у меня эти два слова в контексте нигде не фигурируют! просил рассуждать про "команду" : команду mov rax,[Val] - тут есть смещение/оффсеты-сегментно-виртуально-физических адресов?
    --- Сообщение объединено, 21 фев 2025 в 10:57 ---
    это просто флуд.... в формате данного поста: даже про непонятность промолчу...
     
  6. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    144
    Тут есть оффсет: mov rax,[Val]
     
  7. kol1978

    kol1978 Member

    Публикаций:
    0
    Регистрация:
    16 фев 2025
    Сообщения:
    32
    это звучит так: читайте внимательно документацию интел и не задавайте глупых вопросов... так что это просто флуд.
    --- Сообщение объединено, 21 фев 2025 в 11:06 ---
    считаю что офсета тут нет! вот и что то прояснилось... поясните где офсет? [Val] - это адрес переменной Val.
    --- Сообщение объединено, 21 фев 2025 в 11:09 ---
    В это что то есть...но не совсем понятно - просил перефразировать.
    --- Сообщение объединено, 21 фев 2025 в 11:15 ---
    Давай еще раз - почему файл не компилируется? :
    Код (Text):
    1. .text                      #расположение исполняемого кода
    2.      .globl main;
    3. main:                      #точка входа
    4.         endbr64
    5.         pushq   %rbp
    6.         movq    %rsp, %rbp
    7.         movl    $15, %edx
    8.         leaq    message(%rip), %rax
    9.         movq    %rax, %rsi
    10.         movl    $1, %edi
    11.         call    write
    12.         movl    $0, %edi
    13.         call    _exit
    14.         . = main + 2147483648      #перемещение из позиции гигабайта
    15. .data
    16. message:
    17.         .string "Hello, world!\n"
    компиляция: gcc -mcmodel=large -fpie -Os -m64 hello.s -o hello
     
  8. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    144
    Вот две инструкции загрузки в rax учетверенного слова по смещению
    Снимок экрана_20250221_124033.png
    И вот что из себя представляют их опкоды:
    Снимок экрана_20250221_124046.png
    0x48 это как раз REX.W префикс, дальше опкод 0xA1 или 0x8B и вот уже дальше все зависит от опкода инструкции: для 0xA1 идет просто 64-битное смещение указатель на ячейку памяти, а вот для 0x8B там уже присутствует байт modr/m, который расшифровывается как rip+offset32. Так получается, что данные идут сразу за этими двумя командами и значения разные, но адреса одни и те же.
    Как можете видеть обе команды ссылаются на одну и туже переменную, но вот кодируются по разному.

    --- Сообщение объединено, 21 фев 2025 в 11:36 ---
    Вы разберитесь с терминами в документации intel. А вашем случае этот "адрес переменной" на самом деле оффсет от начала сегмента.
    --- Сообщение объединено, 21 фев 2025 в 11:57 ---
    Вот что по этому поводу говорит документация Intel
    Снимок экрана_20250221_125151.png
    Как можете увидеть для команды A1 в коде установлено использование moffs64 (memory offset 64-bit). Для команды с байтом mod reg, r/m надо смотреть таблицу декодирования этого байта и там мы увидим
    Снимок экрана_20250221_125626.png
    И тут мы видим сокращение disp (displacement), что можно перевести как отступ или смещение (offset)
     
    Последнее редактирование: 21 фев 2025 в 11:59
    kol1978 нравится это.
  9. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    510
    Я попытаюсь еще раз объяснить пользуясь аналогией.
    Когда то был вымышленный 16-битный процессор Extel 666.
    В его системе команд были только 16-битные слова. Регистры были (для простоты объяснения сейчас) только 16-битные и адресное пространство только 16-битное.
    Было только два регистра A и B и были такие команды ассемблера:
    Код (Text):
    1.  
    2. опкод E0: mov A, imm16
    3. опкод E1: mov A, [B]
    4. опкод E2: mov A, [imm16]
    5. опкод E3: mov A, [B+imm16]
    6.  
    в последнем варианте логичнее называть imm16 смещением, т.к. мы как бы смещается относительно базы из регистра B. А в предпоследнем варианте скорее напрашивается addr16 - просто прямой адрес.
    Но с точки зрения кодирования и то и другое это два байта адреса лежащие сразу за опкодом в памяти выполняемой программы.
    Так или иначе addr16 покрывает всё возможное адресное пространство, поэтому он именно терминологически напрашивается как "адрес", хотя под капотом всё отличие в том, что не происходит суммирования с B.

    Далее процессор делают 32-битным. 32-битные варианты регистров называются EA и EB. По умолчанию процессор работает в 32-битном режиме где все эти опкоды начинают работать как:
    Код (Text):
    1.  
    2. опкод E0: mov EA, imm32
    3. опкод E1: mov EA, [EB]
    4. опкод E2: mov EA, [imm32]
    5. опкод E3: mov EA, [EB+imm32]
    6.  
    Опять таки все варианты косвенной адресации продолжают охватывать всё адресное пространство, поэтому третий вариант инструкции это всё равно mov EA, [addr32] чисто по понятиям, а в четвёртом логичнее говорить об offs32, но технически это разница лишь терминологическая в языке.

    Но с переходом на 64 бита создатели архитектуры решили, что слишком жирно все смещения делать 64-битными и сделали вот такой ход конём:
    Код (Text):
    1.  
    2. опкод E0: mov RA, imm64
    3. опкод E1: mov RA, [RB]
    4. опкод E2: mov RA, [RIP+imm32]
    5. опкод E3: mov RA, [RB+imm32]
    6.  
    Только команде загрузки непосредственного значения в регистр RA дали возможность иметь 64-битный immediate.
    Но последние две команды где было смещение - там смещения оставили для экономии 32-битными. В 64-битном адресном пространстве E2: mov RA, [imm32] перестал иметь хороший смысл, поэтому его заменили на mov RA, [RIP+imm32]
    Мы потеряли возможность написать mov RA, [любой_адрес_в_памяти].
    Если хочется такое сделать, то придётся писать две инструкции:
    mov RB, любой_адрес_в_памяти
    mov RA, [RB] ; (с порчей регистра RB)

    В общих чертах - по этой схеме развивалась архитектура i86 и отсюда все вышеозвученные проблемы помноженные еще на десяток сложностей которые есть в архитектуре настоящего процессора.
    И вот поэтому если программа не уложилась в 2Гб нижних адресов ОЗУ линкер спасует перед её линковкой, т.к. не сможет родить правильный mov RA, [RIP+????] и тогда надо компилировать с опцией -mcmodel=large когда компилятор начнёт вставлять дополнительные инструкции чтобы обеспечить 64-битные смещения.
     
    Последнее редактирование: 21 фев 2025 в 12:35
  10. kol1978

    kol1978 Member

    Публикаций:
    0
    Регистрация:
    16 фев 2025
    Сообщения:
    32
    спасибо за ответ! просба : "Вы разберитесь с терминами в документации intel. А вашем случае этот "адрес переменной" на самом деле оффсет от начала сегмента." - давайте не будем друг другу давать указания типа : вы разберитесь. По сути понятно...и согласен, но не до конца проясняет ситуацию... - ответ на вопрос по коду: никак не скомпилировать бинарник 2 гига... - так?
    Код (Text):
    1. .text                      #расположение исполняемого кода
    2.      .globl main;
    3. main:                      #точка входа
    4.         endbr64
    5.         pushq   %rbp
    6.         movq    %rsp, %rbp
    7.         movl    $25, %edx
    8.         leaq    message(%rip), %rax
    9.         movq    %rax, %rsi
    10.         movl    $1, %edi
    11.         call    write
    12.         movl    $0, %edi
    13.         call    _exit
    14.  
    15.     . = main + 2147483648      ##если ваши позиции не достаточно позиционны не меняйте цифру 2147483648, а если поменяете, то читайте GPL отказ от ответственности :)
    16. .data
    17. message:
    18.     .string " \345\215\220 \320\245\320\260\320\271\320\273\321\214 \320\241\320\270 \345\215\220"
    иду на хитрость шутки что бы разрядить обстановку...
    --- Сообщение объединено, 21 фев 2025 в 14:21 ---
    отвечаю перефразированием :
    - так? это имелось в виду? Тогда давайте избавимся от (%rip)..., но почему все равно не помогает?
     
    Последнее редактирование: 21 фев 2025 в 14:41
  11. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    510
    Movl offset message, esi сработает.
    А после этого mov [esi], esi получит желаемое.
    А инструкции которая грузит в регистр любой адрес в 64битной памяти в один присест просто нет.
     
  12. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.564
    Адрес:
    Russia
  13. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.564
    Адрес:
    Russia
    Вам последнее предупреждение.
    В следующий раз в бан
     
  14. kol1978

    kol1978 Member

    Публикаций:
    0
    Регистрация:
    16 фев 2025
    Сообщения:
    32
    ох... уж эти инструкции...ну допустим ведь для загрузки в сегментный регистр тоже нет одной команды...и приходится делать двумя - это не суть. Нужен пример доработанного кода... тогда будет понятно (Movl offset message - это не тот синтаксис который можно понять).
    --- Сообщение объединено, 21 фев 2025 в 15:54 ---

    1. опкод: mov A, [imm16] - здесь нет смещения/офсета
    2. опкод : mov A, [B+imm16] - здесь imm16 это смещение. И! это синтаксис ассемблера... как там и о чём пишет интел
      ...пусть себе пишет... Здесь тоже есть слово смещение и это имеет право на существование, причем и то и другое:
     
    Последнее редактирование: 21 фев 2025 в 16:03
  15. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    144
    Дело не в этом. Бинарник может быть хотя 2, хоть 4 Гб. Вопрос только в том как вы будете добираться до переменных в этом бинарнике. Проблемы начинаются в месте где переменная располагается дальше чем 2Гб от команды, которая её использует. Этой проблемы нету при работе с регистрами напрямую для адресации переменных, но вот при вычислении адресов она может возникнуть. Ведь вам доступно много разных вариантов задания адресов. И вот один из них это как раз с использованием disp32 (видите, что в таблице нету вариантов с disp64). Вот когда в команде потребуется задать disp32 > 2Гб, тогда этого сделать будет нельзя. А вот из-за генерации PIC (Position Independent Code - Кода без привязки к адресам) т.е. использования в коде относительных адресов и регистра rip накладывает ограничение на объем адресуемых данных. Для обхода этого ограничения создаются специальные таблицы адресов переменных, которые точно можно будет использовать через rip+disp32, а сами эти таблицы содержат уже полный 64-битный оффсет для переменной, загружаемый в регистр общего назначения.

    Но в разных форматах бинарников тоже есть разные механизмы создания PIC из обычного кода. В PE для перемещения кода есть секция fixups, которая содержит смещения всех команд в секции кода для обновления этих 32-битных значений после загрузки бинарника в память. И похожий на эту секцию сегмент в ELF тоже есть. Кажется он называется GNU_RELRO.
     
  16. kol1978

    kol1978 Member

    Публикаций:
    0
    Регистрация:
    16 фев 2025
    Сообщения:
    32
    Спасибо за ответ! Тогда так : мною предлагалось не использовать PIC...но это не срабатывает (может только у меня...если да то предложите модификацию кода). Мною говорилось что ограничение 2 гига связанно с
    и тогда можно сказать что ЦП не истинно 64 битный а всего лишь 32...не зависимо от того сколько инструкций не поддерживают полностью 64 битный режим - с этим опять же не согласны и выдают за чушь...
    это не своевременно...и по этому не понятно. в ELF много всяких секций и разного кода который добавляется точно не самим программистом...- эта тема для другого поста.
    По поводу таблицы: [edx]+ disp32 - это стало быть 32битный регистр прибавляется к 32битному числу и по итогу это 32битный операнд - т.е. это не полностью 64 битная инструкция и поэтому, процессор не полностью 64 битный - так можно сказать опираясь на ваше высказывание?

    " Для обхода этого ограничения создаются специальные таблицы адресов переменных, которые точно можно будет использовать через rip+disp32, а сами эти таблицы содержат уже полный 64-битный оффсет для переменной, загружаемый в регистр общего назначения." - что это за механизм и как его можно использовать для при примера(команда; код)?
     
    Последнее редактирование: 21 фев 2025 в 16:52
  17. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    144
    Это не верно. Там вычисляется 64-битный адрес, но в инструкции нельзя напрямую добавить 64-битное смещение. Т.е. [rdx+disp32] и на выходе у вас 64-битный адрес, но относительно rdx можно добраться не более чем на 2 гб в обе стороны.
    --- Сообщение объединено, 21 фев 2025 в 19:04 ---
    Этот пример вам уже показывали
    https://wasm.in/threads/svjazka-visual-c-2017-s-masm.35300/page-2#post-444298
     
  18. kol1978

    kol1978 Member

    Публикаций:
    0
    Регистрация:
    16 фев 2025
    Сообщения:
    32
    значит ограничение создаётся 32-битным целочисленным ограничением (2 147 483 647), которое является максимально возможным целым числом, которое может быть представлено 32 двоичными цифрами и это число dsp32i - это так ?
    и именно при этом 64разрядный регистр заполняется нулями про сложении с dsp32i что бы сумма была 64 разрядной - так?
    --- Сообщение объединено, 22 фев 2025 в 04:19 ---
    --- Сообщение объединено, 21 фев 2025 в 19:04 ---

    Этот пример вам уже показывали
    https://wasm.in/threads/svjazka-visual-c-2017-s-masm.35300/page-2#post-444298[/quote]
    Там какой то отвлеченный пример, кода на Си который будет компилироваться в любом случае. При этом используется флаг gcc -mcmodel=large -no-pie -Os -m64 hello.s -o hello (-mcmodel=large) и ничего об этом флаге не сказано...
    И так код который нужно скомпилировать и который отвечает задачам/параметрам:
    Код (Text):
    1. .text                      #расположение исполняемого кода
    2.      .globl main;
    3. main:                      #точка входа
    4.         endbr64
    5.         pushq   %rbp
    6.         movq    %rsp, %rbp
    7.         movl    $25, %edx
    8.         leaq    message, %rax
    9.         movq    %rax, %rsi
    10.         movl    $1, %edi
    11.         call    write
    12.         movl    $0, %edi
    13.         call    _exit
    14.  
    15.     . = main + 2147483648      ##если ваши позиции не достаточно позиционны не меняйте цифру 2147483648, а если поменяете, то читайте GPL отказ от ответственности :)
    16.         .data
    17. message:
    18.     .string " \345\215\220 \320\245\320\260\320\271\320\273\321\214 \320\241\320\270 \345\215\220"
    19.  
    Я уже говорил что с применением флага -mcmodel=large тоже не получается скомпилировать...
    --- Сообщение объединено, 22 фев 2025 в 04:31 ---
    код на Си :
    Код (Text):
    1. extern int x;
    2. int func()
    3. {
    4.     return x + 1;
    5. }
    с флагом -O3 -std=c++23 -mcmodel=large
    получает :
    Код (Text):
    1. func():
    2.         movabsl x, %eax
    3.         addl    $1, %eax
    4.         ret
    (https://godbolt.org/) как автор кода получал код ассемблера из примера по ссылке не уточняется...и вообще про синтаксис тоже умалчивается...

    Так что вопрос повторяю - ?
     
  19. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    510
    У gcc/clang есть ключ компиляции -S который если указать вместо создания объектного файла (.o) или выполнимого файла создаст файл ассемблера (.S) который если потом компилировать, то получится ровно то же самое, что было из исходного файла. Т.е. всегда можно попросить компилятор объяснить во всех деталях какой машинный код он хочет создать и более того - ассемблерный код этот будет пригоден для дальнейшей компиляции с тем же результатом!

    Этот флаг влияет на поведение компилятора C/C++ - на то какой ассемблерный код он будет создавать из исходника на C/C++, но с готовым ассемблерным кодом он уже ничего делать не будет.

    Угу, потому что попытки объяснить на реальном примере провалились, поэтому была попытка объяснить на пальцах.
     
  20. kol1978

    kol1978 Member

    Публикаций:
    0
    Регистрация:
    16 фев 2025
    Сообщения:
    32
    Код (ASM):
    1.  .text
    2.         .section        .text.startup,"ax",@progbits
    3.         .p2align 4
    4.         .globl  main
    5.         .type   main, @function
    6. main:
    7. .LFB12:
    8.         endbr64
    9. .L2:
    10.         pushq   %r15
    11.         movl    $1, %edi
    12.         movl    $25, %edx
    13.         movabsq $_GLOBAL_OFFSET_TABLE_-.L2, %r11
    14.         leaq    .L2(%rip), %r15
    15.         movabsq $message@GOTOFF, %rax
    16.         addq    %r11, %r15
    17.         leaq    (%r15,%rax), %rsi
    18.         movabsq $write@PLTOFF, %rax
    19.         addq    %r15, %rax
    20.         call    *%rax
    21.         xorl    %edi, %edi
    22.         movabsq $_exit@PLTOFF, %rax
    23.         addq    %r15, %rax
    24.         call    *%rax
    25. . = main + 2147483648
    26.         .globl  message
    27. .data
    28.         .size   message, 25
    29. message:
    30.         .string " \345\215\220 \320\245\320\260\320\271\320\273\321\214 \320\241\320\270 \345\215\220"
    31.  
    компиляция :
    Код (Text):
    1. gcc -O3 -std=c++23 --no-relax -mcmodel=large hail.S -o hail_
    то же самое - если изменить число 2147483648 на например 1147483648, то только в этом случает получается бинарник...
    Вопрос актуален...
    --- Сообщение объединено, 22 фев 2025 в 05:14 ---
    Хорошо! вот вам Сишный код получите из него ассемблер , создайте удаление для переменной(message) на 2гига и скомпилируйте - если ваше объяснение ("на пальцах") правильное это должно работать...:
    Код (C):
    1. #include <unistd.h>
    2. char message[] = " \345\215\220 \320\245\320\260\320\271\320\273\321\214 \320\241\320\270 \345\215\220";
    3. int main()
    4. {
    5.    write(1, message, sizeof(message));
    6.   _exit(0);
    7. }
    Но! не работает...Жду ваш пример?
     
    Последнее редактирование: 22 фев 2025 в 05:17