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

    −2.5

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

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

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

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

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

            LABEL: mov eax, [esp]
            ret

            call LABEL
            Ответить
            • А почему твой способ более правильный? Делает то же самое, но, если не ошибаюсь, дольше. Зачем читать из стека, а потом возвращаться, если можно просто достать?
              Ответить
              • Потому, что вызовы call без обратных ret рассинхронизируют реальный стек с т.н. return address predictor stack, который имеет большинство современных процессоров, в результате чего работа программы после одного-единственного такого финта может сразу замедлиться в разы.
                Ответить
    • автор идиот. Этот код - классический пример вычисления дельта-смещения. Пусть хоть Калашникова почитает, может поймёт...
      Ответить
      • Я знаю, что такое вычисление дельта-смещения. Потрудитесь прочитать мой комментарий выше на тему последствий для производительности. Что простительно вирусне десятилетней давности, непростительно современному приложению, претендующему на полезность.
        Ответить
        • Да, прочитал. Сегодня ещё про 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-файлов.
            Ответить
        • Хотя я понял. Если пишешь ось, то фиксап ей никто не сделает.
          Ответить

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