1. C++ / Говнокод #27068

    −1

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    // https://github.com/dotnet/coreclr/blob/a9f3fc16483eecfc47fb79c362811d870be02249/src/vm/i386/cgenx86.cpp#L1613
    
    PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
    {
    #ifdef UNIX_X86_ABI
        BEGIN_DYNAMIC_HELPER_EMIT(23);
    #else
        BEGIN_DYNAMIC_HELPER_EMIT(17);
    #endif
    
    #ifdef UNIX_X86_ABI
    	// sub esp, 4
    	*p++ = 0x83;
    	*p++ = 0xec;
    	*p++ = 0x4;
    #else
        // pop eax
        *p++ = 0x58;
    #endif
    
        // push arg
        *p++ = 0x68;
        *(INT32 *)p = arg;
        p += 4;
    
        // push arg2
        *p++ = 0x68;
        *(INT32 *)p = arg2;
        p += 4;
    
    #ifdef UNIX_X86_ABI
        // mov eax, target
        *p++ = 0xB8;
        *(INT32 *)p = target;
        p += 4;
    #else
        // push eax
        *p++ = 0x50;
    #endif
    
        *p++ = X86_INSTR_JMP_REL32; // jmp rel32
    #ifdef UNIX_X86_ABI
        *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
    #else
        *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
    #endif
        p += 4;
    
        END_DYNAMIC_HELPER_EMIT();
    }

    Функция из дотнеткора, которая нахерачивает опкодов куда-то.

    Запостил: j123123, 28 Октября 2020

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

    • Тут они почти наверняка обосрались т.к. возможна невыровненная запись в память. Ну вот допустим
      // push arg2
          *p++ = 0x68;
          *(INT32 *)p = arg2;  // допустим что тут указатель делится на 4 и UB нет
          p += 4; // тут он тоже делится на 4
      
      #ifdef UNIX_X86_ABI
          // mov eax, target
          *p++ = 0xB8; // тогда тут он на 4 не делится
          *(INT32 *)p = target; // а тут уже запись в хуйню по невыровненному адресу

      Единственный вариант что тут они не обосрались - это если INT32 тип это хуйня с каким-то специальным атрибутом, что через него можно срать по невыровненным адресам. В ином случае это UB
      Ответить
      • А почему UB то? Разве не просто implementation defined? x86 так то срать на выравнивания если тебе не нужна атомарность. А для других процов этот код бесполезен, там свой будет.
        Ответить
        • Нет, по стандарту крестов (и сишки) это однозначное UB
          https://stackoverflow.com/questions/51126257/
          Ответить
          • А я не был бы так уверен...

            3.9 Types

            5. The alignment of a complete object type is an implementation-defined integer value representing a number of bytes.

            Т.е. стандарт не утверждает, что uint32_t обязан быть выровнен на 4 байта, это реализация решает.
            Ответить
            • Это видимо просто оговорка, что alignment у типа таков, сколько в нем байтов. Скажем, int на одной платформе из 4-х байт, а на другой може быть из 8-ми, и там alignment в первом случае будет 4, а во втором 8. Если у тебя на конкретной платформе для int известно, что у него alingof() равен 4, и ты тыкаешь указателем на int на не делящуийся на 4 адрес, то у тебя будет UB
              Ответить
            • показать все, что скрытоvanished
              Ответить
            • Кстати вот реальный пример
              https://pzemtsov.github.io/2016/11/06/bug-story-alignment-on-x86.html

              Suddenly, our x86 behaves just like RISC: it crashes when a pointer to uint32_t is not aligned by 4.

              Какой багор )))
              Ответить
              • Ничего не понял, но багор )))
                Ответить
                • показать все, что скрытоvanished
                  Ответить
                  • По-моему это хуйня. CISC и RISC это не про то, можно или нельзя делать невыровненный доступ, это про сложность инструкций. Можно и CISC сделать, чтоб там сигфолт происходил от невыровненного разыменования указателя.
                    Ответить
                    • показать все, что скрытоvanished
                      Ответить
                      • https://developer.ibm.com/technologies/systems/articles/pa-dalign/

                        Вот тут упоминается мотороловский процессор 68000 который был CISC и при этом там была хуйня с сигфолтами от невыровненных доступов к памяти, но в чуть более новых процессорах такой хуйни уже не было:

                        > The original 68000 was a processor with two-byte granularity and lacked the circuitry to cope with unaligned addresses. When presented with such an address, the processor would throw an exception. The original Mac OS didn’t take very kindly to this exception, and would usually demand the user restart the machine. Ouch.

                        > Later processors in the 680×0 series, such as the 68020, lifted this restriction and performed the necessary work for you. This explains why some old software that works on the 68020 crashes on the 68000. It also explains why, way back when, some old Mac coders initialized pointers with odd addresses. On the original Mac, if the pointer was accessed without being reassigned to a valid address, the Mac would immediately drop into the debugger. Often they could then examine the calling chain stack and figure out where the mistake was.
                        Ответить
              • показать все, что скрытоvanished
                Ответить
              • >> except it uses movdqu instead of movdqa

                В SSE есть две инструкции для одного и того же, но одна из них падает на невыровненных данных, как в RISC, а другая работает с любым указателем?
                Ответить
    • https://govnokod.ru/25399
      Такого же рода хуйню я находил, когда ковырялся в исходниках GHC. Правда там были не опкоды x86-64, а байткодная поебень для GHCi
      Ответить
    • А как правильно? Программа высирает код для CISC, в котором данные могут быть по любым адресам, в том числе и по нечётным. Нужно отказаться от записи слов, писать только по одному байту?
      Ответить
      • Тут вариантов достаточно много.
        Можно сделать особую функцию с memcpy
        void putuint32(void *addr, uint32_t val)
        {
          memcpy(addr, &val, sizeof(val));
        }


        хотя такую функцию можно и по другому написать, например

        void putuint32(void *addr, uint32_t val)
        {
          uint8_t *tmp = (uint8_t *)addr;
          tmp[0] = (val >> 8*0) & 0xff;
          tmp[1] = (val >> 8*1) & 0xff;
          tmp[2] = (val >> 8*2) & 0xff;
          tmp[3] = (val >> 8*3) & 0xff;
        }


        или так

        void putuint32(void *addr, uint32_t val)
        {
          typedef uint32_t uint32_t_unalign __attribute__ ((aligned (1)));
          // так эта хуйня может указывать на невыровненый адрес
          // хотя в атрибут можно еще какой-нибудь may_alias добавить на всякий случай
          // от этой UB-хуйни всего можно ждать
        
          uint32_t_unalign *ptr = (uint32_t_unalign *)addr;
          *ptr = val;
        }
        Ответить
        • Забавно, что memcpy пирфоманса ради внутри реализовано через "UB". И даже падает если включить AC в rflags.
          Ответить

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