1. Assembler / Говнокод #1520

    +129.5

    1. 1
    2. 2
    call LABEL
    LABEL: pop eax

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

    Запостил: OlegD, 11 Августа 2009

    Комментарии (40) RSS

    • Правильный способ pop eip?
      Ответить
      • Так нельзя.
        Ответить
      • Была непонятная инструкция POP CS на каком-то старом процессоре (то ли 8086/8088, то ли на редком 80186/80188), но потом её отменили за ненадобностью, а её код (0FH) стал использоваться как префикс для второй таблицы инструкций. Что эта инструкция помещала в IP, я не знаю.

        А pop eip никогда не было.
        Ответить
    • бр.. mov eax, eip ?
      Ответить
      • Нет, к eip так просто в Интеле не подобраться.
        Ответить
        • Так а какой же способ - правильный?
          Ответить
          • Ну, раз уж связались со стеком, то надо делать, например, где-то так:

            LABEL: mov eax, [esp]
            ret

            call LABEL
            Ответить
            • А почему твой способ более правильный? Делает то же самое, но, если не ошибаюсь, дольше. Зачем читать из стека, а потом возвращаться, если можно просто достать?
              Ответить
              • Потому, что вызовы call без обратных ret рассинхронизируют реальный стек с т.н. return address predictor stack, который имеет большинство современных процессоров, в результате чего работа программы после одного-единственного такого финта может сразу замедлиться в разы.
                Ответить
                • Правильный код:
                  call LABEL
                  LABEL: pop eax
                  push eax
                  ret
                  Ответить
    • автор идиот. Этот код - классический пример вычисления дельта-смещения. Пусть хоть Калашникова почитает, может поймёт...
      Ответить
      • Я знаю, что такое вычисление дельта-смещения. Потрудитесь прочитать мой комментарий выше на тему последствий для производительности. Что простительно вирусне десятилетней давности, непростительно современному приложению, претендующему на полезность.
        Ответить
        • Да, прочитал. Сегодня ещё про return address predictor stack прочитаю) Но дело в том, что ИМХО такой приём не является говнокодом. Сайт-то про говнокод, а не про не самые производительные решения :)
          Ответить
        • ТТ, а какие еще вариант получения дельта смещения ты знаешь, просвяти, а? семки есть? вы все нешарите ©
          Ответить
    • Жёстко. Зачем этот финт нужен? Кроме как для вирусни, применения ему не нахожу...
      Ответить
      • Для того, чтобы код мог без фиксапов исполняться. Например какойто участок кода, который копируется в буфер, например как хэндлер какоголибо сплайса, да и вобще код, который расположен не в модуле. Или например сжатый участок кода, который будер распакован в памяти..
        Если сегменты имеют нулевые базы, то ситают что сегментации нет; если загрузчик правит адреса из релоков, то считается что база фиксирована, а адрес не является смещением в сегменте.. идти изучать матчасть, а дельфе, скрипты и остальнй шлак фтопку!
        Касательно быстродействия.
        1. Так как афтор явно нуб, как следствие иму не изветны кольца защиты в IA. Раз так, то данный код исполняется в 3-м кольце защиты, тоесть при включённом планировании(разрешённых прерываниях). А это значит что похую конвеер и прочая оптимизация. Поток может завершить свой квант времени на инструкции LABEL. Это железо прерывание сгенерит, например системный таймер. А то время, в течении которого он будет простаивать огромно, камень могбы выполнить тысячи подобных блоков.
        2. Стек обычно расширяется. Тоесть Call LABEl может вызвать обращение к сторожевой странице стека, если его дно находится на границе страниц. В этом случае возникнет исключение, ось расширит стек, на что также требуется время.
        3. Подкачка. Память то подкачиваемая. Хуяк - и сраницы не, менеджер её отгрузил в своп. Вот пока он её оттуда достанет уйдёт значительное время.
        -
        Так что про подобную оптимизацию в условиях мультизадачность говорить бессмысленно. Если это код для исполнения на какомто контроллере, то там нет к примеру предскозания ветвлений и без разницы, что эта пара инструкций, что есчо стопяцот между ними.
        Ответить
    • Что бы писать защиту от взлома программы.
      Ответить
    • такие способы активно используются в многих уважаемых протекторах, т.ч. я не считаю это говнокодом
      Ответить
    • кстати на х64, насколько я помню можно в лоб
      mov rax, rip
      в rax будет указатель на следущую команду.
      Ответить
    • MOV EAX, $ же.
      Ответить
      • Ассемблер сгенерирует MOV EAX, Const, где на константу будет фиксап. Заполнять это значение будет загрузчик кода. Выходной файл либо будет содержать таблицу перемещаемых данных, либо будет пофиксен для загрузки по постоянному адресу.

        Конструкция же CALL LABEL / LABEL: pop eax никаких фиксапов не генерирует. В Линуксе она используется для создание позиционнонезависимого кода (PIC). Он с одной стороны обходится без фиксапов, с другой стороны может грузиться куда угодно. Линуксоиды, поправьте меня, если я ошибся!
        Ответить
        • А чем фиксап плох?
          Ответить
          • Не знаю. Но почему-то с появлением Windows 95 и Windows NT программисты стали фиксить свои программы для запуска по постоянному адресу. Такие пофиксенные программы чуть-чуть поменьше, так как не содержат таблицы перемещаемых данных a. k. a. relocations (самому смешно, экономим считанные килобайты, когда ресурсы с менюшками и иконками могут весить больше), а также загружаются чуть-чуть быстрее, поскольку загрузчику не надо править адреса (тоже смешно, лучше бы сам код оптимизировали), правда Win32s такие программы выплёвывает, поскольку не умеет создавать виртуальные адресные пространства.

            Реально код CALL/POP необходим вирусмейкерам, поскольку добавление ссылок на свой код в таблицу релокейшнов — лишняя задача. Почему в Линуксе такой вирусоподобный код сделали стандартом для SO-библиотек, я не знаю.

            Похоже, что боязнь фиксапов (в Windows — программы для фиксированных адресов, в Линуксе — конструкции CALL/POP) — это ностальгия по эпохе COM-файлов.
            Ответить
        • Хотя я понял. Если пишешь ось, то фиксап ей никто не сделает.
          Ответить
    • Автор обнаружил использование данного кода в теле цикла, которое выполняется миллионы раз? Возможно, тогда это был бы говнокод.
      Ответить
    • >"вызовы call без обратных ret рассинхронизируют реальный стек с т.н. return address predictor stack, который имеет большинство современных процессоров, в результате чего работа программы после одного-единственного такого финта может сразу замедлиться в разы"
      На бошорг, однозначно :lool: Олег, внимательнее маны читай и не пости ерунды. А если сомневаешься, то всегда можно написать пару тестовых программ и сделать замеры. Но я съэкономлю тебе время - разницы не заметишь.
      Ответить
      • Съэкономишь, съэкономишь.
        Ответить
        • Тем не менее, я не редко встречаю и такой код:
          CODE:0041F516 proc            par_CreateBlocks near   ; CODE XREF: main+24p
          CODE:0041F516                 mov     eax, 65536
          CODE:0041F51B                 call    mem_VirtualAlloc
          CODE:0041F520                 mov     [ds:lpOptionsLastBlock], eax
          CODE:0041F525                 mov     [ds:lpOptionsBlocks], eax
          CODE:0041F52A                 add     eax, 65536
          CODE:0041F52F                 mov     [ds:lpOptionsEnd], eax
          CODE:0041F534                 retn
          CODE:0041F534 endp            par_CreateBlocks
          
          CODE:00401032 proc            mem_VirtualAlloc near   ; CODE XREF: mem_Alloc+2Cp
                                        <cut here>
          CODE:00401057                 and     eax, eax
          CODE:00401059                 jz      short @@error
          CODE:0040105B                 retn
          CODE:0040105C @@error:                                ; CODE XREF: mem_VirtualAlloc+27j
          CODE:0040105C                 mov     [ds:errCode], 1
          CODE:00401063                 mov     esp, [ds:AlarmStack]
          CODE:00401069                 stc
          CODE:0040106A                 retn
          CODE:0040106A endp            mem_VirtualAlloc

          Тормозов такая смена верхушки стека не вносит. Процессоры не настолько тупы, чтобы не заметить подмены и не принять соответствующих мер. В конце-концов, игры со стеком свойственны и ЯВУ, особенно при обработках различных исключений.
          Ответить
    • там типа в стеке адрес возврата и мы его оттуда попаем в eax?)
      гг

      напоминает как челы в жабе кидали exception и сразу ловили чтобы узнать текущий стек трейс
      Ответить

    Добавить комментарий