1. Си / Говнокод #25449

    +1

    1. 1
    #define BSWAP16(x) ( (uint16_t)(((uint32_t)x)*(1 | 1 << 16) >> 8) )

    Запостил: j123123, 15 Марта 2019

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

    • #define BSWAP16(x) (uint16_t)( (uint16_t)(x) * (1 | 1 << 16) >> 8)
      Ответить
      • На платформе с 16-битным интом не сработает, в отличие от оригинала.
        Ответить
        • А если в оригинал сунут число > 65536?
          Ответить
        • Разве на платформе с 16-битным интом оно не сломается еще на стадии (1 | 1 << 16) в том числе и на оригинале?

          Надо наверно так, чтоб уж наверняка:
          #define BSWAP16(x) (uint16_t)( (uint16_t)(x) * (1ULL | 1ULL << 16ULL) >> 8ULL)
          Ответить
          • По-моему 0x00010001 нагляднее этих сдвигов будет.
            Ответить
        • показать все, что скрытоvanished
          Ответить
          • Почему? На платформах с 32-битным интом sizeof(long long) * CHAR_BIT может быть (и чаще всего так и есть) равным 64. Аналогично, на платформах с 16-битным интом uint32_t скорее всего будет синонимом unsigned long long.
            Ответить
      • #define BSWAP16(x) (x ^ (uint16_t)(x << 8 | x >> 8) ^ x)
        Ответить
        • uint16_t x = 0xff00;
          x = BSWAP16(x++);
          Ответить
          • #define BSWAP16(x) ({uint16_t t = x; (uint16_t)((t & 0xFF00) >> 8 | t << 8);})
            Ответить
            • Приведение не нужно, оно осталось от предыдущей версии.
              Ответить
              • Кароч, я дебил, раз t это uint16_t то так: t >> 8 | t << 8
                Ответить
                • > я дебил

                  Собственно, по твоей аве это заметно.
                  Ответить
                • >t это uint16_t то так: t >> 8 | t << 8

                  В js никаких uint16_t нету.

                  > ((x << 8 | x >> 8)&0xFFFF) .toString(16);

                  Именно поэтому я за «js».

                  На самом деле касты говно, а такой код будет работать в любом сиподобном языке.
                  Ответить
            • Использовать GNU расширения https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html это как-то неспортивно
              Ответить
          • Зато красиво и симметрично. И гоатсе напоминает.
            #define BSWAP16(x) (uint16_t)(x ^ (x << 8 | x >> 8) ^ x)
            Ответить
            • #define BSWAP16(x) (uint16_t)(x ^ (x << 8 | x >> 8) ^ x)
              Сократил, проверь.
              Очевидно же, что если ксорить какое-то говно два раза с одним и тем же говном, то ничего не поменяется
              Ответить
    • : BSWAP16  ( u -- u )
          BASE @ >R  HEX
          S" FFFF AND 00010001 * 8 RSHIFT FFFF AND" EVALUATE
          R> BASE !
      ; IMMEDIATE
      Работает и в режиме компиляции (как макрос) и в режиме интерпретации.

      https://ideone.com/1KiivR
      Ответить
    • Байтоёбство без побитовых операций:
      #define BSWAP16(x) ({union{uint16_t u16; uint8_t u8[2];} t = {.u16 = x}; (union{uint16_t u16; uint8_t u8[2];}){.u8 = {t.u8[1], t.u8[0]}}.u16;})
      Ответить
      • > Байтоёбство без побитовых операций

        В js тоже есть «union»
        var buffer   = new ArrayBuffer(2);
        var int16View= new Int16Array(buffer);
        var int8View = new Int8Array(buffer);
        
        int16View[0] = 0x326A;
        console.log(int16View[0].toString(16))
        int8View[0] ^= int8View[1];
        int8View[1] ^= int8View[0];
        int8View[0] ^= int8View[1];
        
        console.log(int16View[0].toString(16))
        Ответить
    • ror ax, 8
      Именно поэтому я за "assembler".
      Ответить
      • rev16 r0, r0
        Именно поэтому я за "ARM".
        Ответить
      • xchg ah, al
        Ответить
        • Это в x86 валидная инструкция? О_о
          Ответить
          • Вполне
            Ответить
          • А что не так? ah, al, bh, bl, ch, cl, dh, dl в некоторых инструкциях могут использоваться как самостоятельные регистры.

            Есть инструкция xchg, ожидающая восьмибитные данные. Аргументами могут выступать два восьмибитных регистра либо восьмибитный регистр и указатель на память.
            Ответить
          • Ещё как, у XCHG есть несколько версий: однобайтовая с регисторм AX/EAX (КОП 90-9F), и с батом MOD/RM (КОП 86 -- размер операнда байт, 87 -- WORD/DWORD), что рождает кучу 2-хбайтовых NOP'ов типа XCHG AH, AH XCHG BX, BX и т.п. К сожалению XCHG REG, REG не попадает в множество ASCII :(
            Ответить
            • Зато XOR и SUB попадают. Можно эмулировать XCHG пачкой ксоров или вычитаний.
              Ответить
              • Во множество ASCII-кодов попадают только значения «регистр-память» байта mod R/M. Кобенации «регистр-регистр» за пределами ASCII-кодов.
                Ответить
                • Придумал, как обменять значения ah и al в ascii-кодах.
                  temp equ [bx+40h] ; Произвольный адрес в памяти.
                  ; Значение этой ячейки потом восстановится.
                  xor ah, temp; 2g@ ; в ah теперь ah XOR temp
                  xor temp, al; 0G@ ; в temp теперь temp XOR ah XOR al
                  xor al, temp; 2G@ ; в al теперь temp xor ah
                  xor al, temp; 2G@ ; в al теперь старое значение ah
                  xor ah, temp; 2g@ ; в ah теперь temp XOR al
                  xor temp, ah; 0g@ ; в ah теперь старое значение al
                  xor temp, al; 0G@ ; в temp теперь старое значение temp

                  Итого: 2g@0G@2G@2G@2g@0g@0G@
                  21 байт. Длинновато вышло, зато в ASCII-кодах.
                  Ответить
                  • Ты же не будешь отрицать, что ты поехавший?
                    Ответить
                    • Братишка, давай я насру в уголочке в ASCII-кодах, программисты слетятся, тут мы их и прихлопнем?
                      Ответить
                  • показать все, что скрытоvanished
                    Ответить
                    • У меня шпаргалка есть:
                      http://govnokod.ru/15764#comment419684
                      Ответить
                      • показать все, что скрытоvanished
                        Ответить
                        • У меня тут @ —– это вообще произвольная константа. Вместо неё можно использовать любой октет, только во всей программе один и тот же.

                          g@ –— это ah, [bx+40h]
                          G@ –— это al, [bx+40h]
                          Ответить
                        • Или ты про материал по ссылке? Ну так mod-R/M можно собрать самому по битам, а для тупых даже опубликованы готовые сетки значений mod-R/M и s-i-b:
                          http://www.club155.ru/x86cmdformats-modrm
                          Ответить
                    • Тут самое сложное –— не запутаться, изобретая схему обмена значений. Ещё важно было не забыть восстановить значение ни в чём не повинной переменной, которое мы временно портили.
                      Ответить
                  • 5 байт:
                    PPDXD
                    push ax
                    push ax
                    inc sp
                    pop ax
                    inc sp
                    Ответить
                    • Ты реально аццкий сотона!
                      Ответить
                    • Твоим методом также можно вращать регистры BP, SI, DI, для которых у x86 не было половинок (bpl, sil, dil появились только у x86-64 в длинном режиме, а bph, sih, dih до сих пор не придумали).
                      Ответить
                      • Так можно было бы ещё и числа по регисрам сдвигать:
                        PUSHA
                        PUSH (какой там? AX?)
                        ;INC SP если надо сдвинуть на байт
                        POPA
                        но SP тоже сдвигается, а в старом стеке остаётся число :(
                        Ответить
                        • Да, жаль, что PUSHA и POPA захватывают SP, а то бы легко было эмулировать SIMD.

                          А так придётся SP где-то ещё сохранять, а потом восстанавливать.
                          Ответить
                    • Циклический сдвиг 32-битного числа на 8:
                      #include <stdio.h>
                      #include <stdint.h>
                      char * __attribute__((section(".text"))) vrot8 = "QXPPDXDDD\xc3";
                      int main() {
                          uint32_t __attribute__((fastcall)) (* rotator)(uint32_t) = (void *)vrot8;
                          uint32_t x = 0x12345678;
                          printf("rot(%x) = %x\n", x, rotator(x));
                          return 0;
                      }


                      64-битный вариант (почему-то не работает):
                      #include <stdio.h>
                      #include <stdint.h>
                      char *__attribute__((section(".text"))) vrot8 = "WXPPDXDDDDDDD\xc3";
                      int main() {
                      	uint64_t __attribute__((sysv_abi)) (* rotator)(uint64_t) = (void *)vrot8;
                      	uint64_t x = 0x123456789abcdef;
                      	printf("rot(%lx) = %lx\n", x, rotator(x));
                      	return 0;
                      }
                      Ответить
                      • В 64-битном режиме почему-то после INC RSP неправильно работает POP. Похоже, что POP в длинном режиме выравнивает значение RSP перед тем, как взять данные.
                        Ответить
                        • Например, сдвиг на 16 бит без применения INC/DEC работает:
                          #include <stdio.h>
                          #include <stdint.h>
                          const char __attribute__((section(".text"))) vrot8[] = "WXfPPfXX\xc3";
                          int main() {
                          	uint64_t __attribute__((sysv_abi)) (*rotator)(uint64_t) = vrot8;
                          	uint64_t x = 0x123456789abcdef;
                          	printf("rot(%lx) = %lx\n", x, rotator(x));
                          	return 0;
                          }


                          https://ideone.com/jycvRV
                          Ответить
                        • > почему-то
                          А ты декомпильни этот код. 'D' - не инкремент. Это префикс REX.R
                          Ответить
                          • Вот я идиот! Даже не глянул сетку опкодов. Я думал, что все новые инструкции где-то в пространстве 0F, а опкоды для старых инструкций совпадают, как было при переходе с 16-битного кода на 32-битный. А они, оказывается, часть старых опкодов затёрли новыми инструкциями.

                            Какой багор )))

                            Надо запомнить, что у x86-64 однобайтовых инкрементов/декрементов нет, на их месте ненужные префиксы Рэкс-фас; PUSHA, POPA, BOUND отменили; на месте ARPL теперь MOVSXD; AAA, AAS, DAA, DAS, AAM, AAD отменили; префиксы сегментов отменили, за исключением FS, GS; пуш и поп для сегментных регистров тоже отменили; LES, LDS, CALL FAR, JMP FAR тоже отменили.

                            И зачем-то отменили опкод 82H, который никому не мешал.

                            Как вообще можно что-то делать без однобайтовых инкрементов/декрементов???
                            Ответить
                            • Отредактировал комментарий, потому что ошибся.

                              83H –— это операции с 16/32-битным регистром, у которых в качестве второго аргумента выступало 8-битное непосредственное, которое на лету дополнялось нулями до 16/32-битного. Было очень удобно, потому что не нужно было хранить в программе лишние нули.

                              А 82H –— это просто копия 80H.
                              Ответить
                            • Идея: вместо инкремента вычитать минус единицу. Минус единицу можно представить как 0x5555555555555555 + 0x5555555555555555 + 0x5555555555555555.
                              Ответить
                              • Всё, высрал инкремент:
                                #include <stdio.h>
                                #include <stdint.h>
                                char *__attribute__((section(".text"))) incr = "fhUUfhUUfhUUfhUUTZH+:H+:H+:ZWX\xc3";
                                int main() {
                                    uint64_t __attribute__((sysv_abi)) (* increment)(uint64_t) = (void *)incr;
                                    uint64_t x = 0x123456789abcdef;
                                    printf("inc(%lx) = %lx\n", x, increment(x));
                                    return 0;
                                }


                                https://ideone.com/2Z5MCE

                                Узнал две особенности x86-64:
                                1. У кучи инструкций непосредственный аргумент может быть максимум 32-битным. Старшая половина добивается нулями. Мне пришлось делать четыре 16-битных пуша, чтобы запушить 64-битное число.
                                2. У инструкций с mod-R/M нужно указывать префикс REX.W (H), иначе результат обрежется до 32-битного.

                                Как всё сложно...
                                Ответить
                                • А вот циклический сдвиг 64-битного числа на 8:
                                  https://ideone.com/6T9Ima
                                  Ответить
                                • > высрал инкремент:
                                  $ echo -n -e "fhUUfhUUfhUUfhUUTZH+:H+:H+:ZWX\xc3" |  ndisasm -b 64 -
                                  00000000  66685555          push word 0x5555
                                  00000004  66685555          push word 0x5555
                                  00000008  66685555          push word 0x5555
                                  0000000C  66685555          push word 0x5555
                                  00000010  54                push rsp
                                  00000011  5A                pop rdx
                                  00000012  482B3A            sub rdi,[rdx]
                                  00000015  482B3A            sub rdi,[rdx]
                                  00000018  482B3A            sub rdi,[rdx]
                                  0000001B  5A                pop rdx
                                  0000001C  57                push rdi
                                  0000001D  58                pop rax
                                  0000001E  C3                ret


                                  > fffUUfUU
                                  Что-то мне не нравится pitush word 0x5555.
                                  Ответить
                                  • Я не придумал, как по-другому запушить константу 0x5555555555555555, используя только опкоды из диапазона ASCII.

                                    Есть альтернативный способ?
                                    Ответить
                                    • Ну конечная цель загрузить -1
                                      > вместо инкремента вычитать минус единицу

                                      А через CWD/CDQ не получится?
                                      Ответить
                                      • CWD/CDQ поможет, если у нас изначально была минус единица, хотя бы для более короткого типа. Её нужно откуда-нибудь достать.
                                        Ответить
                                        • > если у нас изначально была минус единица, хотя бы для более короткого типа. Её нужно откуда-нибудь достать

                                          Например
                                          2C ib 	SUB AL, imm8
                                          
                                          
                                          SUB AL, 33
                                          ADD AL, 32
                                          Ответить
                                      • Кстати, я кидал ссылку на крутую шизу, но ту учётку смыло.

                                        Вот тут автор изобрёл компилятор, который получает EXE-файлы для «DOS», состоящие только из печатаемых ASCII-символов (включая заголовок):
                                        http://tom7.org/abc/

                                        https://sourceforge.net/p/tom7misc/svn/HEAD/tree/trunk/abc/

                                        Такой экзешник можно передавать как текстовый файл.
                                        Ответить
                                        • exeшник с текстовыми комментариями. Очень недурно.

                                          Даже file не разгадал
                                          $ file ../paper.exe 
                                          ../paper.exe: ASCII text, with very long lines, with no line terminators
                                          Ответить
                                          • Существует куча COM-файлов, у которых в шизоидном коде только небольшой распаковщик, а остаток файла представляет собой полезный код, закодированный в UUE или в Base64. Даже упаковщики для этого написали.

                                            А вот EXE-файлов, зожатых таким образом, я не видел. Tom7 — первый, кто придумал заголовки EXE составлять из печатаемых символов.
                                            Ответить
                                            • > первый, кто придумал заголовки EXE составлять из печатаемых символов.

                                              В странную эпоху живём.

                                              Появились читабельные EXEшники.

                                              Исчезли читабельные JSники.

                                              Теперь JS имеет читабельность бинарного файла, зато обладает скоростью скриптового языка.
                                              Ответить
                                              • >Появились
                                                Миша Збыковски просил передать, что давно уже
                                                Ответить
                                    • > Есть альтернативный способ?
                                      $ nasm -f elf64 ./dec.asm -l /dev/stdout && less 
                                           1 00000000 31C0                    xor eax,eax
                                           2 00000002 2C55                    sub al,85
                                           3 00000004 2C55                    sub al,85
                                           4 00000006 2C55                    sub al,85
                                           5 00000008 6698                    cbw
                                           6 0000000A 6699                    cwd  
                                           7 0000000C 99                      cdq

                                      cdq не ascii

                                      Именно поэтому я за x86-32
                                      $ nasm -f elf32 ./dec.asm -l /dev/stdout && less 
                                           1 00000000 48                      dec eax
                                           2 00000001 6648                    dec ax
                                      Ответить
                                • $ nasm -f elf64 ./dec.asm -l /dev/stdout && less 
                                       1 2D11622000              sub eax,2122257
                                       2 0510622000              add eax,2122256
                                  Ответить
                          • Кстати, почему в длинном режиме непосредственный аргумент не может быть 64-битным? Они ввели это ограничение, чтобы инструкция не получалась слишком длинной и чтобы с лёгкостью укладывалась в кэш/конвейер/предиктор?
                            Ответить
                            • > The x86 instruction set (16, 32 or 64 bit, all variants/modes) guarantees / requires that instructions are at most 15 bytes.
                              https://stackoverflow.com/questions/14698350/x86-64-asm-maximum-bytes-for-an-instruction

                              «mov qword [rax + rcx + 0x11223344], 0x55667788», например, занимает 12 байт, а с 64-битным аргументом получится 16 (и ещё всякие префиксы могут быть же). Ну и как пишут на SO — 64-битный аргумент можно напрямую загружать в регистр.
                              Ответить
                              • Поглядел, нашёл единственную инструкцию с 64-битным непосредственным: MOV reg, imm (КОП B8H+номер регистра).
                                Ответить
                              • Получается, что если я искусственно соберу длинную инструкцию (нафигачив префиксов сегмента и REP), то программа выполнится на 8086/8088, но упадёт на более современных процессорах (начиная с 80286) даже в реальном режиме?
                                Ответить
                      • Замечание про атрибуты.

                        В 64-битном режиме у «gcc» есть два соглашения о вызове: «sysv_abi» и «ms_abi». При «sysv_abi» первые 6 аргументов передаются через регистры (RDI, RSI, RDX, RCX, R8, R9), при «ms_abi» только четыре первых аргумента передаются через регистры (RCX, RDX, R8, R9). Плавающий питух передаётся через питушиный стек (до четырёх аргументов в случае «ms_abi» и до восьми аргументов в случае «sysv_abi»). Стек в обоих случаях чистит вызываемая функция. Нельзя портить RBX и RBP (а для «ms_abi» ещё и RSI и RDI) и четыре последних нумерных.

                        В 32-битном режиме вариантов способа вызова гораздо больше. «Fastcall» у разных компиляторов реализован по-разному («gcc» первые два аргумента передаёт через ecx и edx).

                        Мне больше всего понравилось, как сделали в «Watcom C»: там с помощью #pragma можно создать своё собственное соглашение, указав, через какие именно регистры нужно передавать аргументы и кто будет чистить стек.
                        Ответить
                        • Нашёл ещё одно отличие ms_abi и sysv_abi в 64-битном режиме: перед вызовом функции большого количества аргументов в ms_abi указатель на вершину стека уменьшается, как будто первые четыре аргумента тоже запушили, хотя реально в этом участке стека лежит мусор.
                          Ответить
                          • https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2017
                            The x64 Application Binary Interface (ABI) uses a four-register fast-call calling convention by default.
                            Space is allocated on the call stack as a shadow store for callees to save those registers.
                            Ответить
                          • В sysv_abi есть ещё одно страшное отличие - red zone. Кусок стека, который можно невозбранно юзать без декремента rsp.

                            Эта хуйня порождает очень весёлые баги в ring 0 (если пишешь своё ядро, например)... Её, конечно, можно отключить. Но для этого про неё надо знать.
                            Ответить
                        • > вызываемая
                          Вызывающая.
                          Ответить
                          • Да, посмотрел дизасм, теперь уже понял, что вызывающая, как в cdecl.

                            То есть можно создавать функции с переменным количеством аргументов, а вот сделать принудительный пуш/поп посредством каламбура типов не получится.
                            Ответить
                            • Напиши себе ascii'шные push и pop. Они то гарантированно будут работать в отличие от игры с конвеншенами. Вот только оптимизатор потом обидится и достанет локальные переменные не оттуда...
                              Ответить
                            • При очистке вызываемым тоже можно замутить переменное количество аргументов. Передаёшь ему количество в регистре или крайнем аргументе да и всё.

                              В общем-то это даже надёжней т.к. вызываемый видит обе границы аргументов. А в cdecl - только нижнюю.
                              Ответить
                              • Как я тебе стек из вызываемой функции чистить буду? RET ожидает кококоличество очищаемых байтиков в непосредственном аргументе.

                                Варианты реализации:
                                1. Патчить код (изменять байтик, следующий за опкодом RET). Для этого нужно снимать защиту.

                                2. Свитч-кейс, в каждой ветке которого будет RET N с соответствующим значением N.

                                3. Извлекать из стека адрес возврата, чистить стек, после этого делать джамп (ну или push+ret) на сохранённый в регистре адрес возврата.
                                Ответить
                                • 3.

                                  А можно даже количество не передавать. Не дочитал все vararg'и - получай UB.
                                  Ответить
                                  • З.Ы. В общем-то вызов printf() с кривой строкой формата - это уже UB. Так что такая реализация имеет право на жизнь.
                                    Ответить
                                • > RET N
                                  Кстати, а ведь ret и ret N спекулятивно возвращаются к вызывающему независимо от N и состояния стека? Т.е. вариант с push + ret будет более правильным, нежели jmp...
                                  Ответить
                                • В жопу стек.
                                  Ответить
    • показать все, что скрытоvanished
      Ответить
      • Задачки, которые нахуй не пригодятся в работе, задавали?
        Ответить
      • Один парень устроился в «Яндекс» и умер. Курьер сервиса «Яндекс.Еда» умер прямо на рабочем месте, отработав 10 часов без перерыва. Молодому человеку исполнился 21 год. Компания «Яндекс» уже принесла извинения родственникам погибшего.

        Пишут, что сервис славится нереальными задачами типа доставить еду на расстояние 4 километра за 14 минут. Напоминаю, что курьер пеший. За опоздание штраф.
        Ответить
        • Ублюдки, пользуются тем, что в сране безработица, выжимают из людей последние силы.
          Ответить
          • На Х-ре уже появилась заметка с интересными комментариями:
            https://habr.com/ru/news/t/448722/

            Один комментатор уже вспомнил песню «16 тонн». Если кто-то не знает, это песня про США эпохи Великой депрессии:
            https://ru.wikipedia.org/wiki/Sixteen_Tons
            Ответить
          • показать все, что скрытоvanished
            Ответить
        • > 10 часов без перерыва
          Имхо, страшнее когда так работают таксисты или газелисты. Зомбаки за рулём.

          > нереально
          Если верить гуглу, то КМС должен пробежать за это время 5км. Так что вполне реально.
          Ответить
        • Какой анскилл )))
          Ответить
    • показать все, что скрытоvanished
      Ответить

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